From 82c9be6af198f8a1cba914690006148827663c7f Mon Sep 17 00:00:00 2001 From: Gonza Montiel Date: Mon, 24 Nov 2025 17:36:29 +0100 Subject: [PATCH 1/9] fix: build contracts with test/mocks (#317) Apparently, EigenLayer started including mock files inside `test/mocks/*`, that weren't in our remappings. That's why the e2e contract building was failing, both for e2e tests and CLI launch, while trying to solve `src/test/mocks/AllocationManagerMock.sol` in our own contracts source. I added the necessary remappings and the tests run now. --- contracts/foundry.toml | 2 ++ test/scripts/fund-validators.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 06f134d8..82cc6bc1 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -28,6 +28,8 @@ "lib/snowbridge/contracts/:prb/math/=lib/snowbridge/contracts/lib/prb-math/", "openzeppelin/=lib/snowbridge/contracts/lib/openzeppelin-contracts/contracts/", "prb/math/=lib/snowbridge/contracts/lib/prb-math/", + "src/contracts/=lib/eigenlayer-contracts/src/contracts/", + "src/test/=lib/eigenlayer-contracts/src/test/", ] # Specifies the exact version of Solidity to use, overriding auto-detection. solc_version = '0.8.28' diff --git a/test/scripts/fund-validators.ts b/test/scripts/fund-validators.ts index d27c7f03..f7ffefd6 100644 --- a/test/scripts/fund-validators.ts +++ b/test/scripts/fund-validators.ts @@ -147,7 +147,7 @@ export const fundValidators = async (options: FundValidatorsOptions): Promise Date: Tue, 25 Nov 2025 14:56:52 +0100 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20=F0=9F=94=A8=20Enable=20BEEFY=20equi?= =?UTF-8?q?vocation=20reporting=20in=20all=20runtimes=20(#320)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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; ``` 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 --- operator/runtime/mainnet/src/configs/mod.rs | 3 ++- operator/runtime/stagenet/src/configs/mod.rs | 3 ++- operator/runtime/testnet/src/configs/mod.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index 13970526..335a409c 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -511,7 +511,8 @@ impl pallet_beefy::Config for Runtime { type AncestryHelper = BeefyMmrLeaf; type WeightInfo = (); type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; } parameter_types! { diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs index 9591759a..55352a33 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -508,7 +508,8 @@ impl pallet_beefy::Config for Runtime { type AncestryHelper = BeefyMmrLeaf; type WeightInfo = (); type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; } parameter_types! { diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs index faa43057..a45515ba 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -511,7 +511,8 @@ impl pallet_beefy::Config for Runtime { type AncestryHelper = BeefyMmrLeaf; type WeightInfo = (); type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; } parameter_types! { From 72c345718321928a8a88dde23017d3fd522a83ad Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:09:19 +0100 Subject: [PATCH 3/9] =?UTF-8?q?fix:=20=F0=9F=94=A8=20Replace=20FreezeChain?= =?UTF-8?q?OnFailedMigration=20with=20SafeMode=20handler=20(#308)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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` type alias that wraps `EnterSafeModeOnFailedMigration` with comprehensive documentation - **All three runtimes** (`mainnet`, `stagenet`, `testnet`): - Updated `pallet_migrations::Config::FailedMigrationHandler` to use `FailedMigrationHandler` - 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 Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> --- operator/runtime/common/src/migrations.rs | 15 +- operator/runtime/mainnet/src/configs/mod.rs | 7 +- operator/runtime/mainnet/tests/migrations.rs | 77 ++++++++- operator/runtime/stagenet/src/configs/mod.rs | 7 +- operator/runtime/stagenet/tests/migrations.rs | 146 ++++++++++++++++++ .../stagenet/tests/safe_mode_tx_pause.rs | 3 +- operator/runtime/testnet/src/configs/mod.rs | 7 +- operator/runtime/testnet/tests/migrations.rs | 146 ++++++++++++++++++ .../testnet/tests/safe_mode_tx_pause.rs | 3 +- 9 files changed, 392 insertions(+), 19 deletions(-) create mode 100644 operator/runtime/stagenet/tests/migrations.rs create mode 100644 operator/runtime/testnet/tests/migrations.rs diff --git a/operator/runtime/common/src/migrations.rs b/operator/runtime/common/src/migrations.rs index a0a7a507..6b342519 100644 --- a/operator/runtime/common/src/migrations.rs +++ b/operator/runtime/common/src/migrations.rs @@ -45,8 +45,19 @@ pub type MultiBlockMigrationList = pallet_migrations::mock_helpers::MockedMigrat /// Placeholder handler for migration status notifications. We do not emit any extra signals yet. pub type MigrationStatusHandler = (); -/// Default handler triggered on migration failures. -pub type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; +/// Handler triggered on migration failures. +/// +/// This handler attempts to enter SafeMode when a migration fails, allowing governance to +/// intervene and fix the issue while preventing regular user transactions from interacting +/// with potentially inconsistent storage state. +/// +/// The handler is parameterized by the SafeMode pallet type from each runtime, with a fallback +/// to freezing the chain if SafeMode cannot be entered. +pub type FailedMigrationHandler = + frame_support::migrations::EnterSafeModeOnFailedMigration< + SafeMode, + frame_support::migrations::FreezeChainOnFailedMigration, + >; /// Multi-block migration for updating the EVM chain ID to the new value. pub mod evm_chain_id { diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index 335a409c..344b5154 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -79,8 +79,8 @@ use datahaven_runtime_common::{ }, gas::WEIGHT_PER_GAS, migrations::{ - FailedMigrationHandler as DefaultFailedMigrationHandler, MigrationCursorMaxLen, - MigrationIdentifierMaxLen, MigrationStatusHandler, + FailedMigrationHandler, MigrationCursorMaxLen, MigrationIdentifierMaxLen, + MigrationStatusHandler, }, safe_mode::{ ReleaseDelayNone, RuntimeCallFilter, SafeModeDuration, SafeModeEnterDeposit, @@ -854,8 +854,7 @@ impl pallet_migrations::Config for Runtime { type CursorMaxLen = MigrationCursorMaxLen; type IdentifierMaxLen = MigrationIdentifierMaxLen; type MigrationStatusHandler = MigrationStatusHandler; - // TODO: Remove this once we have a proper failed migration handler (Safe mode) - type FailedMigrationHandler = DefaultFailedMigrationHandler; + type FailedMigrationHandler = FailedMigrationHandler; type MaxServiceWeight = MaxServiceWeight; type WeightInfo = mainnet_weights::pallet_migrations::WeightInfo; } diff --git a/operator/runtime/mainnet/tests/migrations.rs b/operator/runtime/mainnet/tests/migrations.rs index 288abb68..34292d9b 100644 --- a/operator/runtime/mainnet/tests/migrations.rs +++ b/operator/runtime/mainnet/tests/migrations.rs @@ -18,7 +18,9 @@ mod common; use common::*; -use datahaven_mainnet_runtime::{Runtime, RuntimeCall, RuntimeOrigin}; +use datahaven_mainnet_runtime::{ + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SafeMode, System, +}; use frame_support::{assert_noop, assert_ok}; use pallet_migrations::{Call as MigrationsCall, HistoricCleanupSelector}; use sp_runtime::{traits::Dispatchable, DispatchError}; @@ -69,3 +71,76 @@ fn migrations_force_calls_are_root_only() { assert_ok!(clear_historic.dispatch(RuntimeOrigin::root())); }); } + +#[test] +fn failed_migration_enters_safe_mode() { + ExtBuilder::default().build().execute_with(|| { + // Verify SafeMode is not active initially + assert!( + !SafeMode::is_entered(), + "SafeMode should not be active initially" + ); + + // Simulate a failed migration by directly calling the FailedMigrationHandler + // This tests that when migrations fail, the system enters SafeMode + use frame_support::migrations::FailedMigrationHandler; + type Handler = ::FailedMigrationHandler; + + // Call the failed handler (simulating a migration failure) + let result = Handler::failed(Some(0)); + + // The handler should indicate that SafeMode was entered + assert_eq!( + result, + frame_support::migrations::FailedMigrationHandling::KeepStuck, + "Handler should keep the chain stuck in SafeMode" + ); + + // Verify that SafeMode is now active + assert!( + SafeMode::is_entered(), + "SafeMode should be active after migration failure" + ); + + // Get the block number when SafeMode should expire + let entered_until = pallet_safe_mode::EnteredUntil::::get(); + assert!( + entered_until.is_some(), + "SafeMode should have an expiry block" + ); + + // Verify that the SafeMode event was emitted + let events = System::events(); + assert!( + events.iter().any(|e| matches!( + e.event, + RuntimeEvent::SafeMode(pallet_safe_mode::Event::Entered { .. }) + )), + "SafeMode::Entered event should be emitted" + ); + }); +} + +#[test] +fn safe_mode_allows_governance_during_migration_failure() { + ExtBuilder::default().build().execute_with(|| { + // Simulate a failed migration + use frame_support::migrations::FailedMigrationHandler; + type Handler = ::FailedMigrationHandler; + Handler::failed(Some(0)); + + // Verify SafeMode is active + assert!(SafeMode::is_entered(), "SafeMode should be active"); + + // Test that SafeMode management calls are still allowed + let force_exit_call = RuntimeCall::SafeMode(pallet_safe_mode::Call::force_exit {}); + let result = force_exit_call.dispatch(RuntimeOrigin::root()); + assert_ok!(result); + + // Verify SafeMode is now inactive + assert!( + !SafeMode::is_entered(), + "SafeMode should be inactive after force exit" + ); + }); +} diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs index 55352a33..893d2feb 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -79,8 +79,8 @@ use datahaven_runtime_common::{ }, gas::WEIGHT_PER_GAS, migrations::{ - FailedMigrationHandler as DefaultFailedMigrationHandler, MigrationCursorMaxLen, - MigrationIdentifierMaxLen, MigrationStatusHandler, + FailedMigrationHandler, MigrationCursorMaxLen, MigrationIdentifierMaxLen, + MigrationStatusHandler, }, safe_mode::{ ReleaseDelayNone, RuntimeCallFilter, SafeModeDuration, SafeModeEnterDeposit, @@ -857,8 +857,7 @@ impl pallet_migrations::Config for Runtime { type CursorMaxLen = MigrationCursorMaxLen; type IdentifierMaxLen = MigrationIdentifierMaxLen; type MigrationStatusHandler = MigrationStatusHandler; - // TODO: Remove this once we have a proper failed migration handler (Safe mode) - type FailedMigrationHandler = DefaultFailedMigrationHandler; + type FailedMigrationHandler = FailedMigrationHandler; type MaxServiceWeight = MaxServiceWeight; type WeightInfo = stagenet_weights::pallet_migrations::WeightInfo; } diff --git a/operator/runtime/stagenet/tests/migrations.rs b/operator/runtime/stagenet/tests/migrations.rs new file mode 100644 index 00000000..961f7cc6 --- /dev/null +++ b/operator/runtime/stagenet/tests/migrations.rs @@ -0,0 +1,146 @@ +// Copyright 2025 DataHaven +// This file is part of DataHaven. + +// DataHaven is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// DataHaven is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with DataHaven. If not, see . + +#[path = "common.rs"] +mod common; + +use common::*; +use datahaven_stagenet_runtime::{ + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SafeMode, System, +}; +use frame_support::{assert_noop, assert_ok}; +use pallet_migrations::{Call as MigrationsCall, HistoricCleanupSelector}; +use sp_runtime::{traits::Dispatchable, DispatchError}; + +#[test] +fn migrations_force_calls_are_root_only() { + ExtBuilder::default().build().execute_with(|| { + let signed_origin = RuntimeOrigin::signed(account_id(ALICE)); + + let force_onboard = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::force_onboard_mbms {}); + assert_noop!( + force_onboard.clone().dispatch(signed_origin.clone()), + DispatchError::BadOrigin + ); + assert_ok!(force_onboard.dispatch(RuntimeOrigin::root())); + + let force_set_cursor = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::force_set_cursor { + cursor: None, + }); + assert_noop!( + force_set_cursor.clone().dispatch(signed_origin.clone()), + DispatchError::BadOrigin + ); + assert_ok!(force_set_cursor.dispatch(RuntimeOrigin::root())); + + let force_set_active = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::force_set_active_cursor { + index: 0, + inner_cursor: None, + started_at: None, + }); + assert_noop!( + force_set_active.clone().dispatch(signed_origin.clone()), + DispatchError::BadOrigin + ); + assert_ok!(force_set_active.dispatch(RuntimeOrigin::root())); + + let clear_historic = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::clear_historic { + selector: HistoricCleanupSelector::Specific(Vec::new()), + }); + assert_noop!( + clear_historic.clone().dispatch(signed_origin), + DispatchError::BadOrigin + ); + assert_ok!(clear_historic.dispatch(RuntimeOrigin::root())); + }); +} + +#[test] +fn failed_migration_enters_safe_mode() { + ExtBuilder::default().build().execute_with(|| { + // Verify SafeMode is not active initially + assert!( + !SafeMode::is_entered(), + "SafeMode should not be active initially" + ); + + // Simulate a failed migration by directly calling the FailedMigrationHandler + // This tests that when migrations fail, the system enters SafeMode + use frame_support::migrations::FailedMigrationHandler; + type Handler = ::FailedMigrationHandler; + + // Call the failed handler (simulating a migration failure) + let result = Handler::failed(Some(0)); + + // The handler should indicate that SafeMode was entered + assert_eq!( + result, + frame_support::migrations::FailedMigrationHandling::KeepStuck, + "Handler should keep the chain stuck in SafeMode" + ); + + // Verify that SafeMode is now active + assert!( + SafeMode::is_entered(), + "SafeMode should be active after migration failure" + ); + + // Get the block number when SafeMode should expire + let entered_until = pallet_safe_mode::EnteredUntil::::get(); + assert!( + entered_until.is_some(), + "SafeMode should have an expiry block" + ); + + // Verify that the SafeMode event was emitted + let events = System::events(); + assert!( + events.iter().any(|e| matches!( + e.event, + RuntimeEvent::SafeMode(pallet_safe_mode::Event::Entered { .. }) + )), + "SafeMode::Entered event should be emitted" + ); + }); +} + +#[test] +fn safe_mode_allows_governance_during_migration_failure() { + ExtBuilder::default().build().execute_with(|| { + // Simulate a failed migration + use frame_support::migrations::FailedMigrationHandler; + type Handler = ::FailedMigrationHandler; + Handler::failed(Some(0)); + + // Verify SafeMode is active + assert!(SafeMode::is_entered(), "SafeMode should be active"); + + // Test that SafeMode management calls are still allowed + let force_exit_call = RuntimeCall::SafeMode(pallet_safe_mode::Call::force_exit {}); + let result = force_exit_call.dispatch(RuntimeOrigin::root()); + assert_ok!(result); + + // Verify SafeMode is now inactive + assert!( + !SafeMode::is_entered(), + "SafeMode should be inactive after force exit" + ); + }); +} diff --git a/operator/runtime/stagenet/tests/safe_mode_tx_pause.rs b/operator/runtime/stagenet/tests/safe_mode_tx_pause.rs index fd185f82..6abd142f 100644 --- a/operator/runtime/stagenet/tests/safe_mode_tx_pause.rs +++ b/operator/runtime/stagenet/tests/safe_mode_tx_pause.rs @@ -21,8 +21,7 @@ mod common; use common::{account_id, ExtBuilder, ALICE, BOB}; use datahaven_stagenet_runtime::{ - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SafeMode, System, TxPause, - UncheckedExtrinsic, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, System, UncheckedExtrinsic, }; use frame_support::{assert_noop, assert_ok, BoundedVec}; use pallet_safe_mode::EnteredUntil; diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs index a45515ba..2f7bc6e7 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -79,8 +79,8 @@ use datahaven_runtime_common::{ }, gas::WEIGHT_PER_GAS, migrations::{ - FailedMigrationHandler as DefaultFailedMigrationHandler, MigrationCursorMaxLen, - MigrationIdentifierMaxLen, MigrationStatusHandler, + FailedMigrationHandler, MigrationCursorMaxLen, MigrationIdentifierMaxLen, + MigrationStatusHandler, }, safe_mode::{ ReleaseDelayNone, RuntimeCallFilter, SafeModeDuration, SafeModeEnterDeposit, @@ -860,8 +860,7 @@ impl pallet_migrations::Config for Runtime { type CursorMaxLen = MigrationCursorMaxLen; type IdentifierMaxLen = MigrationIdentifierMaxLen; type MigrationStatusHandler = MigrationStatusHandler; - // TODO: Remove this once we have a proper failed migration handler (Safe mode) - type FailedMigrationHandler = DefaultFailedMigrationHandler; + type FailedMigrationHandler = FailedMigrationHandler; type MaxServiceWeight = MaxServiceWeight; type WeightInfo = testnet_weights::pallet_migrations::WeightInfo; } diff --git a/operator/runtime/testnet/tests/migrations.rs b/operator/runtime/testnet/tests/migrations.rs new file mode 100644 index 00000000..6e76a85e --- /dev/null +++ b/operator/runtime/testnet/tests/migrations.rs @@ -0,0 +1,146 @@ +// Copyright 2025 DataHaven +// This file is part of DataHaven. + +// DataHaven is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// DataHaven is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with DataHaven. If not, see . + +#[path = "common.rs"] +mod common; + +use common::*; +use datahaven_testnet_runtime::{ + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SafeMode, System, +}; +use frame_support::{assert_noop, assert_ok}; +use pallet_migrations::{Call as MigrationsCall, HistoricCleanupSelector}; +use sp_runtime::{traits::Dispatchable, DispatchError}; + +#[test] +fn migrations_force_calls_are_root_only() { + ExtBuilder::default().build().execute_with(|| { + let signed_origin = RuntimeOrigin::signed(account_id(ALICE)); + + let force_onboard = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::force_onboard_mbms {}); + assert_noop!( + force_onboard.clone().dispatch(signed_origin.clone()), + DispatchError::BadOrigin + ); + assert_ok!(force_onboard.dispatch(RuntimeOrigin::root())); + + let force_set_cursor = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::force_set_cursor { + cursor: None, + }); + assert_noop!( + force_set_cursor.clone().dispatch(signed_origin.clone()), + DispatchError::BadOrigin + ); + assert_ok!(force_set_cursor.dispatch(RuntimeOrigin::root())); + + let force_set_active = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::force_set_active_cursor { + index: 0, + inner_cursor: None, + started_at: None, + }); + assert_noop!( + force_set_active.clone().dispatch(signed_origin.clone()), + DispatchError::BadOrigin + ); + assert_ok!(force_set_active.dispatch(RuntimeOrigin::root())); + + let clear_historic = + RuntimeCall::MultiBlockMigrations(MigrationsCall::::clear_historic { + selector: HistoricCleanupSelector::Specific(Vec::new()), + }); + assert_noop!( + clear_historic.clone().dispatch(signed_origin), + DispatchError::BadOrigin + ); + assert_ok!(clear_historic.dispatch(RuntimeOrigin::root())); + }); +} + +#[test] +fn failed_migration_enters_safe_mode() { + ExtBuilder::default().build().execute_with(|| { + // Verify SafeMode is not active initially + assert!( + !SafeMode::is_entered(), + "SafeMode should not be active initially" + ); + + // Simulate a failed migration by directly calling the FailedMigrationHandler + // This tests that when migrations fail, the system enters SafeMode + use frame_support::migrations::FailedMigrationHandler; + type Handler = ::FailedMigrationHandler; + + // Call the failed handler (simulating a migration failure) + let result = Handler::failed(Some(0)); + + // The handler should indicate that SafeMode was entered + assert_eq!( + result, + frame_support::migrations::FailedMigrationHandling::KeepStuck, + "Handler should keep the chain stuck in SafeMode" + ); + + // Verify that SafeMode is now active + assert!( + SafeMode::is_entered(), + "SafeMode should be active after migration failure" + ); + + // Get the block number when SafeMode should expire + let entered_until = pallet_safe_mode::EnteredUntil::::get(); + assert!( + entered_until.is_some(), + "SafeMode should have an expiry block" + ); + + // Verify that the SafeMode event was emitted + let events = System::events(); + assert!( + events.iter().any(|e| matches!( + e.event, + RuntimeEvent::SafeMode(pallet_safe_mode::Event::Entered { .. }) + )), + "SafeMode::Entered event should be emitted" + ); + }); +} + +#[test] +fn safe_mode_allows_governance_during_migration_failure() { + ExtBuilder::default().build().execute_with(|| { + // Simulate a failed migration + use frame_support::migrations::FailedMigrationHandler; + type Handler = ::FailedMigrationHandler; + Handler::failed(Some(0)); + + // Verify SafeMode is active + assert!(SafeMode::is_entered(), "SafeMode should be active"); + + // Test that SafeMode management calls are still allowed + let force_exit_call = RuntimeCall::SafeMode(pallet_safe_mode::Call::force_exit {}); + let result = force_exit_call.dispatch(RuntimeOrigin::root()); + assert_ok!(result); + + // Verify SafeMode is now inactive + assert!( + !SafeMode::is_entered(), + "SafeMode should be inactive after force exit" + ); + }); +} diff --git a/operator/runtime/testnet/tests/safe_mode_tx_pause.rs b/operator/runtime/testnet/tests/safe_mode_tx_pause.rs index acb7e8f5..7f236113 100644 --- a/operator/runtime/testnet/tests/safe_mode_tx_pause.rs +++ b/operator/runtime/testnet/tests/safe_mode_tx_pause.rs @@ -21,8 +21,7 @@ mod common; use common::{account_id, ExtBuilder, ALICE, BOB}; use datahaven_testnet_runtime::{ - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SafeMode, System, TxPause, - UncheckedExtrinsic, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, System, UncheckedExtrinsic, }; use frame_support::{assert_noop, assert_ok, BoundedVec}; use pallet_safe_mode::EnteredUntil; From 92d3c42f79cc54dd2d5d046fcd8e9730b3ccd0a1 Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:44:06 +0100 Subject: [PATCH 4/9] =?UTF-8?q?chore:=20=E2=99=BB=EF=B8=8F=20=20Remove=20e?= =?UTF-8?q?xecuted=20runtime=20migrations=20(#318)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> --- operator/runtime/common/src/migrations.rs | 554 ++----------------- operator/runtime/mainnet/src/configs/mod.rs | 2 +- operator/runtime/stagenet/src/configs/mod.rs | 8 +- operator/runtime/testnet/src/configs/mod.rs | 8 +- 4 files changed, 40 insertions(+), 532 deletions(-) diff --git a/operator/runtime/common/src/migrations.rs b/operator/runtime/common/src/migrations.rs index 6b342519..3eed8295 100644 --- a/operator/runtime/common/src/migrations.rs +++ b/operator/runtime/common/src/migrations.rs @@ -18,6 +18,42 @@ //! //! The types and constants defined here keep the pallet configuration consistent between //! networks while leaving each runtime free to decide which migrations should actually run. +//! +//! ## Migration History +//! +//! This section documents migrations that have been executed and subsequently removed from the +//! codebase. The migration framework tracks completed migrations on-chain, preventing re-execution. +//! +//! ### Executed Migrations (Code Removed) +//! +//! #### 1. EVM Pallet Alias Migration +//! - **Migration ID**: `datahaven-evm-mbm` (version 0 → 1) +//! - **Type**: Multi-block stepped migration +//! - **Original PR**: [#213](https://github.com/datahaven-xyz/datahaven/pull/213) +//! - **Intent**: Fixed `eth_getCode` and other Ethereum RPC calls by renaming the pallet_evm +//! FRAME alias from `Evm` to `EVM`. Frontier's `StorageOverrideHandler` hardcodes the storage +//! prefix as `twox_128("EVM")`, but our runtimes used `Evm`, causing all contract bytecode +//! lookups to fail. This migration realigned the storage prefix with Frontier's expectations. +//! - **Storage Migrated**: +//! - `Evm::AccountCodes` → `EVM::AccountCodes` +//! - `Evm::AccountCodesMetadata` → `EVM::AccountCodesMetadata` +//! - `Evm::AccountStorages` → `EVM::AccountStorages` +//! - **Execution**: Successfully executed on Testnet and Stagenet (39 keys migrated, <0.1% block weight) +//! - **Removed**: 2025-01 ([PR #318](https://github.com/datahaven-xyz/datahaven/pull/318)) +//! +//! #### 2. EVM Chain ID Migration +//! - **Migration ID**: `dh-evm-chain-id-v1` (version 0 → 1) +//! - **Type**: Single-step migration +//! - **Original PR**: [#280](https://github.com/datahaven-xyz/datahaven/pull/280) +//! - **Intent**: Updated chain IDs for all three DataHaven environments to their final assigned +//! values. The stored EVM chain ID in `pallet_evm_chain_id::ChainId` was migrated to match +//! the configured constants after chain ID assignments were finalized. +//! - **Networks**: +//! - Mainnet: Chain ID 55930 (no migration needed, set at genesis) +//! - Testnet: Migrated to chain ID 55931 +//! - Stagenet: Migrated to chain ID 55932 +//! - **Execution**: Successfully executed on Testnet and Stagenet +//! - **Removed**: 2025-01 ([PR #318](https://github.com/datahaven-xyz/datahaven/pull/318)) use frame_support::pallet_prelude::*; @@ -36,7 +72,7 @@ pub type MigrationIdentifierMaxLen = ConstU32; /// The tuple starts empty and can be extended with concrete migrations over time. Keeping it in a /// shared module reduces duplication once we coordinate migrations across networks. #[cfg(not(feature = "runtime-benchmarks"))] -pub type MultiBlockMigrationList = (evm_alias::EvmAliasMigration,); +pub type MultiBlockMigrationList = (); /// During benchmarking we switch to the pallet-provided mocked migrations to guarantee success. #[cfg(feature = "runtime-benchmarks")] @@ -58,519 +94,3 @@ pub type FailedMigrationHandler = SafeMode, frame_support::migrations::FreezeChainOnFailedMigration, >; - -/// Multi-block migration for updating the EVM chain ID to the new value. -pub mod evm_chain_id { - use core::marker::PhantomData; - use frame_support::{ - migrations::{MigrationId, SteppedMigration, SteppedMigrationError}, - pallet_prelude::*, - weights::WeightMeter, - }; - - #[cfg(feature = "try-runtime")] - use codec::Encode; - - /// Multi-block migration that updates the stored EVM chain ID to match the new configuration. - pub struct EvmChainIdMigration(PhantomData); - - impl SteppedMigration for EvmChainIdMigration - where - T: pallet_evm_chain_id::Config, - { - type Cursor = (); - type Identifier = MigrationId<20>; - - fn id() -> Self::Identifier { - MigrationId { - pallet_id: *b"dh-evm-chain-id-v1 ", - version_from: 0, - version_to: 1, - } - } - - fn step( - cursor: Option, - meter: &mut WeightMeter, - ) -> Result, SteppedMigrationError> { - // This migration completes in a single step - if cursor.is_some() { - return Ok(None); - } - - let required = T::DbWeight::get().reads_writes(1, 1); - if meter.try_consume(required).is_err() { - return Err(SteppedMigrationError::InsufficientWeight { required }); - } - - log::info!( - "🔄 [EVM Chain ID Migration] Updating chain ID to {}", - NEW_CHAIN_ID - ); - - // Update the chain ID storage - pallet_evm_chain_id::ChainId::::put(NEW_CHAIN_ID); - - log::info!( - "✅ [EVM Chain ID Migration] Successfully updated chain ID to {}", - NEW_CHAIN_ID - ); - - Ok(None) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - let old_chain_id = pallet_evm_chain_id::ChainId::::get(); - log::info!( - "📋 [EVM Chain ID Migration] Current chain ID: {}", - old_chain_id - ); - Ok(old_chain_id.encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - use codec::Decode; - - let old_chain_id = - u64::decode(&mut &state[..]).map_err(|_| "Failed to decode old chain ID")?; - let new_chain_id = pallet_evm_chain_id::ChainId::::get(); - - log::info!( - "🔍 [EVM Chain ID Migration] Chain ID updated from {} to {}", - old_chain_id, - new_chain_id - ); - - if new_chain_id != NEW_CHAIN_ID { - return Err(sp_runtime::TryRuntimeError::Other( - "Chain ID was not updated correctly", - )); - } - - Ok(()) - } - } -} - -/// Multi-block migration for renaming the EVM pallet alias. -pub mod evm_alias { - use core::marker::PhantomData; - use frame_support::{ - migrations::{MigrationId, SteppedMigration, SteppedMigrationError}, - pallet_prelude::*, - weights::WeightMeter, - BoundedVec, StorageHasher, - }; - use sp_io::storage; - use sp_std::{convert::TryFrom, vec::Vec}; - - #[cfg(feature = "try-runtime")] - use sp_std::collections::btree_map::BTreeMap; - - /// Multi-block migration that renames the Frontier EVM pallet alias from `Evm` to `EVM`. - pub struct EvmAliasMigration(PhantomData); - - impl SteppedMigration for EvmAliasMigration - where - T: pallet_evm::Config, - { - type Cursor = BoundedVec>; - type Identifier = MigrationId<17>; - - fn id() -> Self::Identifier { - MigrationId { - pallet_id: *b"datahaven-evm-mbm", - version_from: 0, - version_to: 1, - } - } - - fn step( - cursor: Option, - meter: &mut WeightMeter, - ) -> Result, SteppedMigrationError> { - if cursor.is_none() { - log::info!( - "🚀 [EVM Migration] Starting pallet alias migration from 'Evm' to 'EVM'" - ); - } - - let old_prefix = Twox128::hash(b"Evm"); - let new_prefix = Twox128::hash(b"EVM"); - let mut current_key: Vec = cursor - .map(Into::into) - .unwrap_or_else(|| old_prefix.to_vec()); - let mut processed = 0u32; - let required = T::DbWeight::get().reads_writes(1, 2); - - loop { - let next_key = match storage::next_key(¤t_key) { - Some(next) if next.starts_with(&old_prefix) => next, - _ => { - log::info!( - "✅ [EVM Migration] Completed! Processed {} keys in this step", - processed - ); - return Ok(None); - } - }; - - if meter.try_consume(required).is_err() { - if processed == 0 { - log::warn!( - "⚠️ [EVM Migration] Insufficient weight for even one key migration" - ); - return Err(SteppedMigrationError::InsufficientWeight { required }); - } - log::info!( - "⏸️ [EVM Migration] Pausing after migrating {} keys (weight limit reached)", - processed - ); - return BoundedVec::try_from(current_key) - .map(Some) - .map_err(|_| SteppedMigrationError::Failed); - } - - if let Some(value) = storage::get(&next_key) { - let mut new_key = Vec::with_capacity(next_key.len()); - new_key.extend_from_slice(&new_prefix); - new_key.extend_from_slice(&next_key[16..]); - storage::set(&new_key, &value); - } - storage::clear(&next_key); - - processed = processed.saturating_add(1); - current_key = next_key; - } - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - use codec::Encode; - - let storage_prefix = |item: &[u8]| { - let mut key = [0u8; 32]; - key[0..16].copy_from_slice(&Twox128::hash(b"Evm")); - key[16..32].copy_from_slice(&Twox128::hash(item)); - key - }; - - let mut counts = BTreeMap::new(); - - for name in [ - b"AccountCodes" as &[u8], - b"AccountCodesMetadata", - b"AccountStorages", - ] { - let count = count_keys(&storage_prefix(name)); - counts.insert(name.to_vec(), count.encode()); - } - - Ok(counts.encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - use codec::Decode; - - let snapshot: BTreeMap, Vec> = - Decode::decode(&mut &state[..]).expect("Failed to decode snapshot"); - - let old_storage = |item: &[u8]| { - let mut key = [0u8; 32]; - key[0..16].copy_from_slice(&Twox128::hash(b"Evm")); - key[16..32].copy_from_slice(&Twox128::hash(item)); - key - }; - - let new_storage = |item: &[u8]| { - let mut key = [0u8; 32]; - key[0..16].copy_from_slice(&Twox128::hash(b"EVM")); - key[16..32].copy_from_slice(&Twox128::hash(item)); - key - }; - - for name in [ - b"AccountCodes" as &[u8], - b"AccountCodesMetadata", - b"AccountStorages", - ] { - let old_count = count_keys(&old_storage(name)); - assert_eq!(old_count, 0, "Old Evm prefix still present after migration"); - - let expected = snapshot - .get(name) - .and_then(|v| u32::decode(&mut &v[..]).ok()) - .unwrap_or(0); - let actual = count_keys(&new_storage(name)); - - assert_eq!(expected, actual, "Migrated entry count mismatch"); - } - - Ok(()) - } - } - - /// Helper function to count storage keys with a given prefix. - #[cfg(any(feature = "try-runtime", test))] - pub(crate) fn count_keys(prefix: &[u8]) -> u32 { - let mut count = 0u32; - let mut cursor = prefix.to_vec(); - loop { - match storage::next_key(&cursor) { - Some(next) if next.starts_with(prefix) => { - count = count.saturating_add(1); - cursor = next; - } - _ => break, - } - } - count - } -} - -#[cfg(test)] -mod tests { - use super::evm_alias::{count_keys, EvmAliasMigration}; - use codec::Encode; - use frame_support::{ - derive_impl, - migrations::SteppedMigration, - parameter_types, - weights::{constants::RocksDbWeight, Weight, WeightMeter}, - StorageHasher, Twox128, - }; - use pallet_evm::AccountCodes; - use sp_core::{H160, H256, U256}; - use sp_io::storage; - use sp_runtime::BuildStorage; - use sp_std::vec; - - frame_support::construct_runtime!( - pub enum TestRuntime { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage}, - EVM: pallet_evm::{Pallet, Call, Storage, Config, Event}, - EvmChainId: pallet_evm_chain_id::{Pallet, Storage}, - } - ); - - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0)); - } - - #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for TestRuntime { - type Nonce = u64; - type Block = frame_system::mocking::MockBlock; - type BlockHashCount = BlockHashCount; - type AccountData = pallet_balances::AccountData; - } - - parameter_types! { - pub const ExistentialDeposit: u64 = 0; - } - - #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] - impl pallet_balances::Config for TestRuntime { - type RuntimeHoldReason = (); - type Balance = u64; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - } - - #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] - impl pallet_timestamp::Config for TestRuntime {} - - parameter_types! { - pub MockPrecompiles: MockPrecompileSet = MockPrecompileSet; - } - - #[derive_impl(pallet_evm::config_preludes::TestDefaultConfig)] - impl pallet_evm::Config for TestRuntime { - type AccountProvider = pallet_evm::FrameSystemAccountProvider; - type FeeCalculator = FixedGasPrice; - type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; - type Currency = Balances; - type Runner = pallet_evm::runner::stack::Runner; - type Timestamp = Timestamp; - type PrecompilesType = MockPrecompileSet; - type PrecompilesValue = MockPrecompiles; - } - - impl pallet_evm_chain_id::Config for TestRuntime {} - - pub struct FixedGasPrice; - impl pallet_evm::FeeCalculator for FixedGasPrice { - fn min_gas_price() -> (U256, Weight) { - (1_000_000_000u128.into(), Weight::from_parts(1_000, 0)) - } - } - - pub struct MockPrecompileSet; - impl pallet_evm::PrecompileSet for MockPrecompileSet { - fn execute( - &self, - _handle: &mut impl pallet_evm::PrecompileHandle, - ) -> Option { - None - } - - fn is_precompile(&self, _address: H160, _gas: u64) -> pallet_evm::IsPrecompileResult { - pallet_evm::IsPrecompileResult::Answer { - is_precompile: false, - extra_cost: 0, - } - } - } - - fn old_storage_prefix(item: &[u8]) -> [u8; 32] { - let mut key = [0u8; 32]; - key[0..16].copy_from_slice(&Twox128::hash(b"Evm")); - key[16..32].copy_from_slice(&Twox128::hash(item)); - key - } - - fn raw_key(storage_name: &[u8], address: H160, index: Option) -> Vec { - let mut key = old_storage_prefix(storage_name).to_vec(); - key.extend_from_slice(&sp_io::hashing::blake2_128(&address.encode())); - key.extend_from_slice(address.as_bytes()); - if let Some(idx) = index { - key.extend_from_slice(&sp_io::hashing::blake2_128(&idx.encode())); - key.extend_from_slice(idx.as_bytes()); - } - key - } - - #[test] - fn multi_block_evm_alias_migration_moves_all_entries() { - let mut storage = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig::::default() - .assimilate_storage(&mut storage) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(storage); - ext.execute_with(|| { - let addresses: Vec = (1u64..=3).map(H160::from_low_u64_be).collect(); - - for (idx, &address) in addresses.iter().enumerate() { - storage::set( - &raw_key(b"AccountCodes", address, None), - &vec![idx as u8; 3].encode(), - ); - storage::set( - &raw_key(b"AccountCodesMetadata", address, None), - &(42u64 + idx as u64, H256::repeat_byte(idx as u8)).encode(), - ); - storage::set( - &raw_key( - b"AccountStorages", - address, - Some(H256::repeat_byte(0xAA + idx as u8)), - ), - &H256::repeat_byte(idx as u8).encode(), - ); - } - - let mut cursor = None; - loop { - let mut meter = WeightMeter::with_limit(RocksDbWeight::get().reads_writes(1, 2)); - match EvmAliasMigration::::step(cursor, &mut meter) { - Ok(None) => break, - Ok(Some(next)) => cursor = Some(next), - Err(err) => panic!("migration failed: {:?}", err), - } - } - - for address in addresses { - assert!(!AccountCodes::::get(address).is_empty()); - } - - assert_eq!(count_keys(&old_storage_prefix(b"AccountCodes")[..]), 0); - assert_eq!( - count_keys(&old_storage_prefix(b"AccountCodesMetadata")[..]), - 0 - ); - assert_eq!(count_keys(&old_storage_prefix(b"AccountStorages")[..]), 0); - }); - } - - #[test] - fn evm_chain_id_migration_updates_storage() { - use super::evm_chain_id::EvmChainIdMigration; - - let mut storage = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig::::default() - .assimilate_storage(&mut storage) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(storage); - - ext.execute_with(|| { - // Set an old chain ID value - const OLD_CHAIN_ID: u64 = 12345; - const NEW_CHAIN_ID: u64 = 55931; - - pallet_evm_chain_id::ChainId::::put(OLD_CHAIN_ID); - assert_eq!( - pallet_evm_chain_id::ChainId::::get(), - OLD_CHAIN_ID - ); - - // Run the migration - let mut meter = WeightMeter::with_limit(Weight::MAX); - let result = EvmChainIdMigration::::step(None, &mut meter); - - // Verify migration succeeded and completed in one step - assert!(result.is_ok()); - assert_eq!(result.unwrap(), None); - - // Verify the chain ID was updated - assert_eq!( - pallet_evm_chain_id::ChainId::::get(), - NEW_CHAIN_ID - ); - }); - } - - #[test] - fn evm_chain_id_migration_is_idempotent() { - use super::evm_chain_id::EvmChainIdMigration; - - let mut storage = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig::::default() - .assimilate_storage(&mut storage) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(storage); - - ext.execute_with(|| { - const NEW_CHAIN_ID: u64 = 55932; - - // Run the migration twice - let mut meter = WeightMeter::with_limit(Weight::MAX); - let result1 = EvmChainIdMigration::::step(None, &mut meter); - - let mut meter = WeightMeter::with_limit(Weight::MAX); - let result2 = EvmChainIdMigration::::step(None, &mut meter); - - // Both should succeed - assert!(result1.is_ok()); - assert!(result2.is_ok()); - - // Chain ID should be set correctly - assert_eq!( - pallet_evm_chain_id::ChainId::::get(), - NEW_CHAIN_ID - ); - }); - } -} diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index 344b5154..fcbc5361 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -848,7 +848,7 @@ impl pallet_parameters::Config for Runtime { impl pallet_migrations::Config for Runtime { type RuntimeEvent = RuntimeEvent; #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; + type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; #[cfg(feature = "runtime-benchmarks")] type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; type CursorMaxLen = MigrationCursorMaxLen; diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs index 893d2feb..cc32e7d2 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -845,13 +845,7 @@ impl pallet_parameters::Config for Runtime { impl pallet_migrations::Config for Runtime { type RuntimeEvent = RuntimeEvent; #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = ( - datahaven_runtime_common::migrations::MultiBlockMigrationList, - datahaven_runtime_common::migrations::evm_chain_id::EvmChainIdMigration< - Runtime, - EVM_CHAIN_ID, - >, - ); + type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; #[cfg(feature = "runtime-benchmarks")] type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; type CursorMaxLen = MigrationCursorMaxLen; diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs index 2f7bc6e7..3cac0126 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -848,13 +848,7 @@ impl pallet_parameters::Config for Runtime { impl pallet_migrations::Config for Runtime { type RuntimeEvent = RuntimeEvent; #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = ( - datahaven_runtime_common::migrations::MultiBlockMigrationList, - datahaven_runtime_common::migrations::evm_chain_id::EvmChainIdMigration< - Runtime, - EVM_CHAIN_ID, - >, - ); + type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; #[cfg(feature = "runtime-benchmarks")] type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList; type CursorMaxLen = MigrationCursorMaxLen; From 71b5e5185f0c4444b2e97801c70db7eff4b05d69 Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:25:24 +0100 Subject: [PATCH 5/9] 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 --- .github/workflows/CI.yml | 3 -- .github/workflows/task-docker-release.yml | 50 +++----------------- operator/runtime/common/src/constants.rs | 5 +- operator/runtime/mainnet/src/configs/mod.rs | 5 +- operator/runtime/stagenet/src/configs/mod.rs | 5 +- operator/runtime/testnet/src/configs/mod.rs | 5 +- 6 files changed, 16 insertions(+), 57 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fb6bdbdf..b3ba0025 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -51,12 +51,9 @@ jobs: binary-hash: ${{ needs.build-operator.outputs.binary-hash }} docker-build-release: - needs: [build-operator] if: github.ref == 'refs/heads/main' uses: ./.github/workflows/task-docker-release.yml secrets: inherit - with: - binary-hash: ${{ needs.build-operator.outputs.binary-hash }} moonwall-tests: needs: [build-operator] diff --git a/.github/workflows/task-docker-release.yml b/.github/workflows/task-docker-release.yml index 1ea5f5ed..1fae2697 100644 --- a/.github/workflows/task-docker-release.yml +++ b/.github/workflows/task-docker-release.yml @@ -17,11 +17,6 @@ on: type: boolean default: false workflow_call: - inputs: - binary-hash: - description: "The hash of the operator binary (for CI builds)" - required: true - type: string outputs: image-tag: description: "The tag portion of the docker image (without registry)" @@ -48,21 +43,6 @@ jobs: ref: ${{ github.event.inputs.branch || github.ref }} - uses: ./.github/workflows/actions/cleanup-runner - if: github.event_name == 'workflow_dispatch' - - # --- Conditional: Download binary for CI builds --- - - name: Download binary artifact (CI build) - if: github.event_name != 'workflow_dispatch' - uses: actions/download-artifact@v4 - with: - name: datahaven-node-${{ inputs.binary-hash }} - path: ./build/ - - - name: Prepare binary (CI build) - if: github.event_name != 'workflow_dispatch' - run: | - chmod +x ./build/datahaven-node - ls -la ./build/ # --- Docker metadata --- - name: Docker meta (dispatch) @@ -100,9 +80,8 @@ jobs: echo "image-tag=$TAG_ONLY" >> $GITHUB_OUTPUT echo "image-name=datahavenxyz/datahaven:$TAG_ONLY" >> $GITHUB_OUTPUT - # --- Conditional: Cargo cache for full builds --- - - name: Set up cargo cache (full build) - if: github.event_name == 'workflow_dispatch' + # --- Cargo cache for full builds --- + - name: Set up cargo cache uses: actions/cache@v4 id: cache with: @@ -115,8 +94,7 @@ jobs: cache-mount-${{ hashFiles('./docker/datahaven-build.Dockerfile') }} cache-mount- - - name: Inject cache into docker (full build) - if: github.event_name == 'workflow_dispatch' + - name: Inject cache into docker uses: reproducible-containers/buildkit-cache-dance@v3.1.0 with: cache-map: | @@ -126,9 +104,8 @@ jobs: } skip-extraction: ${{ steps.cache.outputs.cache-hit }} - # --- Build and push: Full build (workflow_dispatch) --- - - name: Build and push Docker image (full build) - if: github.event_name == 'workflow_dispatch' + # --- Build and push Docker image --- + - name: Build and push Docker image uses: ./.github/workflow-templates/publish-docker with: dockerfile: ./docker/datahaven-build.Dockerfile @@ -136,28 +113,13 @@ jobs: registry: docker.io registry_username: ${{ secrets.DOCKERHUB_USERNAME }} registry_password: ${{ secrets.DOCKERHUB_TOKEN }} - image_tags: ${{ steps.meta-dispatch.outputs.tags }} + image_tags: ${{ steps.meta-dispatch.outputs.tags || steps.meta-ci.outputs.tags }} image_title: "DataHaven Node - Release" image_description: "Release build of DataHaven blockchain node" cache_scope: datahaven-release-build build_args: | FAST_RUNTIME=${{ github.event.inputs.fast_runtime == 'true' && 'TRUE' || 'FALSE' }} - # --- Build and push: CI binary reuse (workflow_call) --- - - name: Build and push Docker image (CI binary) - if: github.event_name != 'workflow_dispatch' - uses: ./.github/workflow-templates/publish-docker - with: - dockerfile: ./operator/Dockerfile - context: . - registry: docker.io - registry_username: ${{ secrets.DOCKERHUB_USERNAME }} - registry_password: ${{ secrets.DOCKERHUB_TOKEN }} - image_tags: ${{ steps.meta-ci.outputs.tags }} - image_title: "DataHaven Node - Release" - image_description: "Release build of DataHaven operator node" - cache_scope: datahaven-release-ci - # --- Smoke tests --- - name: Pull and test node --help run: | diff --git a/operator/runtime/common/src/constants.rs b/operator/runtime/common/src/constants.rs index 4ebb1979..ad358771 100644 --- a/operator/runtime/common/src/constants.rs +++ b/operator/runtime/common/src/constants.rs @@ -26,8 +26,11 @@ pub mod time { const ONE_MINUTE: BlockNumber = MINUTES; frame_support::parameter_types! { + /// Session/epoch duration: + /// - Production: 1 hour (600 blocks) + /// - Fast-runtime: 1 minute (10 blocks) pub const EpochDurationInBlocks: BlockNumber = prod_or_fast!(ONE_HOUR, ONE_MINUTE); - pub const SessionsPerEra: SessionIndex = prod_or_fast!(6, 3); + pub const SessionsPerEra: SessionIndex = prod_or_fast!(6, 1); } // These time units are defined in number of blocks. diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index fcbc5361..4a0602c0 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -86,7 +86,7 @@ use datahaven_runtime_common::{ ReleaseDelayNone, RuntimeCallFilter, SafeModeDuration, SafeModeEnterDeposit, SafeModeExtendDeposit, TxPauseWhitelistedCalls, }, - time::{EpochDurationInBlocks, DAYS, MILLISECS_PER_BLOCK}, + time::{EpochDurationInBlocks, SessionsPerEra, DAYS, MILLISECS_PER_BLOCK}, }; use dhp_bridge::{EigenLayerMessageProcessor, NativeTokenTransferMessageProcessor}; use frame_support::{ @@ -138,7 +138,7 @@ use sp_runtime::{ traits::{Convert, ConvertInto, IdentityLookup, Keccak256, OpaqueKeys, UniqueSaturatedInto}, FixedPointNumber, Perbill, Perquintill, }; -use sp_staking::{EraIndex, SessionIndex}; +use sp_staking::EraIndex; use sp_std::{ convert::{From, Into}, prelude::*, @@ -164,7 +164,6 @@ const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; parameter_types! { pub const MaxAuthorities: u32 = 32; pub const BondingDuration: EraIndex = polkadot_runtime_common::prod_or_fast!(28, 3); - pub const SessionsPerEra: SessionIndex = polkadot_runtime_common::prod_or_fast!(6, 1); pub const AuthorRewardPoints: u32 = 20; } diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs index cc32e7d2..e923ab67 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -86,7 +86,7 @@ use datahaven_runtime_common::{ ReleaseDelayNone, RuntimeCallFilter, SafeModeDuration, SafeModeEnterDeposit, SafeModeExtendDeposit, TxPauseWhitelistedCalls, }, - time::{EpochDurationInBlocks, DAYS, MILLISECS_PER_BLOCK}, + time::{EpochDurationInBlocks, SessionsPerEra, DAYS, MILLISECS_PER_BLOCK}, }; use dhp_bridge::{EigenLayerMessageProcessor, NativeTokenTransferMessageProcessor}; use frame_support::{ @@ -138,7 +138,7 @@ use sp_runtime::{ traits::{Convert, ConvertInto, IdentityLookup, Keccak256, OpaqueKeys, UniqueSaturatedInto}, FixedPointNumber, Perbill, Perquintill, }; -use sp_staking::{EraIndex, SessionIndex}; +use sp_staking::EraIndex; use sp_std::{ convert::{From, Into}, prelude::*, @@ -164,7 +164,6 @@ const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; parameter_types! { pub const MaxAuthorities: u32 = 32; pub const BondingDuration: EraIndex = polkadot_runtime_common::prod_or_fast!(28, 3); - pub const SessionsPerEra: SessionIndex = polkadot_runtime_common::prod_or_fast!(6, 1); pub const AuthorRewardPoints: u32 = 20; } diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs index 3cac0126..72d7f3df 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -86,7 +86,7 @@ use datahaven_runtime_common::{ ReleaseDelayNone, RuntimeCallFilter, SafeModeDuration, SafeModeEnterDeposit, SafeModeExtendDeposit, TxPauseWhitelistedCalls, }, - time::{EpochDurationInBlocks, DAYS, MILLISECS_PER_BLOCK}, + time::{EpochDurationInBlocks, SessionsPerEra, DAYS, MILLISECS_PER_BLOCK}, }; use dhp_bridge::{EigenLayerMessageProcessor, NativeTokenTransferMessageProcessor}; use frame_support::{ @@ -138,7 +138,7 @@ use sp_runtime::{ traits::{Convert, ConvertInto, IdentityLookup, Keccak256, OpaqueKeys, UniqueSaturatedInto}, FixedPointNumber, Perbill, Perquintill, }; -use sp_staking::{EraIndex, SessionIndex}; +use sp_staking::EraIndex; use sp_std::{ convert::{From, Into}, prelude::*, @@ -164,7 +164,6 @@ const SS58_FORMAT: u16 = EVM_CHAIN_ID as u16; parameter_types! { pub const MaxAuthorities: u32 = 32; pub const BondingDuration: EraIndex = polkadot_runtime_common::prod_or_fast!(28, 3); - pub const SessionsPerEra: SessionIndex = polkadot_runtime_common::prod_or_fast!(6, 1); pub const AuthorRewardPoints: u32 = 20; } From 0618e84268c82e4a794ccfbc2308d3af786aa9a1 Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:14:41 +0100 Subject: [PATCH 6/9] feat: set POV gas limit ratio to zero for solo chain (#313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- operator/precompiles/batch/src/mock.rs | 6 +----- operator/precompiles/collective/src/mock.rs | 6 +----- operator/precompiles/conviction-voting/src/mock.rs | 6 +----- operator/precompiles/erc20-balances/src/mock.rs | 6 +----- operator/precompiles/identity/src/mock.rs | 6 +----- operator/precompiles/preimage/src/mock.rs | 6 +----- operator/precompiles/proxy/src/mock.rs | 6 +----- operator/precompiles/referenda/src/mock.rs | 5 +---- operator/runtime/mainnet/src/configs/mod.rs | 9 +++------ operator/runtime/stagenet/src/configs/mod.rs | 9 +++------ operator/runtime/testnet/src/configs/mod.rs | 9 +++------ .../suites/dev/stagenet/eth-fee/test-eth-fee-history.ts | 2 +- 12 files changed, 18 insertions(+), 58 deletions(-) diff --git a/operator/precompiles/batch/src/mock.rs b/operator/precompiles/batch/src/mock.rs index b834a37b..5b51a83e 100644 --- a/operator/precompiles/batch/src/mock.rs +++ b/operator/precompiles/batch/src/mock.rs @@ -123,7 +123,6 @@ pub type PCall = BatchPrecompileCall; mock_account!(Batch, |_| MockAccount::from_u64(1)); mock_account!(Revert, |_| MockAccount::from_u64(2)); -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -131,10 +130,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio: u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/collective/src/mock.rs b/operator/precompiles/collective/src/mock.rs index 6705d921..8a27223e 100644 --- a/operator/precompiles/collective/src/mock.rs +++ b/operator/precompiles/collective/src/mock.rs @@ -124,7 +124,6 @@ pub type Precompiles = PrecompileSetBuilder< pub type PCall = CollectivePrecompileCall; -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -132,10 +131,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio : u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/conviction-voting/src/mock.rs b/operator/precompiles/conviction-voting/src/mock.rs index d6d6d3a2..b0e43275 100644 --- a/operator/precompiles/conviction-voting/src/mock.rs +++ b/operator/precompiles/conviction-voting/src/mock.rs @@ -109,7 +109,6 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -117,10 +116,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio : u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/erc20-balances/src/mock.rs b/operator/precompiles/erc20-balances/src/mock.rs index ebfc4dcf..aaa2a64d 100644 --- a/operator/precompiles/erc20-balances/src/mock.rs +++ b/operator/precompiles/erc20-balances/src/mock.rs @@ -108,7 +108,6 @@ pub type Precompiles = PrecompileSetBuilder< pub type PCall = Erc20BalancesPrecompileCall; -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -116,10 +115,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio: u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/identity/src/mock.rs b/operator/precompiles/identity/src/mock.rs index 36ee0f68..b984730f 100644 --- a/operator/precompiles/identity/src/mock.rs +++ b/operator/precompiles/identity/src/mock.rs @@ -110,7 +110,6 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -118,10 +117,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio: u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/preimage/src/mock.rs b/operator/precompiles/preimage/src/mock.rs index 9e4407af..2c520ef1 100644 --- a/operator/precompiles/preimage/src/mock.rs +++ b/operator/precompiles/preimage/src/mock.rs @@ -101,7 +101,6 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -109,10 +108,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio: u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/proxy/src/mock.rs b/operator/precompiles/proxy/src/mock.rs index f3fc144d..3985182e 100644 --- a/operator/precompiles/proxy/src/mock.rs +++ b/operator/precompiles/proxy/src/mock.rs @@ -150,7 +150,6 @@ impl EnsureAddressOrigin for EnsureAddressAlways { } } -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; /// Block storage limit in bytes. Set to 40 KB. const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024; @@ -158,10 +157,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio: u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/precompiles/referenda/src/mock.rs b/operator/precompiles/referenda/src/mock.rs index 4de6a257..661c11db 100644 --- a/operator/precompiles/referenda/src/mock.rs +++ b/operator/precompiles/referenda/src/mock.rs @@ -152,10 +152,7 @@ parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); pub PrecompilesValue: TestPrecompiles = TestPrecompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; + pub const GasLimitPovSizeRatio: u64 = 0; pub GasLimitStorageGrowthRatio: u64 = { let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT) diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index 4a0602c0..57e73af9 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -995,13 +995,10 @@ parameter_types! { pub PrecompilesValue: Precompiles = DataHavenPrecompiles::::new(); pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); pub SuicideQuickClearLimit: u32 = 0; - /// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare - /// it with the pov_size for a block. E.g. - /// ceil( - /// (max_extrinsic.ref_time() / max_extrinsic.proof_size()) / WEIGHT_PER_GAS - /// ) + /// The amount of gas per pov. Set to 0 because DataHaven is a solo chain and we don't + /// account for POV (Proof-of-Validity) size constraints like parachains do. /// We should re-check `xcm_config::Erc20XcmBridgeTransferGasLimit` when changing this value - pub const GasLimitPovSizeRatio: u64 = 16; + pub const GasLimitPovSizeRatio: u64 = 0; /// The amount of gas per storage (in bytes): BLOCK_GAS_LIMIT / BLOCK_STORAGE_LIMIT /// (60_000_000 / 160 kb) pub GasLimitStorageGrowthRatio: u64 = 366; diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs index e923ab67..3a0bde15 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -992,13 +992,10 @@ parameter_types! { pub PrecompilesValue: Precompiles = DataHavenPrecompiles::::new(); pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); pub SuicideQuickClearLimit: u32 = 0; - /// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare - /// it with the pov_size for a block. E.g. - /// ceil( - /// (max_extrinsic.ref_time() / max_extrinsic.proof_size()) / WEIGHT_PER_GAS - /// ) + /// The amount of gas per pov. Set to 0 because DataHaven is a solo chain and we don't + /// account for POV (Proof-of-Validity) size constraints like parachains do. /// We should re-check `xcm_config::Erc20XcmBridgeTransferGasLimit` when changing this value - pub const GasLimitPovSizeRatio: u64 = 16; + pub const GasLimitPovSizeRatio: u64 = 0; /// The amount of gas per storage (in bytes): BLOCK_GAS_LIMIT / BLOCK_STORAGE_LIMIT /// (60_000_000 / 160 kb) pub GasLimitStorageGrowthRatio: u64 = 366; diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs index 72d7f3df..57f40fb0 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -995,13 +995,10 @@ parameter_types! { pub PrecompilesValue: Precompiles = DataHavenPrecompiles::::new(); pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0); pub SuicideQuickClearLimit: u32 = 0; - /// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare - /// it with the pov_size for a block. E.g. - /// ceil( - /// (max_extrinsic.ref_time() / max_extrinsic.proof_size()) / WEIGHT_PER_GAS - /// ) + /// The amount of gas per pov. Set to 0 because DataHaven is a solo chain and we don't + /// account for POV (Proof-of-Validity) size constraints like parachains do. /// We should re-check `xcm_config::Erc20XcmBridgeTransferGasLimit` when changing this value - pub const GasLimitPovSizeRatio: u64 = 16; + pub const GasLimitPovSizeRatio: u64 = 0; /// The amount of gas per storage (in bytes): BLOCK_GAS_LIMIT / BLOCK_STORAGE_LIMIT /// (60_000_000 / 160 kb) pub GasLimitStorageGrowthRatio: u64 = 366; diff --git a/test/datahaven/suites/dev/stagenet/eth-fee/test-eth-fee-history.ts b/test/datahaven/suites/dev/stagenet/eth-fee/test-eth-fee-history.ts index a29719c3..754b1c2b 100644 --- a/test/datahaven/suites/dev/stagenet/eth-fee/test-eth-fee-history.ts +++ b/test/datahaven/suites/dev/stagenet/eth-fee/test-eth-fee-history.ts @@ -62,7 +62,7 @@ describeSuite({ feeResults.baseFeePerGas.length, "baseFeePerGas should always the requested block range + 1 (the next derived base fee)" ).toBe(block_count + 1); - expect(feeResults.gasUsedRatio).to.be.deep.eq(Array(block_count).fill(0.0105225)); + expect(feeResults.gasUsedRatio).to.be.deep.eq(Array(block_count).fill(0.00773915)); expect( feeResults.reward.length, "should return two-dimensional reward list for the requested block range" From ffd01d8f1d7bbd248f3b65fa4fd1c3b25233e471 Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:06:04 +0100 Subject: [PATCH 7/9] Fix: command cli deploy contracts (#319) ## Summary This PR fixes several issues with the CLI deploy-contracts command to properly support local Anvil deployments and improves the overall contract deployment workflow. ### Key fixes: - Add support for anvil chain in the CLI deploy contracts command - Rename PRIVATE_KEY to DEPLOYER_PRIVATE_KEY for consistency and clarity across the deployment flow - Fix EigenLayer contract status display for local/anvil chains by reading addresses from the deployments file instead of config - Fix runShellCommandWithLogger to properly throw errors on command failure - Correct totalSteps in DeployTestnet.s.sol from 2 to 4 ### Housekeeping: - Update .gitignore to ignore the entire broadcast/ folder (autogenerated Foundry artifacts) - Streamline contracts/README.md with clearer structure and deployment instructions --- contracts/.gitignore | 6 +- contracts/README.md | 156 +++++--------------- contracts/script/deploy/DeployBase.s.sol | 3 +- contracts/script/deploy/DeployTestnet.s.sol | 2 +- test/cli/handlers/contracts/.env.example | 2 +- test/cli/handlers/contracts/README.md | 4 +- test/cli/handlers/contracts/deploy.ts | 10 +- test/cli/handlers/contracts/status.ts | 61 ++++++-- test/cli/index.ts | 30 ++-- test/scripts/deploy-contracts.ts | 14 +- test/utils/shell.ts | 7 +- 11 files changed, 143 insertions(+), 152 deletions(-) diff --git a/contracts/.gitignore b/contracts/.gitignore index 58010582..56cb5437 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -2,10 +2,8 @@ cache/ out/ -# Ignores development broadcast logs -!/broadcast -/broadcast/*/3*/ -/broadcast/**/dry-run/ +# Ignores development broadcast logs (autogenerated by forge script --broadcast) +broadcast/ # Docs docs/ diff --git a/contracts/README.md b/contracts/README.md index d3d03221..cc7db4fe 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -1,145 +1,71 @@ -# DataHaven AVS Smart Contracts 📜 +# DataHaven AVS Smart Contracts -This directory contains the smart contracts for the DataHaven Actively Validated Service (AVS) built on EigenLayer. - -## Overview - -DataHaven is an EVM-compatible Substrate blockchain secured by EigenLayer. These contracts implement the AVS Service Manager, middleware, and associated utilities that integrate with EigenLayer's operator registration, slashing, and rewards infrastructure. +Implements the Actively Validated Service (AVS) logic for DataHaven, secured by EigenLayer. These contracts manage operator registration, handle cross-chain rewards via Snowbridge, and enforce slashing with a veto period. ## Project Structure ``` contracts/ -├── src/ # Smart contract source code +├── src/ │ ├── DataHavenServiceManager.sol # Core AVS service manager -│ ├── RewardsRegistry.sol # Validator performance & rewards tracking -│ ├── VetoableSlasher.sol # Slashing with veto period +│ ├── middleware/ # RewardsRegistry, VetoableSlasher, Snowbridge helpers │ ├── interfaces/ # Contract interfaces -│ ├── libraries/ # Utility libraries -│ └── middleware/ # EigenLayer middleware integration -├── script/ # Deployment & setup scripts -│ └── deploy/ # Environment-specific deployment -├── test/ # Foundry test suites -└── foundry.toml # Foundry configuration +│ └── libraries/ # Utility libraries +├── script/ # Deployment & setup scripts +├── lib/ # External dependencies (EigenLayer, Snowbridge, OpenZeppelin) +└── test/ # Foundry test suites ``` -### Key Contracts +## Key Components -- **DataHavenServiceManager**: Manages operator lifecycle, registration, and deregistration with EigenLayer -- **RewardsRegistry**: Tracks validator performance metrics and handles reward distribution via Snowbridge -- **VetoableSlasher**: Implements slashing mechanism with dispute resolution veto period -- **Middleware**: Integration layer with EigenLayer's core contracts (based on [eigenlayer-middleware](https://github.com/Layr-Labs/eigenlayer-middleware)) +- **DataHavenServiceManager** (`src/DataHavenServiceManager.sol`): Core contract for operator lifecycle; inherits `ServiceManagerBase`. +- **RewardsRegistry** (`src/middleware/RewardsRegistry.sol`): Tracks validator performance and distributes rewards via Snowbridge. +- **VetoableSlasher** (`src/middleware/VetoableSlasher.sol`): Handles slashing requests with a dispute resolution veto window. -## Prerequisites +## Development -- [Foundry](https://book.getfoundry.sh/getting-started/installation) - -## Build - -To build the contracts: +Requires [Foundry](https://book.getfoundry.sh). ```bash -cd contracts +# Build and Test forge build -``` - -This will compile all contracts and generate artifacts in the `out` directory. - -## Test - -Run the test suite with: - -```bash forge test -``` -For more verbose output including logs: - -```bash -forge test -vv -``` - -For maximum verbosity including stack traces: - -```bash -forge test -vvvv -``` - -Run specific test contracts: - -```bash -forge test --match-contract RewardsRegistry -``` - -Run specific test functions: - -```bash -forge test --match-test test_newRewardsMessage -``` - -Exclude specific tests: - -```bash -forge test --no-match-test test_newRewardsMessage_OnlyRewardsAgent -``` - -## Deployment - -### Local Deployment - -1. In a separate terminal, start a local Anvil instance: - -```bash -anvil -``` - -2. Deploy to local Anvil: - -```bash -forge script script/deploy/DeployLocal.s.sol --rpc-url anvil --broadcast -``` - -### Network Deployment - -To deploy to a network configured in `foundry.toml`: - -```bash -forge script script/deploy/DeployLocal.s.sol --rpc-url $NETWORK_RPC_URL --private-key $PRIVATE_KEY --broadcast -``` - -Replace `$NETWORK_RPC_URL` with the RPC endpoint and `$PRIVATE_KEY` with your deployer's private key. - -Or using a network from `foundry.toml`: - -```bash -forge script script/deploy/DeployLocal.s.sol --rpc-url mainnet --private-key $PRIVATE_KEY --broadcast +# Regenerate TS bindings (after contract changes) +cd ../test && bun generate:wagmi ``` ## Configuration -The deployment configuration can be modified in: +Deployment parameters (EigenLayer addresses, initial validators, owners) are defined in `contracts/config/.json`. +- **Do not edit** `Config.sol` or `DeployParams.s.sol` directly; they only load the JSON. +- Ensure `contracts/config/hoodi.json` (or `holesky.json`) matches your target environment before deploying. -- `script/deploy/Config.sol`: Environment-specific configuration -- `script/deploy/DeployParams.s.sol`: Deployment parameters +## Deployment -## Code Generation - -After making changes to contracts, regenerate TypeScript bindings for the test framework: +Two deployment paths exist: **Local** (Anvil) and **Testnet** (Hoodi/Holesky). Both install the **DataHaven AVS contracts** (ServiceManager, RewardsRegistry, VetoableSlasher) and **Snowbridge** (BeefyClient, Gateway, Agent). They differ in EigenLayer setup: +### Local (Anvil) +**`DeployLocal.s.sol`** bootstraps a full EigenLayer core deployment (DelegationManager, StrategyManager, AVSDirectory, etc.) alongside DataHaven AVS and Snowbridge. ```bash -cd ../test -bun generate:wagmi +anvil +forge script script/deploy/DeployLocal.s.sol --rpc-url anvil --broadcast ``` -This generates type-safe contract interfaces used by the E2E test suite. +### Testnet (Hoodi / Holesky) +**`DeployTestnet.s.sol`** references existing EigenLayer contracts (addresses from `contracts/config/.json`) and only deploys DataHaven AVS + Snowbridge. +```bash +NETWORK=hoodi forge script script/deploy/DeployTestnet.s.sol \ + --rpc-url hoodi \ + --private-key $PRIVATE_KEY \ + --broadcast +``` +Supported networks: `hoodi`, `holesky` (no mainnet config yet). Artifacts → `contracts/deployments/.json`. -## Integration with DataHaven +## How It Works +1. **Registration**: Validators register with EigenLayer via `DataHavenServiceManager`. +2. **Performance Tracking**: DataHaven computes reward points and sends a Merkle root to `RewardsRegistry` on Ethereum via Snowbridge. +3. **Rewards Claims**: Validators claim rewards on Ethereum from `RewardsRegistry` using Merkle proofs. +4. **Slashing**: Misbehavior triggers `VetoableSlasher` (subject to veto period). -These contracts integrate with the DataHaven Substrate node through: - -1. **Operator Registration**: Validators register on-chain via `DataHavenServiceManager` -2. **Performance Tracking**: Node submits validator metrics to `RewardsRegistry` -3. **Cross-chain Rewards**: Rewards distributed from Ethereum to DataHaven via Snowbridge -4. **Slashing**: Misbehavior triggers slashing through `VetoableSlasher` with veto period - -For full network integration testing, see the [test directory](../test/README.md). +See `test/README.md` for full network integration tests. diff --git a/contracts/script/deploy/DeployBase.s.sol b/contracts/script/deploy/DeployBase.s.sol index 894f66c9..96d4b307 100644 --- a/contracts/script/deploy/DeployBase.s.sol +++ b/contracts/script/deploy/DeployBase.s.sol @@ -114,7 +114,6 @@ abstract contract DeployBase is Script, DeployParams, Accounts { _logProgress(); // Deploy Snowbridge (same for both modes) - Logging.logHeader("SNOWBRIDGE DEPLOYMENT"); ( BeefyClient beefyClient, AgentExecutor agentExecutor, @@ -170,6 +169,8 @@ abstract contract DeployBase is Script, DeployParams, Accounts { function _deploySnowbridge( SnowbridgeConfig memory config ) internal returns (BeefyClient, AgentExecutor, IGatewayV2, address payable) { + Logging.logHeader("SNOWBRIDGE DEPLOYMENT"); + Logging.logSection("Deploying Snowbridge Core Components"); BeefyClient beefyClient = _deployBeefyClient(config); diff --git a/contracts/script/deploy/DeployTestnet.s.sol b/contracts/script/deploy/DeployTestnet.s.sol index 8236988d..153e5727 100644 --- a/contracts/script/deploy/DeployTestnet.s.sol +++ b/contracts/script/deploy/DeployTestnet.s.sol @@ -57,7 +57,7 @@ contract DeployTestnet is DeployBase { ); currentTestnet = _detectAndValidateNetwork(networkName); - totalSteps = 2; // Reduced steps since we're not deploying EigenLayer + totalSteps = 4; _executeSharedDeployment(); } diff --git a/test/cli/handlers/contracts/.env.example b/test/cli/handlers/contracts/.env.example index 0939183e..d33611b8 100644 --- a/test/cli/handlers/contracts/.env.example +++ b/test/cli/handlers/contracts/.env.example @@ -2,7 +2,7 @@ # Copy this file to .env and fill in your values # Private key for contract deployment (REQUIRED) -PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 +DEPLOYER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # AVS Owner private key (for post-deployment configuration) AVS_OWNER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 diff --git a/test/cli/handlers/contracts/README.md b/test/cli/handlers/contracts/README.md index 1055c21d..2ea44b4a 100644 --- a/test/cli/handlers/contracts/README.md +++ b/test/cli/handlers/contracts/README.md @@ -29,9 +29,9 @@ cd test && cp cli/handlers/contracts/.env.example .env Edit `.env` with your values: ```bash # Required: Private key with deployment funds -PRIVATE_KEY=0x... +DEPLOYER_PRIVATE_KEY=0x... -# Required: AVS owner private key (can be same as PRIVATE_KEY) +# Required: AVS owner private key (can be same as DEPLOYER_PRIVATE_KEY) AVS_OWNER_PRIVATE_KEY=0x... # Optional: For contract verification diff --git a/test/cli/handlers/contracts/deploy.ts b/test/cli/handlers/contracts/deploy.ts index abddeb11..d16daa79 100644 --- a/test/cli/handlers/contracts/deploy.ts +++ b/test/cli/handlers/contracts/deploy.ts @@ -91,17 +91,19 @@ export const contractsPreActionHook = async (thisCommand: any) => { const privateKey = thisCommand.getOptionValue("privateKey"); if (!chain) { - logger.error("❌ Chain is required. Use --chain option (hoodi, holesky, mainnet)"); + logger.error("❌ Chain is required. Use --chain option (hoodi, holesky, mainnet, anvil)"); process.exit(1); } - const supportedChains = ["hoodi", "holesky", "mainnet"]; + const supportedChains = ["hoodi", "holesky", "mainnet", "anvil"]; if (!supportedChains.includes(chain)) { logger.error(`❌ Unsupported chain: ${chain}. Supported chains: ${supportedChains.join(", ")}`); process.exit(1); } - if (!privateKey) { - logger.warn("⚠️ Private key not provided. Will use PRIVATE_KEY environment variable"); + if (!privateKey && !process.env.DEPLOYER_PRIVATE_KEY) { + logger.warn( + "⚠️ Private key not provided. Will use DEPLOYER_PRIVATE_KEY environment variable if set, or default Anvil key." + ); } }; diff --git a/test/cli/handlers/contracts/status.ts b/test/cli/handlers/contracts/status.ts index 684057d9..c984ec6f 100644 --- a/test/cli/handlers/contracts/status.ts +++ b/test/cli/handlers/contracts/status.ts @@ -25,7 +25,8 @@ export const showDeploymentPlanAndStatus = async (chain: string) => { await showEigenLayerContractStatus( config, deploymentParams.chainId.toString(), - deploymentParams.rpcUrl + deploymentParams.rpcUrl, + chain ); printDivider(); @@ -109,27 +110,69 @@ const showDatahavenContractStatus = async (chain: string, rpcUrl: string) => { /** * Shows the status of EigenLayer contracts (verification only) */ -const showEigenLayerContractStatus = async (config: any, chainId: string, rpcUrl: string) => { +const showEigenLayerContractStatus = async ( + config: any, + chainId: string, + rpcUrl: string, + chain: string +) => { try { + // For local/anvil deployments, read addresses from deployments file + // For testnet/mainnet, use addresses from config file + let eigenLayerAddresses: Record = {}; + const isLocal = chain === "anvil" || chain === "local"; + + if (isLocal) { + try { + const deploymentsPath = `../contracts/deployments/${chain === "local" ? "anvil" : chain}.json`; + const deploymentsFile = Bun.file(deploymentsPath); + if (await deploymentsFile.exists()) { + const deployments = await deploymentsFile.json(); + eigenLayerAddresses = { + DelegationManager: deployments.DelegationManager, + StrategyManager: deployments.StrategyManager, + EigenPodManager: deployments.EigenPodManager, + AVSDirectory: deployments.AVSDirectory, + RewardsCoordinator: deployments.RewardsCoordinator, + AllocationManager: deployments.AllocationManager, + PermissionController: deployments.PermissionController + }; + } + } catch (error) { + logger.debug(`Could not read deployments file for EigenLayer contracts: ${error}`); + } + } + const contracts = [ { name: "DelegationManager", - address: config.eigenLayer.delegationManager + address: eigenLayerAddresses.DelegationManager || config.eigenLayer?.delegationManager || "" + }, + { + name: "StrategyManager", + address: eigenLayerAddresses.StrategyManager || config.eigenLayer?.strategyManager || "" + }, + { + name: "EigenPodManager", + address: eigenLayerAddresses.EigenPodManager || config.eigenLayer?.eigenPodManager || "" + }, + { + name: "AVSDirectory", + address: eigenLayerAddresses.AVSDirectory || config.eigenLayer?.avsDirectory || "" }, - { name: "StrategyManager", address: config.eigenLayer.strategyManager }, - { name: "EigenPodManager", address: config.eigenLayer.eigenPodManager }, - { name: "AVSDirectory", address: config.eigenLayer.avsDirectory }, { name: "RewardsCoordinator", - address: config.eigenLayer.rewardsCoordinator + address: + eigenLayerAddresses.RewardsCoordinator || config.eigenLayer?.rewardsCoordinator || "" }, { name: "AllocationManager", - address: config.eigenLayer.allocationManager + address: eigenLayerAddresses.AllocationManager || config.eigenLayer?.allocationManager || "" }, { name: "PermissionController", - address: config.eigenLayer.permissionController + address: + eigenLayerAddresses.PermissionController || config.eigenLayer?.permissionController || "" } ]; diff --git a/test/cli/index.ts b/test/cli/index.ts index 4b81b001..399e5cb3 100644 --- a/test/cli/index.ts +++ b/test/cli/index.ts @@ -198,7 +198,7 @@ const contractsCommand = program - update-metadata: Update the metadata URI of an existing AVS contract Common options: - --chain: Target chain (required: hoodi, holesky, mainnet) + --chain: Target chain (required: hoodi, holesky, mainnet, anvil) --rpc-url: Chain RPC URL (optional, defaults based on chain) --private-key: Private key for deployment --skip-verification: Skip contract verification @@ -210,9 +210,13 @@ const contractsCommand = program contractsCommand .command("status") .description("Show deployment plan, configuration, and status") - .option("--chain ", "Target chain (hoodi, holesky, mainnet)") + .option("--chain ", "Target chain (hoodi, holesky, mainnet, anvil)") .option("--rpc-url ", "Chain RPC URL (optional, defaults based on chain)") - .option("--private-key ", "Private key for deployment", process.env.PRIVATE_KEY || "") + .option( + "--private-key ", + "Private key for deployment", + process.env.DEPLOYER_PRIVATE_KEY || "" + ) .option("--skip-verification", "Skip contract verification", false) .hook("preAction", contractsPreActionHook) .action(contractsCheck); @@ -221,9 +225,13 @@ contractsCommand contractsCommand .command("deploy") .description("Deploy DataHaven AVS contracts to specified chain") - .option("--chain ", "Target chain (hoodi, holesky, mainnet)") + .option("--chain ", "Target chain (hoodi, holesky, mainnet, anvil)") .option("--rpc-url ", "Chain RPC URL (optional, defaults based on chain)") - .option("--private-key ", "Private key for deployment", process.env.PRIVATE_KEY || "") + .option( + "--private-key ", + "Private key for deployment", + process.env.DEPLOYER_PRIVATE_KEY || "" + ) .option("--skip-verification", "Skip contract verification", false) .hook("preAction", contractsPreActionHook) .action(contractsDeploy); @@ -232,7 +240,7 @@ contractsCommand contractsCommand .command("verify") .description("Verify deployed contracts on block explorer") - .option("--chain ", "Target chain (hoodi, holesky, mainnet)") + .option("--chain ", "Target chain (hoodi, holesky, mainnet, anvil)") .option("--rpc-url ", "Chain RPC URL (optional, defaults based on chain)") .option("--skip-verification", "Skip contract verification", false) .hook("preAction", contractsPreActionHook) @@ -242,7 +250,7 @@ contractsCommand contractsCommand .command("update-metadata") .description("Update AVS metadata URI for the DataHaven Service Manager") - .option("--chain ", "Target chain (hoodi, holesky, mainnet)") + .option("--chain ", "Target chain (hoodi, holesky, mainnet, anvil)") .option("--uri ", "New metadata URI (required)") .option("--reset", "Use if you want to reset the metadata URI") .option("--rpc-url ", "Chain RPC URL (optional, defaults based on chain)") @@ -270,9 +278,13 @@ contractsCommand // Default Contracts command (runs check) contractsCommand .description("Show deployment plan, configuration, and status") - .option("--chain ", "Target chain (hoodi, holesky, mainnet)") + .option("--chain ", "Target chain (hoodi, holesky, mainnet, anvil)") .option("--rpc-url ", "Chain RPC URL (optional, defaults based on chain)") - .option("--private-key ", "Private key for deployment", process.env.PRIVATE_KEY || "") + .option( + "--private-key ", + "Private key for deployment", + process.env.DEPLOYER_PRIVATE_KEY || "" + ) .option("--skip-verification", "Skip contract verification", false) .hook("preAction", contractsPreActionHook) .action(contractsCheck); diff --git a/test/scripts/deploy-contracts.ts b/test/scripts/deploy-contracts.ts index 7ceca773..83935da7 100644 --- a/test/scripts/deploy-contracts.ts +++ b/test/scripts/deploy-contracts.ts @@ -54,7 +54,7 @@ export const constructDeployCommand = (options: ContractDeploymentOptions): stri const { chain, rpcUrl, verified, blockscoutBackendUrl } = options; const deploymentScript = - !chain || chain === "anvil" || chain === "local" + !chain || chain === "anvil" ? "script/deploy/DeployLocal.s.sol" : "script/deploy/DeployTestnet.s.sol"; @@ -83,12 +83,16 @@ export const constructDeployCommand = (options: ContractDeploymentOptions): stri export const executeDeployment = async ( deployCommand: string, parameterCollection?: ParameterCollection, - chain?: string + chain?: string, + privateKey?: string ) => { logger.info("⌛️ Deploying contracts (this might take a few minutes)..."); // Using custom shell command to improve logging with forge's stdoutput - await runShellCommandWithLogger(deployCommand, { cwd: "../contracts" }); + await runShellCommandWithLogger(deployCommand, { + cwd: "../contracts", + env: privateKey ? { DEPLOYER_PRIVATE_KEY: privateKey } : undefined + }); // After deployment, read the: // - Gateway address @@ -198,7 +202,7 @@ export const deployContracts = async (options: { // Construct and execute deployment const deployCommand = constructDeployCommand(deploymentOptions); - await executeDeployment(deployCommand); + await executeDeployment(deployCommand, undefined, options.chain, options.privateKey); logger.success(`DataHaven contracts deployed successfully to ${options.chain}`); }; @@ -251,5 +255,5 @@ if (import.meta.main) { await buildContracts(); const deployCommand = constructDeployCommand(options); - await executeDeployment(deployCommand); + await executeDeployment(deployCommand, undefined, undefined, options.privateKey); } diff --git a/test/utils/shell.ts b/test/utils/shell.ts index 4d316a49..d0d450e3 100644 --- a/test/utils/shell.ts +++ b/test/utils/shell.ts @@ -11,9 +11,10 @@ export const runShellCommandWithLogger = async ( env?: object; logLevel?: LogLevel; waitFor?: (...args: unknown[]) => Promise; + throwOnError?: boolean; } ) => { - const { cwd = ".", env = {}, logLevel = "info" as LogLevel } = options || {}; + const { cwd = ".", env = {}, logLevel = "info" as LogLevel, throwOnError = true } = options || {}; try { if (!existsSync(cwd)) { @@ -92,6 +93,10 @@ export const runShellCommandWithLogger = async ( trimmedStderr.includes("\n") ? `>_ \n${trimmedStderr}` : `>_ ${trimmedStderr}` ); } + + if (throwOnError) { + throw new Error(`Command failed with exit code ${exitCode}`); + } } } catch (err) { logger.error("❌ Error running shell command:", command, "in", cwd); From b737bc03ba09450895f7cb8af19218b3072ed5ed Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Date: Fri, 28 Nov 2025 14:01:28 +0100 Subject: [PATCH 8/9] refactor: remove BSP and MSP operator sets (#323) --- contracts/config/anvil.json | 16 +- contracts/script/deploy/Config.sol | 2 - contracts/script/deploy/DeployLocal.s.sol | 6 - contracts/script/deploy/DeployTestnet.s.sol | 2 - contracts/script/transact/SignUpBsp.s.sol | 33 -- contracts/script/transact/SignUpMsp.s.sol | 33 -- .../script/transact/SignUpOperatorBase.s.sol | 2 +- contracts/src/DataHavenServiceManager.sol | 152 +------ .../interfaces/IDataHavenServiceManager.sol | 114 +---- contracts/test/utils/AVSDeployer.sol | 10 +- .../test/utils/SnowbridgeAndAVSDeployer.sol | 4 - test/contract-bindings/generated.ts | 402 ------------------ 12 files changed, 33 insertions(+), 743 deletions(-) delete mode 100644 contracts/script/transact/SignUpBsp.s.sol delete mode 100644 contracts/script/transact/SignUpMsp.s.sol diff --git a/contracts/config/anvil.json b/contracts/config/anvil.json index 720568bc..a8b1122e 100644 --- a/contracts/config/anvil.json +++ b/contracts/config/anvil.json @@ -24,15 +24,13 @@ "allocationConfigurationDelay": 75, "beaconChainGenesisTimestamp": 1695902400 }, - "avs": { - "avsOwner": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", - "rewardsInitiator": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", - "vetoCommitteeMember": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", - "vetoWindowBlocks": 100, - "validatorsStrategies": [], - "bspsStrategies": [], - "mspsStrategies": [] - }, + "avs": { + "avsOwner": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "rewardsInitiator": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", + "vetoCommitteeMember": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", + "vetoWindowBlocks": 100, + "validatorsStrategies": [] + }, "snowbridge": { "randaoCommitDelay": 4, "randaoCommitExpiration": 24, diff --git a/contracts/script/deploy/Config.sol b/contracts/script/deploy/Config.sol index 61fe9208..c23c8804 100644 --- a/contracts/script/deploy/Config.sol +++ b/contracts/script/deploy/Config.sol @@ -20,8 +20,6 @@ contract Config { address vetoCommitteeMember; uint32 vetoWindowBlocks; address[] validatorsStrategies; - address[] bspsStrategies; - address[] mspsStrategies; } // EigenLayer parameters diff --git a/contracts/script/deploy/DeployLocal.s.sol b/contracts/script/deploy/DeployLocal.s.sol index f4be3069..0c6c7066 100644 --- a/contracts/script/deploy/DeployLocal.s.sol +++ b/contracts/script/deploy/DeployLocal.s.sol @@ -194,8 +194,6 @@ contract DeployLocal is DeployBase { params.avsOwner, params.rewardsInitiator, params.validatorsStrategies, - new IStrategy[](0), // FIXME remove when BSPs are removed - new IStrategy[](0), // FIXME remove when MSPs are removed params.gateway ); @@ -662,12 +660,8 @@ contract DeployLocal is DeployBase { ) internal pure { if (config.validatorsStrategies.length == 0) { config.validatorsStrategies = new address[](strategies.length); - config.bspsStrategies = new address[](strategies.length); - config.mspsStrategies = new address[](strategies.length); for (uint256 i = 0; i < strategies.length; i++) { config.validatorsStrategies[i] = strategies[i].address_; - config.bspsStrategies[i] = strategies[i].address_; - config.mspsStrategies[i] = strategies[i].address_; } } } diff --git a/contracts/script/deploy/DeployTestnet.s.sol b/contracts/script/deploy/DeployTestnet.s.sol index 153e5727..1261eba4 100644 --- a/contracts/script/deploy/DeployTestnet.s.sol +++ b/contracts/script/deploy/DeployTestnet.s.sol @@ -132,8 +132,6 @@ contract DeployTestnet is DeployBase { params.avsOwner, params.rewardsInitiator, params.validatorsStrategies, - new IStrategy[](0), // FIXME remove when BSPs and MSPs are removed - new IStrategy[](0), // FIXME remove when BSPs and MSPs are removed params.gateway ); diff --git a/contracts/script/transact/SignUpBsp.s.sol b/contracts/script/transact/SignUpBsp.s.sol deleted file mode 100644 index 782d2396..00000000 --- a/contracts/script/transact/SignUpBsp.s.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.27; - -import {SignUpOperatorBase} from "./SignUpOperatorBase.s.sol"; -import {DataHavenServiceManager} from "../../src/DataHavenServiceManager.sol"; - -/** - * @title SignUpBsp - * @notice Script to sign up a backup storage provider (BSP) for the DataHaven network - */ -contract SignUpBsp is SignUpOperatorBase { - /** - * @inheritdoc SignUpOperatorBase - */ - function _getOperatorSetId() internal view override returns (uint32) { - return serviceManager.BSPS_SET_ID(); - } - - /** - * @inheritdoc SignUpOperatorBase - */ - function _addToAllowlist() internal override { - vm.broadcast(_avsOwnerPrivateKey); - serviceManager.addBspToAllowlist(_operator); - } - - /** - * @inheritdoc SignUpOperatorBase - */ - function _getOperatorTypeName() internal pure override returns (string memory) { - return "BSP"; - } -} diff --git a/contracts/script/transact/SignUpMsp.s.sol b/contracts/script/transact/SignUpMsp.s.sol deleted file mode 100644 index 41c8190f..00000000 --- a/contracts/script/transact/SignUpMsp.s.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.27; - -import {SignUpOperatorBase} from "./SignUpOperatorBase.s.sol"; -import {DataHavenServiceManager} from "../../src/DataHavenServiceManager.sol"; - -/** - * @title SignUpMsp - * @notice Script to sign up a main storage provider (MSP) for the DataHaven network - */ -contract SignUpMsp is SignUpOperatorBase { - /** - * @inheritdoc SignUpOperatorBase - */ - function _getOperatorSetId() internal view override returns (uint32) { - return serviceManager.MSPS_SET_ID(); - } - - /** - * @inheritdoc SignUpOperatorBase - */ - function _addToAllowlist() internal override { - vm.broadcast(_avsOwnerPrivateKey); - serviceManager.addMspToAllowlist(_operator); - } - - /** - * @inheritdoc SignUpOperatorBase - */ - function _getOperatorTypeName() internal pure override returns (string memory) { - return "MSP"; - } -} diff --git a/contracts/script/transact/SignUpOperatorBase.s.sol b/contracts/script/transact/SignUpOperatorBase.s.sol index dfaa2fed..41a7b8a7 100644 --- a/contracts/script/transact/SignUpOperatorBase.s.sol +++ b/contracts/script/transact/SignUpOperatorBase.s.sol @@ -20,7 +20,7 @@ import {Accounts} from "../utils/Accounts.sol"; /** * @title SignUpOperatorBase - * @notice Base contract for signing up different types of operators (Validators, BSPs, MSPs) + * @notice Base contract for signing up validators */ abstract contract SignUpOperatorBase is Script, ELScriptStorage, DHScriptStorage, Accounts { // Progress indicator diff --git a/contracts/src/DataHavenServiceManager.sol b/contracts/src/DataHavenServiceManager.sol index 4d941f1f..d5ff69e2 100644 --- a/contracts/src/DataHavenServiceManager.sol +++ b/contracts/src/DataHavenServiceManager.sol @@ -27,8 +27,7 @@ import {ServiceManagerBase} from "./middleware/ServiceManagerBase.sol"; /** * @title DataHaven ServiceManager contract - * @notice Manages validators, backup storage providers (BSPs), and main storage providers (MSPs) - * in the DataHaven network + * @notice Manages validators in the DataHaven network */ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager { /// @notice The metadata for the DataHaven AVS. @@ -36,17 +35,9 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager /// @notice The EigenLayer operator set ID for the Validators securing the DataHaven network. uint32 public constant VALIDATORS_SET_ID = 0; - /// @notice The EigenLayer operator set ID for the Backup Storage Providers participating in the DataHaven network. - uint32 public constant BSPS_SET_ID = 1; - /// @notice The EigenLayer operator set ID for the Main Storage Providers participating in the DataHaven network. - uint32 public constant MSPS_SET_ID = 2; /// @inheritdoc IDataHavenServiceManager mapping(address => bool) public validatorsAllowlist; - /// @inheritdoc IDataHavenServiceManager - mapping(address => bool) public bspsAllowlist; - /// @inheritdoc IDataHavenServiceManager - mapping(address => bool) public mspsAllowlist; IGatewayV2 private _snowbridgeGateway; @@ -75,8 +66,6 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager address initialOwner, address rewardsInitiator, IStrategy[] memory validatorsStrategies, - IStrategy[] memory bspsStrategies, - IStrategy[] memory mspsStrategies, address _snowbridgeGatewayAddress ) public virtual initializer { __ServiceManagerBase_init(initialOwner, rewardsInitiator); @@ -84,8 +73,8 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager // Register the DataHaven service in the AllocationManager. _allocationManager.updateAVSMetadataURI(address(this), DATAHAVEN_AVS_METADATA); - // Create the operator sets for the DataHaven service. - _createDataHavenOperatorSets(validatorsStrategies, bspsStrategies, mspsStrategies); + // Create the operator set for the DataHaven service. + _createDataHavenOperatorSets(validatorsStrategies); // Set the Snowbridge Gateway address. // This is the contract to which messages are sent, to be relayed to the Solochain network. @@ -159,34 +148,20 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager revert CantRegisterToMultipleOperatorSets(); } - // Case: Validator - if (operatorSetIds[0] == VALIDATORS_SET_ID) { - if (!validatorsAllowlist[operator]) { - revert OperatorNotInAllowlist(); - } - - // In the case of the Validators operator set, expect the data to have the Solochain address of the operator. - // Require validators to provide 20 bytes addresses. - require(data.length == 20, "Invalid solochain address length"); - validatorEthAddressToSolochainAddress[operator] = address(bytes20(data)); - } - // Case: BSP - else if (operatorSetIds[0] == BSPS_SET_ID) { - if (!bspsAllowlist[operator]) { - revert OperatorNotInAllowlist(); - } - } - // Case: MSP - else if (operatorSetIds[0] == MSPS_SET_ID) { - if (!mspsAllowlist[operator]) { - revert OperatorNotInAllowlist(); - } - } - // Case: Invalid operator set ID - else { + // Only validators are supported + if (operatorSetIds[0] != VALIDATORS_SET_ID) { revert InvalidOperatorSetId(); } + if (!validatorsAllowlist[operator]) { + revert OperatorNotInAllowlist(); + } + + // In the case of the Validators operator set, expect the data to have the Solochain address of the operator. + // Require validators to provide 20 bytes addresses. + require(data.length == 20, "Invalid solochain address length"); + validatorEthAddressToSolochainAddress[operator] = address(bytes20(data)); + emit OperatorRegistered(operator, operatorSetIds[0]); } @@ -204,17 +179,12 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager revert CantDeregisterFromMultipleOperatorSets(); } - if ( - operatorSetIds[0] != VALIDATORS_SET_ID && operatorSetIds[0] != BSPS_SET_ID - && operatorSetIds[0] != MSPS_SET_ID - ) { + if (operatorSetIds[0] != VALIDATORS_SET_ID) { revert InvalidOperatorSetId(); } - if (operatorSetIds[0] == VALIDATORS_SET_ID) { - // Remove validator from the addresses mapping - delete validatorEthAddressToSolochainAddress[operator]; - } + // Remove validator from the addresses mapping + delete validatorEthAddressToSolochainAddress[operator]; emit OperatorDeregistered(operator, operatorSetIds[0]); } @@ -227,22 +197,6 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager emit ValidatorAddedToAllowlist(validator); } - /// @inheritdoc IDataHavenServiceManager - function addBspToAllowlist( - address bsp - ) external onlyOwner { - bspsAllowlist[bsp] = true; - emit BspAddedToAllowlist(bsp); - } - - /// @inheritdoc IDataHavenServiceManager - function addMspToAllowlist( - address msp - ) external onlyOwner { - mspsAllowlist[msp] = true; - emit MspAddedToAllowlist(msp); - } - /// @inheritdoc IDataHavenServiceManager function removeValidatorFromAllowlist( address validator @@ -251,22 +205,6 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager emit ValidatorRemovedFromAllowlist(validator); } - /// @inheritdoc IDataHavenServiceManager - function removeBspFromAllowlist( - address bsp - ) external onlyOwner { - bspsAllowlist[bsp] = false; - emit BspRemovedFromAllowlist(bsp); - } - - /// @inheritdoc IDataHavenServiceManager - function removeMspFromAllowlist( - address msp - ) external onlyOwner { - mspsAllowlist[msp] = false; - emit MspRemovedFromAllowlist(msp); - } - /// @inheritdoc IDataHavenServiceManager function validatorsSupportedStrategies() external view returns (IStrategy[] memory) { OperatorSet memory operatorSet = OperatorSet({avs: address(this), id: VALIDATORS_SET_ID}); @@ -289,71 +227,23 @@ contract DataHavenServiceManager is ServiceManagerBase, IDataHavenServiceManager _allocationManager.addStrategiesToOperatorSet(address(this), VALIDATORS_SET_ID, _strategies); } - /// @inheritdoc IDataHavenServiceManager - function bspsSupportedStrategies() external view returns (IStrategy[] memory) { - OperatorSet memory operatorSet = OperatorSet({avs: address(this), id: BSPS_SET_ID}); - return _allocationManager.getStrategiesInOperatorSet(operatorSet); - } - - /// @inheritdoc IDataHavenServiceManager - function removeStrategiesFromBspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external onlyOwner { - _allocationManager.removeStrategiesFromOperatorSet(address(this), BSPS_SET_ID, _strategies); - } - - /// @inheritdoc IDataHavenServiceManager - function addStrategiesToBspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external onlyOwner { - _allocationManager.addStrategiesToOperatorSet(address(this), BSPS_SET_ID, _strategies); - } - - /// @inheritdoc IDataHavenServiceManager - function mspsSupportedStrategies() external view returns (IStrategy[] memory) { - OperatorSet memory operatorSet = OperatorSet({avs: address(this), id: MSPS_SET_ID}); - return _allocationManager.getStrategiesInOperatorSet(operatorSet); - } - - /// @inheritdoc IDataHavenServiceManager - function removeStrategiesFromMspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external onlyOwner { - _allocationManager.removeStrategiesFromOperatorSet(address(this), MSPS_SET_ID, _strategies); - } - - /// @inheritdoc IDataHavenServiceManager - function addStrategiesToMspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external onlyOwner { - _allocationManager.addStrategiesToOperatorSet(address(this), MSPS_SET_ID, _strategies); - } - /// @inheritdoc IDataHavenServiceManager function snowbridgeGateway() external view returns (address) { return address(_snowbridgeGateway); } /** - * @notice Creates the initial operator sets for DataHaven in the AllocationManager. - * @dev This function should be called during initialisation to set up the required operator sets. + * @notice Creates the initial operator set for DataHaven in the AllocationManager. + * @dev This function should be called during initialisation to set up the required operator set. */ function _createDataHavenOperatorSets( - IStrategy[] memory validatorsStrategies, - IStrategy[] memory bspsStrategies, - IStrategy[] memory mspsStrategies + IStrategy[] memory validatorsStrategies ) internal { IAllocationManagerTypes.CreateSetParams[] memory operatorSets = - new IAllocationManagerTypes.CreateSetParams[](3); + new IAllocationManagerTypes.CreateSetParams[](1); operatorSets[0] = IAllocationManagerTypes.CreateSetParams({ operatorSetId: VALIDATORS_SET_ID, strategies: validatorsStrategies }); - operatorSets[1] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: BSPS_SET_ID, strategies: bspsStrategies - }); - operatorSets[2] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: MSPS_SET_ID, strategies: mspsStrategies - }); _allocationManager.createOperatorSets(address(this), operatorSets); } } diff --git a/contracts/src/interfaces/IDataHavenServiceManager.sol b/contracts/src/interfaces/IDataHavenServiceManager.sol index b64ca3be..89b663cc 100644 --- a/contracts/src/interfaces/IDataHavenServiceManager.sol +++ b/contracts/src/interfaces/IDataHavenServiceManager.sol @@ -43,26 +43,10 @@ interface IDataHavenServiceManagerEvents { /// @param validator Address of the validator added to the allowlist event ValidatorAddedToAllowlist(address indexed validator); - /// @notice Emitted when a Backup Storage Provider is added to the allowlist - /// @param bsp Address of the BSP added to the allowlist - event BspAddedToAllowlist(address indexed bsp); - - /// @notice Emitted when a Main Storage Provider is added to the allowlist - /// @param msp Address of the MSP added to the allowlist - event MspAddedToAllowlist(address indexed msp); - /// @notice Emitted when a validator is removed from the allowlist /// @param validator Address of the validator removed from the allowlist event ValidatorRemovedFromAllowlist(address indexed validator); - /// @notice Emitted when a Backup Storage Provider is removed from the allowlist - /// @param bsp Address of the BSP removed from the allowlist - event BspRemovedFromAllowlist(address indexed bsp); - - /// @notice Emitted when a Main Storage Provider is removed from the allowlist - /// @param msp Address of the MSP removed from the allowlist - event MspRemovedFromAllowlist(address indexed msp); - /// @notice Emitted when the Snowbridge Gateway address is set /// @param snowbridgeGateway Address of the Snowbridge Gateway event SnowbridgeGatewaySet(address indexed snowbridgeGateway); @@ -70,8 +54,8 @@ interface IDataHavenServiceManagerEvents { /** * @title DataHaven Service Manager Interface - * @notice Defines the interface for the DataHaven Service Manager, which manages validators, - * backup storage providers (BSPs), and main storage providers (MSPs) in the DataHaven network + * @notice Defines the interface for the DataHaven Service Manager, which manages validators + * in the DataHaven network */ interface IDataHavenServiceManager is IDataHavenServiceManagerErrors, @@ -84,20 +68,6 @@ interface IDataHavenServiceManager is address validator ) external view returns (bool); - /// @notice Checks if a BSP address is in the allowlist - /// @param bsp Address to check - /// @return True if the BSP is in the allowlist, false otherwise - function bspsAllowlist( - address bsp - ) external view returns (bool); - - /// @notice Checks if an MSP address is in the allowlist - /// @param msp Address to check - /// @return True if the MSP is in the allowlist, false otherwise - function mspsAllowlist( - address msp - ) external view returns (bool); - /// @notice Returns the Snowbridge Gateway address /// @return The Snowbridge gateway address function snowbridgeGateway() external view returns (address); @@ -116,15 +86,11 @@ interface IDataHavenServiceManager is * @param initialOwner Address of the initial owner * @param rewardsInitiator Address authorized to initiate rewards * @param validatorsStrategies Array of strategies supported by validators - * @param bspsStrategies Array of strategies supported by BSPs - * @param mspsStrategies Array of strategies supported by MSPs */ function initialise( address initialOwner, address rewardsInitiator, IStrategy[] memory validatorsStrategies, - IStrategy[] memory bspsStrategies, - IStrategy[] memory mspsStrategies, address _snowbridgeGatewayAddress ) external; @@ -174,22 +140,6 @@ interface IDataHavenServiceManager is address validator ) external; - /** - * @notice Adds a BSP to the allowlist - * @param bsp Address of the BSP to add - */ - function addBspToAllowlist( - address bsp - ) external; - - /** - * @notice Adds an MSP to the allowlist - * @param msp Address of the MSP to add - */ - function addMspToAllowlist( - address msp - ) external; - /** * @notice Removes a validator from the allowlist * @param validator Address of the validator to remove @@ -198,22 +148,6 @@ interface IDataHavenServiceManager is address validator ) external; - /** - * @notice Removes a BSP from the allowlist - * @param bsp Address of the BSP to remove - */ - function removeBspFromAllowlist( - address bsp - ) external; - - /** - * @notice Removes an MSP from the allowlist - * @param msp Address of the MSP to remove - */ - function removeMspFromAllowlist( - address msp - ) external; - /** * @notice Returns all strategies supported by the DataHaven Validators operator set * @return An array of strategy contracts that validators can delegate to @@ -235,48 +169,4 @@ interface IDataHavenServiceManager is function addStrategiesToValidatorsSupportedStrategies( IStrategy[] calldata _strategies ) external; - - /** - * @notice Returns all strategies supported by the Backup Storage Providers (BSPs) operator set - * @return An array of strategy contracts that BSPs can delegate to - */ - function bspsSupportedStrategies() external view returns (IStrategy[] memory); - - /** - * @notice Removes strategies from the list of supported strategies for Backup Storage Providers - * @param _strategies Array of strategy contracts to remove from BSPs operator set - */ - function removeStrategiesFromBspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external; - - /** - * @notice Adds strategies to the list of supported strategies for Backup Storage Providers - * @param _strategies Array of strategy contracts to add to BSPs operator set - */ - function addStrategiesToBspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external; - - /** - * @notice Returns all strategies supported by the Main Storage Providers (MSPs) operator set - * @return An array of strategy contracts that MSPs can delegate to - */ - function mspsSupportedStrategies() external view returns (IStrategy[] memory); - - /** - * @notice Removes strategies from the list of supported strategies for Main Storage Providers - * @param _strategies Array of strategy contracts to remove from MSPs operator set - */ - function removeStrategiesFromMspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external; - - /** - * @notice Adds strategies to the list of supported strategies for Main Storage Providers - * @param _strategies Array of strategy contracts to add to MSPs operator set - */ - function addStrategiesToMspsSupportedStrategies( - IStrategy[] calldata _strategies - ) external; } diff --git a/contracts/test/utils/AVSDeployer.sol b/contracts/test/utils/AVSDeployer.sol index a39ba845..b29b59d9 100644 --- a/contracts/test/utils/AVSDeployer.sol +++ b/contracts/test/utils/AVSDeployer.sol @@ -252,16 +252,12 @@ contract AVSDeployer is Test { rewardsCoordinator, permissionControllerMock, allocationManager ); - // Create arrays for the three sets of strategies required by DataHavenServiceManager + // Create array for validators strategies required by DataHavenServiceManager IStrategy[] memory validatorsStrategies = new IStrategy[](deployedStrategies.length); - IStrategy[] memory bspsStrategies = new IStrategy[](deployedStrategies.length); - IStrategy[] memory mspsStrategies = new IStrategy[](deployedStrategies.length); - // For testing purposes, we'll use the same strategies for all three sets + // For testing purposes, we'll use the deployed strategies for validators for (uint256 i = 0; i < deployedStrategies.length; i++) { validatorsStrategies[i] = deployedStrategies[i]; - bspsStrategies[i] = deployedStrategies[i]; - mspsStrategies[i] = deployedStrategies[i]; } serviceManager = DataHavenServiceManager( @@ -274,8 +270,6 @@ contract AVSDeployer is Test { avsOwner, rewardsInitiator, validatorsStrategies, - bspsStrategies, - mspsStrategies, address(0) // This deployment does not use Snowbridge ) ) diff --git a/contracts/test/utils/SnowbridgeAndAVSDeployer.sol b/contracts/test/utils/SnowbridgeAndAVSDeployer.sol index 6a766c0a..42da525c 100644 --- a/contracts/test/utils/SnowbridgeAndAVSDeployer.sol +++ b/contracts/test/utils/SnowbridgeAndAVSDeployer.sol @@ -43,10 +43,6 @@ contract SnowbridgeAndAVSDeployer is AVSDeployer { 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f, // Ninth pre-funded address in anvil 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 // Tenth pre-funded address in anvil ]; - // The addresses of the Backup Storage Providers that are allowed to register to the DataHaven service. - address[] public bspsAllowlist; - // The addresses of the Main Storage Providers that are allowed to register to the DataHaven service. - address[] public mspsAllowlist; // Snowbridge contracts params // The hashes of the initial (current) Validators in the DataHaven solochain. diff --git a/test/contract-bindings/generated.ts b/test/contract-bindings/generated.ts index d28315cd..7dfe74d3 100644 --- a/test/contract-bindings/generated.ts +++ b/test/contract-bindings/generated.ts @@ -2045,13 +2045,6 @@ export const dataHavenServiceManagerAbi = [ ], stateMutability: 'nonpayable', }, - { - type: 'function', - inputs: [], - name: 'BSPS_SET_ID', - outputs: [{ name: '', internalType: 'uint32', type: 'uint32' }], - stateMutability: 'view', - }, { type: 'function', inputs: [], @@ -2059,13 +2052,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [{ name: '', internalType: 'string', type: 'string' }], stateMutability: 'view', }, - { - type: 'function', - inputs: [], - name: 'MSPS_SET_ID', - outputs: [{ name: '', internalType: 'uint32', type: 'uint32' }], - stateMutability: 'view', - }, { type: 'function', inputs: [], @@ -2073,20 +2059,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [{ name: '', internalType: 'uint32', type: 'uint32' }], stateMutability: 'view', }, - { - type: 'function', - inputs: [{ name: 'bsp', internalType: 'address', type: 'address' }], - name: 'addBspToAllowlist', - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - inputs: [{ name: 'msp', internalType: 'address', type: 'address' }], - name: 'addMspToAllowlist', - outputs: [], - stateMutability: 'nonpayable', - }, { type: 'function', inputs: [{ name: 'admin', internalType: 'address', type: 'address' }], @@ -2094,32 +2066,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [], stateMutability: 'nonpayable', }, - { - type: 'function', - inputs: [ - { - name: '_strategies', - internalType: 'contract IStrategy[]', - type: 'address[]', - }, - ], - name: 'addStrategiesToBspsSupportedStrategies', - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - inputs: [ - { - name: '_strategies', - internalType: 'contract IStrategy[]', - type: 'address[]', - }, - ], - name: 'addStrategiesToMspsSupportedStrategies', - outputs: [], - stateMutability: 'nonpayable', - }, { type: 'function', inputs: [ @@ -2161,22 +2107,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [{ name: '', internalType: 'address', type: 'address' }], stateMutability: 'view', }, - { - type: 'function', - inputs: [{ name: '', internalType: 'address', type: 'address' }], - name: 'bspsAllowlist', - outputs: [{ name: '', internalType: 'bool', type: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - inputs: [], - name: 'bspsSupportedStrategies', - outputs: [ - { name: '', internalType: 'contract IStrategy[]', type: 'address[]' }, - ], - stateMutability: 'view', - }, { type: 'function', inputs: [], @@ -2390,16 +2320,6 @@ export const dataHavenServiceManagerAbi = [ internalType: 'contract IStrategy[]', type: 'address[]', }, - { - name: 'bspsStrategies', - internalType: 'contract IStrategy[]', - type: 'address[]', - }, - { - name: 'mspsStrategies', - internalType: 'contract IStrategy[]', - type: 'address[]', - }, { name: '_snowbridgeGatewayAddress', internalType: 'address', @@ -2410,22 +2330,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [], stateMutability: 'nonpayable', }, - { - type: 'function', - inputs: [{ name: '', internalType: 'address', type: 'address' }], - name: 'mspsAllowlist', - outputs: [{ name: '', internalType: 'bool', type: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - inputs: [], - name: 'mspsSupportedStrategies', - outputs: [ - { name: '', internalType: 'contract IStrategy[]', type: 'address[]' }, - ], - stateMutability: 'view', - }, { type: 'function', inputs: [{ name: '', internalType: 'uint32', type: 'uint32' }], @@ -2516,20 +2420,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [], stateMutability: 'nonpayable', }, - { - type: 'function', - inputs: [{ name: 'bsp', internalType: 'address', type: 'address' }], - name: 'removeBspFromAllowlist', - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - inputs: [{ name: 'msp', internalType: 'address', type: 'address' }], - name: 'removeMspFromAllowlist', - outputs: [], - stateMutability: 'nonpayable', - }, { type: 'function', inputs: [ @@ -2539,32 +2429,6 @@ export const dataHavenServiceManagerAbi = [ outputs: [], stateMutability: 'nonpayable', }, - { - type: 'function', - inputs: [ - { - name: '_strategies', - internalType: 'contract IStrategy[]', - type: 'address[]', - }, - ], - name: 'removeStrategiesFromBspsSupportedStrategies', - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - inputs: [ - { - name: '_strategies', - internalType: 'contract IStrategy[]', - type: 'address[]', - }, - ], - name: 'removeStrategiesFromMspsSupportedStrategies', - outputs: [], - stateMutability: 'nonpayable', - }, { type: 'function', inputs: [ @@ -2760,22 +2624,6 @@ export const dataHavenServiceManagerAbi = [ ], stateMutability: 'view', }, - { - type: 'event', - anonymous: false, - inputs: [ - { name: 'bsp', internalType: 'address', type: 'address', indexed: true }, - ], - name: 'BspAddedToAllowlist', - }, - { - type: 'event', - anonymous: false, - inputs: [ - { name: 'bsp', internalType: 'address', type: 'address', indexed: true }, - ], - name: 'BspRemovedFromAllowlist', - }, { type: 'event', anonymous: false, @@ -2784,22 +2632,6 @@ export const dataHavenServiceManagerAbi = [ ], name: 'Initialized', }, - { - type: 'event', - anonymous: false, - inputs: [ - { name: 'msp', internalType: 'address', type: 'address', indexed: true }, - ], - name: 'MspAddedToAllowlist', - }, - { - type: 'event', - anonymous: false, - inputs: [ - { name: 'msp', internalType: 'address', type: 'address', indexed: true }, - ], - name: 'MspRemovedFromAllowlist', - }, { type: 'event', anonymous: false, @@ -11594,15 +11426,6 @@ export const readDataHavenServiceManager = /*#__PURE__*/ createReadContract({ abi: dataHavenServiceManagerAbi, }) -/** - * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"BSPS_SET_ID"` - */ -export const readDataHavenServiceManagerBspsSetId = - /*#__PURE__*/ createReadContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'BSPS_SET_ID', - }) - /** * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"DATAHAVEN_AVS_METADATA"` */ @@ -11612,15 +11435,6 @@ export const readDataHavenServiceManagerDatahavenAvsMetadata = functionName: 'DATAHAVEN_AVS_METADATA', }) -/** - * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"MSPS_SET_ID"` - */ -export const readDataHavenServiceManagerMspsSetId = - /*#__PURE__*/ createReadContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'MSPS_SET_ID', - }) - /** * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"VALIDATORS_SET_ID"` */ @@ -11638,24 +11452,6 @@ export const readDataHavenServiceManagerAvs = /*#__PURE__*/ createReadContract({ functionName: 'avs', }) -/** - * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"bspsAllowlist"` - */ -export const readDataHavenServiceManagerBspsAllowlist = - /*#__PURE__*/ createReadContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'bspsAllowlist', - }) - -/** - * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"bspsSupportedStrategies"` - */ -export const readDataHavenServiceManagerBspsSupportedStrategies = - /*#__PURE__*/ createReadContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'bspsSupportedStrategies', - }) - /** * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"buildNewValidatorSetMessage"` */ @@ -11683,24 +11479,6 @@ export const readDataHavenServiceManagerGetRestakeableStrategies = functionName: 'getRestakeableStrategies', }) -/** - * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"mspsAllowlist"` - */ -export const readDataHavenServiceManagerMspsAllowlist = - /*#__PURE__*/ createReadContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'mspsAllowlist', - }) - -/** - * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"mspsSupportedStrategies"` - */ -export const readDataHavenServiceManagerMspsSupportedStrategies = - /*#__PURE__*/ createReadContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'mspsSupportedStrategies', - }) - /** * Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"operatorSetToRewardsRegistry"` */ @@ -11780,24 +11558,6 @@ export const writeDataHavenServiceManager = /*#__PURE__*/ createWriteContract({ abi: dataHavenServiceManagerAbi, }) -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addBspToAllowlist"` - */ -export const writeDataHavenServiceManagerAddBspToAllowlist = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addBspToAllowlist', - }) - -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addMspToAllowlist"` - */ -export const writeDataHavenServiceManagerAddMspToAllowlist = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addMspToAllowlist', - }) - /** * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addPendingAdmin"` */ @@ -11807,24 +11567,6 @@ export const writeDataHavenServiceManagerAddPendingAdmin = functionName: 'addPendingAdmin', }) -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addStrategiesToBspsSupportedStrategies"` - */ -export const writeDataHavenServiceManagerAddStrategiesToBspsSupportedStrategies = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addStrategiesToBspsSupportedStrategies', - }) - -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addStrategiesToMspsSupportedStrategies"` - */ -export const writeDataHavenServiceManagerAddStrategiesToMspsSupportedStrategies = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addStrategiesToMspsSupportedStrategies', - }) - /** * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addStrategiesToOperatorSet"` */ @@ -11996,24 +11738,6 @@ export const writeDataHavenServiceManagerRemoveAppointee = functionName: 'removeAppointee', }) -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeBspFromAllowlist"` - */ -export const writeDataHavenServiceManagerRemoveBspFromAllowlist = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeBspFromAllowlist', - }) - -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeMspFromAllowlist"` - */ -export const writeDataHavenServiceManagerRemoveMspFromAllowlist = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeMspFromAllowlist', - }) - /** * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removePendingAdmin"` */ @@ -12023,24 +11747,6 @@ export const writeDataHavenServiceManagerRemovePendingAdmin = functionName: 'removePendingAdmin', }) -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeStrategiesFromBspsSupportedStrategies"` - */ -export const writeDataHavenServiceManagerRemoveStrategiesFromBspsSupportedStrategies = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeStrategiesFromBspsSupportedStrategies', - }) - -/** - * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeStrategiesFromMspsSupportedStrategies"` - */ -export const writeDataHavenServiceManagerRemoveStrategiesFromMspsSupportedStrategies = - /*#__PURE__*/ createWriteContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeStrategiesFromMspsSupportedStrategies', - }) - /** * Wraps __{@link writeContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeStrategiesFromOperatorSet"` */ @@ -12182,24 +11888,6 @@ export const writeDataHavenServiceManagerUpdateSolochainAddressForValidator = export const simulateDataHavenServiceManager = /*#__PURE__*/ createSimulateContract({ abi: dataHavenServiceManagerAbi }) -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addBspToAllowlist"` - */ -export const simulateDataHavenServiceManagerAddBspToAllowlist = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addBspToAllowlist', - }) - -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addMspToAllowlist"` - */ -export const simulateDataHavenServiceManagerAddMspToAllowlist = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addMspToAllowlist', - }) - /** * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addPendingAdmin"` */ @@ -12209,24 +11897,6 @@ export const simulateDataHavenServiceManagerAddPendingAdmin = functionName: 'addPendingAdmin', }) -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addStrategiesToBspsSupportedStrategies"` - */ -export const simulateDataHavenServiceManagerAddStrategiesToBspsSupportedStrategies = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addStrategiesToBspsSupportedStrategies', - }) - -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addStrategiesToMspsSupportedStrategies"` - */ -export const simulateDataHavenServiceManagerAddStrategiesToMspsSupportedStrategies = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'addStrategiesToMspsSupportedStrategies', - }) - /** * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"addStrategiesToOperatorSet"` */ @@ -12398,24 +12068,6 @@ export const simulateDataHavenServiceManagerRemoveAppointee = functionName: 'removeAppointee', }) -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeBspFromAllowlist"` - */ -export const simulateDataHavenServiceManagerRemoveBspFromAllowlist = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeBspFromAllowlist', - }) - -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeMspFromAllowlist"` - */ -export const simulateDataHavenServiceManagerRemoveMspFromAllowlist = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeMspFromAllowlist', - }) - /** * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removePendingAdmin"` */ @@ -12425,24 +12077,6 @@ export const simulateDataHavenServiceManagerRemovePendingAdmin = functionName: 'removePendingAdmin', }) -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeStrategiesFromBspsSupportedStrategies"` - */ -export const simulateDataHavenServiceManagerRemoveStrategiesFromBspsSupportedStrategies = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeStrategiesFromBspsSupportedStrategies', - }) - -/** - * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeStrategiesFromMspsSupportedStrategies"` - */ -export const simulateDataHavenServiceManagerRemoveStrategiesFromMspsSupportedStrategies = - /*#__PURE__*/ createSimulateContract({ - abi: dataHavenServiceManagerAbi, - functionName: 'removeStrategiesFromMspsSupportedStrategies', - }) - /** * Wraps __{@link simulateContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"removeStrategiesFromOperatorSet"` */ @@ -12584,24 +12218,6 @@ export const simulateDataHavenServiceManagerUpdateSolochainAddressForValidator = export const watchDataHavenServiceManagerEvent = /*#__PURE__*/ createWatchContractEvent({ abi: dataHavenServiceManagerAbi }) -/** - * Wraps __{@link watchContractEvent}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `eventName` set to `"BspAddedToAllowlist"` - */ -export const watchDataHavenServiceManagerBspAddedToAllowlistEvent = - /*#__PURE__*/ createWatchContractEvent({ - abi: dataHavenServiceManagerAbi, - eventName: 'BspAddedToAllowlist', - }) - -/** - * Wraps __{@link watchContractEvent}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `eventName` set to `"BspRemovedFromAllowlist"` - */ -export const watchDataHavenServiceManagerBspRemovedFromAllowlistEvent = - /*#__PURE__*/ createWatchContractEvent({ - abi: dataHavenServiceManagerAbi, - eventName: 'BspRemovedFromAllowlist', - }) - /** * Wraps __{@link watchContractEvent}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `eventName` set to `"Initialized"` */ @@ -12611,24 +12227,6 @@ export const watchDataHavenServiceManagerInitializedEvent = eventName: 'Initialized', }) -/** - * Wraps __{@link watchContractEvent}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `eventName` set to `"MspAddedToAllowlist"` - */ -export const watchDataHavenServiceManagerMspAddedToAllowlistEvent = - /*#__PURE__*/ createWatchContractEvent({ - abi: dataHavenServiceManagerAbi, - eventName: 'MspAddedToAllowlist', - }) - -/** - * Wraps __{@link watchContractEvent}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `eventName` set to `"MspRemovedFromAllowlist"` - */ -export const watchDataHavenServiceManagerMspRemovedFromAllowlistEvent = - /*#__PURE__*/ createWatchContractEvent({ - abi: dataHavenServiceManagerAbi, - eventName: 'MspRemovedFromAllowlist', - }) - /** * Wraps __{@link watchContractEvent}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `eventName` set to `"OperatorDeregistered"` */ From f84b6debb7284b92d3a68519f068048b5f20fc04 Mon Sep 17 00:00:00 2001 From: undercover-cactus Date: Fri, 28 Nov 2025 14:38:05 +0100 Subject: [PATCH 9/9] feat: statically build binary (#292) Co-authored-by: Gonza Montiel --- .../build-prod-binary/action.yml | 21 +++++++++ .../workflows/actions/setup-env/action.yml | 24 ++++++---- .../workflows/task-build-static-operator.yml | 46 +++++++++++++++++++ .github/workflows/task-publish-binary.yml | 7 +++ docker/datahaven-production.Dockerfile | 7 ++- operator/.cargo/config.toml | 2 +- operator/Cargo.lock | 12 +++++ operator/Cargo.toml | 4 ++ operator/node/Cargo.toml | 5 ++ operator/scripts/verify-licenses.sh | 1 + 10 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/task-build-static-operator.yml diff --git a/.github/workflow-templates/build-prod-binary/action.yml b/.github/workflow-templates/build-prod-binary/action.yml index 0e491fdf..55fba473 100644 --- a/.github/workflow-templates/build-prod-binary/action.yml +++ b/.github/workflow-templates/build-prod-binary/action.yml @@ -6,6 +6,9 @@ inputs: target: description: The CPU target for the binary required: true + build: + description: If we should statically build it or not + required: false runs: using: "composite" @@ -24,6 +27,7 @@ runs: --tag prod --no-cache \ --build-arg="COMMIT=${{ github.event.inputs.sha }}" \ --build-arg="RUSTFLAGS=-C target-cpu=${{ inputs.target }}" \ + --build-arg="BUILD=${{ inputs.build }}" \ --file ./docker/datahaven-production.Dockerfile \ . @@ -46,12 +50,29 @@ runs: docker rmi prod - name: Save DataHaven node binary + if: inputs.build != 'static' shell: bash run: | mkdir -p build cp datahaven-node build/datahaven-node-${{ inputs.target }} + + - name: Save DataHaven node binary + if: inputs.build == 'static' + shell: bash + run: | + mkdir -p build + cp datahaven-node build/datahaven-node-${{ inputs.target }}-static + - name: Upload binary + if: inputs.build != 'static' uses: actions/upload-artifact@v4 with: name: datahaven-binaries-${{inputs.target}} path: build/datahaven-node-${{inputs.target }} + + - name: Upload binary + if: inputs.build == 'static' + uses: actions/upload-artifact@v4 + with: + name: datahaven-binaries-${{inputs.target}}-static + path: build/datahaven-node-${{inputs.target }}-static diff --git a/.github/workflows/actions/setup-env/action.yml b/.github/workflows/actions/setup-env/action.yml index 5407aeef..a4b5b98c 100644 --- a/.github/workflows/actions/setup-env/action.yml +++ b/.github/workflows/actions/setup-env/action.yml @@ -12,6 +12,11 @@ inputs: required: false default: "true" + skip-libpq: + description: "Indicate to skip postgres lib install or not" + required: false + default: "false" + runs: using: "composite" steps: @@ -68,9 +73,16 @@ runs: shell: bash run: sudo apt-get update && sudo apt-get install -y libpq-dev libclang-dev - # Auto-install missing dependencies when install-deps is false (for self-hosted runners) + # Install protoc only when install-deps is true + - name: Install Protoc + if: inputs.install-deps == 'true' + uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ github.token }} + + # Auto-install missing dependencies when install-deps is false (for self-hosted runners) can be skipped when statically build the node - name: Setup system dependencies (self-hosted) - if: inputs.install-deps == 'false' + if: inputs.install-deps == 'false' && inputs.skip-libpq == 'false' shell: bash run: | echo "Checking and installing system dependencies locally if needed..." @@ -172,13 +184,7 @@ runs: echo "All required system dependencies ready!" - # Install protoc only when install-deps is true - - name: Install Protoc - if: inputs.install-deps == 'true' - uses: arduino/setup-protoc@v3 - with: - repo-token: ${{ github.token }} - + # Auto-install protoc when install-deps is false (for self-hosted runners) - name: Setup Protoc (self-hosted) if: inputs.install-deps == 'false' diff --git a/.github/workflows/task-build-static-operator.yml b/.github/workflows/task-build-static-operator.yml new file mode 100644 index 00000000..add5b029 --- /dev/null +++ b/.github/workflows/task-build-static-operator.yml @@ -0,0 +1,46 @@ +name: DataHaven Operator Static Build + +on: + pull_request: + paths: + # Only check the build if we are adding a dependency to save runner time + - 'operator/Cargo.toml' + - 'operator/Cargo.lock' + +jobs: + build-node: + name: Build operator binary + runs-on: + group: DH-runners + env: + RUSTC_WRAPPER: "sccache" + CARGO_INCREMENTAL: "0" + CARGO_TERM_COLOR: always + SCCACHE_GHA_ENABLED: "true" + defaults: + run: + working-directory: ./operator + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 1 + + - uses: ./.github/workflows/actions/setup-env + with: + cache-key: BUILD-RELEASE + install-deps: false + skip-libpq: true + + - name: Set build flags + run: echo "RUSTFLAGS=${{ env.RUSTFLAGS }} -C linker=clang -C link-arg=-fuse-ld=mold" >> $GITHUB_ENV + + - name: Build node binary + run: | + cargo build --release --locked --features fast-runtime,static + + - name: Test binary + run: | + ldd ./target/release/datahaven-node + ./target/release/datahaven-node --version + diff --git a/.github/workflows/task-publish-binary.yml b/.github/workflows/task-publish-binary.yml index 9e19bd89..1a04ab34 100644 --- a/.github/workflows/task-publish-binary.yml +++ b/.github/workflows/task-publish-binary.yml @@ -37,6 +37,12 @@ jobs: strategy: matrix: cpu: ["x86-64", "skylake", "znver3"] + build: ["default", "static"] + exclude: + - cpu: skylake + build: static + - cpu: znver3 + build: static steps: - name: Checkout uses: actions/checkout@v5 @@ -46,6 +52,7 @@ jobs: uses: ./.github/workflow-templates/build-prod-binary with: target: ${{ matrix.cpu }} + build: ${{ matrix.build }} ####### Prepare and publish the release draft ####### diff --git a/docker/datahaven-production.Dockerfile b/docker/datahaven-production.Dockerfile index 9c8f6226..c7257b71 100644 --- a/docker/datahaven-production.Dockerfile +++ b/docker/datahaven-production.Dockerfile @@ -7,6 +7,7 @@ FROM docker.io/library/ubuntu:22.04 AS builder # Branch or tag to build DataHaven from ARG COMMIT="main" ARG RUSTFLAGS="" +ARG BUILD="" ENV RUSTFLAGS=$RUSTFLAGS ENV DEBIAN_FRONTEND=noninteractive ENV PROTOC_VER=21.12 @@ -37,7 +38,11 @@ WORKDIR /datahaven RUN rustc --print target-cpus RUN echo "*** Building DataHaven ***" -RUN cargo build --profile=production --all +RUN if [ "$BUILD" = "static" ]; then \ + cargo build --profile=production --all --features static + else \ + cargo build --profile=production --all + fi FROM debian:stable-slim LABEL maintainer="steve@moonsonglabs.com" diff --git a/operator/.cargo/config.toml b/operator/.cargo/config.toml index f42e1463..b7a46ab6 100644 --- a/operator/.cargo/config.toml +++ b/operator/.cargo/config.toml @@ -3,4 +3,4 @@ lto = "thin" opt-level = 2 [registries.crates-io] -protocol = "sparse" \ No newline at end of file +protocol = "sparse" diff --git a/operator/Cargo.lock b/operator/Cargo.lock index 5e98f5e9..da644ca0 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -3100,6 +3100,7 @@ dependencies = [ "pallet-transaction-payment-rpc", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", + "pq-sys", "sc-basic-authorship", "sc-cli", "sc-client-api", @@ -11921,6 +11922,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pq-src" +version = "0.3.10+libpq-18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ef39ce621f4993d6084fdcd4cbf1e01c84bdba53109cfad095d2cf441b85b9" +dependencies = [ + "cc", + "openssl-sys", +] + [[package]] name = "pq-sys" version = "0.7.4" @@ -11929,6 +11940,7 @@ checksum = "089d5dc8f44104b719912ad4478fd558b59a431ce19ef9101f637be8c656b90a" dependencies = [ "libc", "pkg-config", + "pq-src", "vcpkg", ] diff --git a/operator/Cargo.toml b/operator/Cargo.toml index 67586bc7..d19fab19 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -304,6 +304,10 @@ shp-types = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v ## Precompiles pallet-evm-precompile-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.1.4", default-features = false } +# Static linking +#### Needed to build static binaries #### +pq-sys = { version = "0.7.4" } + # The list of dependencies below (which can be both direct and indirect dependencies) are crates # that are suspected to be CPU-intensive, and that are unlikely to require debugging (as some of # their debug info might be missing) or to require to be frequently recompiled. We compile these diff --git a/operator/node/Cargo.toml b/operator/node/Cargo.toml index 831f71c5..22e0f645 100644 --- a/operator/node/Cargo.toml +++ b/operator/node/Cargo.toml @@ -132,6 +132,10 @@ serde = { workspace = true, default-features = true } cumulus-client-service = { workspace = true } toml = { workspace = true } +# Static linking +#### Needed to build static binaries #### +pq-sys = { workspace = true, optional = true } + [build-dependencies] substrate-build-script-utils = { workspace = true, default-features = true } @@ -144,6 +148,7 @@ std = [ "datahaven-testnet-runtime/std", "shp-opaque/std" ] +static = ["pq-sys", "pq-sys/bundled"] # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ diff --git a/operator/scripts/verify-licenses.sh b/operator/scripts/verify-licenses.sh index 6362029c..11ed3ae5 100755 --- a/operator/scripts/verify-licenses.sh +++ b/operator/scripts/verify-licenses.sh @@ -51,6 +51,7 @@ AUTHORS=( ) NAMES=( "ring" # v0.16.20 has null license metadata but contains Apache-2.0 AND ISC LICENSE file + "pq-src" # License is the same as postgres "shp-tx-implicits-runtime-api" ) licenses_filter=$(printf ' .license != "%s" and' "${LICENSES[@]}")