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/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/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-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/.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/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/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/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/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/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/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 8236988d..1261eba4 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(); } @@ -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/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/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/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/common/src/migrations.rs b/operator/runtime/common/src/migrations.rs index a0a7a507..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")] @@ -45,521 +81,16 @@ 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; - -/// 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 - ); - }); - } -} +/// 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, + >; diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs index 13970526..57e73af9 100644 --- a/operator/runtime/mainnet/src/configs/mod.rs +++ b/operator/runtime/mainnet/src/configs/mod.rs @@ -79,14 +79,14 @@ 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, 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; } @@ -511,7 +510,8 @@ impl pallet_beefy::Config for Runtime { type AncestryHelper = BeefyMmrLeaf; type WeightInfo = (); type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; } parameter_types! { @@ -847,14 +847,13 @@ 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; 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; } @@ -996,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/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 9591759a..3a0bde15 100644 --- a/operator/runtime/stagenet/src/configs/mod.rs +++ b/operator/runtime/stagenet/src/configs/mod.rs @@ -79,14 +79,14 @@ 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, 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; } @@ -508,7 +507,8 @@ impl pallet_beefy::Config for Runtime { type AncestryHelper = BeefyMmrLeaf; type WeightInfo = (); type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; } parameter_types! { @@ -844,20 +844,13 @@ 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; 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; } @@ -999,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/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 faa43057..57f40fb0 100644 --- a/operator/runtime/testnet/src/configs/mod.rs +++ b/operator/runtime/testnet/src/configs/mod.rs @@ -79,14 +79,14 @@ 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, 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; } @@ -511,7 +510,8 @@ impl pallet_beefy::Config for Runtime { type AncestryHelper = BeefyMmrLeaf; type WeightInfo = (); type KeyOwnerProof = >::Proof; - type EquivocationReportSystem = (); + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; } parameter_types! { @@ -847,20 +847,13 @@ 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; 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; } @@ -1002,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/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; 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[@]}") 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/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"` */ 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" 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/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 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);