Merge branch 'main' into sde/storagehub-docs

This commit is contained in:
Steve Degosserie 2025-12-01 12:29:13 +01:00 committed by GitHub
commit 948a57832e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 769 additions and 1576 deletions

View file

@ -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

View file

@ -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]

View file

@ -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'

View file

@ -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

View file

@ -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: |

View file

@ -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 #######

View file

@ -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/

View file

@ -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/<network>.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/<network>.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/<network>.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.

View file

@ -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,

View file

@ -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'

View file

@ -20,8 +20,6 @@ contract Config {
address vetoCommitteeMember;
uint32 vetoWindowBlocks;
address[] validatorsStrategies;
address[] bspsStrategies;
address[] mspsStrategies;
}
// EigenLayer parameters

View file

@ -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);

View file

@ -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_;
}
}
}

View file

@ -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
);

View file

@ -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";
}
}

View file

@ -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";
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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
)
)

View file

@ -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.

View file

@ -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"

View file

@ -3,4 +3,4 @@ lto = "thin"
opt-level = 2
[registries.crates-io]
protocol = "sparse"
protocol = "sparse"

12
operator/Cargo.lock generated
View file

@ -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",
]

View file

@ -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

View file

@ -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 = [

View file

@ -123,7 +123,6 @@ pub type PCall = BatchPrecompileCall<Runtime>;
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<Runtime> = 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)

View file

@ -124,7 +124,6 @@ pub type Precompiles<R> = PrecompileSetBuilder<
pub type PCall = CollectivePrecompileCall<Runtime, pallet_collective::Instance1>;
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<Runtime> = 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)

View file

@ -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<Runtime> = 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)

View file

@ -108,7 +108,6 @@ pub type Precompiles<R> = PrecompileSetBuilder<
pub type PCall = Erc20BalancesPrecompileCall<Runtime, NativeErc20Metadata, ()>;
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<Runtime> = 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)

View file

@ -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<Runtime> = 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)

View file

@ -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<Runtime> = 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)

View file

@ -150,7 +150,6 @@ impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> 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<Runtime> = 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)

View file

@ -152,10 +152,7 @@ parameter_types! {
pub BlockGasLimit: U256 = U256::from(u64::MAX);
pub PrecompilesValue: TestPrecompiles<Runtime> = 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)

View file

@ -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.

View file

@ -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<MIGRATION_IDENTIFIER_MAX_LEN>;
/// 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<T> = (evm_alias::EvmAliasMigration<T>,);
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<T, const NEW_CHAIN_ID: u64>(PhantomData<T>);
impl<T, const NEW_CHAIN_ID: u64> SteppedMigration for EvmChainIdMigration<T, NEW_CHAIN_ID>
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<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, 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::<T>::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<Vec<u8>, sp_runtime::TryRuntimeError> {
let old_chain_id = pallet_evm_chain_id::ChainId::<T>::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<u8>) -> 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::<T>::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<T>(PhantomData<T>);
impl<T> SteppedMigration for EvmAliasMigration<T>
where
T: pallet_evm::Config,
{
type Cursor = BoundedVec<u8, ConstU32<128>>;
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<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, 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<u8> = 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(&current_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<Vec<u8>, 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<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
use codec::Decode;
let snapshot: BTreeMap<Vec<u8>, Vec<u8>> =
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<T>, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Timestamp: pallet_timestamp::{Pallet, Call, Storage},
EVM: pallet_evm::{Pallet, Call, Storage, Config<T>, Event<T>},
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<Self>;
type BlockHashCount = BlockHashCount;
type AccountData = pallet_balances::AccountData<u64>;
}
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<Self>;
type FeeCalculator = FixedGasPrice;
type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
type Currency = Balances;
type Runner = pallet_evm::runner::stack::Runner<Self>;
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<pallet_evm::PrecompileResult> {
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<H256>) -> Vec<u8> {
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::<TestRuntime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<TestRuntime>::default()
.assimilate_storage(&mut storage)
.unwrap();
let mut ext = sp_io::TestExternalities::new(storage);
ext.execute_with(|| {
let addresses: Vec<H160> = (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::<TestRuntime>::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::<TestRuntime>::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::<TestRuntime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<TestRuntime>::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::<TestRuntime>::put(OLD_CHAIN_ID);
assert_eq!(
pallet_evm_chain_id::ChainId::<TestRuntime>::get(),
OLD_CHAIN_ID
);
// Run the migration
let mut meter = WeightMeter::with_limit(Weight::MAX);
let result = EvmChainIdMigration::<TestRuntime, NEW_CHAIN_ID>::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::<TestRuntime>::get(),
NEW_CHAIN_ID
);
});
}
#[test]
fn evm_chain_id_migration_is_idempotent() {
use super::evm_chain_id::EvmChainIdMigration;
let mut storage = frame_system::GenesisConfig::<TestRuntime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<TestRuntime>::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::<TestRuntime, NEW_CHAIN_ID>::step(None, &mut meter);
let mut meter = WeightMeter::with_limit(Weight::MAX);
let result2 = EvmChainIdMigration::<TestRuntime, NEW_CHAIN_ID>::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::<TestRuntime>::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<SafeMode> =
frame_support::migrations::EnterSafeModeOnFailedMigration<
SafeMode,
frame_support::migrations::FreezeChainOnFailedMigration,
>;

View file

@ -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 = <Historical as KeyOwnerProofSystem<(KeyTypeId, BeefyId)>>::Proof;
type EquivocationReportSystem = ();
type EquivocationReportSystem =
pallet_beefy::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
}
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<Runtime>;
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<SafeMode>;
type MaxServiceWeight = MaxServiceWeight;
type WeightInfo = mainnet_weights::pallet_migrations::WeightInfo<Runtime>;
}
@ -996,13 +995,10 @@ parameter_types! {
pub PrecompilesValue: Precompiles = DataHavenPrecompiles::<Runtime>::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;

View file

@ -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 = <Runtime as pallet_migrations::Config>::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::<Runtime>::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 = <Runtime as pallet_migrations::Config>::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"
);
});
}

View file

@ -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 = <Historical as KeyOwnerProofSystem<(KeyTypeId, BeefyId)>>::Proof;
type EquivocationReportSystem = ();
type EquivocationReportSystem =
pallet_beefy::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
}
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<Runtime>,
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<SafeMode>;
type MaxServiceWeight = MaxServiceWeight;
type WeightInfo = stagenet_weights::pallet_migrations::WeightInfo<Runtime>;
}
@ -999,13 +992,10 @@ parameter_types! {
pub PrecompilesValue: Precompiles = DataHavenPrecompiles::<Runtime>::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;

View file

@ -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 <http://www.gnu.org/licenses/>.
#[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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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 = <Runtime as pallet_migrations::Config>::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::<Runtime>::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 = <Runtime as pallet_migrations::Config>::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"
);
});
}

View file

@ -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;

View file

@ -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 = <Historical as KeyOwnerProofSystem<(KeyTypeId, BeefyId)>>::Proof;
type EquivocationReportSystem = ();
type EquivocationReportSystem =
pallet_beefy::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
}
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<Runtime>,
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<SafeMode>;
type MaxServiceWeight = MaxServiceWeight;
type WeightInfo = testnet_weights::pallet_migrations::WeightInfo<Runtime>;
}
@ -1002,13 +995,10 @@ parameter_types! {
pub PrecompilesValue: Precompiles = DataHavenPrecompiles::<Runtime>::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;

View file

@ -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 <http://www.gnu.org/licenses/>.
#[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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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 = <Runtime as pallet_migrations::Config>::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::<Runtime>::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 = <Runtime as pallet_migrations::Config>::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"
);
});
}

View file

@ -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;

View file

@ -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[@]}")

View file

@ -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

View file

@ -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

View file

@ -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."
);
}
};

View file

@ -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<string, string> = {};
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 || ""
}
];

View file

@ -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 <value>", "Target chain (hoodi, holesky, mainnet)")
.option("--chain <value>", "Target chain (hoodi, holesky, mainnet, anvil)")
.option("--rpc-url <value>", "Chain RPC URL (optional, defaults based on chain)")
.option("--private-key <value>", "Private key for deployment", process.env.PRIVATE_KEY || "")
.option(
"--private-key <value>",
"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 <value>", "Target chain (hoodi, holesky, mainnet)")
.option("--chain <value>", "Target chain (hoodi, holesky, mainnet, anvil)")
.option("--rpc-url <value>", "Chain RPC URL (optional, defaults based on chain)")
.option("--private-key <value>", "Private key for deployment", process.env.PRIVATE_KEY || "")
.option(
"--private-key <value>",
"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 <value>", "Target chain (hoodi, holesky, mainnet)")
.option("--chain <value>", "Target chain (hoodi, holesky, mainnet, anvil)")
.option("--rpc-url <value>", "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 <value>", "Target chain (hoodi, holesky, mainnet)")
.option("--chain <value>", "Target chain (hoodi, holesky, mainnet, anvil)")
.option("--uri <value>", "New metadata URI (required)")
.option("--reset", "Use if you want to reset the metadata URI")
.option("--rpc-url <value>", "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 <value>", "Target chain (hoodi, holesky, mainnet)")
.option("--chain <value>", "Target chain (hoodi, holesky, mainnet, anvil)")
.option("--rpc-url <value>", "Chain RPC URL (optional, defaults based on chain)")
.option("--private-key <value>", "Private key for deployment", process.env.PRIVATE_KEY || "")
.option(
"--private-key <value>",
"Private key for deployment",
process.env.DEPLOYER_PRIVATE_KEY || ""
)
.option("--skip-verification", "Skip contract verification", false)
.hook("preAction", contractsPreActionHook)
.action(contractsCheck);

View file

@ -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"`
*/

View file

@ -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"

View file

@ -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);
}

View file

@ -147,7 +147,7 @@ export const fundValidators = async (options: FundValidatorsOptions): Promise<bo
logger.debug(`Found token creator's private key for address ${tokenCreator}`);
// Get the ERC20 balance of the token creator and its ETH balance as well
const getErc20BalanceCmd = `${castExecutable} balance --erc20 ${underlyingTokenAddress} ${tokenCreator} --rpc-url ${rpcUrl}`;
const getErc20BalanceCmd = `${castExecutable} call ${underlyingTokenAddress} "balanceOf(address)(uint256)" ${tokenCreator} --rpc-url ${rpcUrl}`;
const getEthBalanceCmd = `${castExecutable} balance ${tokenCreator} --rpc-url ${rpcUrl}`;
const { stdout: erc20BalanceOutput } = await $`sh -c ${getErc20BalanceCmd}`.quiet();
const { stdout: ethBalanceOutput } = await $`sh -c ${getEthBalanceCmd}`.quiet();

View file

@ -11,9 +11,10 @@ export const runShellCommandWithLogger = async (
env?: object;
logLevel?: LogLevel;
waitFor?: (...args: unknown[]) => Promise<void>;
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);