Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib-blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@ tempfile = "3.0"
[[example]]
name = "full_consensus_integration"
path = "examples/full_consensus_integration.rs"

# Local DAO registry (Phase-0 contract module)
[dependencies.dao-registry]
path = "src/contracts/dao_registry"
31 changes: 31 additions & 0 deletions lib-blockchain/README_DAO_REGISTRY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# DAO Registry (SOV-P0-2.3)

This module implements the Phase-0 DAO Registry contract referenced by SOV-P0-2.3.

Overview
- Stores DAO metadata and addresses in a gas-efficient registry.
- Primary storage: `HashMap<[u8;32], DAOEntry>` (dao_id -> DAOEntry).
- Secondary index: `HashMap<[u8;32], [u8;32]>` (token_addr -> dao_id) for O(1) lookup by token address.

Core functions
- `register_dao(token_addr, class, metadata_hash, treasury, owner) -> DaoId`
- `get_dao(token_addr) -> DAOMetadata`
- `list_daos() -> Vec<DAOEntry>` (sorted by creation date; consider pagination for production)
- `update_metadata(dao_id, metadata_hash)` (owner-only)

Events
- `DaoRegistered` emitted when a DAO is registered
- `DaoUpdated` emitted when DAO metadata is updated

WASM
- `wasm.rs` exposes simple wrappers for WASM runtimes to call into the contract logic.

Testing
- Comprehensive unit tests are included in `src/contracts/dao_registry/tests.rs`.
- A convenience script `scripts/run_dao_registry_tests.sh` runs the tests locally.

Notes
- The registry uses deterministic `dao_id` generation via blake3 over token, owner, and timestamp.
- The contract is intentionally lightweight for Phase-0; pagination and indexed storage for large-scale registries are recommended for future improvements.

License: MIT
7 changes: 7 additions & 0 deletions lib-blockchain/scripts/run_dao_registry_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

# Run only DAO registry related tests
cargo test -p lib-blockchain --lib --test-threads=1 -- --nocapture --test-threads=1

echo "DAO registry tests completed"
127 changes: 127 additions & 0 deletions lib-blockchain/src/contracts/dao_registry/core.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crate::contracts::utils::id_generation;
use crate::contracts::integration::ContractEvent;
use crate::contracts::dao_registry::types::*;
use crate::integration::crypto_integration::PublicKey;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// DAO Registry contract
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DAORegistry {
/// Map from dao_id -> DAOEntry
pub registry: HashMap<[u8; 32], DAOEntry>,
/// Index from token_addr -> dao_id for fast lookup
pub token_index: HashMap<[u8; 32], [u8; 32]>,
}

impl DAORegistry {
/// Create empty registry
pub fn new() -> Self {
Self {
registry: HashMap::new(),
token_index: HashMap::new(),
}
}

/// Register a new DAO
/// Returns (dao_id, ContractEvent::DaoRegistered)
pub fn register_dao(
&mut self,
token_addr: [u8; 32],
class: String,
metadata_hash: Option<[u8; 32]>,
treasury: PublicKey,
owner: PublicKey,
) -> Result<([u8; 32], ContractEvent), String> {
if self.token_index.contains_key(&token_addr) {
return Err("DAO for token already registered".to_string());
}

// Generate unique DAO id using token_addr + owner + timestamp
let timestamp = crate::utils::time::current_timestamp();
let dao_id = id_generation::generate_contract_id(&[
&token_addr,
&owner.as_bytes(),
&timestamp.to_le_bytes(),
]);

let entry = DAOEntry {
dao_id,
token_addr,
class: class.clone(),
metadata_hash,
treasury: treasury.clone(),
owner: owner.clone(),
created_at: timestamp,
};

self.registry.insert(dao_id, entry);
self.token_index.insert(token_addr, dao_id);

let event = ContractEvent::DaoRegistered {
dao_id,
token_addr,
owner: owner.clone(),
treasury,
class,
metadata_hash,
};

Ok((dao_id, event))
}

/// Lookup a DAO by token address
pub fn get_dao(&self, token_addr: [u8; 32]) -> Result<DAOMetadata, String> {
match self.token_index.get(&token_addr) {
Some(dao_id) => match self.registry.get(dao_id) {
Some(entry) => Ok(DAOMetadata {
dao_id: entry.dao_id,
token_addr: entry.token_addr,
class: entry.class.clone(),
metadata_hash: entry.metadata_hash,
treasury: entry.treasury.clone(),
owner: entry.owner.clone(),
created_at: entry.created_at,
}),
None => Err("DAO entry not found for id".to_string()),
},
None => Err("DAO not found for token address".to_string()),
}
}

/// List all DAOs sorted by creation date ascending
/// Note: Pagination not implemented yet; consider adding (cursor, limit) later
pub fn list_daos(&self) -> Vec<DAOEntry> {
let mut all: Vec<DAOEntry> = self.registry.values().cloned().collect();
all.sort_by_key(|e| e.created_at);
all
}

/// Update metadata hash for a DAO. Only owner can update metadata.
/// Returns ContractEvent::DaoUpdated on success
pub fn update_metadata(
&mut self,
dao_id: [u8; 32],
updater: PublicKey,
metadata_hash: Option<[u8; 32]>,
) -> Result<ContractEvent, String> {
let entry = self
.registry
.get_mut(&dao_id)
.ok_or_else(|| "DAO not found".to_string())?;

if entry.owner != updater {
return Err("Only DAO owner can update metadata".to_string());
}

entry.metadata_hash = metadata_hash;

let event = ContractEvent::DaoUpdated {
dao_id,
updater,
metadata_hash,
};

Ok(event)
}
}
7 changes: 7 additions & 0 deletions lib-blockchain/src/contracts/dao_registry/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub mod core;
pub mod types;
#[cfg(feature = "contracts")]
pub mod wasm;

pub use core::DAORegistry;
pub use types::{DAOEntry, DAOMetadata};
Loading
Loading