## Summary
- Add stagenet-hoodi deployment artifacts (contract addresses, rewards
info) and update Snowbridge config with latest validator set
- Fix the `bun cli contracts verify` command to correctly verify all
deployed contracts, including proxy contracts and Snowbridge
dependencies
- Fix the `bun cli contracts update-metadata` command to use the correct
config file when `--environment` is specified
## Contract verification fixes
The verification CLI hardcoded all contract source paths as
`src/<Name>.sol`, which failed for:
- **Snowbridge contracts** (Gateway, BeefyClient, AgentExecutor) — these
live in `lib/snowbridge/contracts/src/`
- **Gateway proxy** — the `Gateway` deployment address is actually a
`GatewayProxy`, not the Gateway implementation. The implementation
address needs to be resolved from the ERC1967 storage slot
- **ServiceManager proxy** — was not being verified at all
Changes:
- Added `contractPath` field to `ContractToVerify` so each contract
specifies its source location relative to the contracts directory
- Added `guessConstructorArgs` option for proxy contracts with complex
encoded init data (uses forge's `--guess-constructor-args`)
- Gateway is now verified as two separate contracts: Gateway
Implementation (address resolved from ERC1967 proxy slot) and
GatewayProxy
- ServiceManager proxy is now verified as `TransparentUpgradeableProxy`
## Update-metadata fix
The `update-metadata` command was ignoring the `--environment` flag when
selecting the deployments file:
1. The handler received a pre-built networkId (`"stagenet-hoodi"`) as
the chain parameter, which `getChainDeploymentParams` couldn't resolve
(falling back to anvil). Now chain and environment are passed
separately.
2. Commander.js routed `--environment` to the parent contracts command,
leaving `options.environment` undefined on the subcommand. Added the
same parent-fallback logic already used for `--chain`.
## Test plan
- [x] `bun typecheck` passes
- [x] Ran `bun cli contracts verify --chain hoodi --environment
stagenet` — all contracts verified successfully on Etherscan
- [x] `bun cli contracts update-metadata --chain hoodi --environment
stagenet` now reads the correct `stagenet-hoodi.json` deployments file
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Wire the Frontier `EthPubSub` module into the node's RPC layer,
enabling WebSocket-based `eth_subscribe`/`eth_unsubscribe` support for
`newHeads`, `logs`, and `newPendingTransactions`
- Use Ethereum-style hex (`0x`-prefixed) subscription IDs via
`EthereumSubIdProvider` for client compatibility
- Add Moonwall test suites (adapted from Moonbeam) covering block header
subscriptions, log filtering (by address, topics, wildcards, conditional
parameters), and pending transaction notifications
## Changes
### `operator/node/src/rpc.rs`
- Import and merge `EthPubSub` / `EthPubSubApiServer` into the RPC
module
- Accept `subscription_task_executor` and `pubsub_notification_sinks`
parameters in `create_full()`
- Remove stale commented-out boilerplate
### `operator/node/src/service.rs`
- Clone `pubsub_notification_sinks` and forward it (along with
`subscription_executor`) into the RPC factory closure
- Set `config.rpc.id_provider` to `EthereumSubIdProvider` for
Ethereum-compatible subscription IDs
### `test/moonwall/suites/dev/stagenet/subscription/`
- `test-subscription.ts` — `newHeads`: subscription ID format, block
header field validation
- `test-subscription-logs.ts` — `logs`: basic log notification on
contract deployment
- `test-subscription-logs2.ts` — `logs`: filtering by single/multiple
addresses, topics, wildcards, conditional and combined parameters (8
cases)
- `test-subscription-pending.ts` — `newPendingTransactions`: pending tx
hash notification
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
## Summary
We are now launching the MSP backend when starting stpragehub services.
In this PR, we also fix the MSP and BSP node configuration and register
it with the correct keys.
## What changed
* Added a launch Backend MSP function that is called when launching
storage hub services
* Fix the wrong genesis error message in storagehub node by removing the
`--chain dev` flags (so it can be launch of the same network as our
local datahaven nodes).
* Use the correct keys to register MSP and BSP. We were injecting
different keys that the one we used for MSP and BSP registration leading
to the MSP and BSP node to never fully register as storage providers.
---------
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
## Summary
- Add multi-environment deployment support (stagenet, testnet, mainnet)
to CLI and contracts
- Configure stagenet and testnet runtimes with correct genesis hashes
and Snowbridge Agent IDs
- Add CLI commands for BEEFY checkpoint updates and rewards origin
computation
- Add ETH validator strategies (native beacon chain ETH + LSTs) to all
config files
## Changes
### Runtime Configuration
**Stagenet Runtime:**
- Set `StagenetGenesisHash` to DataHaven stagenet genesis hash
- Configure `RewardsAgentOrigin` with computed Snowbridge Agent ID
- Add tests verifying rewards account derivation and agent ID
computation
**Testnet Runtime:**
- Set `TestnetGenesisHash` to DataHaven testnet genesis hash
- Configure `RewardsAgentOrigin` with computed Snowbridge Agent ID
- Add tests verifying rewards account derivation and agent ID
computation
The Rewards Agent ID is computed following Snowbridge's location
description pattern:
```
blake2_256(SCALE_ENCODE("GlobalConsensus", ByGenesis(genesis), "AccountKey20", rewards_account))
```
### CLI Enhancements
- All contracts subcommands (`status`, `deploy`, `verify`,
`update-metadata`) now accept `--environment` option
- Config and deployment files use environment-prefixed naming (e.g.,
`stagenet-hoodi.json`, `testnet-hoodi.json`)
- New `update-beefy-checkpoint` command that:
- Connects to a live DataHaven chain via WebSocket RPC
- Fetches all BEEFY data at the same finalized block for consistency
- Uses parallel queries with `Promise.all` for better performance
- Computes authority hashes (keccak256 of Ethereum addresses derived
from BEEFY public keys)
- Uses Snowbridge's quorum formula `n - floor((n-1)/3)` for strictly >
2/3 majority
- New `update-rewards-origin` command that computes the Snowbridge Agent
ID for the rewards pallet
- Centralized validation via `contractsPreActionHook` for all contract
commands
- Environment validation against allowlist (`stagenet`, `testnet`,
`mainnet`)
### Contract Changes
- Network validation uses explicit allowlist instead of suffix matching
- Added `initialValidatorSetId` and `nextValidatorSetId` fields to
`SnowbridgeConfig` struct
- `DeployBase.s.sol` now uses config values for validator set IDs
instead of hardcoded 0/1
- `DeployParams.s.sol` loads validator set IDs from config with
backwards compatibility
### Validator Strategies
Added ETH-equivalent strategies to allow validators to stake using
native ETH or LSTs:
**All Networks:**
- `0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0` - Native beacon chain ETH
(virtual strategy)
**Hoodi Testnet:**
- `0xf8a1a66130d614c7360e868576d5e59203475fe0` - stETH
- `0x24579aD4fe83aC53546E5c2D3dF5F85D6383420d` - WETH
**Ethereum Mainnet:**
- `0x93c4b944D05dfe6df7645A86cd2206016c51564D` - stETH
- `0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2` - rETH
- `0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc` - cbETH
### Config Files
- `stagenet-hoodi.json` - Hoodi testnet with stagenet EigenLayer
addresses
- `testnet-hoodi.json` - Hoodi testnet with testnet EigenLayer addresses
- `mainnet-ethereum.json` - Ethereum mainnet with mainnet EigenLayer
addresses
- Removed `hoodi.json` (replaced by environment-prefixed files)
## Usage
```bash
# Deploy to stagenet on Hoodi
bun cli contracts deploy --chain hoodi --environment stagenet
# Update BEEFY checkpoint from live chain
bun cli contracts update-beefy-checkpoint \
--chain hoodi \
--environment stagenet \
--rpc-url wss://services.datahaven-dev.network/stagenet
# Compute rewards origin for a chain
bun cli contracts update-rewards-origin \
--chain hoodi \
--environment stagenet \
--rpc-url wss://services.datahaven-dev.network/stagenet
# Check deployment status
bun cli contracts status --chain hoodi --environment stagenet
```
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Import `pallet-proxy-genesis-companion` from Moonbeam to enable proxy
account configuration at genesis time
- Configure the pallet in all runtimes (mainnet, stagenet, testnet) with
pallet index 106
- Add `Serialize`/`Deserialize` derives to `ProxyType` enum to satisfy
`MaybeSerializeDeserialize` bounds
- Include mock runtime and unit tests adapted for polkadot-stable2412-6
This pallet extends `pallet-proxy` with genesis configuration support,
allowing proxy relationships to be established at chain genesis rather
than requiring extrinsic calls after launch.
### Key adaptations from Moonbeam
The pallet was modified to work with the DataHaven SDK version
(polkadot-stable2412-6):
- Removed `BlockNumberProvider` associated type constraint (not present
in this version of pallet-proxy)
- Uses `frame_system::pallet_prelude::BlockNumberFor<T>` directly for
delay parameter
- Uses `MaybeSerializeDeserialize` trait bound for `ProxyType`
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
### Summary
Set username deletion to use a 30‑day grace period (in blocks) and added
a non‑zero username deposit, both based on the Moonbeam's runtimes. This
makes username unbinding wait before deletion and makes authority‑issued
usernames non‑free, mitigating the sybil vector while aligning with
existing identity config patterns.
### Changes
- Configures for `pallet_identity`
- `UsernameGracePeriod = 30 * DAYS`
- `UsernameGracePeriod = deposit(0, MaxUsernameLength::get())`
Upgrades to StorageHub version v0.3.3. This upgrade requires both a
runtime and client upgrade.
---------
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
## Summary
### Configuration
- Remove deprecated `deny_warnings` config key from foundry.toml
- Add global `[lint]` config to suppress naming convention warnings for
AVS/EL/ERC patterns (`mixed-case-function`, `mixed-case-variable`)
### DataHavenServiceManager Refactoring
- Rename immutable variables to SCREAMING_SNAKE_CASE
(`_allocationManager` → `_ALLOCATION_MANAGER`, `_rewardsCoordinator` →
`_REWARDS_COORDINATOR`)
- Wrap modifier logic in internal functions (`_checkRewardsInitiator`,
`_checkValidator`, `_checkAllocationManager`) to reduce contract size
- Add `_toAddress` helper with assembly for safe bytes-to-address
conversion
### Safe Typecasting
- Replace direct typecasts with OpenZeppelin's SafeCast library in
deploy scripts and test utilities
- Use `.toUint32()`, `.toUint64()`, `.toUint160()` for
overflow-protected conversions
- Replace `bytes32("wrong origin")` string cast with hex literal in test
deployer
### Code Cleanup
- Remove 25+ unused imports across script and test files
- Convert plain imports to named imports for better clarity
- Use `SafeERC20.safeTransfer()` for token transfers in tests
- Change `view` to `pure` where appropriate
## Test plan
- [x] `forge build` completes with no warnings
- [x] `forge test` passes all 10 tests
## Summary
Reorganizes the test directory structure for better clarity and
maintainability:
- **Rename `test/datahaven/` → `test/moonwall/`**: Clearly identifies
these as Moonwall single-node tests
- **Move `test/framework/` → `test/e2e/framework/`**: Groups e2e test
utilities under a dedicated folder
- **Move `test/suites/` → `test/e2e/suites/`**: Groups e2e test suites
with the framework
- **Add `test/e2e/framework/validators.ts`**: Extracts validator test
helpers from utils into the e2e framework
- **Update documentation**: README.md and E2E_FRAMEWORK_OVERVIEW.md
reflect the new structure
### New Directory Structure
```
test/
├── e2e/
│ ├── suites/ # E2E test suites (Kurtosis-based)
│ └── framework/ # E2E test utilities & helpers
├── moonwall/ # Moonwall single-node tests
├── launcher/ # Network deployment tools
└── ...
```
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Add zero address validation across all functions that accept address
parameters to prevent misconfiguration
- Fix race condition in `buildNewValidatorSetMessage()` that could cause
reverts during validator deregistration
- Refactor contract for improved readability and reduced code
duplication
- Update AVS metadata URL to point to the correct hosted JSON file
## Changes
### Security & Validation
- Add `ZeroAddress` error and validate all address inputs in
`initialize`, `setRewardsInitiator`, `setSnowbridgeGateway`,
`addValidatorToAllowlist`, `registerOperator`, and
`updateSolochainAddressForValidator`
- Fix race condition: filter out zero solochain addresses in
`buildNewValidatorSetMessage()` to prevent reverts when a validator is
mid-deregistration
### Refactoring
- Replace verbose `if/revert` patterns with `require` statements for
consistency
- Inline single-use internal functions (`_createDataHavenOperatorSets`,
`_setRewardsInitiator`)
- Consolidate duplicate error types into single `ZeroAddress` error
- Rename `initialise` → `initialize` to maintain consistency with the
transparent upgradability pattern
- Optimize validator set message encoding by removing redundant wrapper
function
### Observability
- Add `SolochainAddressUpdated` event for tracking validator address
changes
### Cleanup
- Remove unused remappings from `foundry.toml`
- Fix typo in metadata description
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
## Summary
Remove `compressedPubKeyToEthereumAddress` and the `crypto.ts` because
the function is not being used and it has been already defined in the
file `datahaven.ts`.
## What changed
* Deleted the file `test/launcher/utils/crypto.ts` as the function what
the only function defined in the file
* Update `index.ts` to reflect the change mentioned above
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
## Summary
- Increase `MaxBatchConfirmStorageRequests` runtime constant from 10 to
100
- Applied across all runtime environments: mainnet, stagenet, and
testnet
## Test plan
- [x] Verify builds pass for all runtime configurations
## Summary
This PR integrate the slashing feature with EigenLayer. With this PR,
slashing can now be relayed to our Datahaven AVS and then executed
within EigenLayer. In addition some refactoring of the original slashing
pallet has been done.
## Motivation
To avoid misbehaving actor in the network, Datahaven has implemented a
slashing pallet in which offenses can be reported and then if adequate
can lead to a sanction on the misbehaving node. It incentive nodes to
only follow good behavior in addition to the reward incentive. The
rewards flow is managed directly into EigenLayer (see
https://github.com/datahaven-xyz/datahaven/pull/351).
## Slashing flow
<img width="2355" height="946" alt="Slashing Flow"
src="https://github.com/user-attachments/assets/c1ddc3dc-2a7e-429d-94e0-1e02a3f65246"
/>
## What changes
* Implemented `slashValidatorsOperator` in `DataHavenServiceManager`. It
received all the slashing requests batched (every new era the queued
slashing are being relayed from substrate to Ethereum). It handle the
slashing of the operators reported into the Validator set.
* Added a `slashes_adapter.rs` utility file to remove the duplication
for each runtime. In addition, we made use of the `sol!` macro from
alloy to encode the calldata for the Ethereum call. This avoid rewriting
encoding logic and allow to remove the hardcoded selector value used to
call the slashing function.
* Added some tests in solidity to test the registering and slashing of
an operator in Ethereum via Eigen Layer.
* Added e2e tests that test the injection of a slash request, it being
relayed via the snowbridge relayer and executed by our Datahaven AVS.
## What could be better
* We are only deploying one strategy for now so it is hardcoded in the
slashing flow. We should be able to update the pallet in case we are
adding a new strategy. So communication from Ethereum should be relayed.
* We don't have error being return in case the slashing fail. Which
could happen if we don't have the right number of strategy or the
validator is not registered... etc.
* More tests for the unhappy path
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>
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>
## 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>
## Summary
This PR improve the generating state workflow. It will also check for
outdated state-diff.json and add a practical script to easily generate a
new one.
The way we generate state has also been changed to make it work with
macOS M1 system. We don't run the tool in the container anymore but
instead directly on the machine.
## What changes
* A check-generated-state.js script was added to quickly look for
outdated test
* The check was added in the CI
* A generate-contracts.ts script was added to easily generate the new
state with the new instructions to run on MacOS
---------
Co-authored-by: Gonza Montiel <gon.montiel@gmail.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
## Summary
This PR removes sources of flakiness in the rewards message E2E test by:
1. Selecting a validator whose Ethereum key is not shared with the
relayers
2. Using specific block numbers for balance reads to avoid stale RPC
data
## Changes
- Select a non-relayer validator from rewardPoints.individual when
claiming rewards
- Remove the conditional updateSolochainAddressForValidator block
(mapping is set during registration)
- Read balances at specific block numbers to ensure deterministic
comparisons
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
## 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>
## Summary
This PR remove the middlewares contracts from eigen layer. Instead we
are planning to use the eigne layer contract directly. It also removes
the tests related to the middleware slasher code and the mock contract
used in it.
## Motivation
When slashing an operator in the Dathaven we are going through the
substrate slashing pallet already implemented. It already allow to
configure a slashing window and/or to cancel a slashing. In the future
it will also be compatible with a government pallet. This part of code
is therefore redundant. For the same reason we remove the tests because
we are not using the slashing middleware contracts.
## What changed
* Remove the slasher middleware files
* Remove the tests related to the middleware slasher file
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>
## Summary
Optimizes the DataHaven parameter configuration during E2E test
infrastructure setup by batching multiple extrinsics into a single
transaction, reducing setup time.
## Problem
Setting runtime parameters required 5 separate
`Parameters.set_parameter` calls, each waiting for block finality. This
created unnecessary delays during infrastructure setup since each call
blocked sequentially.
## Solution
- **Batch parameter updates:** Combine all `Parameters.set_parameter
`calls into a single `Utility.batch_all` transaction wrapped in
`Sudo.sudo`.
- **~5× faster parameter setup:** Only wait for finality once instead of
5 separate times
- **Code simplification:** Refactored parameter handling code, removing
~190 lines of unnecessary abstractions and complexity
Decrease the number of Ethereum nodes being spawn when launching the
network locally.
It is not needed to have several nodes to be able to produce blocks only
one EL, CL and validator works fine. This will save some resources.
Unfortunately it doesn't reduce the time to setup by a significant
amount.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
## 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>
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>
### 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>
## Summary
The e2e test is currently not being executed and return an `error code
-32000: intrinsic gas too high` error during the setup. This is somehow
happening with reth v1.7.0. It is fixed by specifying a more recent reth
version. In this PR we pin it too the latest v1.9.3.
The complete error
```
[11:07:06.624] ERROR (544356): Failed to setup test suite: slash
# Unhandled error between tests
-------------------------------
150 | const getErc20BalanceCmd = `${castExecutable} call ${underlyingTokenAddress} "balanceOf(address)(uint256)" ${tokenCreator} --rpc-url ${rpcUrl}`;
151 | const getEthBalanceCmd = `${castExecutable} balance ${tokenCreator} --rpc-url ${rpcUrl}`;
152 | console.log(getErc20BalanceCmd);
153 | const { stdout: ethBalanceOutput } = await $`sh -c ${getEthBalanceCmd}`.quiet();
154 | console.log(ethBalanceOutput.toString().trim());
155 | const { stdout: erc20BalanceOutput } = await $`sh -c ${getErc20BalanceCmd}`.quiet();
^
ShellError: Failed with exit code 1
exitCode: 1,
stdout: "",
stderr: "Error: server returned an error response: error code -32000: intrinsic gas too high\n",
at new ShellError (13:16)
at new ShellPromise (75:16)
at BunShell (191:35)
at <anonymous> (/home/lola/Workspace/Moonsonglabs/datahaven/test/scripts/fund-validators.ts:155:50)
at /home/lola/Workspace/Moonsonglabs/datahaven/test/suites/slash.test.ts:22:1
```
## What changed
* Updated the kurtosis `minimal.yml` file to pin the reth image to the
v1.9.3 (the latest to this date)
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
# Enable AVS owner workflow
Until now, the deployer of the contracts and the owner of the deployed
contracts where the same account. Even if we allowed a different owner
to be specified, we were using the same. For this reason, a private key
was required, so after the deployment we could execute owned
transactions needed for the CLI.
In this PR we:
- Add a mechanism to the CLI to specify a different owner account other
than the deployer via `--avs-owner-address`
- Add CLI flags `--avs-owner-key` and`--execute-owner-transactions` so
account ownership vs. immediate execution is explicit and deferred. If
both previous parameters are provided, the CLI will execute the
transactions using the private key provided.
- Allow DataHaven AVS deploy scripts to toggle owner-call execution via
an env flag `TX_EXECUTION`
- Add documentation on how the new parameters work in `test/README.md`
and `test/docs/deployment.md`.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
## ⚠️ 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`.