## Summary Building on #304, this PR implements two complementary mechanisms to improve validator incentives and network performance: 1. **Performance-Based Validator Rewards** (session-level) 2. **Inflation Scaling** (era-level) ## Reward Model Comparison ### Old Model (main branch) vs New Model | Metric | Old Model (20 pts/block) | New Model (320 pts/block pool) | |--------|--------------------------|--------------------------------| | **Per Block** | Author: 20 pts, Others: 0 | Author: ~196 pts, Others: ~4 pts each | | **Formula** | Direct author reward | 60% authoring + 30% liveness + 10% base | | **Per Session** (600 blocks, 32 validators) | 12,000 total pts | 192,000 total pts | | **Per Validator/Session** (uniform) | ~375 pts | ~6,000 pts | | **Per Validator/Era** (6 sessions) | ~2,250 pts | ~36,000 pts | | **Offline Validator** | 0 pts | ~600 pts/session (base only) | | **Over-performer (150% blocks)** | 150% of fair share | Up to 130% reward (soft cap) | ### Key Differences - **Pool-based**: New model adds 320 points to a shared pool per block, distributed via formula - **Liveness rewarded**: 30% of rewards go to validators who are online (heartbeat OR block authorship) - **Base guarantee**: 10% ensures all active validators receive minimum rewards - **Soft cap**: Prevents extreme over-performance rewards (max 150% of fair share credited) ## Performance-Based Validator Rewards Introduces a **60/30/10 reward formula** that rewards validators based on their contribution during each session: - **60%** based on block production (with soft cap allowing up to 150% of fair share) - **30%** based on liveness (ImOnline heartbeat OR block authorship) - **10%** guaranteed base reward for all active validators ### Key Features - Tracks individual validator block authorship per session - Calculates fair share dynamically: `fair_share = total_blocks / total_validator_count` - Fair share uses **total** validator count (including whitelisted) since all validators occupy block slots - **Soft cap**: Over-performers can earn credit up to 150% of their fair share (configurable via `OperatorRewardsFairShareCap` at 50%) - With 60% BlockAuthoringWeight, this gives over-performers up to **30% bonus reward** - **BasePointsPerBlock**: Defines points added to pool per block produced (default: 320) - Integrates with SessionManager for automatic point awards at session end - Excludes whitelisted validators from rewards (but includes them in fair share calculation) - Slashing check disabled but hook retained for future use - Points accumulate across sessions within an era ### Dynamic Parameters (Governance-Adjustable) - `OperatorRewardsBlockAuthoringWeight`: Weight for block authoring (default: 60%) - `OperatorRewardsLivenessWeight`: Weight for liveness (default: 30%) - `OperatorRewardsFairShareCap`: Soft cap percentage above fair share (default: 50%) ## Inflation Scaling Implements **dynamic inflation scaling** that adjusts total inflation based on network block production: - **Minimum**: 20% of base inflation (network halt protection) - **Maximum**: 100% of base inflation (caps at expected blocks) - **Linear scaling** between minimum and maximum based on performance ### Scaling Examples - 0% blocks produced → 20% inflation (safety floor) - 50% blocks produced → 60% inflation - 100% blocks produced → 100% inflation - >100% blocks produced → capped at 100% ### Configuration - **ExpectedBlocksPerEra**: Computed as `SessionsPerEra × EpochDurationInBlocks` - **MinInflationPercent**: 20% - **MaxInflationPercent**: 100% ## Combined Effect These mechanisms work together to create a comprehensive incentive structure: 1. **Session rewards** encourage individual validator performance and uptime 2. **Era inflation scaling** incentivizes collective network health 3. **Minimum inflation floor** protects against network halt 4. **Soft cap** allows over-performers to earn up to 30% bonus while preventing extreme centralization ## Implementation Details ### Pallet Changes - Add `BlocksAuthoredInSession` storage for per-validator tracking - Add `BlocksProducedInEra` storage for total network tracking (cleaned up with HistoryDepth) - Add `note_block_author()` function called on block production - Add `award_session_performance_points()` function with configurable 60/30/10 formula - Add `calculate_scaled_inflation()` function for era-level scaling - Update `on_era_end()` to use scaled inflation - Integrate with SessionManager via wrapper types - Defensive weight validation: proportionally scales if sum > 100% ### Configuration Parameters - `ValidatorSet`: Provides active validator list - `LivenessCheck`: Uses `ImOnline::is_online()` (heartbeat OR block authorship) - `SlashingCheck`: Integration with slashing pallet (currently disabled) - `BasePointsPerBlock`: Points added to pool per block (default: 320) - `BlockAuthoringWeight`: Dynamic parameter (60%) - `LivenessWeight`: Dynamic parameter (30%) - `FairShareCap`: Dynamic parameter (50%) - `ExpectedBlocksPerEra`: Computed from session/epoch config - `MinInflationPercent`: 20% - `MaxInflationPercent`: 100% ### Runtime Updates - Full configuration added to mainnet, testnet, and stagenet runtimes - Dynamic parameters added to `runtime_params.rs` for governance control - Uses `prod_or_fast!()` macro for environment-specific parameters - `ValidatorIsOnline` uses `ImOnline::is_online()` for accurate liveness detection ## Testing - **76 tests passing** ✅ - Comprehensive coverage of both mechanisms ### Test Coverage - Inflation scaling at 0%, 25%, 50%, 75%, 100%, >100% blocks - Session performance with 60/30/10 formula - Fair share calculations with soft cap (150%) - Whitelisted validator exclusion from rewards (with correct fair share using total count) - Total points verification (sum of individual = total) - Whitelisted over-producer scenarios - Overflow protection (large block counts, near-u32::MAX) - End-to-end session to era flow - MockLivenessCheck mirrors ImOnline behavior (block authorship = online) - Multiple eras with different performance levels - Edge cases (zero participation, single validator, large numbers) - BlocksProducedInEra cleanup on era start ## ⚠️ Breaking Changes ⚠️ ### Reward Distribution Previously, rewards were distributed equally among all validators regardless of their contribution. Now: - **Performance-based**: Validators earn rewards proportional to their block production (60%), liveness (30%), and a guaranteed base (10%) - **Pool-based**: `BasePointsPerBlock` defines points added to pool per block (320), distributed via formula - **Fair share uses total validators**: Ensures non-whitelisted aren't penalized for whitelisted validators' block slots - **Soft cap**: Block production rewards allow up to 150% of fair share (50% cap = 30% bonus with 60% weight) - **Slashing check disabled**: Hook retained for future use, but currently not applied ### Inflation Mechanism Previously, the full calculated inflation was minted each era. Now: - **Scaled by performance**: Total inflation scales between 20%-100% based on actual blocks produced vs expected - **Safety floor**: Even with zero blocks, 20% of inflation is still minted to prevent complete halt - **Network incentive**: Collective block production directly impacts total rewards available ### Pallet Configuration The `pallet-external-validators-rewards` Config now requires additional types: - `BlockAuthoringWeight`, `LivenessWeight`, `FairShareCap` for reward formula - `ValidatorSet`, `LivenessCheck`, `SlashingCheck` for validator tracking - `ExpectedBlocksPerEra`, `MinInflationPercent`, `MaxInflationPercent` for inflation scaling --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| benchmarking | ||
| node | ||
| pallets | ||
| precompiles | ||
| primitives | ||
| runtime | ||
| scripts | ||
| .dockerignore | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| DOCKER-COMPOSE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| rust-toolchain.toml | ||
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, 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 (latest stable)
- Substrate dependencies
- Zig (macOS only, for cross-compilation)
Building
Development Build (Fast Runtime)
For local development with shorter epochs and eras:
cargo build --release --features fast-runtime
This switches runtime parameters to the fast variants (1-minute epochs, 3 sessions per era) while the block time remains 6 seconds.
Production Build
For production or stagenet deployments:
cargo build --release
Running Tests
# Run all tests
cargo test
# Run tests for specific pallet
cargo test -p pallet-external-validators
# Run with output
cargo test -- --nocapture
Code Quality
# Format code
cargo fmt
# Lint with clippy
cargo clippy --all-targets --all-features
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 withcargo install frame-omni-bencher --profile=production
Running Benchmarks
Execute from the operator directory:
# 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:
- Discover all available pallets
- Build runtime WASM with
runtime-benchmarksfeature - Generate weight files in
runtime/{runtime}/src/weights/ - Provide summary of results
Parameters:
runtime: Runtime to benchmark (testnet, stagenet, mainnet). Default: testnetsteps: Number of steps. Default: 50repeat: Number of repetitions. Default: 20
Zombienet Testing
Zombienet provides local multi-validator network testing.
Setup
-
Install Zombienet:
# Download binary from releases # Or install via npm npm install -g @zombienet/cli -
Spawn local network with four validators:
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:
cd ../test
bun build:docker:operator
This creates datahavenxyz/datahaven:local using optimized caching:
- sccache: Rust build caching
- cargo-chef: Dependency layer caching
- BuildKit cache mounts: External cache restoration
Type Generation
After runtime changes, regenerate Polkadot-API TypeScript types:
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:
cd ../test
bun cli launch # Interactive launcher
bun test:e2e # Run E2E test suite
See the test directory 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.