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
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ members = [
"pallets/fee-handler",
"pallets/origins",
"pallets/pool-withdrawal-fix",
"pallets/dac-registry",
"runtime/cere",
"runtime/cere-dev",
"contracts/customer-deposit",
Expand Down Expand Up @@ -196,6 +197,7 @@ pallet-erc721 = { path = "pallets/erc721", default-features = false }
pallet-origins = { path = "pallets/origins", default-features = false }
pallet-pool-withdrawal-fix = { path = "pallets/pool-withdrawal-fix", default-features = false }
pallet-fee-handler = { path = "pallets/fee-handler", default-features = false }
pallet-dac-registry = { path = "pallets/dac-registry", default-features = false }

# Cere External Dependencies
ddc-primitives = { git = "https://github.com/Cerebellum-Network/ddc-primitives.git", branch = "dev", default-features = false }
Expand Down
44 changes: 44 additions & 0 deletions pallets/dac-registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[package]
name = "pallet-dac-registry"
version = "0.1.0"
authors = ["Cere Network"]
edition = "2021"
homepage = "https://cere.network"
license = "Apache-2.0"
repository = "https://github.com/cere-network/cere-blockchain"
description = "DAC Registry Pallet for managing WASM modules"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec.workspace = true
scale-info.workspace = true
serde.workspace = true

# Substrate
frame-benchmarking = { workspace = true, optional = true }
frame-support.workspace = true
frame-system.workspace = true
sp-api.workspace = true
sp-core.workspace = true
sp-io.workspace = true
sp-runtime.workspace = true
sp-std.workspace = true

[features]
default = ["std"]
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
try-runtime = ["frame-support/try-runtime"]
std = [
"codec/std",
"scale-info/std",
"serde/std",
"frame-support/std",
"frame-system/std",
"sp-api/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
140 changes: 140 additions & 0 deletions pallets/dac-registry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# DAC Registry Pallet

The **DAC Registry Pallet** provides an on-chain system to register and manage versions of DAC (Data Aggregation Component) WASM modules used by DDC clusters.

## Overview

This pallet enables governance to:
- Add new DAC versions without runtime upgrades
- Manage version lifecycles (activation, updates, deprecations)
- Maintain transparent, auditable history of all DAC versions

Instead of embedding DAC logic directly into the runtime, the pallet stores DAC modules (WASM binaries) on-chain, making DAC logic modular, governance-controlled, and easily upgradeable.

## Key Features

- **Governance Controlled:** Only governance can register, update, or deprecate DAC versions
- **Single-Step Upload:** Full WASM file uploaded in one atomic transaction
- **Versioned Metadata:** Tracks API version, semantic version, and activation block
- **Dynamic Retrieval:** DDC and Inspector components fetch versions by hash
- **Auditable Lifecycle:** Every change and event stored on-chain
- **No Maintenance Needed:** No chunking, CLI, or cleanup process required

## Storage

| Storage Item | Type | Description |
| --- | --- | --- |
| `CodeByHash` | `Map<CodeHash, CodeMeta>` | Metadata for each DAC version |
| `WasmCode` | `Map<CodeHash, Bytes>` | Full DAC WASM binary |
| `Deregistered` | `Map<CodeHash, bool>` | Marks deprecated or disabled DAC versions |

## Metadata Structure

Each DAC version includes:
- **code_hash:** Unique blake2_256 hash of the WASM
- **api_version:** Major/minor compatibility identifier
- **semver:** Semantic version (e.g., `1.5.0`)
- **allowed_from:** Block number when the version becomes active
- **length:** Size of the WASM binary (bytes)

## Extrinsics

### `register_code`

Registers a new DAC version on-chain.

**Parameters:**
- `code`: The WASM binary code
- `api_version`: API version (major.minor)
- `semver`: Semantic version (major.minor.patch)
- `allowed_from`: Block number when this version becomes active

**Origin:** Governance only

### `update_meta`

Updates metadata for an existing DAC version.

**Parameters:**
- `code_hash`: Hash of the code to update
- `api_version`: New API version (major.minor)
- `semver`: New semantic version (major.minor.patch)
- `allowed_from`: New activation block

**Origin:** Governance only

### `deregister_code`

Marks an existing DAC version as inactive or deprecated.

**Parameters:**
- `code_hash`: Hash of the code to deregister

**Origin:** Governance only

## Events

| Event | Description |
| --- | --- |
| `CodeRegistered` | A new DAC version was successfully registered |
| `CodeMetaUpdated` | Metadata of a DAC version was updated |
| `CodeDeregistered` | DAC version marked as inactive or deprecated |

## Public Interface

The pallet provides several public functions for querying the registry:

- `get_code(code_hash)`: Get the WASM code for a given code hash
- `get_metadata(code_hash)`: Get the metadata for a given code hash
- `is_code_active(code_hash)`: Check if a code hash is active (exists and not deregistered)
- `is_code_ready(code_hash)`: Check if a code is ready for use (active and past activation block)

## Configuration

The pallet requires the following configuration:

- `GovernanceOrigin`: Origin that can perform registry operations
- `MaxCodeSize`: Maximum size of a WASM code in bytes
- `WeightInfo`: Weight functions for benchmarking

## Usage Example

```rust
// Register a new DAC version
DacRegistry::register_code(
RuntimeOrigin::root(),
wasm_code,
(1, 0), // API version
(1, 0, 0), // Semantic version
100, // Activation block
)?;

// Check if code is ready
let code_hash = compute_code_hash(&wasm_code);
if DacRegistry::is_code_ready(code_hash) {
let code = DacRegistry::get_code(code_hash).unwrap();
// Execute the DAC code
}
```

## Testing

The pallet includes comprehensive tests covering:
- Successful registration and retrieval
- Input validation
- Error conditions
- Event emission
- Public interface functions
- Governance origin enforcement

Run tests with:
```bash
cargo test -p pallet-dac-registry
```

## Benchmarking

The pallet includes weight functions for benchmarking. Run benchmarks with:
```bash
cargo run --release --features runtime-benchmarks -- benchmark pallet --pallet=pallet_dac_registry --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096
```
113 changes: 113 additions & 0 deletions pallets/dac-registry/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! Benchmarking for the DAC Registry pallet.

use super::*;
use frame_benchmarking::benchmarks;
use frame_system::RawOrigin;
use sp_core::Get;
use sp_core::H256;
use sp_runtime::traits::Hash;
use sp_std::iter;
use sp_std::vec;
use sp_std::vec::Vec;

/// Helper function to create test WASM code of a given size
fn create_test_wasm_code(size: u32) -> Vec<u8> {
// Create a simple WASM module with the specified size
let mut code = vec![
0x00, 0x61, 0x73, 0x6d, // WASM magic number
0x01, 0x00, 0x00, 0x00, // Version 1
];

// Add padding to reach the desired size
let padding_size = size.saturating_sub(code.len() as u32);
code.extend(iter::repeat_n(0x00, padding_size as usize));

code
}

benchmarks! {
register_code {
let code_size = T::MaxCodeSize::get();
let wasm_code = create_test_wasm_code(code_size);
let api_version = (1, 0);
let semver = (1, 0, 0);
let allowed_from = 0u32.into();
}: _(RawOrigin::Root, wasm_code, api_version, semver, allowed_from)

update_meta {
// First register some code
let code_size = 1024; // Use a smaller size for setup
let wasm_code = create_test_wasm_code(code_size);
let api_version = (1, 0);
let semver = (1, 0, 0);
let allowed_from = 0u32.into();
let code_hash = H256::from_slice(<T as frame_system::Config>::Hashing::hash(&wasm_code).as_ref());

// Register the code first
Pallet::<T>::register_code(RawOrigin::Root.into(), wasm_code, api_version, semver, allowed_from)?;

// Create new metadata for update
let new_api_version = (2, 0);
let new_semver = (1, 1, 0);
let new_allowed_from = 100u32.into();
}: _(RawOrigin::Root, code_hash, new_api_version, new_semver, new_allowed_from)

deregister_code {
// First register some code
let code_size = 1024; // Use a smaller size for setup
let wasm_code = create_test_wasm_code(code_size);
let api_version = (1, 0);
let semver = (1, 0, 0);
let allowed_from = 0u32.into();
let code_hash = H256::from_slice(<T as frame_system::Config>::Hashing::hash(&wasm_code).as_ref());

// Register the code first
Pallet::<T>::register_code(RawOrigin::Root.into(), wasm_code, api_version, semver, allowed_from)?;
}: _(RawOrigin::Root, code_hash)

// Benchmark for query operations
is_code_active {
// First register some code
let code_size = 1024;
let wasm_code = create_test_wasm_code(code_size);
let api_version = (1, 0);
let semver = (1, 0, 0);
let allowed_from = 0u32.into();
let code_hash = H256::from_slice(<T as frame_system::Config>::Hashing::hash(&wasm_code).as_ref());

// Register the code first
Pallet::<T>::register_code(RawOrigin::Root.into(), wasm_code, api_version, semver, allowed_from)?;
}: {
let _ = Pallet::<T>::is_code_active(code_hash);
}

is_code_ready {
// First register some code
let code_size = 1024;
let wasm_code = create_test_wasm_code(code_size);
let api_version = (1, 0);
let semver = (1, 0, 0);
let allowed_from = 0u32.into();
let code_hash = H256::from_slice(<T as frame_system::Config>::Hashing::hash(&wasm_code).as_ref());

// Register the code first
Pallet::<T>::register_code(RawOrigin::Root.into(), wasm_code, api_version, semver, allowed_from)?;
}: {
let _ = Pallet::<T>::is_code_ready(code_hash);
}

get_code {
// First register some code
let code_size = 1024;
let wasm_code = create_test_wasm_code(code_size);
let api_version = (1, 0);
let semver = (1, 0, 0);
let allowed_from = 0u32.into();
let code_hash = H256::from_slice(<T as frame_system::Config>::Hashing::hash(&wasm_code).as_ref());

// Register the code first
Pallet::<T>::register_code(RawOrigin::Root.into(), wasm_code, api_version, semver, allowed_from)?;
}: {
let _ = Pallet::<T>::get_code(code_hash);
}
}
Loading
Loading