## Summary
- guard `DataHavenServiceManager.submitRewards` by `(startTimestamp,
duration, token)` so each reward window can only be submitted once per
token
- expose the replay-guard state and error in the interface, add Foundry
coverage, wire the missing runtime `std` features, and regenerate the
Wagmi/storage/state-diff artifacts
- fix the local slash E2E path by aligning the `anvil` Snowbridge
`messageOrigin` with `stagenet-local`, refreshing the tracked anvil
deployment metadata, and waiting for `ServiceManager.SlashingComplete`
## Testing
- `cargo fmt --all -- --check`
- `forge test --match-contract RewardsSubmitterTest`
- `forge test --match-contract StorageLayoutTest -vvv`
- `./scripts/check-storage-layout.sh`
- `./scripts/check-storage-layout-negative.sh`
- `bun ./scripts/check-generated-state.ts`
- `bun generate:wagmi`
- `bun test ./e2e/suites/slash.test.ts --timeout 1200000
--test-name-pattern "verify we have the agent origin set|Activate
slashing|use sudo to slash operator"`
## Notes
- Slash E2E verification reran the previously failing sudo slash path;
the long liveness scenario was not rerun end to end.
## Summary
Replace per-era rewards submission with EigenLayer-aligned window-based
submission. Instead of submitting one rewards message per era, points
are now accumulated into fixed-duration time-aligned windows as blocks
are produced, and inflation is proportionally distributed across windows
at era boundaries. Closed windows are submitted as
`OperatorDirectedRewardsSubmissions` to EigenLayer via Snowbridge.
This ensures rewards submissions match EigenLayer's expected window
boundaries (`GENESIS_REWARDS_TIMESTAMP` + N ×
`CALCULATION_INTERVAL_SECONDS`), eliminating the mismatch between
Substrate era timing and EigenLayer reward windows.
### How rewards windows work
**Points accumulation**: Each time `reward_by_ids()` is called (at
session end), the current timestamp is snapped to the nearest
EigenLayer-aligned window boundary. Points are written to
`WindowOperatorPoints[window_start]`, so all sessions ending within the
same window contribute to the same bucket.
**Inflation allocation**: At era end, the era's scaled inflation is
split across all windows that overlap with the era's time span,
proportionally to the overlap duration:
```
overlap = min(era_end, window_end) - max(era_start, window_start)
portion = scaled_inflation * overlap / era_duration
```
Integer division remainder is assigned to the last overlapping window to
ensure conservation.
**Submission**: `process_closed_windows()` iterates all fully elapsed
windows starting from `NextWindowToSubmit`. For each closed window, if
it has both points and inflation, a cross-chain message is sent via
Snowbridge. Empty or zero-point windows are skipped. The pointer
advances regardless of success or failure.
### Key changes
#### Pallet (`external-validators-rewards`)
- **Window-based point accumulation**: `reward_by_ids()` now writes
operator points into `WindowOperatorPoints[window_start]` in addition to
the existing per-era storage, where the window is determined by snapping
the current timestamp to the nearest EigenLayer-aligned boundary
- **Proportional inflation allocation**: At era end,
`allocate_era_inflation_to_windows()` distributes the era's scaled
inflation across overlapping windows based on time overlap, with
rounding remainder assigned to the last window
- **Closed window submission**: `process_closed_windows()` iterates
fully elapsed windows, combines their points and inflation into a
`RewardsPeriodUtils`, and sends the cross-chain message. A
`NextWindowToSubmit` pointer ensures idempotent progression
- **Removed legacy path**: The per-era `generate_era_rewards_utils` and
direct submission in `on_era_end` are removed. The `rewards_duration()`
method is removed from `RewardsSubmissionConfig` trait — duration now
comes from `RewardsPeriodUtils`
- **Renamed `EraRewardsUtils` to `RewardsPeriodUtils`**: The struct and
its fields are renamed to reflect window/period semantics (`era_index` →
`period_index`, `era_start_timestamp` → `period_start`)
- **New pallet config types**: `RewardsWindowGenesisTimestamp`
(immutable EigenLayer genesis), `RewardsWindowDuration`
(governance-tunable via `RewardsDuration` runtime param), and `UnixTime`
- **Storage version bump**: 0 → 1 for the three new storage items
- **New events**: `RewardsWindowSubmitted`,
`RewardsWindowSubmissionFailed`, `RewardsWindowSkipped` (replaces
`RewardsMessageSent`)
#### Contracts (`DataHavenServiceManager.sol`)
- **Duplicate operator merge**: Replace `_sortOperatorRewards` with
`_sortAndMergeDuplicateOperators` — after solochain-to-ETH address
translation, multiple solochain addresses can map to the same ETH
operator (e.g., operator deregistered and re-registered with a new
solochain address within the same reward window). The new function sorts
then merges consecutive duplicates by summing their amounts, satisfying
EigenLayer's strict ascending uniqueness requirement.
### New storage items
| Storage | Type | Purpose |
|---------|------|---------|
| `WindowOperatorPoints` | `Map<u32, BTreeMap<H160, u32>>` | Per-window
operator reward points |
| `WindowInflationAmount` | `Map<u32, u128>` | Per-window allocated
inflation |
| `NextWindowToSubmit` | `Value<u32>` | Pointer to next window to
process |
### Out of scope (follow-up PRs)
- **Translation robustness**: Retain
`validatorSolochainAddressToEthAddress` on deregistration to prevent
`UnknownSolochainAddress` reverts for delayed window submissions
- **Retry-safe failure handling**: Preserve failed windows for replay
- **Retained window snapshots**: Audit/replay support
- **Benchmarks/weights**: Re-run pallet benchmarks for updated
`on_era_end` weight
## Test plan
- [x] Pallet unit tests pass (`cargo test -p
pallet-external-validators-rewards` — 81 tests)
- [x] `window_mode_attributes_points_to_aligned_window` — points land in
correct aligned window
- [x] `window_mode_submits_closed_windows_and_advances_pointer` — closed
window submission and pointer advancement
- [x] `window_mode_era_inflation_split_across_multiple_windows` —
pro-rata inflation splitting across 4 windows with remainder
conservation
- [x] `window_mode_submits_multiple_closed_windows_in_single_era_end` —
batch submission of multiple closed windows in one `on_era_end`
- [x] `window_mode_delivery_failure_emits_submission_failed_event` —
delivery failure emits `RewardsWindowSubmissionFailed`, clears storage,
advances pointer
- [x] Existing pallet tests updated to use window-based events
(`RewardsWindowSubmitted`, `RewardsWindowSkipped`)
- [x] Contract tests pass (`forge test --match-contract
RewardsSubmitter` — 12 tests)
- [x] `test_submitRewards_mergesDuplicateTranslatedOperators` — two
solochain addresses mapping to same ETH operator are merged into single
entry
- [x] Runtime configs compile with new config types
(mainnet/stagenet/testnet)
- [x] E2E test with local network to verify cross-chain message reaches
EigenLayer contracts
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
## Polkadot upgrade 2503
In this PR, we upgrade form Polkadot SDK 2412 to Polkadot SDK 2503. In
order to upgrade the SDK we need to upgrade some dependencies :
StorageHub and frontier simultaneously.
## What changes
### Trivial changes
* https://github.com/paritytech/polkadot-sdk/pull/7634 -> The new trait
is required in all the pallets using scale encoding.
* https://github.com/paritytech/polkadot-sdk/pull/7043 -> Remove
deprecated `sp-std` and replace with `alloc` or `core`.
* https://github.com/paritytech/polkadot-sdk/pull/6140 -> Accurate
weight reclaim with frame_system::WeightReclaim
### Breaking changes
* https://github.com/paritytech/polkadot-sdk/pull/2072 -> There is a
change in `pallet-referenda`. Now, the tracks are retrieved as a list of
`Track`s. Also, the names of the tracks might have some trailing null
values (`\0`). This means display representation of the tracks' names
must be sanitized.
* https://github.com/paritytech/polkadot-sdk/pull/5723 -> adds the
ability for these pallets to specify their source of the block number.
This is useful when these pallets are migrated from the relay chain to a
parachain and vice versa. (Not entirely sure it is breaking as it is
being marked as backward compatible).
* https://github.com/paritytech/polkadot-sdk/pull/6338 -> Update
Referenda to Support Block Number Provider
### Notable changes
* https://github.com/paritytech/polkadot-sdk/pull/5703 -> Not changes
required in the codebase but impact fastSync mode. Should improve
testing.
* https://github.com/paritytech/polkadot-sdk/pull/5842 -> Removes
`libp2p` types in authority-discovery, and replace them with network
backend agnostic types from `sc-network-types`. The `sc-network`
interface is therefore updated accordingly.
## What changes in Frontier
* https://github.com/polkadot-evm/frontier/pull/1693 -> Add support for
EIP 7702 which has been enable with Pectra. This EIP add a new field
`AuthorizationList` in Ethereum transaction.
Changes example :
```rust
#[test]
fn validate_transaction_fails_on_filtered_call() {
...
pallet_evm::Call::<Runtime>::call {
source: H160::default(),
target: H160::default(),
input: Vec::new(),
value: sp_core::U256::zero(),
gas_limit: 21000,
max_fee_per_gas: sp_core::U256::zero(),
max_priority_fee_per_gas: Some(sp_core::U256::zero()),
nonce: None,
access_list: Vec::new(),
authorization_list: Vec::new(),
}
.into(),
```
## ⚠️ Breaking Changes ⚠️
* Upgrade to Stirage hub v0.5.1
* Support for Ethereum latest upgrade (txs now have the
`authoriation_list` for support EIP 7702)
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
Introduces typed offence classification, a linear Perbill-to-WAD
conversion for EigenLayer slashing, historical offence filtering, and a
new E2E test proving end-to-end liveness detection through
`pallet_im_online`.
---
### OffenceKind enum
New `OffenceKind` enum classifies consensus offences:
- `LivenessOffence` — missed heartbeats (ImOnline)
- `BabeEquivocation` — double block production
- `GrandpaEquivocation` — double finality votes
- `BeefyEquivocation` — double BEEFY votes / fork voting / future block
voting
- `Custom(BoundedVec<u8, 256>)` — manual / governance slashes
Each variant carries a human-readable description string through the
Snowbridge message to EigenLayer's
`DatahavenServiceManager.slashValidatorsOperator()`.
### EquivocationReportWrapper
Generic wrapper around `ReportOffence` wired for BABE, GRANDPA, BEEFY,
and ImOnline in all three runtimes:
1. **Filters historical offences** — discards reports whose session
predates the bonding period, using `BondedEras` storage (analogous to
`FilterHistoricalOffences` in `pallet_staking`, but adapted to this
pallet's own era tracking).
2. **Tags offence kind** — stores the `OffenceKind` in
`PendingOffenceKind` double-map `(SessionIndex, ValidatorId)` before
delegating to `pallet_offences`. The `on_offence` handler reads it via
`take()` in the same block.
3. **Cleans up on failure** — removes stale `PendingOffenceKind` entries
if the inner reporter returns an error (e.g. duplicate report),
preventing them from leaking into unrelated future offences.
### Perbill to WAD conversion and MaxSlashWad
#### How Substrate computes slash fractions
Each offence type in Substrate defines its own
`slash_fraction(offenders_count)` returning a `Perbill`:
| Offence | Formula | Typical range |
|---|---|---|
| **BABE equivocation** | `min((3k/n)^2, 1)` | 1 offender / 100
validators: ~0.09%; 1/2: capped to 100% |
| **GRANDPA equivocation** | `min((3k/n)^2, 1)` | Same as BABE |
| **BEEFY double-vote** | `min((3k/n)^2, 1)` | Same as BABE/GRANDPA |
| **BEEFY fork/future voting** | Fixed `50%` | Always 50% |
| **ImOnline liveness** | `min(3*(k - floor(n/10) - 1)/n, 1) * 7%` | 10%
or fewer offline: **0%**; ~33% offline: ~5%; ~43% offline: 7% (max) |
Where `k` = number of concurrent offenders, `n` = validator set size.
**Key behavior for small validator sets (E2E):** With n=2, the ImOnline
threshold is `floor(2/10) + 1 = 1`. A single offender (`k=1`) fails
`checked_sub(1)` giving `Perbill(0)`. This means no `Slashes` storage
entry is created (since `compute_slash` returns `None` when the new
fraction doesn't exceed the prior slash), but the `SlashReported` event
is still emitted, proving the full detection pipeline works.
#### Linear conversion to EigenLayer WAD
The Substrate `Perbill` is linearly mapped to a WAD value capped by
`MaxSlashWad`:
```
WAD = perbill.deconstruct() * MaxSlashWad / 1_000_000_000
```
- `MaxSlashWad` default: **5e16** (= 5% in WAD format, where 1e18 =
100%)
- Governance-changeable dynamic runtime parameter (codec index 46)
- `Perbill(100%)` maps to exactly `MaxSlashWad` (the cap)
- `Perbill(0%)` maps to 0 (no slash sent to EigenLayer)
#### Concrete examples (with default MaxSlashWad = 5%)
| Scenario | Substrate Perbill | WAD sent to EigenLayer | EigenLayer % |
|---|---|---|---|
| BABE equivocation (1 of 100 validators) | ~0.09% | ~4.5e13 | ~0.0045%
|
| BABE equivocation (1 of 2 validators) | 100% (capped) | 5e16 | 5%
(max) |
| BEEFY fork voting | 50% | 2.5e16 | 2.5% |
| ImOnline liveness (1 of 2 offline) | 0% | 0 (no slash) | 0% |
| ImOnline liveness (~33% of large set offline) | ~5% | ~2.5e15 | ~0.25%
|
| Manual `force_inject_slash` at 20% | 20% | 1e16 | 1% |
| Manual `force_inject_slash` at 100% | 100% | 5e16 | 5% (max) |
The same WAD value is applied uniformly to all configured strategies via
the `SlashingRequest` struct sent through Snowbridge to
`DatahavenServiceManager.slashValidatorsOperator()`.
### E2E liveness slashing test
New test scenario (`should detect and slash an unresponsive validator`)
validates the full liveness detection pipeline:
1. Pauses bob's Docker container (preserving GRANDPA state via `docker
pause`)
2. Waits 200s (>= 2 full sessions) for `pallet_im_online` to detect
missed heartbeats
3. Unpauses bob to restore GRANDPA finality (2/2 validators needed)
4. Polls for `SlashReported` event (not `Slashes` storage — see slash
fraction note above)
5. Verifies the event confirms the full pipeline: `pallet_im_online ->
EquivocationReportWrapper -> pallet_offences -> on_offence`
The test uses `try/finally` to always unpause bob, `{ at: "best" }`
queries for non-finalized chain state during the pause, and drains prior
`SlashReported` events before starting.
### Tests
- **10 new unit tests**: `PendingOffenceKind` double-map semantics,
session isolation, wrapper historical filtering, error cleanup, WAD
conversion (100%, 50%, 0%), offence kind description propagation
- **New mock infrastructure**: `MockInnerReporter`, `MockOffence`,
`MockOkOutboundQueue` with slash data capture
- **E2E**: Updated `force_inject_slash` test to use `offence_kind` enum,
new liveness detection test
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
Co-authored-by: undercover-cactus <lola@moonsonglabs.com>
## Summary
The rewards message sent to EigenLayer AVS via Snowbridge was reporting
the **full inflation amount** (100%) as the distributable rewards, while
only **80%** was actually minted to the rewards account (the other 20%
goes to treasury). This mismatch could cause underfunding, failed reward
claims, or accounting discrepancies on the Ethereum side.
### Root Cause
In `on_era_end`, the same `scaled_inflation` value was passed to both
`mint_inflation` (which internally splits 80/20) and
`generate_era_rewards_utils` (which builds the outbound AVS message).
The message therefore claimed more tokens were available for
distribution than were actually in the rewards account.
### Fix
- Changed `HandleInflation::mint_inflation` to return a self-documenting
`InflationMintResult` struct instead of `DispatchResult`:
```rust
pub struct InflationMintResult {
pub rewards_amount: u128, // minted to rewards account (for AVS
distribution)
pub treasury_amount: u128, // minted to treasury account
}
```
- Restructured `on_era_end` to use `mint_result.rewards_amount`
(post-treasury split) when building the AVS message and emitting the
event
- Added an explicit reward-points check before minting to preserve the
existing behavior of skipping inflation when no validators earned
rewards
### Files Changed
- **`types.rs`** — Added `InflationMintResult` struct; updated
`HandleInflation` trait signature
- **`inflation.rs`** — Updated handler to return `InflationMintResult`;
strengthened unit tests to assert both fields
- **`lib.rs`** — Restructured `on_era_end`: check points → mint → build
message with `mint_result.rewards_amount`
- **`mock.rs`** — Updated mock to return `InflationMintResult`
- **Runtime configs** (mainnet, stagenet, testnet) — Updated wrapper
impls
- **`tests.rs`** — Updated event assertion to expect post-treasury
amount
## Test plan
- [x] All 76 pallet unit tests pass (`cargo test -p
pallet-external-validators-rewards --lib`)
- [ ] CI passes
## Add missing weights
The aim of this PR is to complete our weights by enabling more runtime
benchmarks from the pallets used in DataHaven. I will start this effort
with Storage Hub pallets.
## What's included
- [x] `pallet_nfts`
- [x] Added signing helper for pallet nfts
- [x] Add pallet to benchmarks
- [x] Run benches and generate weights
- [x] Wire up weights to runtimes
- [x] `pallet_session`
- [x] Added a `pallet_session_benchmarking` crate
- [x] Added signing helpers
- [x] Add pallet to benchmarks
- [x] Run benches and generate weights
- [x] Wire up weights to runtimes
- [x] `pallet_payment_streams`
- [x] Add `TreasuryAccount` helper and configure properly
- [x] Add pallet to benchmarks
- [x] Run benches and generate weights
- [x] Wire up weights to runtimes
- [x] `pallet_storage_providers`
- [x] Add `TreasuryAccount` helper and configure properly
- [x] Add pallet to benchmarks
- [x] Run benches and generate weights
- [x] Wire up weights to runtimes
- [x] `pallet_file_system`
- [x] Add pallet to benchmarks and configure properly
- [ ] Run benches and generate weights
- [x] Wire up weights to runtimes
- [x] `pallet_proofs_dealer`
- [x] Add pallet to benchmarks and configure properly
- [x] Run benches and generate weights
- [x] Wire up weights to runtimes
## What's not included
- `pallet_identity` - We'll enable it once we update to `2506`, that
will allow us to have a BenchmarkHelper in the config on the pallet (see
[here](ac28323e7d/operator/runtime/mainnet/src/configs/mod.rs (L632-L643)))
- `pallet_grandpa` - the upstream pallet defines
[benchmarks](bbc435c766/substrate/frame/grandpa/src/benchmarking.rs (L25))
for `check_equivocation_proof` and `note_stalled`, but the required
weights to be wired are actually `report_equivocation`,
`report_equivocation_unsigned` and `note_stalled`. That means including
`pallet_grandpa` in the benchmarks results in an inconsistent
`WeightInfo` implementation, so further understanding in the pallet's
approach to benchmarking is needed.
- `pallet_file_system` -> Run benches and generate weights. Weights will
fail because of a hardcoded `AccountId32` on the
[benchmarks](57d2a195d5/pallets/file-system/src/benchmark_proofs.rs (L69-L71)).
I'll create a PR for SH soon.
These two are left for a follow up PR.
## 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
## 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
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>
## 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>
## 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>
Update the 3 DataHaven environments' chain IDs & native token ticker as
follows:
* **Mainnet**
* **Chain ID**: 55930
* **Ticker**: HAVE
* **TestNet**
* **Chain ID**: 55931
* **Ticker**: MOCK
* **Stagenet**
* **Chain ID**: 55932
* **Ticker**: STAGE
The PR includes a storage migration for the Stagenet & Testnet
environments, that are already live, to update the EVM Chain ID stored
in the `pallet-evm-chain-id` pallet.
Note: the token symbol will only be updated with the genesis config
presets or newly generated chain specs. For already live networks, the
existing chain spec must be updated (i.e. the tokenSymbol property
changed) and used by all nodes in the network. This change in the chain
spec will not alter the chain genesis so it safe to do (in the very
early stages of the chain obviously).
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Adds automated license compliance checking via GitHub Actions CI
workflow
- Implements a license verification script that validates all Rust
dependencies against approved licenses, authors, and packages
- Standardizes author metadata across Cargo manifests to "Moonsong Labs"
## Changes
**CI Workflow** (`.github/workflows/task-check-licenses.yml`)
- Triggers on pull requests and manual dispatch
- Installs Rust 1.88.0 toolchain and `cargo-license` tool
- Executes license verification script to enforce compliance
**License Verification Script** (`operator/scripts/verify-licenses.sh`)
- Uses `cargo-license` to extract dependency license information
- Maintains three allowlists:
- **Licenses**: Apache-2.0, MIT, BSD variants, GPL-3.0, MPL-2.0, and
compatible combinations
- **Authors**: PureStake, Parity Technologies, Moonsong Labs, Frontier
developers, StorageHub Team
- **Package Names**: Known safe packages like ring
- Fails the build if any dependency has unapproved license/author/name
combination
**Cargo Manifest Updates**
- `operator/Cargo.toml`: Standardized workspace author to "Moonsong
Labs"
- `operator/precompiles/precompile-registry/Cargo.toml`: Uses workspace
author field
- `operator/runtime/common/Cargo.toml`: Added workspace author field
## Benefits
- **Legal Compliance**: Ensures all dependencies use OSI-approved or
compatible licenses
- **Supply Chain Security**: Validates dependencies come from trusted
sources
- **Automated Enforcement**: Catches licensing issues during PR review
rather than at release time
- **Transparency**: Provides clear audit trail of approved licenses and
authors
## Summary
- rename the FRAME alias for `pallet_evm` from `Evm` to `EVM` across the
mainnet, stagenet, and testnet runtimes
- adjust benchmarks, configuration modules, genesis builders, and
runtime tests to rely on the new alias
- keep precompile genesis setup and proxy/precompile tests aligned with
the updated names
## Context
Frontier’s `StorageOverrideHandler` (see
`fc_storage::StorageQuerier::account_code`) reads contract bytecode from
`pallet_evm::AccountCodes` using the constant `PALLET_EVM = b"EVM"` to
build the storage key:
`twox_128("EVM") ++ twox_128("AccountCodes") ++ …`
Our runtimes exported `pallet_evm` as `Evm`, so substrate stored
bytecode under the *camel-cased* prefix (`twox_128("Evm")`). Every call
that ultimately hits the storage override—including `eth_getCode`,
`eth_call`, and state queries during replay—therefore failed to locate
code for *any* account (deployed contracts and precompiles alike).
Renaming the alias to `EVM` realigns the storage prefix with Frontier’s
expectations so the override layers can pull bytecode correctly.
## Testing
- `cargo check -p datahaven-node`
- `cargo build --release -p datahaven-node`
- `eth_getCode 0x0000000000000000000000000000000000000802` → returns
`0x60006000fd`
## Storage Migration
Renaming a pallet alias changes the storage prefix for all pallet data.
Without migration, existing EVM data (smart contracts, account codes,
storage) would become inaccessible.
**Migration details:**
- **Type**: Multi-Block Migration (MBM)
- **Storage migrated**: `AccountCodes`, `AccountCodesMetadata`,
`AccountStorages`
- **Migration ID**: `datahaven-evm-mbm` (version 0 → 1)
**Testing the migration:**
```bash
# Build runtime with try-runtime
cargo build --release --features try-runtime -p
datahaven-stagenet-runtime
# Test against stagenet
try-runtime \
--runtime
./target/release/wbuild/datahaven-stagenet-runtime/datahaven_stagenet_runtime.wasm
\
on-runtime-upgrade \
--blocktime 6000 \
--checks all \
--disable-spec-version-check \
live --uri wss://dh-validator-0.datahaven-kt.xyz
```
Test results from stagenet:
- ✅ Migration completes in 1 block
- ✅ PoV size: ~5.3 KB
- ✅ Weight consumption: <0.1% of block capacity
- ✅ All 39 keys successfully migrated
## ⚠️ Breaking Changes ⚠️
If you are manually computing storage keys for the EVM pallet (e.g., directly querying chain state), you must update your code to use the new storage prefix:
- Old prefix: twox128("Evm") = 0x8b90cb...
- New prefix: twox128("EVM") = 0x6a5e91...
All EVM-facing interfaces remain unchanged.
## Overview
This PR integrates Substrate's `pallet-migrations` into all DataHaven
runtimes (mainnet, stagenet, testnet) to enable robust multi-block
migration capabilities. This infrastructure allows complex runtime
upgrades to be executed across multiple blocks while maintaining chain
stability and providing governance controls.
## What Changed
### Core Integration
- **Added `pallet-migrations` dependency** across all runtime
configurations
- **Integrated migration pallet** as pallet index 39 in all runtimes
- **Created shared migration configuration** in
`datahaven-runtime-common`
### Runtime Configuration
- **Mainnet, Stagenet, and Testnet** now include identical migration
configurations
- **MaxServiceWeight** parameter set to 75% of max block weight for safe
migration execution
- **Migration cursor limits** configured (65KB max cursor, 256B max
identifier)
- **Failure handling** configured to freeze the chain on migration
failures (similar to Moonbeam's maintenance mode)
## Future Work
- [ ] Add custom failure handler (safe mode) to replace chain freeze
- [ ] Generate DataHaven-specific benchmarks for migration weights
---------
Co-authored-by: undercover-cactus <lola@moonsonglabs.com>
## Summary
This PR integrates the Substrate Proxy pallet into DataHaven runtimes
(testnet, stagenet, mainnet) with comprehensive test coverage. The proxy
pallet enables account delegation functionality, allowing accounts to
authorize other accounts to execute calls on their behalf with
configurable permissions.
## Changes
### Proxy Pallet Integration
- **Added proxy pallet** to all three DataHaven runtimes (testnet,
stagenet, mainnet)
- **Configured custom ProxyType enum** with DataHaven-specific proxy
types:
- `Any` - Unrestricted proxy access
- `NonTransfer` - All calls except balance transfers
- `Governance` - Governance and utility calls only
- `Staking` - Staking operations (placeholder)
- `CancelProxy` - Proxy announcement cancellation
- `Balances` - Balance transfer operations only
- `IdentityJudgement` - Identity judgement operations
- `SudoOnly` - Privileged sudo operations only
### Runtime Configuration
- **Integrated pallet-proxy** into runtime construction macros
- **Configured proxy deposits** (base + per-proxy fees)
- **Set proxy limits** (maximum proxies per account)
- **Implemented InstanceFilter** for call filtering per proxy type
- **Added proxy pallet to runtime APIs** and metadata
### Comprehensive Test Suite
- `operator/runtime/testnet/tests/proxy.rs` - 24 comprehensive proxy
tests
- `operator/runtime/stagenet/tests/proxy.rs` - 24 comprehensive proxy
tests
- `operator/runtime/mainnet/tests/proxy.rs` - 24 comprehensive proxy
tests
- Updated `lib.rs` files in all three runtimes to include proxy test
modules
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Enable account proxies across mainnet, stagenet, and testnet with
configurable types, delays, and per-type call permissions.
- Support anonymous (pure) proxies, proxy announcements with delays, and
deposit/limit parameters for proxy management.
- Tests
- Add comprehensive integration tests covering proxy lifecycle,
filtering, pure proxies, announcements, batching, chaining, multisig,
identity, and sudo paths.
- Test builder now supports optional sudo setup.
- Chores
- Add benchmarking, weights, and try-runtime support for proxies.
- Update internal package metadata version.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
This PR integrates the Substrate FRAME Treasury pallet across all three
DataHaven runtime environments (`testnet`, `mainnet`, `stagenet`) with a
custom fee allocation mechanism and comprehensive test coverage.
### Key Changes
#### Treasury Pallet Integration:
- Added Treasury pallet to all three runtimes (testnet, mainnet,
stagenet) with 20% fee allocation and 80% burn mechanism.
- Implemented dynamic fee proportion control via
`FeesTreasuryProportion` runtime parameter.
- Integrated treasury with custom fee handlers:
`DealWithEthereumBaseFees`, `DealWithEthereumPriorityFees`, and
`DealWithSubstrateFeesAndTip`.
#### Comprehensive Testing Infrastructure:
- Created robust test architecture with session management and validator
setup across all runtimes
- Added block author management utilities for treasury testing requiring
pallet_authorship integration
- Implemented 15 total tests (5 tests × 3 runtimes) covering:
- EVM transaction fee allocation with/without priority fees
- Substrate fee and tip handling validation
- Treasury spending functionality via sudo
- Complete fee flow verification with actual network base fee
calculations
#### Technnical Implementation
Fee Allocation Logic:
- Base fees: 20% to treasury, 80% burned
- Priority fees: 100% to block author
- Substrate tips: 100% to block author
The implementation is based on
https://github.com/moonbeam-foundation/moonbeam/pull/3120 in
[Moonbeam](https://github.com/moonbeam-foundation/moonbeam), and
assisted by Claude Code.
### Summary
- Implement native token transfers from DataHaven to Ethereum using
Snowbridge infrastructure
- Add comprehensive datahaven-native-transfer pallet with lock-and-mint
mechanism for cross-chain token representation
### Key Features
**New Pallet: datahaven-native-transfer**
- Cross-chain transfers: Transfer DataHaven native tokens to Ethereum
addresses via `transfer_to_ethereum extrinsic`
- Token locking mechanism: Secure token storage in deterministic
Ethereum sovereign account during transfers
- Fee management: Required fees for all transfers to compensate relayers
and prevent spam
- Emergency controls: Pause/unpause functionality
- Token registration: Integration with Snowbridge's token registration
for native token identification
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced the DataHaven Native Transfer pallet enabling secure
cross-chain transfers of DataHaven native tokens to and from Ethereum
via Snowbridge.
- Added token locking on DataHaven, minting on Ethereum, and mandatory
fee collection to cover gas costs and incentivize relayers.
- Implemented pause and unpause controls for emergency management of
token transfers.
- **Configuration**
- Integrated the new pallet into mainnet, stagenet, and testnet runtimes
with updated network, account, and treasury settings.
- Added Ethereum sovereign account constants and native token ID support
for optimized cross-chain operations.
- **Documentation**
- Added comprehensive README and inline documentation detailing pallet
features, extrinsics, events, errors, and security considerations.
- **Tests**
- Added extensive unit and integration tests covering token transfers,
native token registration, fee handling, pause functionality, and event
validation across all supported networks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
This PR:
- Adds a check to make sure the BEEFY RPC endpoint is ready before
spinning up the BEEFY relayer, otherwise it would just fail and crash.
- Adds the `--enable-offchain-indexing=true` flag to the Datahaven nodes
run when starting up the E2E infra. This is needed because otherwise
nodes can't be queried by the relayer to generate the required proofs
since they would not store the MMR leafs/nodes/root, so the relayer
would just crash.
- Updates the way we were generating the merkle root of the validator
set.
- The BEEFY pallet (and as such, the relayer) generate the validator set
merkle tree by getting the validator list and treating it as an already
ordered set of merkle leafs, hashing each pair in succession without
caring what each leaf value is.
- Meanwhile, the OpenZeppelin crypto library (and as such, the
EigenLayer contracts) also gets the merkle leafs list but hashes each
pair of leafs in value order.
- This means that, for example, if the list of leafs is: ["0x124",
"0x123"]
- BEEFY would do `hash("0x124123")` to get the value of the parent node.
- OZ and EigenLayer would do `hash("0x123124")` to get the value of the
parent node.
- This created a mismatch between what the BEEFY relayer was expecting
and what was actually calculated in our script. A way to obtain a merkle
tree using the BEEFY way was added to solve this.
- Updates the authority set to not be a hardcoded array of keys, now the
BEEFY keys are obtained by directly querying the runtime before
deploying the BEEFY contracts.
- Renames a few files from `flamingo` to `datahaven`.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added support for calculating and generating Merkle roots and proofs
without sorting, enabling new use cases for validator set management.
- Introduced dynamic fetching and configuration of Datahaven validator
authorities during network launch.
- Added readiness check for the BEEFY protocol before relayer startup,
improving reliability of relayer operations.
- **Bug Fixes**
- Fixed indentation issues in configuration files for improved
readability and consistency.
- **Chores**
- Updated validator lists and addresses in configuration files.
- Enhanced e2e test scripts and added new commands for relayer and
minimal test scenarios.
- Added new dependency to test package.
- Updated version strings in package metadata.
- **Refactor**
- Improved logging and configuration handling during network and relayer
launches.
- Simplified import statements and removed unused code.
- **Style**
- Reformatted configuration and TypeScript files for better readability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Tim B <79199034+timbrinded@users.noreply.github.com>
Co-authored-by: Facundo Farall <37149322+ffarall@users.noreply.github.com>
This PR replaces `pallet-validator-set` with a new
`pallet-external-validators` pallet from Tanssi.
## Key Changes
- **New ExternalValidators Pallet**
- Supports whitelisted validators (set by governance, not rewarded)
- Manages external validators (can be enabled/disabled)
- Implements era-based rotation (eras change after configurable
sessions)
- **Bridge Integration**
- Updated `EigenLayerMessageProcessor` in
`operator/primitives/bridge/src/lib.rs`
- Replaced the old `SetValidators` command with
```rust
ReceiveValidators { validators, external_index }
```
Add support for multiple runtimes: `stagenet`, `testnet` & `mainnet`.
- Moved common types to `datahaven-runtime-common` crate.
- Made the node's command & service code generic over different
runtimes. Each runtime has a `dev` & `local` genesis config preset.
- More types / constants can be moved to the `datahaven-runtime-common`
crate ... this will be done in subsequent PRs.
---------
Co-authored-by: Gonza Montiel <gon.montiel@gmail.com>
Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
Co-authored-by: Facundo Farall <37149322+ffarall@users.noreply.github.com>
This pull request integrates the snowbridge-pallet-outbound-queue-v2 and
its dependencies into the DataHaven runtime. This pallet is responsible
for handling the submission, processing, and commitment of outbound
messages destined for the Ethereum network via the Snowbridge bridge
(v2).
### Key Changes
#### Added New Pallets
- **snowbridge-pallet-outbound-queue-v2**: The core pallet for managing
the outbound message lifecycle.
- **pallet-message-queue**: Introduced to handle the queuing and
processing of messages.
#### Added Crates to Datahaven codebase
- snowbridge-merkle-tree
- bridge-hub-common
- snowbridge-outbound-queue-v2-runtime-api
---------
Co-authored-by: Facundo Farall <37149322+ffarall@users.noreply.github.com>
I noticed `cargo clippy` was failing in CI, I fixed it. I still think we
should tweak the lints.
For example, clippy complains at things like `1 * HOURS ` because it's
redundant to multiply by 1, buy maybe we want it explicitly that way, so
it's more evident it can be changed. In this PR I don't tweak the rule
but try to make clippy happy.
Adds the `Substrate` node and runtime, as well as configuration and test
files, from https://github.com/Moonsong-Labs/flamingo to the `operator`
folder in DataHaven