Commit graph

206 commits

Author SHA1 Message Date
Facundo Farall
15e536780d
build: ⬆️ Upgrade to SH release v0.3.1 (#393)
This PR upgrades the StorageHub version to
[v0.3.1](https://github.com/Moonsong-Labs/storage-hub/releases/tag/v0.3.1).
The changes applied are the ones suggested in the corresponding release
notes, which in short are:
- Adding the `get_number_of_active_users_of_provider` runtime API to the
`PaymentStreams` pallet runtime APIs.
- Supporting `--max-open-forests` CLI param (has defaults).
- Supporting Prometheus telemetry.

IMPORTANT: This upgrade requires a Runtime upgrade as well as a Client
upgrade.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2026-01-14 13:10:53 +01:00
Steve Degosserie
9de44b84fe
revert: ♻ Revert Rust toolchain to 1.88.0 (revert PR #362) (#392)
Revert #362, back to Rust toolchain v1.88.0, as the newer version causes
an issue in the runtime release publishing flow.
2026-01-14 08:37:27 +01:00
Gonza Montiel
66843fde0d
fix: update weights for RT910 (#384)
- [x] Updated weights for testnet
- [x] Updated weights for stagenet
- [x] Updated weights for mainnet

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
2026-01-12 19:50:25 +01:00
Ahmad Kaouk
e93bbb4832
test: fix rewards e2e test (#385) 2026-01-12 16:55:46 +01:00
Steve Degosserie
916261ee8b
feat: Bump client version to v0.13.0 (#388) 2026-01-12 14:40:01 +01:00
Ahmad Kaouk
9be1acc97e
refactor: cleanup old rewards model (#383)
## Summary

This PR removes the old merkle root-based rewards model and completes
the migration to EigenLayer Rewards V2 distribution. The old model
required operators to claim rewards by providing merkle proofs, while
the new model uses `submitRewards` to send rewards directly to
EigenLayer's `RewardsCoordinator`.

### Key Changes

- **Smart Contracts**: Removed `RewardsRegistry`,
`RewardsRegistryStorage`, `IRewardsRegistry`, and `SortedMerkleProof`
contracts along with all merkle claim functions from
`ServiceManagerBase`
- **Substrate Pallets**: Removed merkle proof generation from
`external-validators-rewards` pallet and deleted the entire
`runtime-api` crate (no longer needed)
- **Test Framework**: Removed all RewardsRegistry-related code from
deployment scripts, CLI handlers, and TypeScript bindings
- **Runtimes**: Cleaned up all three runtimes (testnet, stagenet,
mainnet) to remove runtime API implementations and unused imports

### Files Removed

**Contracts:**
- `contracts/src/middleware/RewardsRegistry.sol`
- `contracts/src/middleware/RewardsRegistryStorage.sol`
- `contracts/src/interfaces/IRewardsRegistry.sol`
- `contracts/src/libraries/SortedMerkleProof.sol`
- `contracts/test/RewardsRegistry.t.sol`
- `contracts/test/ServiceManagerRewardsRegistry.t.sol`

**Substrate:**
- `operator/pallets/external-validators-rewards/runtime-api/` (entire
crate)

**Test Framework:**
- `test/suites/rewards-message.test.ts`

### Files Modified

**Contracts:**
- `ServiceManagerBase.sol` - Removed merkle claim functions
- `ServiceManagerBaseStorage.sol` - Removed
`operatorSetToRewardsRegistry` mapping
- `IServiceManager.sol` - Removed interface members

**Substrate:**
- `external-validators-rewards` pallet - Removed merkle proof
generation, simplified `EraRewardsUtils` struct
- All runtime configs - Removed `ExternalValidatorsRewardsApi`
implementations

**Test Framework:**
- Updated deployment scripts, CLI handlers, relayer configs, and
TypeScript bindings

### Stats

```
50 files changed, 966 insertions(+), 4453 deletions(-)
```

## Test plan

- [x] All Rust tests pass (`cargo test`)
- [x] All contract tests pass (`forge test`)
- [x] TypeScript type checking passes (`bun typecheck`)
- [x] Contracts build successfully (`forge build`)
- [x] Operator builds successfully (`cargo build --release --features
fast-runtime`)
- [ ] E2E tests pass (`bun test:e2e`)
2026-01-09 15:25:49 +01:00
Facundo Farall
21fa0af8df
feat: ⬆️ Upgrade to StorageHub version 0.3.0 (#381)
Upgrade to StorageHub version 0.3.0. This is a minor release, including
breaking changes.

## ⚠️ Breaking Changes ⚠️
The changes applied in this PR are according to the suggested changes in
StorageHub's [v0.3.0
release](https://github.com/Moonsong-Labs/storage-hub/releases/tag/v0.3.0)

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2026-01-07 20:34:03 +01:00
Steve Degosserie
9bca5c467b
fix: Use block authorship as liveness indicator for validator rewards (#367)
## Summary

Use block authorship as direct proof of liveness for the 30% liveness
component of validator rewards. Validators who author at least one block
in a session are considered online and receive the full liveness bonus.

## Problem

The rewards pallet was checking validator liveness via ImOnline
**after** the session had rotated - at which point ImOnline had already
cleared its `AuthoredBlocks` storage. This caused all validators to
appear offline, resulting in only ~70% of expected rewards being
allocated (missing the 30% liveness bonus).

## Solution

Use **block authorship as the proxy for liveness**:

- A validator who authored at least one block is definitively online
- Liveness is determined directly in `award_session_performance_points`
via `blocks_authored > 0`
- No dependency on external liveness checks (ImOnline)

### Rewards Formula

- **60%** Block authorship (proportional to blocks produced)
- **30%** Liveness (full bonus if authored ≥1 block, zero otherwise)
- **10%** Base reward (for being in the validator set)

### Files Changed

- `pallets/external-validators-rewards/src/lib.rs` - Core logic changes
- `pallets/external-validators-rewards/src/mock.rs` - Test mock updates
- `pallets/external-validators-rewards/src/tests.rs` - Test updates
- `runtime/{mainnet,testnet,stagenet}/src/configs/mod.rs` - Config
updates

## Testing

- All 76 pallet tests pass
- Local testing should show correct points per session (e.g., 3200
points for 2 validators with 10 blocks)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2026-01-07 13:41:40 +00:00
Steve Degosserie
3de271f2c5
feat: Bump runtime version to 910 (#382) 2026-01-07 10:29:27 +01:00
Ahmad Kaouk
268427be8d
feat: Implement EigenLayer Rewards V2 distribution (#351)
### Summary

This PR implements the EigenLayer Rewards Distribution V2 model for
DataHaven, replacing the previous merkle-root-based rewards registry
approach with EigenLayer's native `OperatorDirectedRewardsSubmission`
API. This enables direct integration with EigenLayer's
RewardsCoordinator for validator rewards distribution.

### Motivation

EigenLayer's V2 rewards model provides several advantages:
- **Direct integration**: Uses EigenLayer's native
`createOperatorDirectedOperatorSetRewardsSubmission` API
- **Per-operator rewards**: Distributes rewards proportionally to
individual operators based on their earned points
- **Simplified architecture**: Removes the need for a separate
RewardsRegistry contract
- **Better UX**: Operators receive rewards directly through EigenLayer's
established claiming mechanism


### Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                       DataHaven Substrate                       │
├─────────────────────────────────────────────────────────────────┤
│  Era End                                                        │
│    │                                                            │
│    ▼                                                            │
│  external-validators-rewards pallet                             │
│    │ generate_era_rewards_utils()                               │
│    │   • Calculate individual points per validator              │
│    │   • Compute total inflation amount                         │
│    │                                                            │
│    ▼                                                            │
│  RewardsSubmissionAdapter (runtime_common)                      │
│    │ build() → points_to_rewards() → encode_rewards_calldata()  │
│    │                                                            │
│    ▼                                                            │
│  Snowbridge Outbound Queue                                      │
│    │ CallContract(ServiceManager.submitRewards(...))            │
└────│────────────────────────────────────────────────────────────┘
     │
     ▼ Cross-chain message via Snowbridge
┌─────────────────────────────────────────────────────────────────┐
│                         Ethereum                                │
├─────────────────────────────────────────────────────────────────┤
│  DataHavenServiceManager                                        │
│    │ submitRewards(OperatorDirectedRewardsSubmission)           │
│    │   • Approve wHAVE tokens to RewardsCoordinator             │
│    │                                                            │
│    ▼                                                            │
│  EigenLayer RewardsCoordinator                                  │
│    │ createOperatorDirectedOperatorSetRewardsSubmission()       │
│    │                                                            │
│    ▼                                                            │
│  Operators claim rewards via EigenLayer                         │
└─────────────────────────────────────────────────────────────────┘
```

### Changes Overview

#### Smart Contracts (`contracts/`)

**DataHavenServiceManager.sol**
- Added `submitRewards(OperatorDirectedRewardsSubmission)` function to
submit rewards to EigenLayer's RewardsCoordinator
- Implements `SafeERC20` for secure token approvals
- Uses `onlyRewardsInitiator` modifier for access control (Snowbridge
Agent)
- Emits `RewardsSubmitted` and `RewardsInitiatorSet` events for tracking

**IDataHavenServiceManager.sol**
- Added `submitRewards()` interface for EigenLayer rewards submission
- Added `setRewardsInitiator()` interface for configuring the authorized
caller
- Added new events: `RewardsSubmitted`, `RewardsInitiatorSet`

**New Test: RewardsSubmitter.t.sol**
- Comprehensive test suite covering:
  - Access control (only rewards initiator can submit)
  - Single and multiple operator rewards
  - Multiple consecutive submissions
  - Custom descriptions and different tokens

#### Substrate Runtime (`operator/`)

**New: `runtime/common/src/rewards_adapter.rs` (934 lines)**

A generic, configurable adapter for building EigenLayer rewards
messages:

- **`RewardsSubmissionConfig` trait**: Runtime-agnostic configuration
interface
  - `OutboundQueue`: Snowbridge outbound queue type
  - `rewards_duration()`: Reward period duration (typically 86400s)
  - `whave_token_address()`: wHAVE ERC20 token on Ethereum
  - `service_manager_address()`: ServiceManager contract address
  - `rewards_agent_origin()`: Snowbridge agent origin

- **`RewardsSubmissionAdapter<C>`**: Generic implementation of
`SendMessage` trait

- **`points_to_rewards()`**: Converts validator points to token amounts
  - Proportional distribution based on total points
  - Returns remainder (dust) from integer division
  - Arithmetic overflow/underflow protection

- **`encode_rewards_calldata()`**: ABI-encodes the `submitRewards` call
  - Uses `alloy-core` for type-safe Solidity ABI encoding
  - Validates `uint96` multiplier bounds

- **Comprehensive test suite** covering:
  - Basic and edge-case reward calculations
  - Remainder/dust handling
  - Overflow/underflow protection
  - ABI encoding round-trip verification
  - Message building with various configurations

**Modified: `pallets/external-validators-rewards/`**

- **`types.rs`**: Extended `EraRewardsUtils` struct:
  ```rust
  pub struct EraRewardsUtils {
      pub era_index: u32,                    // NEW
      pub rewards_merkle_root: H256,
      pub leaves: Vec<H256>,
      pub leaf_index: Option<u64>,
      pub total_points: u128,
      pub individual_points: Vec<(H160, u32)>, // NEW
      pub inflation_amount: u128,             // NEW
      pub era_start_timestamp: u32       // NEW
  }
  ```

- **`lib.rs`**: Updated `generate_era_rewards_utils()`:
  - Now accepts `inflation_amount` parameter
  - Extracts `individual_points` as `(H160, u32)` tuples for EigenLayer
- Returns `None` when `total_points` is zero (prevents inflation with no
distribution)

- **`mock.rs`**: Updated test mock to use `H160` as `AccountId`
(matching DataHaven's EVM-compatible account model)

**Modified: Runtime Configurations**

All three runtimes (mainnet, stagenet, testnet) updated:

1. **New runtime parameters** (`runtime_params.rs`):
- `ServiceManagerAddress`: DataHaven ServiceManager contract on Ethereum
   - `WHAVETokenAddress`: wHAVE ERC20 token address
   - `RewardsGenesisTimestamp`: EigenLayer-aligned genesis timestamp
   - `RewardsDuration`: Rewards period (default: 86400 = 1 day)

2. **Refactored `RewardsSendAdapter`**:
- Replaced inline implementation with `RewardsSubmissionAdapter<Config>`
   - Each runtime implements `RewardsSubmissionConfig` trait
   - Cleaner, DRY configuration

## ⚠️ Breaking Changes ⚠️

- **Runtime Parameters**: New parameters must be configured via
governance before rewards submission will work:
  - `ServiceManagerAddress` (replaces `RewardsRegistryAddress`)
  - `WHAVETokenAddress`
  - `RewardsGenesisTimestamp`
  
- **Contract Interface**: `submitRewards()` now accepts a full
`OperatorDirectedRewardsSubmission` struct instead of a merkle root

---------

Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
2026-01-06 23:53:03 +00:00
undercover-cactus
a8d811fde8
feat: add feature to build binary with postgres bundled (#346)
## Summary

Re-add the static build feature option to bundle postgres dependency
into the binary. It simplify the installation because now to run the
node the operator doesn't need to have postgres dependencies installed
on its system.

## What changed ?

* Added a `static` feature that can be activated to add the extra
dependencies during the build.
* A task that run every time a dependency has been modified so we can
make sure the build with the feature is still working correctly. (we are
assuming simple change in the code won't have an impact on it because
postgres is being used in diesel which is not a direct dependecy to
datahaven).
2026-01-06 13:13:25 +00:00
Steve Degosserie
ae03649a4f
feat: Bump client version to v0.12.0 (#378) 2026-01-05 14:38:42 +01:00
Steve Degosserie
d22d0d5dcb
feat: Bump client version to v0.11.0 (#376) 2026-01-03 23:03:39 +01:00
Facundo Farall
e01237fe31
build: ⬆️ Upgrade SH version to 0.2.9 (#374)
Upgrades to StorageHub version 0.2.9. This is a patch release, no
breaking changes, which fixes a bug where a misuse of a runtime API was
causing the MSP to lag behind in block processing.
2026-01-02 15:25:16 +01:00
Facundo Farall
5310ce63a5
feat: 🔊 Add CPUs log at startup (#372)
Adds a log at the startup of the node to show the number of logical CPUs
available to use for parallelism.
2026-01-02 12:52:23 +01:00
Steve Degosserie
c401278ea9
feat: Bump client version to v0.10.0 (#371) 2025-12-29 17:50:32 +01:00
Facundo Farall
908137510c
build: ⬆️ Upgrade to StorageHub version 0.2.8 (#370)
Upgrade to StorageHub release version v0.2.8. This is just a patch release with some non-breaking bug fixes and improvements to logs.
2025-12-29 17:01:10 +01:00
Steve Degosserie
3d895f95c9
feat: 📈 Implement linear (non-compounding) inflation model (#363)
## Summary

This PR replaces the percentage-based compounding inflation model with a
**linear (non-compounding) inflation model** where a fixed amount of
tokens is minted annually, regardless of current total supply.

### Key Changes

- **`ExternalRewardsEraInflationProvider`** now calculates per-era
inflation from a fixed annual amount instead of a percentage of current
total issuance
- New **`InflationAnnualAmount`** runtime parameter using the formula:
`5_000_000 * HAVE * SUPPLY_FACTOR`
- Consistent configuration across all runtimes using `SUPPLY_FACTOR`

### Inflation Configuration

| Runtime | SUPPLY_FACTOR | Genesis Supply | Annual Inflation | Per-Era
Inflation |

|---------|---------------|----------------|------------------|-------------------|
| **Mainnet** | 100 | 10B HAVE | 500M HAVE (5%) | ~342,231 HAVE |
| **Stagenet** | 1 | 100M HAVE | 5M HAVE (5%) | ~3,422 HAVE |
| **Testnet** | 1 | 100M HAVE | 5M HAVE (5%) | ~3,422 HAVE |

### Benefits

- **Predictable rewards**: Validators and stakers receive consistent
emissions
- **Publicly auditable**: All emissions recorded on-chain
- **Non-compounding**: Same absolute amount minted each year (not
percentage of growing supply)
- **Governance-upgradeable**: `InflationAnnualAmount` can be changed via
runtime parameters

### Comparison: Before vs After

| Aspect | Before (Compounding) | After (Linear) |
|--------|---------------------|----------------|
| Formula | 5% × current_supply | Fixed 500M HAVE |
| Year 1 (10B supply) | 500M HAVE | 500M HAVE |
| Year 2 (10.5B supply) | 525M HAVE | 500M HAVE |
| Year 10 | ~814M HAVE | 500M HAVE |

## ⚠️ Breaking Changes ⚠️

- **Runtime parameter renamed**: `InflationTargetedAnnualRate` (Perbill)
→ `InflationAnnualAmount` (Balance)
  - Old: percentage-based rate applied to current total issuance
  - New: fixed annual amount in base units (wei)
- **`ExternalRewardsEraInflationProvider` type parameters changed**:
- Removed: `Balances` (fungible::Inspect) and `AnnualRate`
(Get<Perbill>)
  - Added: `AnnualAmount` (Get<u128>)
- **Inflation behavior change**: Inflation is now linear (fixed amount)
instead of compounding (percentage of supply)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
2025-12-29 16:45:15 +01:00
Ahmad Kaouk
41788d56bb
test: refactor e2e tests (#365)
This PR significantly refactors and improves the end-to-end testing
framework and infrastructure. The primary focus was on simplifying the
test suites, improving reliability through better resource management,
and hardening the relayer infrastructure.

All E2E tests are now passing on the CI and demonstrate consistent
reliability when run locally.

### Key Changes

#### 1. E2E Test Suite Refactor & Cleanup
* **Simplified Test Logic**: Heavily refactored the core test suites
(`native-token-transfer.test.ts`, `rewards-message.test.ts`, and
`validator-set-update.test.ts`). The new implementation is much cleaner,
utilizing shared helpers to reduce boilerplate.
* **Utility Consolidation**: Removed redundant utility files
(`storage.ts`, `rewards-helpers.ts`) and simplified `events.ts`. Event
waiting now uses `rxjs` for Substrate and native `viem` watchers for
Ethereum, which is more robust and easier to maintain.
* **Better Connector Management**: Unified the creation and cleanup of
test clients in `ConnectorFactory`. It now handles the lifecycle of
WebSocket connections more gracefully, including clearing the
`socketClientCache` to prevent reconnection noise during teardown.

#### 2. Infrastructure & Stability
* **Relayer Relaunch Policy**: Added a restart policy for Snowbridge
relayer containers. They are now configured with `--restart
on-failure:5`, ensuring that relayers automatically relaunch if they
crash during the sensitive initialization phase.
*   **WebSocket Integration**: 
* Updated the `ConnectorFactory` to prefer **WebSockets** for the
Ethereum public client, which is essential for efficient, event-heavy
E2E testing.
* Enhanced `launchKurtosisNetwork` to correctly identify and register
the Execution Layer's WebSocket endpoint from Kurtosis.
* **Disabled Contract Injection**: This PR temporarily disables the
automatic injection of contracts into the genesis state by default.
* *Reason*: I encountered issues generating a valid `state-diff.json`
for the latest contract versions. Even after applying several
workarounds, the injected state remained unstable. As a result, I've
reverted to manual contract deployment during the launch sequence for
better reliability for now.

#### 3. Documentation & Maintenance
* Removed obsolete documentation (`event-utilities-guide.md`) that no
longer reflects the simplified event-handling API.
* Cleaned up `test/launcher/validators.ts` and moved logic into more
appropriate helpers.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-12-24 13:31:40 +01:00
Steve Degosserie
58296f5e87
build: ⬆️ Bump Rust version to 1.90.0 (#362)
## Summary
- Bump Rust toolchain from 1.88.0 to 1.90.0 in
`operator/rust-toolchain.toml`
- Update hardcoded Rust version in
`.github/workflows/task-check-licenses.yml` to match

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-12-19 08:43:01 +01:00
Steve Degosserie
67f375860b
feat: Performance-Based Validator Rewards and Inflation Scaling (#306)
## 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>
2025-12-16 16:27:03 +01:00
Facundo Farall
d5e64d59e8
build: ⬆️ Upgrade to StorageHub version 0.2.6 (#357)
Upgrade to StorageHub release v0.2.6

No breaking changes, just a patch release

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-12-16 11:22:33 +01:00
Gonza Montiel
733218ac79
fix: 🛡️ Check origin for validator set messages (#343)
### Context
The function `v2_sendMessage()` on Snowbridge Gateway contract is
**permissionless** (I'm shocked this is the design choice). Any
EOA/contract on Ethereum can build a message and send it through our DH
bridge. While we don't change our Snowbridge fork, then this will
continue to be the case.

### Problem
We use `v2_sendMessage()` to send **permissioned** operations to our
chain. For instance: update our validator set message (coming next,
_slashing-related_ messages). So we do need to restrict the processing
of the incoming messages on the Substrate side.

### Fix
- I've added a check to `EigenLayerMessageProcessor` that enforces
`message.origin` to be only a configured `AuthorisedOrigin`.
- I've added an `AuthorisedOrigin` to
`pallet_external_validators::Config`
- I've configured the `AuthorisedOrigin` to be
`DatahavenServiceManagerAddress` in all three runtimes

### Stages
- [x] Implementation
- [x] Runtime integration tests
- [x] Collect `DatahavenServiceManagerAddress` parameter for e2e tests
to work

Fixes https://github.com/datahaven-xyz/sr-datahaven/issues/12

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-12-15 14:11:08 +01:00
Steve Degosserie
ef3ddaaf69
build: 🔨 Optimize node dependencies by feature-gating benchmarking (#341)
## Summary

- Feature-gate `frame-benchmarking-cli` behind `runtime-benchmarks`
feature, making it an optional dependency
- Remove unused `cumulus-client-service` workspace dependency
- Remove unused `storage-hub-runtime` workspace dependency
- Add `#[cfg(feature = "runtime-benchmarks")]` guards to
benchmark-related code

## Motivation

The `frame-benchmarking-cli` crate pulls in
`cumulus-client-parachain-inherent` and other cumulus dependencies
transitively. Since DataHaven is a solochain (not a parachain), these
dependencies are unnecessary for regular builds.

By making the benchmarking CLI optional and only compiling it when the
`runtime-benchmarks` feature is enabled, we reduce:
- Compile time for regular development builds
- Final binary size (when not benchmarking)
- Dependency tree complexity

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-09 12:26:05 +01:00
Facundo Farall
0d5f294097
build: ⬆️ Upgrade to StorageHub v0.2.5 (#347)
Upgrades to StorageHub patch release v0.2.5
2025-12-09 11:30:21 +01:00
Tobi Demeco
37f7fe9b3b
build: ⬆️ Upgrade to SH v0.2.4 (#342)
Upgrade StorageHub to v0.2.4 which includes a fixes for the fisherman
and indexer flows plus a **indexer DB migration.**
2025-12-07 22:26:52 +01:00
Gonza Montiel
bd99fb7c4e
fix: pallet collective weights aliases (#337)
We have two instances of `pallet_collective`
- `pallet_collective_treasury_council`
- `pallet_collective_technical_committee`

Our weights template automatically generates an implementation for
`pallet_collective_treasury_council::WeightInfo` or
`pallet_collective_technical_committee::WeightInfo`, which don't exist,
making the compilation fail right after running benches.
I created aliases for both pallet names, and added a small tweak to the
template so it does not break anymore.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-12-05 14:22:52 +01:00
Ahmad Kaouk
207f2ff86a
chore: remove redundant cargo config file (#324)
## Summary

- Remove .cargo/config.toml that was unintentionally overriding release
and **production** profile settings
  - Fix outdated comment referencing Moonbeam instead of Datahaven

  ## Details

- The `operator/.cargo/config.toml` file was overriding the release
profile configuration defined in `Cargo.toml`. Removing it ensures the
intended `Cargo.toml` settings are used.
- The protocol = "sparse" setting for crates.io is no longer needed as
it has been the default since Cargo 1.70 (Rust 1.70.0).

## Important

This override also affected the production profile . Since
`[profile.production]` inherits from release, the `opt-level = 2` from
`config.toml` propagated to production builds.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-12-05 11:31:19 +01:00
Tobi Demeco
5f2b366031
build: ⬆️ Upgrade to SH v0.2.3 (#340)
Upgrade StorageHub to v0.2.3 which includes some fixes for the indexer
plus minor changes to the runtime (which required a DataHaven metadata
update).
2025-12-04 20:46:40 +01:00
Facundo Farall
1ce068eebe
build: ⬆️ Upgrade to SH v0.2.2 (#336)
Upgrades to StorageHub version 0.2.2, which includes a fix in the
Indexer handling of files in the database. No breaking changes.
2025-12-03 20:53:39 +01:00
Steve Degosserie
377e7372b9
feat: Bump client version to v0.9.0 & Runtime version to RT900 (#333) 2025-12-03 14:55:59 +01:00
Gonza Montiel
e1e566568d
fix: update weights for RT800 (#329)
Weights update for release v0.8.0 (RT800).

- [x] Stagenet
- [x] Testnet
- [x] Mainnet

Showing some +10% changes:

Network | Pallet | Call | Old weight | New weight | Change %
-- | -- | -- | -- | -- | --
mainnet | frame_system | authorize_upgrade | 12496000 | 22629000 |
81.09%
mainnet | frame_system | remark | 30798441 | 8695261 | -71.77%
mainnet | pallet_conviction_voting | undelegate | 21395950 | 23795285 |
11.21%
mainnet | pallet_external_validators | remove_whitelisted | 17773467 |
15404854 | -13.33%
mainnet | pallet_multisig | as_multi_complete | 58059065 | 50189845 |
-13.55%
mainnet | pallet_preimage | request_no_deposit_preimage | 18808000 |
29216000 | 55.34%
mainnet | pallet_preimage | request_preimage | 26608000 | 40720000 |
53.04%
mainnet | pallet_preimage | request_requested_preimage | 13880000 |
16912000 | 21.84%
mainnet | pallet_preimage | request_unnoted_preimage | 19245000 |
33023000 | 71.59%
mainnet | pallet_preimage | unnote_no_deposit_preimage | 32229000 |
46218000 | 43.41%
mainnet | pallet_preimage | unnote_preimage | 70700000 | 91185000 |
28.97%
mainnet | pallet_preimage | unrequest_multi_referenced_preimage |
13961000 | 16754000 | 20.01%
mainnet | pallet_preimage | unrequest_preimage | 27274000 | 42435000 |
55.59%
mainnet | pallet_preimage | unrequest_unnoted_preimage | 13946000 |
16113000 | 15.54%
mainnet | pallet_scheduler | service_task_fetched | 24397000 | 4128742 |
-83.08%
mainnet | pallet_sudo | check_only_sudo_account | 5248000 | 6034000 |
14.98%
mainnet | pallet_utility | batch | 18376045 | 14956939 | -18.61%
mainnet | pallet_utility | batch_all | 13623778 | 9310421 | -31.66%
mainnet | pallet_utility | force_batch | 13126526 | 11589563 | -11.71%
stagenet | frame_system | authorize_upgrade | 12866000 | 23295000 |
81.06%
stagenet | frame_system | remark | 33694157 | 25734885 | -23.62%
stagenet | pallet_external_validators_rewards | on_era_end | 1583951000
| 1749571000 | 10.46%
stagenet | pallet_multisig | as_multi_complete | 57510735 | 51491532 |
-10.47%
stagenet | pallet_preimage | request_no_deposit_preimage | 18612000 |
30396000 | 63.31%
stagenet | pallet_preimage | request_preimage | 26129000 | 37317000 |
42.82%
stagenet | pallet_preimage | request_requested_preimage | 13912000 |
17268000 | 24.12%
stagenet | pallet_preimage | request_unnoted_preimage | 19198000 |
35428000 | 84.54%
stagenet | pallet_preimage | unnote_no_deposit_preimage | 31195000 |
45321000 | 45.28%
stagenet | pallet_preimage | unnote_preimage | 70675000 | 89916000 |
27.22%
stagenet | pallet_preimage | unrequest_multi_referenced_preimage |
13422000 | 16452000 | 22.57%
stagenet | pallet_preimage | unrequest_preimage | 27349000 | 43421000 |
58.77%
stagenet | pallet_preimage | unrequest_unnoted_preimage | 14104000 |
16640000 | 17.98%
stagenet | pallet_sudo | check_only_sudo_account | 5247000 | 5923000 |
12.88%
stagenet | pallet_utility | batch | 17077975 | 10949120 | -35.89%
stagenet | pallet_utility | batch_all | 11105082 | 4628520 | -58.32%
stagenet | pallet_utility | force_batch | 9291805 | 4409497 | -52.54%
testnet | frame_system | authorize_upgrade | 12576000 | 24096000 |
91.60%
testnet | frame_system | remark | 49594571 | 14515706 | -70.73%
testnet | pallet_external_validators_rewards | on_era_end | 1547405000 |
1732185000 | 11.94%
testnet | pallet_preimage | request_no_deposit_preimage | 17857000 |
30407000 | 70.28%
testnet | pallet_preimage | request_preimage | 25375000 | 42504000 |
67.50%
testnet | pallet_preimage | request_requested_preimage | 14069000 |
16591000 | 17.93%
testnet | pallet_preimage | request_unnoted_preimage | 19174000 |
34375000 | 79.28%
testnet | pallet_preimage | unnote_no_deposit_preimage | 29893000 |
46805000 | 56.58%
testnet | pallet_preimage | unnote_preimage | 71971000 | 89858000 |
24.85%
testnet | pallet_preimage | unrequest_multi_referenced_preimage |
13934000 | 16085000 | 15.44%
testnet | pallet_preimage | unrequest_preimage | 28700000 | 43751000 |
52.44%
testnet | pallet_preimage | unrequest_unnoted_preimage | 13989000 |
17138000 | 22.51%
testnet | pallet_sudo | check_only_sudo_account | 5279000 | 5881000 |
11.40%
testnet | pallet_utility | batch | 17940544 | 20806579 | 15.98%
testnet | pallet_utility | batch_all | 20328522 | 18215679 | -10.39%
testnet | pallet_utility | force_batch | 13240132 | 18456500 | 39.40%

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-12-02 22:46:27 +01:00
Facundo Farall
c1202e5d51
build: ⬆️ Upgrade to SH release 0.2.1 (#331) 2025-12-02 21:38:59 +01:00
Steve Degosserie
51ffcae5f0
Revert "feat: statically build binary (#292)" (#330)
This reverts commit f84b6debb7.
2025-12-02 15:42:43 +01:00
Gonza Montiel
82c581d495
feat: Add datahaven native transfer precompile (#309)
## DataHaven Native Transfer Precompile

Implements EVM precompile at address
`0x00000000000000000000000000000007F5` (2073) to expose
`pallet-datahaven-native-transfer` functionality to the EVM layer.

### Features
- **Transfer to Ethereum**: Locks native tokens and sends them via
Snowbridge to Ethereum addresses
- **Pause/Unpause**: Admin controls to halt/resume transfers
- **View Functions**: Query paused state, total locked balance, and
sovereign account address

### Implementation
- Precompile using `#[precompile_utils::precompile]` macro with proper
gas accounting
- 15+ test cases covering success/failure scenarios
- Solidity interface with NatSpec documentation for contract integration

Enables seamless cross-chain transfers of DataHaven native tokens to
Ethereum L1.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-12-02 13:57:40 +01:00
Steve Degosserie
063773eb05
fix: 🔨 Update Snowbridge Ethereum network configuration with correct genesis hashes and fork versions (#310)
## Summary

This PR fixes the Snowbridge Ethereum network configuration across all
three runtimes (Mainnet, Stagenet, and Testnet) by replacing placeholder
genesis hashes with correct values and updating fork versions to match
the target Ethereum networks.

### Changes Made

#### **Stagenet Runtime**
-  Updated genesis hash from `[3u8; 32]` to Hoodi testnet genesis:
`0xbbe312868b376a3001692a646dd2d7d1e4406380dfd86b98aa8a34d1557c971b`
-  Fixed chain ID from `3151908` to `560048` (Hoodi testnet)
-  Updated fork versions to Hoodi testnet configuration (0x10000910
series)
-  Added Fulu fork at epoch 50688 (activated Oct 28, 2025)

#### **Testnet Runtime**
-  Updated genesis hash from `[2u8; 32]` to Hoodi testnet genesis:
`0xbbe312868b376a3001692a646dd2d7d1e4406380dfd86b98aa8a34d1557c971b`
-  Updated fork versions from Holesky to Hoodi testnet configuration
-  Added Fulu fork at epoch 50688

#### **Mainnet Runtime**
-  Updated genesis hash from `[1u8; 32]` to Ethereum mainnet genesis:
`0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3`
-  Updated fork versions from Holesky to Ethereum mainnet configuration
(0x00000000 series)
-  Added Fulu fork at epoch 411392 (scheduled for Dec 3, 2025)

#### **Core Updates**
-  Added `fulu` field to `ForkVersions` struct in
`snowbridge-beacon-primitives`
-  Updated `select_fork_version` function in ethereum-client pallet to
handle Fulu fork

### Technical Details

**Hoodi Testnet (Stagenet & Testnet):**
- Chain ID: 560048
- Genesis:
`0xbbe312868b376a3001692a646dd2d7d1e4406380dfd86b98aa8a34d1557c971b`
- Fork versions: Genesis (0x10000910) → Altair → Bellatrix → Capella →
Deneb → Electra (epoch 2048) → Fulu (epoch 50688)
- Source: https://github.com/eth-clients/hoodi

**Ethereum Mainnet:**
- Chain ID: 1
- Genesis:
`0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3`
- Fork versions: Genesis (0x00000000) → Altair (epoch 74240) → Bellatrix
(144896) → Capella (194048) → Deneb (269568) → Electra (364032) → Fulu
(411392)
- Source: https://github.com/ethereum/consensus-specs

### Why This Matters

The Snowbridge light client relies on accurate Ethereum network
parameters to verify consensus proofs. Incorrect genesis hashes or fork
versions would cause signature verification failures and prevent the
bridge from functioning correctly.

### Fulu Fork Support

The Fulu fork (part of Fusaka upgrade) adds a `proposer_lookahead` field
to BeaconState but does **not** change generalized indices for sync
committees or finalized roots, so it reuses Electra's configuration
constants.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: undercover-cactus <lola@moonsonglabs.com>
2025-12-02 12:49:18 +01:00
Facundo Farall
4d5716fdcb
build(operator): ⬆️ Upgrade to storage-hub 0.2.0 (#327)
## ⚠️ Breaking Changes ⚠️

Upgrades to SH version 0.2.0. Breaking changes for this version are
outlined in the corresponding
[release](https://github.com/Moonsong-Labs/storage-hub/releases/tag/v0.2.0).

Particularly, in this PR, the following breaking changes are implemented
for DH node operators:

### Breaking CLI changes vs `main`

- **Fisherman vs provider role**  
- `--fisherman` now has `conflicts_with = "provider"`
(`FishermanConfigurations::fisherman`).
- Any existing scripts that started a node with both `--provider` and
`--fisherman` will now fail clap validation.

- **Removed / replaced fisherman tuning flags**  
- The following flags no longer exist and will cause errors if still
used:
- `--fisherman-incomplete-sync-max` (field
`fisherman_incomplete_sync_max`)
- `--fisherman-incomplete-sync-page-size` (field
`fisherman_incomplete_sync_page_size`)
- `--fisherman-sync-mode-min-blocks-behind` (field
`fisherman_sync_mode_min_blocks_behind`)
  - They are replaced by:  
- `--fisherman-batch-interval-seconds`
(`fisherman_batch_interval_seconds`, default `60`)
- `--fisherman-batch-deletion-limit` (`fisherman_batch_deletion_limit`,
default `1000`)

- **MSP DB wiring no longer piggybacks on the indexer DB**  
- Previously, enabling the indexer (`IndexerConfigurations`) also wired
its DB pool into the MSP move‑bucket path via
`with_indexer_db_pool(maybe_indexer_db_pool)`.
- Now, MSP DB access is **only** configured if you pass the new
`--msp-database-url` provider flag; the indexer’s `--indexer` /
`--indexer-database-url` no longer implicitly provide DB access to MSP
logic. This will change behaviour for MSP nodes that relied on just the
indexer flags.

### New / additive CLI flags (non‑breaking but behaviourally relevant)

- **Provider flags**  
- `--pending-db-url` (`pending_db_url`, env `SH_PENDING_DB_URL`) for
persisting pending extrinsics.
- `--internal-buffer-size` (`internal_buffer_size`, default `1024`) for
DB chunk batching during file transfer.

- **Reordered but unchanged**  
- `--msp-distribute-files` still exists (bool flag), just moved within
`ProviderConfigurations`; name and type are unchanged, but now also
explicitly toggles `enable_msp_distribute_files` only when
`provider_type == msp`.
2025-12-02 11:55:31 +01:00
undercover-cactus
f84b6debb7
feat: statically build binary (#292)
Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
2025-11-28 13:38:05 +00:00
Ahmad Kaouk
0618e84268
feat: set POV gas limit ratio to zero for solo chain (#313)
Set `GasLimitPovSizeRatio` to 0 across all runtime environments
(mainnet, stagenet, testnet) since DataHaven operates as a solo chain
and doesn't need to account for Proof-of-Validity size constraints that
parachains require.

  ## ⚠️ Breaking Changes ⚠️
  - `GasLimitPovSizeRatio` is now set to 0 across all runtimes
  - Gas calculations will no longer account for POV size constraints
2025-11-27 11:14:41 +01:00
Steve Degosserie
71b5e5185f
fix: consolidate session timing and simplify docker release workflow (#321)
## Summary

- Consolidates `SessionsPerEra` definition in common runtime (removes
duplicate definitions)
- Simplifies docker release workflow to always use full Docker builds
- Removes binary reuse path from release workflow

## Changes

### Runtime Configuration
- Remove duplicate `SessionsPerEra` definitions from individual runtimes
- Import `SessionsPerEra` from `datahaven_runtime_common::time` instead
- This fixes inconsistency where individual runtimes had
`prod_or_fast!(6, 1)` while common had `prod_or_fast!(6, 3)`

### Docker Release Workflow
- Remove binary reuse path - now always does full Docker build
- Remove `binary-hash` input from `workflow_call`
- Consolidate to single build step using `datahaven-build.Dockerfile`
- `docker-build-release` now runs in parallel on main branch (no
dependency on `build-operator`)

## Timing Configuration

### Production Runtime
| Parameter        | Value       | Duration   |
|------------------|-------------|------------|
| Session          | 600 blocks  | 1 hour     |
| Sessions per era | 6           | -          |
| Era              | 6 sessions  | 6 hours    |
| Bonding duration | 28 eras     | 7 days     |

### Fast Runtime (for testing)
| Parameter        | Value       | Duration   |
|------------------|-------------|------------|
| Session          | 10 blocks   | 1 minute   |
| Sessions per era | 1           | -          |
| Era              | 1 session   | 1 minute   |
| Bonding duration | 3 eras      | 3 minutes  |

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-26 10:25:24 +01:00
Steve Degosserie
92d3c42f79
chore: ♻️ Remove executed runtime migrations (#318)
## Summary

Removes old runtime migrations that have already been executed on
Stagenet and Testnet environments, reducing code complexity and
maintenance burden.

## Changes

### Migration Cleanup
- **Removed `evm_alias::EvmAliasMigration`** (~532 lines)
- Multi-block migration that renamed the Frontier EVM pallet alias from
`Evm` to `EVM`
  - Migrated AccountCodes, AccountCodesMetadata, and AccountStorages
  
- **Removed `evm_chain_id::EvmChainIdMigration`**
- Single-step migration that updated stored EVM chain IDs to match new
configuration
  - Applied to testnet (55931) and stagenet (55932)

### Runtime Updates
- **Simplified `MultiBlockMigrationList`** to empty tuple `()` in
`runtime/common/src/migrations.rs`
- **Updated all runtime configs** to use simplified migration list:
  - `runtime/mainnet/src/configs/mod.rs`
  - `runtime/stagenet/src/configs/mod.rs`
  - `runtime/testnet/src/configs/mod.rs`
- Removed `Runtime` type parameter from migration configurations

### What Remains
- `pallet_migrations` infrastructure stays in place for future
migrations
- Migration test file (`mainnet/tests/migrations.rs`) preserved for
testing pallet administrative functions
- Configuration types and constants (cursor/identifier lengths,
handlers)

## Impact

- **Code reduction**: 532 lines removed
- **No functional change**: These migrations have already executed
successfully
- **Future-ready**: Migration infrastructure remains for new migrations
when needed

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-11-25 18:44:06 +01:00
Steve Degosserie
72c3457183
fix: 🔨 Replace FreezeChainOnFailedMigration with SafeMode handler (#308)
## Summary

Replaced the `DefaultFailedMigrationHandler` (which completely froze the
chain on migration failures) with `EnterSafeModeOnFailedMigration`
across all three runtimes (mainnet, stagenet, testnet). When a migration
fails, the chain now automatically enters SafeMode instead of freezing,
allowing governance to intervene and fix issues while preventing regular
user transactions.

## Problem

Previously, when a runtime migration failed, the chain would use
`FreezeChainOnFailedMigration`, which completely halted all operations
including governance functions. This made it impossible to recover from
migration failures without manual intervention at the node level.

## Solution

Implemented `EnterSafeModeOnFailedMigration` which:
- **Enters SafeMode** when a migration fails: the chain remains
_indefinitely_ under safe mode until it is disabled, either with Sudo or
Governance.
- **Allows governance operations** to continue (Sudo, SafeMode, TxPause,
Preimage, Scheduler, etc.)
- **Blocks regular user transactions** to prevent interaction with
potentially inconsistent storage
- **Falls back to freezing** if SafeMode cannot be entered

## Changes

### Core Implementation
- **`runtime/common/src/migrations.rs`**: Added
`FailedMigrationHandler<SafeMode>` type alias that wraps
`EnterSafeModeOnFailedMigration` with comprehensive documentation
- **All three runtimes** (`mainnet`, `stagenet`, `testnet`):
- Updated `pallet_migrations::Config::FailedMigrationHandler` to use
`FailedMigrationHandler<SafeMode>`
  - Removed obsolete TODO comments

### Tests
Added comprehensive migration failure tests to all three runtimes:
- **`failed_migration_enters_safe_mode`**: Verifies SafeMode is
activated, expiry is set, and event is emitted
- **`safe_mode_allows_governance_during_migration_failure`**: Confirms
governance can exit SafeMode after migration failure
- **`migrations_force_calls_are_root_only`**: Existing test for
migration management permissions

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-11-25 16:09:19 +01:00
Steve Degosserie
79339c5c3d
fix: 🔨 Enable BEEFY equivocation reporting in all runtimes (#320)
## Summary

- Enables BEEFY equivocation reporting which was previously disabled
(set to `()`)
- Configures `pallet_beefy::EquivocationReportSystem` in all three
runtimes (mainnet, stagenet, testnet)
- Without this fix, validators could sign conflicting BEEFY commitments
without any slashing consequences

## Problem

The `EquivocationReportSystem` type in `pallet_beefy::Config` was set to
`()`, which completely disabled BEEFY equivocation reporting. This is a
security issue because:

1. BEEFY validators could sign two different commitments at the same
block height (equivocation)
2. There was no mechanism to report and slash such misbehavior
3. This undermines the security guarantees of the BEEFY consensus
protocol

## Solution

Configure the proper equivocation report system using the same pattern
as BABE and GRANDPA:

```rust
type EquivocationReportSystem =
    pallet_beefy::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
```

This uses:
- `Offences` pallet to record equivocations
- `Historical` pallet for validator proof verification  
- `ReportLongevity` parameter (based on bonding duration) for the
reporting window

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-25 14:56:52 +01:00
Steve Degosserie
a5522659bf
feat: Add Docker Compose setup for local DataHaven network (#314)
## 🎯 Overview

This PR introduces a comprehensive Docker Compose configuration for
running a complete local DataHaven network, making it significantly
easier for developers to spin up and test the entire stack locally.

## 🏗️ Architecture

```mermaid
graph TB
    subgraph "DataHaven Network (Docker)"
        subgraph "Consensus Layer"
            Alice["🔷 Alice (Validator)<br/>:9944 RPC<br/>4 Keys: GRAN, BABE, IMON, BEEF"]
            Bob["🔷 Bob (Validator)<br/>:9945 RPC<br/>4 Keys: GRAN, BABE, IMON, BEEF"]
            Alice <-->|P2P/mDNS| Bob
        end

        subgraph "Storage Provider Layer"
            MSP["💾 MSP (Charlie)<br/>:9946 RPC<br/>1 GiB Storage<br/>1 Key: BCSV"]
            BSP01["💾 BSP01 (Dave)<br/>:9947 RPC<br/>1 GiB Storage<br/>1 Key: BCSV"]
            BSP02["💾 BSP02 (Eve)<br/>:9948 RPC<br/>1 GiB Storage<br/>1 Key: BCSV"]
        end

        subgraph "Monitoring Layer"
            Indexer["📊 Indexer<br/>:9949 RPC<br/>Full Mode<br/>No Keys"]
            Fisherman["🎣 Fisherman (Gustavo)<br/>:9950 RPC<br/>Storage Monitor<br/>1 Key: BCSV"]
            DB["🗄️ PostgreSQL<br/>:5432<br/>indexer/datahaven"]
        end

        Alice -.->|Syncs| MSP
        Bob -.->|Syncs| MSP
        Alice -.->|Syncs| BSP01
        Bob -.->|Syncs| BSP02
        Alice -.->|Syncs| Indexer
        Bob -.->|Syncs| Fisherman
        
        MSP -.->|Monitors| Fisherman
        BSP01 -.->|Monitors| Fisherman
        BSP02 -.->|Monitors| Fisherman
        
        Indexer -->|Writes| DB
        Fisherman -->|Writes| DB
    end

    style Alice fill:#4a90e2,stroke:#2e5c8a,color:#fff
    style Bob fill:#4a90e2,stroke:#2e5c8a,color:#fff
    style MSP fill:#50c878,stroke:#2d7a4a,color:#fff
    style BSP01 fill:#50c878,stroke:#2d7a4a,color:#fff
    style BSP02 fill:#50c878,stroke:#2d7a4a,color:#fff
    style Indexer fill:#f5a623,stroke:#b87818,color:#fff
    style Fisherman fill:#f5a623,stroke:#b87818,color:#fff
    style DB fill:#9b59b6,stroke:#6c3a82,color:#fff
```

**Legend:**
- 🔷 Validators - Consensus and block production
- 💾 Storage Providers - File storage and retrieval
- 📊 Indexer - Full blockchain indexing
- 🎣 Fisherman - Storage provider monitoring
- 🗄️ PostgreSQL - Database for indexer/fisherman
- `<-->` P2P Communication | `-.->` Network Sync | `-->` Database
Connection

##  What's Included

### Core Network (8 Services)
- **2 Validator Nodes** (Alice & Bob) - Consensus and block production
- **1 Main Storage Provider (MSP)** - Charlie with 1 GiB storage
capacity
- **2 Backup Storage Providers (BSPs)** - Dave and Eve with 1 GiB each
- **1 StorageHub Indexer** - Full blockchain indexer with PostgreSQL
- **1 Fisherman Node** - Gustavo monitoring storage provider behavior
- **1 PostgreSQL Database** - Shared database for indexer and fisherman

### Key Features
 **Automated Key Injection** - All validator and storage provider keys
automatically injected on startup
 **Health Checks** - Validators have health checks that verify RPC port
is listening before dependent services start
 **Orchestrated Startup** - Services start in correct order with
health-based dependencies
 **Persistent Storage** - Chain data, keystores, and database all
persisted in Docker volumes
 **Unified Entrypoint Script** - Single script handles all node types
(validator, MSP, BSP, fisherman)
 **Proper User Permissions** - Root for setup, switches to datahaven
user for node execution
 **mDNS Peer Discovery** - Nodes automatically discover each other on
the Docker network
 **Comprehensive Documentation** - Full setup guide, troubleshooting,
and verification steps

### 🔄 Startup Sequence

The network starts in a carefully orchestrated sequence to ensure
stability:

1. **Alice (Validator)** starts first
   - Injects 4 keys (GRAN, BABE, IMON, BEEF)
   - Health check waits for RPC port 9944 to be listening
   - Uses `/proc/net/tcp` for minimal-dependency port checking

2. **Bob (Validator)** waits for Alice to be healthy
   - Ensures at least one validator is fully operational
   - Enables block production to start immediately

3. **Storage Providers & Monitoring** wait for both validators to be
healthy
   - MSP, BSP01, BSP02 start after validators are ready
   - Indexer and Fisherman wait for validators before syncing
   - PostgreSQL starts independently with its own health check

This dependency chain prevents race conditions and ensures reliable
network formation.

## 🚀 Quick Start

```bash
cd operator

# Build the binary (development mode with fast blocks)
./scripts/docker-prepare.sh --fast

# Start the entire network
docker-compose up -d

# View logs
docker-compose logs -f

# Check service health
docker-compose ps

# Stop the network
docker-compose down -v
```

## 📋 Port Mappings

| Service | RPC/WebSocket | Prometheus | P2P | Database/API |
|---------|---------------|------------|-----|--------------|
| Alice | 9944 | 9615 | 30333 | - |
| Bob | 9945 | 9616 | 30334 | - |
| MSP | 9946 | 9617 | 30335 | - |
| BSP01 | 9947 | 9618 | 30336 | - |
| BSP02 | 9948 | 9619 | 30337 | - |
| Indexer | 9949 | 9620 | 30338 | - |
| Fisherman | 9950 | 9621 | 30339 | - |
| PostgreSQL | - | - | - | 5432 |

## 🔑 Key Injection

All cryptographic keys are automatically injected on startup:

**Validators (Alice & Bob)** - 4 keys each:
- GRANDPA (ed25519) - Finality
- BABE (sr25519) - Block authoring
- ImOnline (sr25519) - Heartbeat
- BEEFY (ecdsa) - Bridge consensus

**Storage Providers (MSP, BSPs, Fisherman)** - 1 key each:
- BCSV (ecdsa) - Storage provider identity

**Indexer** - No keys required (non-validating node)

## 🩺 Health Checks

Validator nodes (Alice & Bob) implement health checks to ensure proper
startup sequencing:

**Health Check Method:**
- Reads `/proc/net/tcp` directly to check if RPC port is listening
- Zero dependencies - works in minimal debian:stable-slim containers
- Converts port to hex and searches for LISTEN state (0A)

**Configuration:**
- Start period: 30s (allows node initialization)
- Interval: 10s (check every 10 seconds)
- Timeout: 5s (per health check)
- Retries: 5 (must pass 5 consecutive checks)

**Benefits:**
- Prevents dependent services from starting before validators are ready
- Eliminates race conditions during network formation
- No additional packages required in Docker image

## 🍎 macOS Requirement

**Important:** On Docker Desktop for macOS, you must use the
**experimental DockerVMM** virtualization framework:

1. Open Docker Desktop settings
2. Go to "General" tab
3. Enable "Use experimental virtualization framework (DockerVMM)"
4. Restart Docker Desktop

The default Apple Virtualization Framework causes networking issues with
P2P connections.

## 📁 Files Changed

- `operator/docker-compose.yml` - Main orchestration configuration
- `operator/scripts/docker-entrypoint.sh` - Unified key injection script
- `operator/scripts/docker-prepare.sh` - Binary build helper
- `operator/scripts/docker-healthcheck.sh` - Health check script for
validators
- `operator/DOCKER-COMPOSE.md` - Comprehensive documentation

## 🔍 Testing

The configuration has been tested on:
-  Docker Desktop for macOS (with DockerVMM)
-  Docker on Linux/Ubuntu

All nodes successfully:
- Inject required keys
- Pass health checks
- Discover peers via mDNS
- Sync blocks and finalize
- Connect to PostgreSQL database (indexer/fisherman)

## 📝 Notes

- All settings are configured for **local development only**
- Uses well-known test seed phrase (⚠️ never use in production!)
- RPC exposed without authentication
- Unsafe flags enabled for convenience

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-22 14:07:46 +01:00
Steve Degosserie
7f09949e64
feat: Implement inflation mechanism for validator rewards (#304)
## Summary

This PR introduces a configurable inflation system for validator rewards
with an annual target rate and optional treasury allocation.

## Changes

### Inflation Mechanism
- **Annual inflation rate runtime parameter**: Set to 5% default
- **EraInflationProvider**: Calculates per-era inflation based on total
issuance and annual rate
- Formula: `per_era_inflation = (total_issuance × annual_rate) /
eras_per_year`

### Treasury Allocation
- **InflationTreasuryProportion parameter**: Set to 20% default
- **ExternalRewardsInflationHandler**: Mints inflation and distributes
between:
  - 80% to rewards account (for validator rewards)
  - 20% to treasury account
- Treasury receives allocation via `mul_floor()`, with remainder going
to rewards to ensure no tokens lost to rounding

### Runtime Integration
- Configured across all three runtimes: mainnet, testnet, and stagenet
- Consistent parameters across all environments

### Testing
- Updated all tests to account for 80/20 split between rewards and
treasury
- Added precision tolerance (±1 unit) for Perbill rounding edge cases

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-22 11:00:50 +01:00
Steve Degosserie
32480f1bbc
feat: Bump client version to v0.8.0 & Runtime version to RT800 (#312) 2025-11-19 18:30:02 +00:00
Ahmad Kaouk
7e6033097e
doc: Fix fast-runtime documentation (#311)
`fast-runtime` features does not shortens block time to 3 s. It keeps it at 6 s and instead shortens epochs (1‑minute) and eras (3 sessions) to speed up validator churn and testing workflows.
2025-11-19 17:23:10 +00:00
Steve Degosserie
9ecad7f119
fix: 🔨 Use correct TreasuryAccount in StorageHub pallets configuration (#305) 2025-11-18 20:57:26 +01:00
Steve Degosserie
bb410e0fa9
fix: 🔨 Correctly set the offences pallet's OnOffenceHandler to the external-validator-slashes pallet (#303) 2025-11-18 08:51:06 +01:00
Facundo Farall
ae9eef7307
build: ⬆️ Upgrade to StorageHub release 0.1.4 (#298)
Upgrades to StorageHub release
[v0.1.4](https://github.com/Moonsong-Labs/storage-hub/releases/tag/v0.1.4).

## ⚠️ Breaking Changes ⚠️
- A DB migration for the indexer DB. Should be auto-applied by the
indexer node on startup, if this is not disabled by the env var
`SH_INDEXER_DB_AUTO_MIGRATE`. By default, it applies them.
- A new runtime API (`shp_tx_implicits_runtime_api::TxImplicitsApi`)
needed for StorageHub's Blockchain Service to build transactions using
the runtime spec version from the currently run runtime.

---------

Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
2025-11-16 16:44:17 +01:00