datahaven/operator/README.md

196 lines
5.7 KiB
Markdown
Raw Permalink Normal View History

# DataHaven Operator (Substrate Node) 🫎
The DataHaven operator is a Substrate-based blockchain node that serves as an EigenLayer AVS operator. It combines Substrate's modular framework with EVM compatibility (via Frontier) and cross-chain capabilities (via Snowbridge).
## Overview
Built on the [polkadot-sdk-solochain-template](https://github.com/paritytech/polkadot-sdk-solochain-template), this node implements:
- **EVM Compatibility**: Full Ethereum compatibility via Frontier pallets
- **EigenLayer Integration**: Operator registration and management via AVS contracts
- **External Validators**: Dynamic validator set controlled by EigenLayer registry
- **Cross-chain Communication**: Token and message passing via Snowbridge
- **Rewards System**: Performance-based validator rewards from Ethereum
## Project Structure
```
operator/
├── node/ # Node implementation
│ ├── src/
│ │ ├── chain_spec.rs # Chain specification & genesis config
│ │ ├── cli.rs # CLI interface
│ │ ├── command.rs # Command handlers
│ │ ├── rpc.rs # RPC configuration
│ │ └── service.rs # Node service setup
├── pallets/ # Custom pallets
│ ├── external-validators/ # EigenLayer validator set management
│ ├── native-transfer/ # Cross-chain token transfers
│ └── rewards/ # Validator rewards distribution
├── runtime/ # Runtime configurations
│ ├── mainnet/ # Mainnet runtime
│ ├── stagenet/ # Stagenet runtime
│ └── testnet/ # Testnet runtime (with fast-runtime feature)
└── scripts/ # Utility scripts
└── run-benchmarks.sh # Runtime benchmarking automation
```
## Prerequisites
- [Rust](https://www.rust-lang.org/tools/install) (latest stable)
- [Substrate dependencies](https://docs.substrate.io/install/)
- [Zig](https://ziglang.org/) (macOS only, for cross-compilation)
## Building
### Development Build (Fast Runtime)
For local development with faster block times:
```bash
cargo build --release --features fast-runtime
```
This enables 3-second block times instead of the production 12-second blocks.
### Production Build
For production or stagenet deployments:
```bash
cargo build --release
```
### Running Tests
```bash
# Run all tests
cargo test
# Run tests for specific pallet
cargo test -p pallet-external-validators
# Run with output
cargo test -- --nocapture
```
### Code Quality
```bash
# Format code
cargo fmt
# Lint with clippy
cargo clippy --all-targets --all-features
```
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
## Benchmarking
DataHaven uses runtime benchmarking to generate accurate weight calculations for all pallets. The benchmarking process is automated using `frame-omni-bencher`.
### Requirements
- Latest Rust stable version
- `frame-omni-bencher`: Install with `cargo install frame-omni-bencher --profile=production`
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
### Running Benchmarks
Execute from the operator directory:
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
```bash
# Benchmark all pallets for testnet runtime (default)
./scripts/run-benchmarks.sh
# Benchmark specific runtime
./scripts/run-benchmarks.sh mainnet
# Custom steps and repetitions
./scripts/run-benchmarks.sh testnet 100 50
```
The script will:
1. Discover all available pallets
2. Build runtime WASM with `runtime-benchmarks` feature
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
3. Generate weight files in `runtime/{runtime}/src/weights/`
4. Provide summary of results
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
**Parameters**:
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
- `runtime`: Runtime to benchmark (testnet, stagenet, mainnet). Default: testnet
- `steps`: Number of steps. Default: 50
test: init benchmark infra (#110) * Add `run_benchmarks.sh` script to run runtime benchmarks * Sets up benchmark configs and directory structure to store weights (`operator/runtime/<RUNTIME>/weights`) * (naive) fixes to some benchmarks: * `pallet_datahaven_native_transfer`: * use a mock for `NativeTokenId` * look at the balance difference of the treasury instead of the total (this makes the benchmark agnostic to genesis setup) * `snowbridge_pallet_system` / `snowbridge_pallet_system_v2` use native token xcm location vs relay chain one. Add missing benchmark methods and update fixture with valid data. * `snowbridge_pallet_ethereum_client`: update fixtures with valid data * `snowbrige_pallet_inbound_queue_v2`: set EthereumGatewayAddress when initializing storage on benchmark and use a mock message processor ( as fixture has `CreateAsset` payload which is not supported in the `EigenLayerMessageProcessor`) * `snowbridge_pallet_outbound_queue_v2`: add missing `submit_delivery_receipt` benchmark which required a dedicated fixture (all copied from the upstream pallet) * `pallet_treasury`: Use an `ExistentialDeposit` of `1` on benchmark, else payout fails. * `pallet_transaction_payment`: Use a custom `WeightToFee` that makes the Fee small, else account in benchmark cannot pay for fees (It is funded a multiplier of `ExistentialDeposit` and is expected for that to be enough, but it's not in our particular setup). * comment out `pallet_identity` and `pallet_im_online` due to incompatibilities (to be addressed later) * Basic benchmark run to set `WeightInfo` from `weights` in configs (real run should be done later using target hardware) --------- Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com> Co-authored-by: Tobi Demeco <50408393+TDemeco@users.noreply.github.com> Co-authored-by: undercover-cactus <lola@moonsonglabs.com> Co-authored-by: TDemeco <tdemeco@itba.edu.ar>
2025-08-06 11:44:42 +00:00
- `repeat`: Number of repetitions. Default: 20
## Zombienet Testing
[Zombienet](https://github.com/paritytech/zombienet) provides local multi-validator network testing.
### Setup
1. Install Zombienet:
```bash
# Download binary from releases
# Or install via npm
npm install -g @zombienet/cli
```
2. Spawn local network with four validators:
```bash
zombienet -p native spawn test/config/zombie-datahaven-local.toml
```
This launches a local solochain with BABE consensus for testing validator coordination.
## Docker Image
Build local Docker image for testing:
```bash
cd ../test
bun build:docker:operator
```
This creates `datahavenxyz/datahaven:local` using optimized caching:
- [sccache](https://github.com/mozilla/sccache): Rust build caching
- [cargo-chef](https://lpalmieri.com/posts/fast-rust-docker-builds/): Dependency layer caching
- BuildKit cache mounts: External cache restoration
## Type Generation
After runtime changes, regenerate Polkadot-API TypeScript types:
```bash
cd ../test
bun generate:types # Production runtime
bun generate:types:fast # Fast runtime (development)
```
## Integration Testing
For full network integration tests with Ethereum, Snowbridge, and contracts:
```bash
cd ../test
bun cli launch # Interactive launcher
bun test:e2e # Run E2E test suite
```
See the [test directory](../test/README.md) for comprehensive testing documentation.
## Custom Pallets
### External Validators
Manages the dynamic validator set based on EigenLayer operator registry. Syncs validator changes from Ethereum to the Substrate consensus layer.
**Location**: `pallets/external-validators/`
### Native Transfer
Handles cross-chain token transfers between Ethereum and DataHaven via Snowbridge messaging.
**Location**: `pallets/native-transfer/`
### Rewards
Distributes performance-based rewards to validators, processing reward messages from the Ethereum `RewardsRegistry` contract.
**Location**: `pallets/rewards/`
Each pallet includes its own tests and benchmarks. See pallet-specific README files for details.