diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d8d2ba06..63221122 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -44,6 +44,8 @@ jobs: uses: ./.github/workflows/task-rust-tests.yml contract-tests: uses: ./.github/workflows/task-foundry-tests.yml + storage-layout: + uses: ./.github/workflows/task-storage-layout.yml rust-lint: needs: [warm-sccache] uses: ./.github/workflows/task-rust-lint.yml diff --git a/.github/workflows/task-e2e.yml b/.github/workflows/task-e2e.yml index 694b0906..dfd8cee6 100644 --- a/.github/workflows/task-e2e.yml +++ b/.github/workflows/task-e2e.yml @@ -118,23 +118,6 @@ jobs: key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} restore-keys: | ${{ runner.os }}-bun- - - name: Cache Foundry libraries - uses: actions/cache/restore@v4 - with: - path: ../contracts/lib - key: ${{ runner.os }}-foundry-libs-${{ hashFiles('.gitmodules') }} - restore-keys: | - ${{ runner.os }}-foundry-libs- - - - name: Cache Foundry build artifacts - uses: actions/cache/restore@v4 - with: - path: | - ../contracts/out - ../contracts/cache - key: ${{ runner.os }}-foundry-build-${{ hashFiles('contracts/foundry.toml', 'contracts/**/*.sol') }} - restore-keys: | - ${{ runner.os }}-foundry-build- - uses: docker/login-action@v3 with: registry: ghcr.io diff --git a/.github/workflows/task-foundry-tests.yml b/.github/workflows/task-foundry-tests.yml index c6bc6d81..81288ee6 100644 --- a/.github/workflows/task-foundry-tests.yml +++ b/.github/workflows/task-foundry-tests.yml @@ -38,16 +38,6 @@ jobs: with: version: v1.4.3 - - name: Cache Foundry build artifacts - uses: actions/cache@v4 - with: - path: | - contracts/out - contracts/cache - key: ${{ runner.os }}-foundry-build-${{ hashFiles('contracts/foundry.toml', 'contracts/**/*.sol') }} - restore-keys: | - ${{ runner.os }}-foundry-build- - - run: forge --version - run: forge fmt --check - run: forge build --sizes diff --git a/.github/workflows/task-storage-layout.yml b/.github/workflows/task-storage-layout.yml new file mode 100644 index 00000000..2a1a78a5 --- /dev/null +++ b/.github/workflows/task-storage-layout.yml @@ -0,0 +1,51 @@ +# Storage Layout Check: Validates storage layout for upgradeable contracts +# +# Overview: +# 1. Compares current storage layout against committed snapshot +# 2. Runs upgrade simulation tests to verify state preservation + +name: Storage Layout Check + +on: + workflow_dispatch: + workflow_call: + +# Explicit minimal permissions +permissions: + contents: read + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + name: Storage Layout + runs-on: ubuntu-latest + defaults: + run: + working-directory: contracts + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: v1.4.3 + + - name: Build contracts + run: forge build --extra-output storageLayout + + - name: Negative check storage layout (should fail) + run: | + chmod +x scripts/check-storage-layout-negative.sh + ./scripts/check-storage-layout-negative.sh + + - name: Check storage layout + run: | + chmod +x scripts/check-storage-layout.sh + ./scripts/check-storage-layout.sh + + - name: Run upgrade simulation tests + run: forge test --match-contract StorageLayoutTest -vvv diff --git a/contracts/README.md b/contracts/README.md index 9a737048..0d4042d2 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -1,6 +1,6 @@ # DataHaven AVS Smart Contracts -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. +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. ## Project Structure @@ -65,6 +65,6 @@ Supported networks: `hoodi` (no mainnet config yet). Artifacts → `contracts/de 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 slashing (subject to veto period). +4. **Slashing**: Misbehavior triggers slashing. See `test/README.md` for full network integration tests. diff --git a/contracts/config/anvil.json b/contracts/config/anvil.json index ca1afec2..5b8cce9b 100644 --- a/contracts/config/anvil.json +++ b/contracts/config/anvil.json @@ -27,8 +27,6 @@ "avs": { "avsOwner": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", "rewardsInitiator": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", - "vetoCommitteeMember": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", - "vetoWindowBlocks": 100, "validatorsStrategies": [ "0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0" ] diff --git a/contracts/config/example.jsonc b/contracts/config/example.jsonc index 2550cb11..e57c1ec7 100644 --- a/contracts/config/example.jsonc +++ b/contracts/config/example.jsonc @@ -69,10 +69,6 @@ /// This is for the EigenLayer rewards distribution way, using the RewardsCoordinator. /// But for now, we're not using it, and instead sending the rewards directly. "rewardsInitiator": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", - /// The address of the account that is a member of the Veto Committee for vetoing slashing. - "vetoCommitteeMember": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", - /// The number of blocks that the Veto Committee will have to submit a veto. - "vetoWindowBlocks": 100, /// The EigenLayer strategy addresses for the Validators to stake into. /// The beaconChainETHStrategy is a virtual address representing native beacon chain ETH. /// All networks: diff --git a/contracts/config/mainnet-ethereum.json b/contracts/config/mainnet-ethereum.json index 6481710d..5a0e13fb 100644 --- a/contracts/config/mainnet-ethereum.json +++ b/contracts/config/mainnet-ethereum.json @@ -31,8 +31,6 @@ "avs": { "avsOwner": "0x0000000000000000000000000000000000000000", "rewardsInitiator": "0x0000000000000000000000000000000000000000", - "vetoCommitteeMember": "0x0000000000000000000000000000000000000000", - "vetoWindowBlocks": 7200, "validatorsStrategies": [ "0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0", "0x93c4b944D05dfe6df7645A86cd2206016c51564D", diff --git a/contracts/config/stagenet-hoodi.json b/contracts/config/stagenet-hoodi.json index 0f8ed550..0dbd84c1 100644 --- a/contracts/config/stagenet-hoodi.json +++ b/contracts/config/stagenet-hoodi.json @@ -33,8 +33,6 @@ "avs": { "avsOwner": "0xe30a38ac89ffE5A86D5389Bfbf70C7EC766FbB6e", "rewardsInitiator": "0xe30a38ac89ffE5A86D5389Bfbf70C7EC766FbB6e", - "vetoCommitteeMember": "0xe30a38ac89ffE5A86D5389Bfbf70C7EC766FbB6e", - "vetoWindowBlocks": 100, "validatorsStrategies": [ "0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0", "0xf8a1a66130d614c7360e868576d5e59203475fe0", diff --git a/contracts/config/testnet-hoodi.json b/contracts/config/testnet-hoodi.json index 784fba9d..1aa56fee 100644 --- a/contracts/config/testnet-hoodi.json +++ b/contracts/config/testnet-hoodi.json @@ -33,8 +33,6 @@ "avs": { "avsOwner": "0x0000000000000000000000000000000000000000", "rewardsInitiator": "0x0000000000000000000000000000000000000000", - "vetoCommitteeMember": "0x0000000000000000000000000000000000000000", - "vetoWindowBlocks": 100, "validatorsStrategies": [ "0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0", "0xf8a1a66130d614c7360e868576d5e59203475fe0", diff --git a/contracts/script/deploy/Config.sol b/contracts/script/deploy/Config.sol index 5d731e95..ccb764b2 100644 --- a/contracts/script/deploy/Config.sol +++ b/contracts/script/deploy/Config.sol @@ -19,8 +19,6 @@ contract Config { struct AVSConfig { address avsOwner; address rewardsInitiator; - address vetoCommitteeMember; - uint32 vetoWindowBlocks; address[] validatorsStrategies; } diff --git a/contracts/script/deploy/DeployParams.s.sol b/contracts/script/deploy/DeployParams.s.sol index ca390006..1c21f32d 100644 --- a/contracts/script/deploy/DeployParams.s.sol +++ b/contracts/script/deploy/DeployParams.s.sol @@ -76,8 +76,6 @@ contract DeployParams is Script, Config { config.avsOwner = vm.parseJsonAddress(configJson, ".avs.avsOwner"); } config.rewardsInitiator = vm.parseJsonAddress(configJson, ".avs.rewardsInitiator"); - config.vetoCommitteeMember = vm.parseJsonAddress(configJson, ".avs.vetoCommitteeMember"); - config.vetoWindowBlocks = vm.parseJsonUint(configJson, ".avs.vetoWindowBlocks").toUint32(); config.validatorsStrategies = vm.parseJsonAddressArray(configJson, ".avs.validatorsStrategies"); diff --git a/contracts/script/fixtures/DataHavenServiceManagerBadLayout.sol b/contracts/script/fixtures/DataHavenServiceManagerBadLayout.sol new file mode 100644 index 00000000..919cc346 --- /dev/null +++ b/contracts/script/fixtures/DataHavenServiceManagerBadLayout.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.27; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; + +import {IGatewayV2} from "snowbridge/src/v2/IGateway.sol"; + +/// @notice Test-only fixture contract with intentionally broken storage layout. +/// @dev This contract is used to validate the snapshot-diff storage layout check fails as expected. +contract DataHavenServiceManagerBadLayout is OwnableUpgradeable { + // Deliberate layout shift: inserted before all original state vars + uint256 public layoutBreaker; + + // Original variables (shifted by one slot) + address public rewardsInitiator; + mapping(address => bool) public validatorsAllowlist; + IGatewayV2 private _snowbridgeGateway; + mapping(address => address) public validatorEthAddressToSolochainAddress; + + // Keep the original gap size to mirror shape, despite the shift + uint256[46] private __GAP; + + // Keep a compatible constructor signature for upgrade tests. + constructor( + address, + address + ) { + _disableInitializers(); + } +} + diff --git a/contracts/scripts/check-storage-layout-negative.sh b/contracts/scripts/check-storage-layout-negative.sh new file mode 100755 index 00000000..18d842cf --- /dev/null +++ b/contracts/scripts/check-storage-layout-negative.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -euo pipefail + +# Negative check: ensure the snapshot-diff storage layout script fails when the layout is broken. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR/.." + +set +e +OUTPUT="$( + CONTRACT="DataHavenServiceManagerBadLayout" \ + SNAPSHOT="storage-snapshots/DataHavenServiceManager.storage.json" \ + ./scripts/check-storage-layout.sh 2>&1 +)" +EXIT_CODE=$? +set -e + +if [ "$EXIT_CODE" -eq 0 ]; then + echo "ERROR: Expected storage layout check to fail for DataHavenServiceManagerBadLayout, but it succeeded." + exit 1 +fi + +if ! printf '%s\n' "$OUTPUT" | grep -q "ERROR: Storage layout has changed!"; then + echo "ERROR: Storage layout check failed, but not for the expected reason." + echo "" + echo "Output:" + echo "$OUTPUT" + exit 1 +fi + +echo "Negative check OK: storage layout check failed as expected for DataHavenServiceManagerBadLayout." + diff --git a/contracts/scripts/check-storage-layout.sh b/contracts/scripts/check-storage-layout.sh new file mode 100755 index 00000000..8e2aeb09 --- /dev/null +++ b/contracts/scripts/check-storage-layout.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -e + +# Storage Layout Check Script +# Compares current storage layout against committed snapshot to detect unintended changes. + +CONTRACT="${CONTRACT:-DataHavenServiceManager}" +SNAPSHOT_DIR="${SNAPSHOT_DIR:-storage-snapshots}" +SNAPSHOT="${SNAPSHOT:-${SNAPSHOT_DIR}/${CONTRACT}.storage.json}" + +# Ensure we're in the contracts directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR/.." + +# Check if snapshot exists +if [ ! -f "$SNAPSHOT" ]; then + echo "ERROR: Snapshot file not found: $SNAPSHOT" + echo "Generate it with: mkdir -p $SNAPSHOT_DIR && forge inspect $CONTRACT storage --json > $SNAPSHOT" + exit 1 +fi + +# Generate current layout +echo "Generating current storage layout for $CONTRACT..." +forge inspect "$CONTRACT" storage --json > /tmp/current_layout.json + +# Normalize both files for comparison: +# - Remove astId (changes with compiler runs) +# - Remove contract field (contains full path) +# - Remove types section (contains unstable AST IDs) +# - Sort by slot number +normalize_json() { + jq 'del(.types) + | .storage + | map( + del(.astId, .contract) + # Remove unstable AST ID suffixes from type strings (e.g., t_contract(IGatewayV2)12345) + | .type |= sub("\\)[0-9]+$"; ")") + ) + | sort_by(.slot | tonumber)' "$1" +} + +echo "Comparing storage layouts..." +normalize_json "$SNAPSHOT" > /tmp/snap_normalized.json +normalize_json /tmp/current_layout.json > /tmp/curr_normalized.json + +if ! diff -q /tmp/snap_normalized.json /tmp/curr_normalized.json > /dev/null 2>&1; then + echo "" + echo "==========================================" + echo "ERROR: Storage layout has changed!" + echo "==========================================" + echo "" + echo "Differences found:" + diff /tmp/snap_normalized.json /tmp/curr_normalized.json || true + echo "" + echo "If this change is intentional, update the snapshot:" + echo " forge inspect $CONTRACT storage --json > $SNAPSHOT" + echo "" + echo "WARNING: Unintended storage layout changes can corrupt state during upgrades!" + exit 1 +fi + +echo "Storage layout OK - no changes detected" diff --git a/contracts/storage-snapshots/DataHavenServiceManager.storage.json b/contracts/storage-snapshots/DataHavenServiceManager.storage.json new file mode 100644 index 00000000..37f6fa6d --- /dev/null +++ b/contracts/storage-snapshots/DataHavenServiceManager.storage.json @@ -0,0 +1,143 @@ +{ + "storage": [ + { + "astId": 138, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 141, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 671, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 10, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address" + }, + { + "astId": 130, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 23771, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "rewardsInitiator", + "offset": 0, + "slot": "101", + "type": "t_address" + }, + { + "astId": 23776, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "validatorsAllowlist", + "offset": 0, + "slot": "102", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 23780, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "_snowbridgeGateway", + "offset": 0, + "slot": "103", + "type": "t_contract(IGatewayV2)23481" + }, + { + "astId": 23785, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "validatorEthAddressToSolochainAddress", + "offset": 0, + "slot": "104", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 23790, + "contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager", + "label": "__GAP", + "offset": 0, + "slot": "105", + "type": "t_array(t_uint256)46_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)46_storage": { + "encoding": "inplace", + "label": "uint256[46]", + "numberOfBytes": "1472", + "base": "t_uint256" + }, + "t_array(t_uint256)49_storage": { + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568", + "base": "t_uint256" + }, + "t_array(t_uint256)50_storage": { + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600", + "base": "t_uint256" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(IGatewayV2)23481": { + "encoding": "inplace", + "label": "contract IGatewayV2", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } +} diff --git a/contracts/storage-snapshots/README.md b/contracts/storage-snapshots/README.md new file mode 100644 index 00000000..bf708a11 --- /dev/null +++ b/contracts/storage-snapshots/README.md @@ -0,0 +1,75 @@ +# Storage Layout Snapshots + +This directory contains storage layout snapshots for upgradeable contracts. These snapshots are used to detect unintended storage layout changes that could corrupt state during proxy upgrades. + +## How It Works + +1. **Snapshot Comparison**: CI compares the current storage layout against committed snapshots +2. **Upgrade Simulation**: Foundry tests verify state preservation across upgrades + +## Updating Snapshots + +When you intentionally modify the storage layout of a contract (e.g., adding new state variables), you must update the snapshot: + +```bash +cd contracts +forge inspect DataHavenServiceManager storage --json > storage-snapshots/DataHavenServiceManager.storage.json +``` + +## Important Guidelines + +- **Never reorder existing variables** - This corrupts existing state +- **Never change types of existing variables** - This corrupts existing state +- **Always add new variables before the `__GAP`** - This preserves upgrade safety +- **Reduce gap size when adding variables** - Keep total slot count constant +- **Review snapshot diffs carefully** - Ensure changes are intentional + +## Current Contracts + +| Contract | Gap Size | Gap Slot | +|----------|----------|----------| +| DataHavenServiceManager | 46 | 105 | + +## Verification Commands + +```bash +# Check storage layout (CI script) +./scripts/check-storage-layout.sh + +# Negative check (proves detector fails on broken layout) +./scripts/check-storage-layout-negative.sh + +# Run upgrade simulation tests +forge test --match-contract StorageLayoutTest -vvv + +# View human-readable layout +forge inspect DataHavenServiceManager storage --pretty +``` + +## How Normalization Works + +The snapshot comparison normalizes both files to avoid false positives: + +- **Removes `astId`**: Changes with each compiler run +- **Removes `contract`**: Contains full file path +- **Removes `.types` section**: Contains unstable AST IDs that cause false diffs +- **Normalizes type IDs**: Strips unstable numeric suffixes from `type` (e.g., `t_contract(IGatewayV2)12345`) +- **Sorts by slot**: Ensures deterministic comparison + +This approach detects: +- Variable reordering or slot changes +- Top-level type changes (primitives, mappings, arrays) +- Gap size modifications + +## Note on Struct Storage + +If you add struct-typed storage variables in the future, be aware that **internal struct field changes may not be detected** by the snapshot diff. This is because: + +1. The `.types` section (which contains struct field definitions) is dropped to avoid unstable AST IDs +2. The storage slot assignment for a struct variable doesn't change when its internal fields change + +**However, this does not break upgrades** in the traditional sense. Struct field reordering or type changes within a struct would cause data misinterpretation (reading field A as field B), but the slot-level layout remains stable. + +**Mitigation**: If adding struct storage, ensure the upgrade simulation tests (`StorageLayoutTest`) explicitly verify struct field values survive upgrades. + +**Current status**: DataHavenServiceManager has no struct-typed storage variables, so this limitation does not apply. diff --git a/contracts/test/storage/StorageLayout.t.sol b/contracts/test/storage/StorageLayout.t.sol new file mode 100644 index 00000000..de81e0b9 --- /dev/null +++ b/contracts/test/storage/StorageLayout.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.27; + +import {Test} from "forge-std/Test.sol"; +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {AVSDeployer} from "../utils/AVSDeployer.sol"; +import {DataHavenServiceManager} from "../../src/DataHavenServiceManager.sol"; + +/// @title Storage Layout Tests for DataHavenServiceManager +/// @notice Verifies that proxy upgrades preserve state correctly +contract StorageLayoutTest is AVSDeployer { + function setUp() public { + _deployMockEigenLayerAndAVS(); + } + + /// @notice Proves state is preserved across proxy upgrade + function test_upgradePreservesState() public { + // 1. Populate state + address testValidator = address(0x1234); + address newRewardsInitiator = address(0x9999); + + vm.startPrank(avsOwner); + serviceManager.addValidatorToAllowlist(testValidator); + serviceManager.setRewardsInitiator(newRewardsInitiator); + vm.stopPrank(); + + // 2. Record state before upgrade + bool allowlistBefore = serviceManager.validatorsAllowlist(testValidator); + address rewardsInitiatorBefore = serviceManager.rewardsInitiator(); + address ownerBefore = serviceManager.owner(); + address gatewayBefore = serviceManager.snowbridgeGateway(); + + // 3. Deploy new implementation + DataHavenServiceManager newImpl = + new DataHavenServiceManager(rewardsCoordinator, allocationManager); + + // 4. Upgrade proxy + vm.prank(proxyAdminOwner); + proxyAdmin.upgrade(ITransparentUpgradeableProxy(address(serviceManager)), address(newImpl)); + + // 5. Verify state preserved + assertEq( + serviceManager.validatorsAllowlist(testValidator), + allowlistBefore, + "validatorsAllowlist should be preserved" + ); + assertEq( + serviceManager.rewardsInitiator(), + rewardsInitiatorBefore, + "rewardsInitiator should be preserved" + ); + assertEq(serviceManager.owner(), ownerBefore, "owner should be preserved"); + assertEq( + serviceManager.snowbridgeGateway(), + gatewayBefore, + "snowbridgeGateway should be preserved" + ); + } + + /// @notice Verifies validatorEthAddressToSolochainAddress mapping is preserved + function test_upgradePreservesValidatorMappings() public { + address testValidator = address(0xABCD); + address testSolochainAddress = address(0xDEF0); + + // Add validator to allowlist first + vm.prank(avsOwner); + serviceManager.addValidatorToAllowlist(testValidator); + + // Register operator via allocationManager to set the solochain address mapping + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; // VALIDATORS_SET_ID + + vm.prank(address(allocationManager)); + serviceManager.registerOperator( + testValidator, + address(serviceManager), + operatorSetIds, + abi.encodePacked(testSolochainAddress) + ); + + // Record state before upgrade + bool inAllowlistBefore = serviceManager.validatorsAllowlist(testValidator); + address solochainAddressBefore = + serviceManager.validatorEthAddressToSolochainAddress(testValidator); + + // Verify the mapping was set correctly before upgrade + assertEq(solochainAddressBefore, testSolochainAddress, "Solochain address should be set"); + + // Deploy new implementation and upgrade + DataHavenServiceManager newImpl = + new DataHavenServiceManager(rewardsCoordinator, allocationManager); + + vm.prank(proxyAdminOwner); + proxyAdmin.upgrade(ITransparentUpgradeableProxy(address(serviceManager)), address(newImpl)); + + // Verify both mappings preserved after upgrade + assertEq( + serviceManager.validatorsAllowlist(testValidator), + inAllowlistBefore, + "validatorsAllowlist mapping should be preserved after upgrade" + ); + assertEq( + serviceManager.validatorEthAddressToSolochainAddress(testValidator), + solochainAddressBefore, + "validatorEthAddressToSolochainAddress mapping should be preserved after upgrade" + ); + assertEq( + serviceManager.validatorEthAddressToSolochainAddress(testValidator), + testSolochainAddress, + "validatorEthAddressToSolochainAddress should have correct value after upgrade" + ); + } + + /// @notice Verifies multiple validators in allowlist are preserved + function test_upgradePreservesMultipleValidators() public { + address[] memory validators = new address[](3); + validators[0] = address(0x1111); + validators[1] = address(0x2222); + validators[2] = address(0x3333); + + // Add multiple validators + vm.startPrank(avsOwner); + for (uint256 i = 0; i < validators.length; i++) { + serviceManager.addValidatorToAllowlist(validators[i]); + } + vm.stopPrank(); + + // Deploy new implementation and upgrade + DataHavenServiceManager newImpl = + new DataHavenServiceManager(rewardsCoordinator, allocationManager); + + vm.prank(proxyAdminOwner); + proxyAdmin.upgrade(ITransparentUpgradeableProxy(address(serviceManager)), address(newImpl)); + + // Verify all validators still in allowlist + for (uint256 i = 0; i < validators.length; i++) { + assertTrue( + serviceManager.validatorsAllowlist(validators[i]), + "All validators should remain in allowlist after upgrade" + ); + } + } + + /// @notice Verifies that upgrade doesn't affect functionality + function test_functionalityAfterUpgrade() public { + // Deploy new implementation and upgrade + DataHavenServiceManager newImpl = + new DataHavenServiceManager(rewardsCoordinator, allocationManager); + + vm.prank(proxyAdminOwner); + proxyAdmin.upgrade(ITransparentUpgradeableProxy(address(serviceManager)), address(newImpl)); + + // Verify functionality still works + address newValidator = address(0xBEEF); + + vm.prank(avsOwner); + serviceManager.addValidatorToAllowlist(newValidator); + + assertTrue( + serviceManager.validatorsAllowlist(newValidator), + "Should be able to add validators after upgrade" + ); + + vm.prank(avsOwner); + serviceManager.removeValidatorFromAllowlist(newValidator); + + assertFalse( + serviceManager.validatorsAllowlist(newValidator), + "Should be able to remove validators after upgrade" + ); + } +} diff --git a/contracts/test/utils/AVSDeployer.sol b/contracts/test/utils/AVSDeployer.sol index 13a4633d..a5fedb08 100644 --- a/contracts/test/utils/AVSDeployer.sol +++ b/contracts/test/utils/AVSDeployer.sol @@ -48,11 +48,6 @@ contract AVSDeployer is Test { DataHavenServiceManager public serviceManager; DataHavenServiceManager public serviceManagerImplementation; - // Truncation is intentional - deriving a deterministic mock address from hash - address public vetoCommitteeMember = - address(uint160(uint256(keccak256("vetoCommitteeMember")))); - uint32 public vetoWindowBlocks = 100; // 100 blocks veto window for tests - // EigenLayer contracts StrategyManager public strategyManager; StrategyManager public strategyManagerImplementation; diff --git a/operator/Cargo.lock b/operator/Cargo.lock index 1289c252..1f6d04ec 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -1521,7 +1521,7 @@ dependencies = [ "pallet-message-queue", "parity-scale-codec", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "sp-core", "sp-runtime", "sp-std", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "datahaven-mainnet-runtime" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "bridge-hub-common 0.13.1", @@ -2719,8 +2719,8 @@ dependencies = [ "shp-treasury-funding", "shp-tx-implicits-runtime-api", "smallvec", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -2763,7 +2763,7 @@ dependencies = [ [[package]] name = "datahaven-node" -version = "0.22.0" +version = "0.23.0" dependencies = [ "async-channel 1.9.0", "clap", @@ -2876,7 +2876,7 @@ dependencies = [ [[package]] name = "datahaven-runtime-common" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "fp-account", @@ -2910,7 +2910,7 @@ dependencies = [ [[package]] name = "datahaven-stagenet-runtime" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "bridge-hub-common 0.13.1", @@ -3022,8 +3022,8 @@ dependencies = [ "shp-treasury-funding", "shp-tx-implicits-runtime-api", "smallvec", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -3066,7 +3066,7 @@ dependencies = [ [[package]] name = "datahaven-testnet-runtime" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "bridge-hub-common 0.13.1", @@ -3178,8 +3178,8 @@ dependencies = [ "shp-treasury-funding", "shp-tx-implicits-runtime-api", "smallvec", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -3371,7 +3371,7 @@ dependencies = [ [[package]] name = "dhp-bridge" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-support", "frame-system", @@ -3379,7 +3379,7 @@ dependencies = [ "pallet-datahaven-native-transfer", "pallet-external-validators", "parity-scale-codec", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "sp-core", "sp-std", @@ -8639,8 +8639,8 @@ dependencies = [ [[package]] name = "pallet-bucket-nfts" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-benchmarking", "frame-support", @@ -8696,8 +8696,8 @@ dependencies = [ [[package]] name = "pallet-cr-randomness" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-support", "frame-system", @@ -8716,7 +8716,7 @@ dependencies = [ [[package]] name = "pallet-datahaven-native-transfer" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -8724,7 +8724,7 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -8828,7 +8828,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-balances-erc20" -version = "0.22.0" +version = "0.23.0" dependencies = [ "fp-evm", "frame-support", @@ -8851,7 +8851,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-batch" -version = "0.22.0" +version = "0.23.0" dependencies = [ "evm", "fp-evm", @@ -8890,7 +8890,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-call-permit" -version = "0.22.0" +version = "0.23.0" dependencies = [ "evm", "fp-evm", @@ -8956,7 +8956,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-datahaven-native-transfer" -version = "0.22.0" +version = "0.23.0" dependencies = [ "evm", "fp-evm", @@ -8970,7 +8970,7 @@ dependencies = [ "parity-scale-codec", "precompile-utils", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -8980,8 +8980,8 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-file-system" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "fp-account", "fp-evm", @@ -9049,7 +9049,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-proxy" -version = "0.22.0" +version = "0.23.0" dependencies = [ "evm", "fp-evm", @@ -9093,7 +9093,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-registry" -version = "0.22.0" +version = "0.23.0" dependencies = [ "fp-evm", "frame-support", @@ -9144,7 +9144,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -9154,7 +9154,7 @@ dependencies = [ [[package]] name = "pallet-external-validators" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9178,7 +9178,7 @@ dependencies = [ [[package]] name = "pallet-external-validators-rewards" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9191,7 +9191,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-outbound-queue-primitives", "sp-core", "sp-io", @@ -9220,8 +9220,8 @@ dependencies = [ [[package]] name = "pallet-file-system" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-benchmarking", "frame-support", @@ -9249,8 +9249,8 @@ dependencies = [ [[package]] name = "pallet-file-system-runtime-api" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "parity-scale-codec", "scale-info", @@ -9417,7 +9417,7 @@ dependencies = [ [[package]] name = "pallet-outbound-commitment-store" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-support", "frame-system", @@ -9445,8 +9445,8 @@ dependencies = [ [[package]] name = "pallet-payment-streams" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-benchmarking", "frame-support", @@ -9465,8 +9465,8 @@ dependencies = [ [[package]] name = "pallet-payment-streams-runtime-api" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "parity-scale-codec", "scale-info", @@ -9493,8 +9493,8 @@ dependencies = [ [[package]] name = "pallet-proofs-dealer" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-benchmarking", "frame-support", @@ -9519,8 +9519,8 @@ dependencies = [ [[package]] name = "pallet-proofs-dealer-runtime-api" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "parity-scale-codec", "scale-info", @@ -9541,7 +9541,7 @@ dependencies = [ [[package]] name = "pallet-proxy-genesis-companion" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-support", "frame-system", @@ -9558,8 +9558,8 @@ dependencies = [ [[package]] name = "pallet-randomness" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-benchmarking", "frame-support", @@ -9652,7 +9652,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9696,8 +9696,8 @@ dependencies = [ [[package]] name = "pallet-storage-providers" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-benchmarking", "frame-support", @@ -9718,8 +9718,8 @@ dependencies = [ [[package]] name = "pallet-storage-providers-runtime-api" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "parity-scale-codec", "scale-info", @@ -13881,8 +13881,8 @@ dependencies = [ [[package]] name = "shc-actors-derive" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "once_cell", "proc-macro2", @@ -13894,8 +13894,8 @@ dependencies = [ [[package]] name = "shc-actors-framework" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "bincode", @@ -13913,8 +13913,8 @@ dependencies = [ [[package]] name = "shc-blockchain-service" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "array-bytes", @@ -13969,8 +13969,8 @@ dependencies = [ [[package]] name = "shc-blockchain-service-db" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "chrono", "diesel", @@ -13993,8 +13993,8 @@ dependencies = [ [[package]] name = "shc-client" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "array-bytes", @@ -14067,8 +14067,8 @@ dependencies = [ [[package]] name = "shc-common" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "bigdecimal", @@ -14131,8 +14131,8 @@ dependencies = [ [[package]] name = "shc-file-manager" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "bincode", "hash-db", @@ -14155,8 +14155,8 @@ dependencies = [ [[package]] name = "shc-file-transfer-service" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "array-bytes", @@ -14184,8 +14184,8 @@ dependencies = [ [[package]] name = "shc-fisherman-service" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "async-trait", "diesel", @@ -14215,8 +14215,8 @@ dependencies = [ [[package]] name = "shc-forest-manager" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "async-trait", @@ -14241,8 +14241,8 @@ dependencies = [ [[package]] name = "shc-indexer-db" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "bigdecimal", "chrono", @@ -14269,8 +14269,8 @@ dependencies = [ [[package]] name = "shc-indexer-service" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "anyhow", "array-bytes", @@ -14320,8 +14320,8 @@ dependencies = [ [[package]] name = "shc-rpc" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "array-bytes", "async-trait", @@ -14366,8 +14366,8 @@ dependencies = [ [[package]] name = "shc-telemetry" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -14383,8 +14383,8 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "shp-constants" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "sp-core", "sp-runtime", @@ -14392,8 +14392,8 @@ dependencies = [ [[package]] name = "shp-data-price-updater" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-support", "parity-scale-codec", @@ -14407,8 +14407,8 @@ dependencies = [ [[package]] name = "shp-file-key-verifier" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-support", "parity-scale-codec", @@ -14425,8 +14425,8 @@ dependencies = [ [[package]] name = "shp-file-metadata" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "hex", "num-bigint", @@ -14441,8 +14441,8 @@ dependencies = [ [[package]] name = "shp-forest-verifier" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-support", "parity-scale-codec", @@ -14458,16 +14458,16 @@ dependencies = [ [[package]] name = "shp-opaque" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "sp-runtime", ] [[package]] name = "shp-session-keys" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "async-trait", "parity-scale-codec", @@ -14481,8 +14481,8 @@ dependencies = [ [[package]] name = "shp-traits" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "frame-support", "parity-scale-codec", @@ -14495,8 +14495,8 @@ dependencies = [ [[package]] name = "shp-treasury-funding" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "log", "shp-traits", @@ -14506,8 +14506,8 @@ dependencies = [ [[package]] name = "shp-tx-implicits-runtime-api" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "parity-scale-codec", "scale-info", @@ -14519,8 +14519,8 @@ dependencies = [ [[package]] name = "shp-types" -version = "0.3.5" -source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.3.5#e21f2316c07e9fc43fc67f20373e6c24a8f4d5ae" +version = "0.4.0" +source = "git+https://github.com/Moonsong-Labs/storage-hub.git?tag=v0.4.0#ea2611cb3b47e448fa2812082e130c697b66277a" dependencies = [ "sp-core", "sp-runtime", @@ -14800,7 +14800,7 @@ dependencies = [ [[package]] name = "snowbridge-beacon-primitives" -version = "0.22.0" +version = "0.23.0" dependencies = [ "byte-slice-cast", "frame-support", @@ -14845,7 +14845,7 @@ dependencies = [ [[package]] name = "snowbridge-core" -version = "0.22.0" +version = "0.23.0" dependencies = [ "bp-relayers", "ethabi-decode", @@ -14922,8 +14922,8 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-verification-primitives", "sp-core", "sp-io", @@ -14936,7 +14936,7 @@ dependencies = [ [[package]] name = "snowbridge-merkle-tree" -version = "0.22.0" +version = "0.23.0" dependencies = [ "array-bytes", "hex", @@ -14977,7 +14977,7 @@ dependencies = [ [[package]] name = "snowbridge-outbound-queue-primitives" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "ethabi-decode", @@ -14989,7 +14989,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-verification-primitives", "sp-arithmetic", "sp-core", @@ -15003,12 +15003,12 @@ dependencies = [ [[package]] name = "snowbridge-outbound-queue-v2-runtime-api" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", "sp-api", @@ -15018,7 +15018,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-ethereum-client" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -15031,8 +15031,8 @@ dependencies = [ "scale-info", "serde", "serde_json", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-ethereum 0.3.0", "snowbridge-inbound-queue-primitives", "snowbridge-pallet-ethereum-client-fixtures", @@ -15048,8 +15048,8 @@ name = "snowbridge-pallet-ethereum-client-fixtures" version = "0.9.0" dependencies = [ "hex-literal 0.3.4", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "sp-core", "sp-std", @@ -15057,7 +15057,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-inbound-queue-v2" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "bp-relayers", @@ -15071,8 +15071,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue-v2-fixtures", @@ -15093,8 +15093,8 @@ name = "snowbridge-pallet-inbound-queue-v2-fixtures" version = "0.10.0" dependencies = [ "hex-literal 0.3.4", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "sp-core", "sp-std", @@ -15124,7 +15124,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-outbound-queue-v2" -version = "0.22.0" +version = "0.23.0" dependencies = [ "alloy-core", "bp-relayers", @@ -15138,8 +15138,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "snowbridge-beacon-primitives 0.22.0", - "snowbridge-core 0.22.0", + "snowbridge-beacon-primitives 0.23.0", + "snowbridge-core 0.23.0", "snowbridge-inbound-queue-primitives", "snowbridge-merkle-tree", "snowbridge-outbound-queue-primitives", @@ -15170,7 +15170,7 @@ dependencies = [ "parity-scale-codec", "polkadot-primitives", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-outbound-queue-primitives", "snowbridge-pallet-outbound-queue", "sp-core", @@ -15183,7 +15183,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-system-v2" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -15195,7 +15195,7 @@ dependencies = [ "parity-scale-codec", "polkadot-primitives", "scale-info", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "snowbridge-outbound-queue-primitives", "snowbridge-pallet-outbound-queue-v2", "snowbridge-pallet-system", @@ -15211,10 +15211,10 @@ dependencies = [ [[package]] name = "snowbridge-system-v2-runtime-api" -version = "0.22.0" +version = "0.23.0" dependencies = [ "parity-scale-codec", - "snowbridge-core 0.22.0", + "snowbridge-core 0.23.0", "sp-api", "sp-std", "staging-xcm", @@ -15222,7 +15222,7 @@ dependencies = [ [[package]] name = "snowbridge-test-utils" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -15242,12 +15242,12 @@ dependencies = [ [[package]] name = "snowbridge-verification-primitives" -version = "0.22.0" +version = "0.23.0" dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "snowbridge-beacon-primitives 0.22.0", + "snowbridge-beacon-primitives 0.23.0", "sp-core", "sp-std", ] diff --git a/operator/Cargo.toml b/operator/Cargo.toml index 0299bb3b..ee2d6933 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" homepage = "https://datahaven.xyz/" license = "GPL-3" repository = "https://github.com/datahavenxyz/datahaven" -version = "0.22.0" +version = "0.23.0" [workspace] members = [ @@ -28,7 +28,6 @@ datahaven-stagenet-runtime = { path = "./runtime/stagenet", default-features = f datahaven-testnet-runtime = { path = "./runtime/testnet", default-features = false } dhp-bridge = { path = "./primitives/bridge", default-features = false } pallet-datahaven-native-transfer = { path = "./pallets/datahaven-native-transfer", default-features = false } -pallet-session-benchmarking = { path = "./pallets/session-benchmarking", default-features = false } pallet-evm-precompile-balances-erc20 = { path = "./precompiles/erc20-balances", default-features = false } pallet-evm-precompile-batch = { path = "./precompiles/batch", default-features = false } pallet-evm-precompile-call-permit = { path = "./precompiles/call-permit", default-features = false } @@ -45,6 +44,7 @@ pallet-external-validators = { path = "./pallets/external-validators", default-f pallet-external-validators-rewards = { path = "./pallets/external-validators-rewards", default-features = false } pallet-outbound-commitment-store = { path = "./pallets/outbound-commitment-store", default-features = false } pallet-proxy-genesis-companion = { path = "./pallets/proxy-genesis-companion", default-features = false } +pallet-session-benchmarking = { path = "./pallets/session-benchmarking", default-features = false } # Crates.io (wasm) alloy-core = { version = "0.8.15", default-features = false } @@ -272,42 +272,42 @@ fc-storage = { git = "https://github.com/polkadot-evm/frontier", branch = "stabl # StorageHub ## Runtime -pallet-bucket-nfts = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-cr-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-file-system-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-payment-streams = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-payment-streams-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-proofs-dealer = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-proofs-dealer-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-storage-providers = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -pallet-storage-providers-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-constants = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-data-price-updater = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-file-key-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-file-metadata = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-forest-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-traits = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-treasury-funding = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } +pallet-bucket-nfts = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-cr-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-file-system-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-payment-streams = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-payment-streams-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-proofs-dealer = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-proofs-dealer-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-storage-providers = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +pallet-storage-providers-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-constants = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-data-price-updater = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-file-key-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-file-metadata = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-forest-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-traits = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-treasury-funding = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } ## Client -shc-actors-derive = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-actors-framework = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-blockchain-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-client = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-common = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-file-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-file-transfer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-fisherman-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-forest-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-indexer-db = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-indexer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shc-rpc = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-opaque = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-tx-implicits-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } -shp-types = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } +shc-actors-derive = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-actors-framework = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-blockchain-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-client = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-common = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-file-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-file-transfer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-fisherman-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-forest-manager = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-indexer-db = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-indexer-service = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shc-rpc = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-opaque = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-tx-implicits-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } +shp-types = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } ## Precompiles -pallet-evm-precompile-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.3.5", default-features = false } +pallet-evm-precompile-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", tag = "v0.4.0", default-features = false } # Static linking diff --git a/operator/node/Cargo.toml b/operator/node/Cargo.toml index 8c6df8ed..68861344 100644 --- a/operator/node/Cargo.toml +++ b/operator/node/Cargo.toml @@ -141,7 +141,6 @@ toml = { workspace = true } #### Needed to build static binaries #### pq-sys = { workspace = true, optional = true } - [build-dependencies] substrate-build-script-utils = { workspace = true, default-features = true } diff --git a/operator/node/src/cli.rs b/operator/node/src/cli.rs index 1573aa77..51a881a4 100644 --- a/operator/node/src/cli.rs +++ b/operator/node/src/cli.rs @@ -741,9 +741,23 @@ pub struct FishermanConfigurations { pub fisherman_database_url: Option, /// Duration between batch deletion processing cycles (in seconds). - #[arg(long, default_value = "60", value_parser = clap::value_parser!(u64).range(1..))] + #[arg(long, default_value = "30", value_parser = clap::value_parser!(u64).range(1..))] pub fisherman_batch_interval_seconds: u64, + /// Cooldown between batch deletion attempts (in seconds). + /// + /// Set to `0` to disable cooldown. + #[arg(long, default_value = "1", value_parser = clap::value_parser!(u64).range(0..))] + pub fisherman_batch_cooldown_seconds: u64, + + /// Number of consecutive no-work batches required before switching to the slower idle polling interval. + /// + /// The minimum value is 2 because there are two kinds of work: User and Incomplete. + /// If we set the value to 1, a non-work batch in one kind of work will trigger the idle poll interval + /// on the other kind of work. + #[arg(long, default_value = "4", value_parser = clap::value_parser!(u8).range(1..))] + pub fisherman_consecutive_no_work_batches_threshold: u8, + /// Maximum number of files to process per batch deletion cycle. #[arg(long, default_value = "1000", value_parser = clap::value_parser!(u64).range(1..))] pub fisherman_batch_deletion_limit: u64, @@ -803,6 +817,9 @@ impl FishermanConfigurations { .expect("Fisherman database URL is required"), batch_interval_seconds: self.fisherman_batch_interval_seconds, batch_deletion_limit: self.fisherman_batch_deletion_limit, + batch_cooldown_seconds: self.fisherman_batch_cooldown_seconds, + consecutive_no_work_batches_threshold: self + .fisherman_consecutive_no_work_batches_threshold, maintenance_mode: false, // Skipping maintenance mode for now filtering, ordering, diff --git a/operator/node/src/rpc.rs b/operator/node/src/rpc.rs index 81d59467..25380cdb 100644 --- a/operator/node/src/rpc.rs +++ b/operator/node/src/rpc.rs @@ -24,10 +24,13 @@ use crate::consensus::BabeConsensusDataProvider; use crate::eth::DefaultEthConfig; use datahaven_runtime_common::{time::SLOT_DURATION, Block, BlockNumber, Hash}; -use fc_rpc::TxPool; use fc_rpc::{Eth, EthBlockDataCacheTask, EthFilter, Net, Web3}; +use fc_rpc::{EthPubSub, TxPool}; use fc_rpc_core::types::{FeeHistoryCache, FilterPool}; -use fc_rpc_core::{EthApiServer, EthFilterApiServer, NetApiServer, TxPoolApiServer, Web3ApiServer}; +use fc_rpc_core::{ + EthApiServer, EthFilterApiServer, EthPubSubApiServer, NetApiServer, TxPoolApiServer, + Web3ApiServer, +}; use fc_storage::StorageOverride; use fp_rpc::EthereumRuntimeRPCApi; use jsonrpsee::RpcModule; @@ -111,6 +114,12 @@ where /// Instantiate all full RPC extensions. pub fn create_full( deps: FullDeps, + subscription_task_executor: sc_rpc::SubscriptionTaskExecutor, + pubsub_notification_sinks: Arc< + fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + >, + >, ) -> Result, Box> where P: TransactionPool + 'static, @@ -263,6 +272,17 @@ where )?; module.merge(Web3::new(Arc::clone(&client)).into_rpc())?; + module.merge( + EthPubSub::new( + pool, + Arc::clone(&client), + sync.clone(), + subscription_task_executor, + overrides, + pubsub_notification_sinks.clone(), + ) + .into_rpc(), + )?; if let Some(command_sink) = command_sink { module.merge( @@ -275,19 +295,5 @@ where let tx_pool = TxPool::new(client.clone(), graph.clone()); module.merge(tx_pool.into_rpc())?; - // module.merge(FrontierFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?; - - // Extend this RPC with a custom API by using the following syntax. - // `YourRpcStruct` should have a reference to a client, which is needed - // to call into the runtime. - // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;` - - // You probably want to enable the `rpc v2 chainSpec` API as well - // - // let chain_name = chain_spec.name().to_string(); - // let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); - // let properties = chain_spec.properties(); - // module.merge(ChainSpec::new(chain_name, genesis_hash, properties).into_rpc())?; - Ok(module) } diff --git a/operator/node/src/service.rs b/operator/node/src/service.rs index 11c6a247..710d6e81 100644 --- a/operator/node/src/service.rs +++ b/operator/node/src/service.rs @@ -439,7 +439,7 @@ pub async fn new_full_impl< RuntimeApi, N: sc_network::NetworkBackend::Hash>, >( - config: Configuration, + mut config: Configuration, mut eth_config: EthConfiguration, role_options: Option, indexer_options: Option, @@ -673,7 +673,7 @@ where }, storage_override, sync: sync_service.clone(), - pubsub_notification_sinks, + pubsub_notification_sinks: pubsub_notification_sinks.clone(), }, ) .await; @@ -693,38 +693,52 @@ where let fee_history_limit = eth_config.fee_history_limit; let sync = sync_service.clone(); - Box::new(move |subscription_executor| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: pool.clone(), - graph: pool.pool().clone(), - beefy: BeefyDeps:: { - beefy_finality_proof_stream: beefy_rpc_links.from_voter_justif_stream.clone(), - beefy_best_block_stream: beefy_rpc_links.from_voter_best_beefy_stream.clone(), + Box::new( + move |subscription_executor: sc_rpc::SubscriptionTaskExecutor| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: pool.clone(), + graph: pool.pool().clone(), + beefy: BeefyDeps:: { + beefy_finality_proof_stream: beefy_rpc_links + .from_voter_justif_stream + .clone(), + beefy_best_block_stream: beefy_rpc_links + .from_voter_best_beefy_stream + .clone(), + subscription_executor: subscription_executor.clone(), + }, + max_past_logs, + fee_history_limit, + fee_history_cache: fee_history_cache.clone(), + network: Arc::new(network.clone()), + sync: sync.clone(), + filter_pool: filter_pool.clone(), + block_data_cache: block_data_cache.clone(), + overrides: overrides.clone(), + is_authority: is_authority.clone(), + command_sink: command_sink.clone(), + backend: backend.clone(), + frontier_backend: match &*frontier_backend { + fc_db::Backend::KeyValue(b) => b.clone(), + fc_db::Backend::Sql(b) => b.clone(), + }, + forced_parent_hashes: None, + maybe_storage_hub_client_config: maybe_storage_hub_client_rpc_config.clone(), + }; + crate::rpc::create_full( + deps, subscription_executor, - }, - max_past_logs, - fee_history_limit, - fee_history_cache: fee_history_cache.clone(), - network: Arc::new(network.clone()), - sync: sync.clone(), - filter_pool: filter_pool.clone(), - block_data_cache: block_data_cache.clone(), - overrides: overrides.clone(), - is_authority: is_authority.clone(), - command_sink: command_sink.clone(), - backend: backend.clone(), - frontier_backend: match &*frontier_backend { - fc_db::Backend::KeyValue(b) => b.clone(), - fc_db::Backend::Sql(b) => b.clone(), - }, - forced_parent_hashes: None, - maybe_storage_hub_client_config: maybe_storage_hub_client_rpc_config.clone(), - }; - crate::rpc::create_full(deps).map_err(Into::into) - }) + pubsub_notification_sinks.clone(), + ) + .map_err(Into::into) + }, + ) }; + // Use Ethereum-style hex subscription IDs (0x-prefixed) instead of jsonrpsee defaults. + config.rpc.id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); + let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { network: Arc::new(network.clone()), client: client.clone(), diff --git a/operator/runtime/mainnet/src/configs/runtime_params.rs b/operator/runtime/mainnet/src/configs/runtime_params.rs index d034192a..605c9f73 100644 --- a/operator/runtime/mainnet/src/configs/runtime_params.rs +++ b/operator/runtime/mainnet/src/configs/runtime_params.rs @@ -128,9 +128,9 @@ pub mod dynamic_params { /// /// [`MaxPrice`] = [`MostlyStablePrice`] + u * e ^ ( 1 - [`SystemUtilisationUpperThresholdPercentage`] ) /// - /// 500 = 50 + u * (e ^ (1 - 0.95) - 1) - /// u = (500 - 50) / (e ^ (1 - 0.95) - 1) ≈ 8777 - pub static UpperExponentFactor: u32 = 8777; + /// 500 GIGAWEI = 50 GIGAWEI + u * (e ^ (1 - 0.95) - 1) + /// u = (500 GIGAWEI - 50 GIGAWEI) / (e ^ (1 - 0.95) - 1) ≈ 8,776,874,921,880 + pub static UpperExponentFactor: Balance = 8_776_874_921_880; #[codec(index = 15)] #[allow(non_upper_case_globals)] @@ -139,9 +139,9 @@ pub mod dynamic_params { /// /// [`MinPrice`] = [`MostlyStablePrice`] - u * e ^ ( [`SystemUtilisationLowerThresholdPercentage`] - 0 ) /// - /// 10 = 50 - l * (e ^ (0.3 - 0) - 1) - /// l = (50 - 10) / (e ^ (0.3 - 0) - 1) ≈ 114 - pub static LowerExponentFactor: u32 = 114; + /// 10 GIGAWEI = 50 GIGAWEI - l * (e ^ (0.3 - 0) - 1) + /// l = (50 GIGAWEI - 10 GIGAWEI) / (e ^ (0.3 - 0) - 1) ≈ 114,331,836,540 + pub static LowerExponentFactor: Balance = 114_331_836_540; #[codec(index = 16)] #[allow(non_upper_case_globals)] diff --git a/operator/runtime/mainnet/src/lib.rs b/operator/runtime/mainnet/src/lib.rs index 7473be08..ceb8f373 100644 --- a/operator/runtime/mainnet/src/lib.rs +++ b/operator/runtime/mainnet/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 200 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 1100, + spec_version: 1200, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -1264,6 +1264,9 @@ impl_runtime_apis! { ) -> Vec { FileSystem::query_pending_bsp_confirm_storage_requests(bsp_id, file_keys) } + fn get_max_batch_confirm_storage_requests() -> BlockNumber { + FileSystem::get_max_batch_confirm_storage_requests() + } } impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { diff --git a/operator/runtime/stagenet/src/configs/runtime_params.rs b/operator/runtime/stagenet/src/configs/runtime_params.rs index 8b9a35ca..f05caff5 100644 --- a/operator/runtime/stagenet/src/configs/runtime_params.rs +++ b/operator/runtime/stagenet/src/configs/runtime_params.rs @@ -135,9 +135,9 @@ pub mod dynamic_params { /// /// [`MaxPrice`] = [`MostlyStablePrice`] + u * e ^ ( 1 - [`SystemUtilisationUpperThresholdPercentage`] ) /// - /// 500 = 50 + u * (e ^ (1 - 0.95) - 1) - /// u = (500 - 50) / (e ^ (1 - 0.95) - 1) ≈ 8777 - pub static UpperExponentFactor: u32 = 8777; + /// 500 GIGAWEI = 50 GIGAWEI + u * (e ^ (1 - 0.95) - 1) + /// u = (500 GIGAWEI - 50 GIGAWEI) / (e ^ (1 - 0.95) - 1) ≈ 8,776,874,921,880 + pub static UpperExponentFactor: Balance = 8_776_874_921_880; #[codec(index = 15)] #[allow(non_upper_case_globals)] @@ -146,9 +146,9 @@ pub mod dynamic_params { /// /// [`MinPrice`] = [`MostlyStablePrice`] - u * e ^ ( [`SystemUtilisationLowerThresholdPercentage`] - 0 ) /// - /// 10 = 50 - l * (e ^ (0.3 - 0) - 1) - /// l = (50 - 10) / (e ^ (0.3 - 0) - 1) ≈ 114 - pub static LowerExponentFactor: u32 = 114; + /// 10 GIGAWEI = 50 GIGAWEI - l * (e ^ (0.3 - 0) - 1) + /// l = (50 GIGAWEI - 10 GIGAWEI) / (e ^ (0.3 - 0) - 1) ≈ 114,331,836,540 + pub static LowerExponentFactor: Balance = 114_331_836_540; #[codec(index = 16)] #[allow(non_upper_case_globals)] diff --git a/operator/runtime/stagenet/src/lib.rs b/operator/runtime/stagenet/src/lib.rs index 072d5a1b..2bc4d60b 100644 --- a/operator/runtime/stagenet/src/lib.rs +++ b/operator/runtime/stagenet/src/lib.rs @@ -145,7 +145,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 200 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 1100, + spec_version: 1200, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -1267,6 +1267,9 @@ impl_runtime_apis! { ) -> Vec { FileSystem::query_pending_bsp_confirm_storage_requests(bsp_id, file_keys) } + fn get_max_batch_confirm_storage_requests() -> BlockNumber { + FileSystem::get_max_batch_confirm_storage_requests() + } } impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { diff --git a/operator/runtime/testnet/src/configs/runtime_params.rs b/operator/runtime/testnet/src/configs/runtime_params.rs index c9a77d8c..bc218f6c 100644 --- a/operator/runtime/testnet/src/configs/runtime_params.rs +++ b/operator/runtime/testnet/src/configs/runtime_params.rs @@ -133,9 +133,9 @@ pub mod dynamic_params { /// /// [`MaxPrice`] = [`MostlyStablePrice`] + u * e ^ ( 1 - [`SystemUtilisationUpperThresholdPercentage`] ) /// - /// 500 = 50 + u * (e ^ (1 - 0.95) - 1) - /// u = (500 - 50) / (e ^ (1 - 0.95) - 1) ≈ 8777 - pub static UpperExponentFactor: u32 = 8777; + /// 500 GIGAWEI = 50 GIGAWEI + u * (e ^ (1 - 0.95) - 1) + /// u = (500 GIGAWEI - 50 GIGAWEI) / (e ^ (1 - 0.95) - 1) ≈ 8,776,874,921,880 + pub static UpperExponentFactor: Balance = 8_776_874_921_880; #[codec(index = 15)] #[allow(non_upper_case_globals)] @@ -144,9 +144,9 @@ pub mod dynamic_params { /// /// [`MinPrice`] = [`MostlyStablePrice`] - u * e ^ ( [`SystemUtilisationLowerThresholdPercentage`] - 0 ) /// - /// 10 = 50 - l * (e ^ (0.3 - 0) - 1) - /// l = (50 - 10) / (e ^ (0.3 - 0) - 1) ≈ 114 - pub static LowerExponentFactor: u32 = 114; + /// 10 GIGAWEI = 50 GIGAWEI - l * (e ^ (0.3 - 0) - 1) + /// l = (50 GIGAWEI - 10 GIGAWEI) / (e ^ (0.3 - 0) - 1) ≈ 114,331,836,540 + pub static LowerExponentFactor: Balance = 114_331_836_540; #[codec(index = 16)] #[allow(non_upper_case_globals)] diff --git a/operator/runtime/testnet/src/lib.rs b/operator/runtime/testnet/src/lib.rs index ec43d783..88ef82eb 100644 --- a/operator/runtime/testnet/src/lib.rs +++ b/operator/runtime/testnet/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 200 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 1100, + spec_version: 1200, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -1264,6 +1264,9 @@ impl_runtime_apis! { ) -> Vec { FileSystem::query_pending_bsp_confirm_storage_requests(bsp_id, file_keys) } + fn get_max_batch_confirm_storage_requests() -> BlockNumber { + FileSystem::get_max_batch_confirm_storage_requests() + } } impl pallet_payment_streams_runtime_api::PaymentStreamsApi, Balance, AccountId> for Runtime { diff --git a/test/.papi/descriptors/package.json b/test/.papi/descriptors/package.json index 8ece9265..b7060c4b 100644 --- a/test/.papi/descriptors/package.json +++ b/test/.papi/descriptors/package.json @@ -1,5 +1,5 @@ { - "version": "0.1.0-autogenerated.14314240478086326730", + "version": "0.1.0-autogenerated.17981369281038341211", "name": "@polkadot-api/descriptors", "files": [ "dist" diff --git a/test/.papi/metadata/datahaven.scale b/test/.papi/metadata/datahaven.scale index fdcedc84..7f390905 100644 Binary files a/test/.papi/metadata/datahaven.scale and b/test/.papi/metadata/datahaven.scale differ diff --git a/test/cli/handlers/contracts/status.ts b/test/cli/handlers/contracts/status.ts index 76a86638..3c61e04b 100644 --- a/test/cli/handlers/contracts/status.ts +++ b/test/cli/handlers/contracts/status.ts @@ -24,8 +24,7 @@ export const showDeploymentPlanAndStatus = async (chain: string, environment?: s "Block Explorer": deploymentParams.blockExplorer, "Genesis Time": new Date(deploymentParams.genesisTime * 1000).toISOString(), "AVS Owner": `${config.avs.avsOwner.slice(0, 10)}...${config.avs.avsOwner.slice(-8)}`, - "Rewards Initiator": `${config.avs.rewardsInitiator.slice(0, 10)}...${config.avs.rewardsInitiator.slice(-8)}`, - "Veto Committee Member": `${config.avs.vetoCommitteeMember.slice(0, 10)}...${config.avs.vetoCommitteeMember.slice(-8)}` + "Rewards Initiator": `${config.avs.rewardsInitiator.slice(0, 10)}...${config.avs.rewardsInitiator.slice(-8)}` }; if (environment) { diff --git a/test/cli/handlers/launch/storagehub.ts b/test/cli/handlers/launch/storagehub.ts index fc162dde..51eca67e 100644 --- a/test/cli/handlers/launch/storagehub.ts +++ b/test/cli/handlers/launch/storagehub.ts @@ -1,6 +1,7 @@ import { logger, printHeader } from "utils"; import type { DataHavenOptions } from "../../../launcher/datahaven"; import { + launchBackend, launchBspNode, launchFishermanNode, launchIndexerNode, @@ -99,5 +100,9 @@ async function launchStorageHubDocker( logger.info("📝 Registering providers..."); await registerProviders({ launchedNetwork }); + // Launch Backend MSP + logger.info("📦 Launching StorageHub Backend..."); + await launchBackend(datahavenOptions, launchedNetwork); + logger.success("All StorageHub components launched and registered"); } diff --git a/test/launcher/storagehub-docker.ts b/test/launcher/storagehub-docker.ts index 3300768e..a9f64c0e 100644 --- a/test/launcher/storagehub-docker.ts +++ b/test/launcher/storagehub-docker.ts @@ -1,11 +1,15 @@ import { $ } from "bun"; import { getPublicPort, killExistingContainers, logger, waitForContainerToStart } from "utils"; -import { DEFAULT_SUBSTRATE_WS_PORT } from "utils/constants"; +import { DEFAULT_SUBSTRATE_WS_PORT, SUBSTRATE_FUNDED_ACCOUNTS } from "utils/constants"; import { waitFor } from "utils/waits"; import type { DataHavenOptions } from "./datahaven"; import { isNetworkReady } from "./datahaven"; import type { LaunchedNetwork } from "./types/launchedNetwork"; +/** + * Important ! This is for local deployment only. We are using mDNS discovery when startinn node with the `--discover-local` flag + */ + /** * PostgreSQL configuration for StorageHub Indexer and Fisherman */ @@ -109,48 +113,25 @@ export const getPostgresUrl = (networkId: string): string => { * Injects a BCSV ECDSA key into a StorageHub provider node's keystore. * * @param containerName - Name of the Docker container - * @param seed - The seed phrase for key generation - * @param derivation - Key derivation path (e.g., "//Charlie") + * @param secretKey - The secret key (or private key) we want to add to the node */ export const injectStorageHubKey = async ( containerName: string, - seed: string, - derivation: string + secretKey: string ): Promise => { - logger.info(`🔑 Injecting key ${derivation} into ${containerName}...`); - - const suri = `${seed}${derivation}`; + logger.info(`🔑 Injecting key into ${containerName}...`); // Use Bun's $ directly with docker exec (no sh -c wrapper needed) // This properly handles the spaces in the seed phrase try { - await $`docker exec ${containerName} datahaven-node key insert --chain dev --key-type bcsv --scheme ecdsa --suri ${suri}`.nothrow(); - logger.success(`Key ${derivation} injected successfully`); + await $`docker exec ${containerName} datahaven-node key insert --base-path /data --key-type bcsv --scheme ecdsa --suri ${secretKey}`; + logger.success("Key injected successfully"); } catch (error) { - logger.error(`Failed to inject key ${derivation}: ${error}`); + logger.error(`Failed to inject key : ${error}`); throw error; } }; -/** - * Gets the bootnode address from a running validator node. - * - * For local development with Docker, nodes on the same network can discover each other - * via mDNS (--discover-local flag), so explicit bootnodes are optional. - * - * To use explicit bootnodes, we'd need to extract the peer ID from the validator node, - * which requires querying the RPC endpoint. For simplicity in local dev, we skip this. - * - * @param containerName - Name of the validator container (e.g., datahaven-alice-cli-launch) - * @returns Multiaddress string for bootnode, or empty string to skip bootnodes - */ -export const getBootnodeAddress = async (containerName: string): Promise => { - // For local Docker development, nodes discover each other via mDNS - // No explicit bootnode needed with --discover-local flag - logger.debug(`Skipping explicit bootnode for ${containerName} - using mDNS discovery`); - return ""; -}; - /** * Launches a StorageHub MSP (Main Storage Provider) node. * @@ -166,10 +147,6 @@ export const launchMspNode = async ( const containerName = `storagehub-msp-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9945; // External port for MSP node - const aliceContainer = `datahaven-alice-${options.networkId}`; - - // Get bootnode address (empty for local dev with mDNS discovery) - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const command: string[] = [ "docker", @@ -182,10 +159,10 @@ export const launchMspNode = async ( "-p", `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, - "--chain", - "dev", "--name", "msp-charlie", + "--chain", + "local", "--rpc-port", `${DEFAULT_SUBSTRATE_WS_PORT}`, "--rpc-external", @@ -208,19 +185,13 @@ export const launchMspNode = async ( "1073741824" // 1 GiB ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); await waitForContainerToStart(containerName); // Inject key - const seed = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; - await injectStorageHubKey(containerName, seed, "//Charlie"); + await injectStorageHubKey(containerName, SUBSTRATE_FUNDED_ACCOUNTS.CHARLETH.privateKey); // Restart container to load key logger.info("🔄 Restarting MSP node to load key..."); @@ -263,10 +234,6 @@ export const launchBspNode = async ( const containerName = `storagehub-bsp-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9946; // External port for BSP node - const aliceContainer = `datahaven-alice-${options.networkId}`; - - // Get bootnode address (empty for local dev with mDNS discovery) - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const command: string[] = [ "docker", @@ -279,10 +246,10 @@ export const launchBspNode = async ( "-p", `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, - "--chain", - "dev", "--name", - "bsp-dave", + "bsp-dorothy", + "--chain", + "local", "--rpc-port", `${DEFAULT_SUBSTRATE_WS_PORT}`, "--rpc-external", @@ -303,19 +270,13 @@ export const launchBspNode = async ( "1073741824" // 1 GiB ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); await waitForContainerToStart(containerName); - // Inject key (using Dave instead of Eve for BSP) - const seed = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; - await injectStorageHubKey(containerName, seed, "//Dave"); + // Inject key + await injectStorageHubKey(containerName, SUBSTRATE_FUNDED_ACCOUNTS.DOROTHY.privateKey); // Restart container to load key logger.info("🔄 Restarting BSP node to load key..."); @@ -358,10 +319,7 @@ export const launchIndexerNode = async ( const containerName = `storagehub-indexer-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9947; // External port for Indexer node - const aliceContainer = `datahaven-alice-${options.networkId}`; - // Get bootnode address (empty for local dev with mDNS discovery) and PostgreSQL URL - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const postgresUrl = getPostgresUrl(options.networkId); const command: string[] = [ @@ -375,10 +333,10 @@ export const launchIndexerNode = async ( "-p", `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, - "--chain", - "dev", "--name", "indexer", + "--chain", + "local", "--rpc-port", `${DEFAULT_SUBSTRATE_WS_PORT}`, "--rpc-external", @@ -395,11 +353,6 @@ export const launchIndexerNode = async ( postgresUrl ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); @@ -441,10 +394,7 @@ export const launchFishermanNode = async ( const containerName = `storagehub-fisherman-${options.networkId}`; const dockerNetworkName = `datahaven-${options.networkId}`; const wsPort = 9948; // External port for Fisherman node - const aliceContainer = `datahaven-alice-${options.networkId}`; - // Get bootnode address (empty for local dev with mDNS discovery) and PostgreSQL URL - const bootnodeAddr = await getBootnodeAddress(aliceContainer); const postgresUrl = getPostgresUrl(options.networkId); const command: string[] = [ @@ -459,7 +409,7 @@ export const launchFishermanNode = async ( `${wsPort}:${DEFAULT_SUBSTRATE_WS_PORT}`, options.datahavenImageTag, "--chain", - "dev", + "local", "--name", "fisherman", "--rpc-port", @@ -476,11 +426,6 @@ export const launchFishermanNode = async ( postgresUrl ]; - // Only add bootnodes if we have a valid address - if (bootnodeAddr) { - command.push("--bootnodes", bootnodeAddr); - } - logger.debug(`Executing: ${command.join(" ")}`); await $`sh -c "${command.join(" ")}"`.nothrow(); @@ -507,6 +452,63 @@ export const launchFishermanNode = async ( logger.success(`Fisherman node started on port ${wsPort}`); }; +/** + * Launches a StorageHub Backend container. + * + * @param options - Configuration options for launching the network + * @param launchedNetwork - The launched network instance to track the node + */ +export const launchBackend = async ( + options: DataHavenOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + logger.info("🚀 Launching StorageHub Backend..."); + + const backendImage = "moonsonglabs/storage-hub-msp-backend:latest"; + const containerName = `storagehub-backend-${options.networkId}`; + const dockerNetworkName = `datahaven-${options.networkId}`; + const containerNameMSP = `storagehub-msp-${options.networkId}`; + const postgresUrl = getPostgresUrl(options.networkId); + const apiPort = 8080; + + const command: string[] = [ + "docker", + "run", + "-d", + "--name", + containerName, + "--network", + dockerNetworkName, + "-p", + `${apiPort}:8080`, + "-e", + "RUST_LOG=info", + backendImage, + "--chain", + "local", + "--log-format", + "text", + "--database-url", + postgresUrl, + "--rpc-url", + `ws://${containerNameMSP}:${DEFAULT_SUBSTRATE_WS_PORT}`, + "--msp-callback-url", + `http://${containerName}:8080`, + "--msp-trusted-file-transfer-server-url", + `http://${containerNameMSP}:7070` + ]; + + logger.debug(`Executing: ${command.join(" ")}`); + await $`sh -c "${command.join(" ")}"`.nothrow(); + + await waitForContainerToStart(containerName); + + // Register in launched network + launchedNetwork.addContainer(containerName, { http: apiPort }, { http: apiPort }); + + logger.success(`StorageHub Backend container started on port ${apiPort}`); +}; + /** * Stops and removes all StorageHub containers. * diff --git a/test/moonwall/suites/dev/stagenet/subscription/test-subscription-logs.ts b/test/moonwall/suites/dev/stagenet/subscription/test-subscription-logs.ts new file mode 100644 index 00000000..1f33aaa1 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/subscription/test-subscription-logs.ts @@ -0,0 +1,43 @@ +import { describeSuite, expect } from "@moonwall/cli"; + +describeSuite({ + id: "D023501", + title: "Subscription - Logs", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should send a notification on new transaction", + test: async function () { + const logs: any[] = []; + const sub = await context.web3().eth.subscribe("logs"); + + await new Promise(async (resolve, reject) => { + sub.once("data", async (event) => { + logs.push(event); + resolve("success"); + }); + + sub.once("error", (error) => { + console.error(error); + reject(error); + }); + + await context.deployContract!("EventEmitter"); + }); + + const block = await context.viem().getBlock(); + + expect(logs[0]).to.include({ + blockHash: block.hash, + blockNumber: block.number, + data: "0x", + logIndex: 0n, + removed: false, + transactionHash: block.transactions[0], + transactionIndex: 0n, + }); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/subscription/test-subscription-logs2.ts b/test/moonwall/suites/dev/stagenet/subscription/test-subscription-logs2.ts new file mode 100644 index 00000000..2b58d33b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/subscription/test-subscription-logs2.ts @@ -0,0 +1,195 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { ALITH_CONTRACT_ADDRESSES } from "@moonwall/util"; +import type { Log } from "web3"; + +describeSuite({ + id: "D023502", + title: "Subscription - Logs", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let deployedContract: `0x${string}`; + let deployHash: `0x${string}`; + + let subSingleAddPromise: Promise; + let subMultiAddPromise: Promise; + let subTopicPromise: Promise; + let subTopicWildcardPromise: Promise; + let subTopicListPromise: Promise; + let subTopicCondPromise: Promise; + let subTopicMultiCondPromise: Promise; + let subTopicWildAndCondPromise: Promise; + + beforeAll(async () => { + const openSub = async (filter?: object) => await context.web3().eth.subscribe("logs", filter); + + const onData = (logSub: any) => { + return new Promise((resolve) => { + logSub.once("data", resolve); + }); + }; + + const [ + singleSub, + multiSub, + subTopic, + subTopicWildcard, + subTopicList, + subTopicCond, + subTopicMultiCond, + subTopicWildAndCond, + ] = await Promise.all([ + openSub({ + address: ALITH_CONTRACT_ADDRESSES[0], + }), + openSub({ + address: [ + ALITH_CONTRACT_ADDRESSES[3], + ALITH_CONTRACT_ADDRESSES[2], + ALITH_CONTRACT_ADDRESSES[1], + ALITH_CONTRACT_ADDRESSES[0], + ], + }), + openSub({ + topics: ["0x0040d54d5e5b097202376b55bcbaaedd2ee468ce4496f1d30030c4e5308bf94d"], + }), + openSub({ + topics: [null, "0x000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac"], + }), + openSub({ + topics: [ + ["0x0040d54d5e5b097202376b55bcbaaedd2ee468ce4496f1d30030c4e5308bf94d"], + ["0x000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac"], + ], + }), + + openSub({ + topics: [ + "0x0040d54d5e5b097202376b55bcbaaedd2ee468ce4496f1d30030c4e5308bf94d", + ["0x000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac"], + ], + }), + + openSub({ + topics: [ + "0x0040d54d5e5b097202376b55bcbaaedd2ee468ce4496f1d30030c4e5308bf94d", + [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + ], + ], + }), + openSub({ + topics: [ + null, + [ + "0x000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], + null, + ], + }), + ]); + + subSingleAddPromise = onData(singleSub); + subMultiAddPromise = onData(multiSub); + subTopicPromise = onData(subTopic); + subTopicWildcardPromise = onData(subTopicWildcard); + subTopicListPromise = onData(subTopicList); + subTopicCondPromise = onData(subTopicCond); + subTopicMultiCondPromise = onData(subTopicMultiCond); + subTopicWildAndCondPromise = onData(subTopicWildAndCond); + + const { contractAddress, hash } = await context.deployContract!("EventEmitter"); + deployedContract = contractAddress; + deployHash = hash; + }); + + it({ + id: "T01", + title: "should be able to filter by address", + test: async function () { + const eventLog = await subSingleAddPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T02", + title: "should be able to filter by multiple addresses", + test: async function () { + const eventLog = await subMultiAddPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T03", + title: "should be able to filter by topic", + test: async function () { + const eventLog = await subTopicPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T04", + title: "should be able to filter by topic wildcards", + test: async function () { + const eventLog = await subTopicWildcardPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T05", + title: "should be able to filter by topic list", + test: async function () { + const eventLog = await subTopicListPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T06", + title: "should be able to filter by topic conditional parameters", + test: async function () { + const eventLog = await subTopicCondPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T07", + title: "should support multiple topic conditional parameters", + test: async function () { + const eventLog = await subTopicMultiCondPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + + it({ + id: "T08", + title: "should combine topic wildcards and conditional parameters", + test: async function () { + const eventLog = await subTopicWildAndCondPromise; + expect(eventLog.blockNumber).toBe(1n); + expect(eventLog.address).toBe(deployedContract.toLowerCase()); + expect(eventLog.transactionHash).toBe(deployHash); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/subscription/test-subscription-pending.ts b/test/moonwall/suites/dev/stagenet/subscription/test-subscription-pending.ts new file mode 100644 index 00000000..d053667b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/subscription/test-subscription-pending.ts @@ -0,0 +1,30 @@ +import { describeSuite, expect } from "@moonwall/cli"; +import { BALTATHAR_ADDRESS, GLMR, createRawTransfer, sendRawTransaction } from "@moonwall/util"; +import { setTimeout } from "node:timers/promises"; + +describeSuite({ + id: "D023504", + title: "Subscription - Pending transactions", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should return a valid subscriptionId", + test: async function () { + let response: any; + const sub = await context.web3().eth.subscribe("newPendingTransactions"); + + sub.once("data", (data) => { + response = data; + }); + + const rawTx = await createRawTransfer(context, BALTATHAR_ADDRESS, GLMR); + const hash = await sendRawTransaction(context, rawTx); + await setTimeout(200); + + expect(response).not.toBeUndefined(); + expect(response).toBe(hash); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/subscription/test-subscription.ts b/test/moonwall/suites/dev/stagenet/subscription/test-subscription.ts new file mode 100644 index 00000000..ae853cfb --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/subscription/test-subscription.ts @@ -0,0 +1,61 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { ALITH_ADDRESS, BALTATHAR_ADDRESS, createRawTransfer } from "@moonwall/util"; +import { type PublicClient, createPublicClient, webSocket } from "viem"; + +describeSuite({ + id: "D023505", + title: "Subscription - Block headers", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let client: PublicClient; + + beforeAll(async () => { + const transport = webSocket(context.viem().transport.url.replace("http", "ws")); + client = createPublicClient({ + transport, + }); + }); + + it({ + id: "T01", + title: "should return a valid subscriptionId", + test: async function () { + const result = (await client.transport.request({ + method: "eth_subscribe", + params: ["newHeads"], + })) as any; + + expect(result.length).toBe(34); + }, + }); + + it({ + id: "T02", + title: "should send notification on new block", + test: async function () { + const blocks: any[] = []; + const unwatch = client.watchBlocks({ + onBlock: (block) => blocks.push(block), + }); + + await context.createBlock(createRawTransfer(context, BALTATHAR_ADDRESS, 0)); + unwatch(); + + const block = await context.viem().getBlock(); + + expect(blocks[0]).to.include({ + author: ALITH_ADDRESS.toLowerCase(), + difficulty: 0n, + extraData: "0x", + logsBloom: `0x${"0".repeat(512)}`, + miner: ALITH_ADDRESS.toLowerCase(), + sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + }); + expect(blocks[0].nonce).to.be.eq("0x0000000000000000"); + // Verify subscription roots match the block fetched via RPC + expect(blocks[0].receiptsRoot).toBe(block.receiptsRoot); + expect(blocks[0].transactionsRoot).toBe(block.transactionsRoot); + }, + }); + }, +}); diff --git a/test/resources/datahaven-integration-test-flow.md b/test/resources/datahaven-integration-test-flow.md index 82b2a7f6..4dcc14ce 100644 --- a/test/resources/datahaven-integration-test-flow.md +++ b/test/resources/datahaven-integration-test-flow.md @@ -207,7 +207,6 @@ Another testing scenario is testing the operational aspects of the validator set 3. **Slashing Mechanisms** - Test slashing for various offenses - - Test veto committee mechanisms 4. **Operator Set Modifications** - Modify operator sets from Ethereum diff --git a/test/scripts/fund-providers.ts b/test/scripts/fund-providers.ts index 1487fd2e..098226ea 100644 --- a/test/scripts/fund-providers.ts +++ b/test/scripts/fund-providers.ts @@ -7,32 +7,6 @@ export interface FundProvidersOptions { launchedNetwork: LaunchedNetwork; } -/** - * Provider account information for MSP and BSP nodes. - * - * DataHaven uses AccountId20 (Ethereum-style 20-byte addresses). - * In dev chains, CHARLETH and DOROTHY are pre-funded development accounts - * that correspond to //Charlie and //Dave derivations. - * - * For StorageHub providers, we use: - * - CHARLETH (//Charlie equivalent) for MSP - * - DOROTHY (//Dave equivalent) for BSP (as //Eve might not have pre-funded AccountId20) - */ -const PROVIDER_ACCOUNTS = { - // MSP account (Charleth = Charlie in AccountId20 format) - msp: { - name: "Charleth", - address: SUBSTRATE_FUNDED_ACCOUNTS.CHARLETH.publicKey, // 20-byte address - derivation: "//Charlie" - }, - // BSP account (Dorothy = Dave in AccountId20 format, using instead of Eve) - bsp: { - name: "Dorothy", - address: SUBSTRATE_FUNDED_ACCOUNTS.DOROTHY.publicKey, // 20-byte address - derivation: "//Dave" // Using Dave instead of Eve for BSP - } -} as const; - /** * Minimum balance required for provider operations. * This includes: @@ -61,30 +35,34 @@ export async function fundProviders(options: FundProvidersOptions): Promise