datahaven/contracts/script/fixtures/DataHavenServiceManagerBadLayout.sol

33 lines
1.2 KiB
Solidity
Raw Permalink Normal View History

test: ✨ Add storage layout checks for upgradeable contracts (#420) ## Summary Implements storage layout testing for the upgradeable `DataHavenServiceManager` contract to prevent state corruption during proxy upgrades. ## Changes ### New Files - **`contracts/storage-snapshots/DataHavenServiceManager.storage.json`** - Baseline storage layout snapshot - **`contracts/storage-snapshots/README.md`** - Documentation for updating snapshots and known limitations - **`contracts/scripts/check-storage-layout.sh`** - CI script that compares current layout against snapshot - **`contracts/test/storage/StorageLayout.t.sol`** - Upgrade simulation tests verifying state preservation - **`.github/workflows/task-storage-layout.yml`** - CI workflow for storage layout checks ### Modified Files - **`.github/workflows/CI.yml`** - Added `storage-layout` job to run in parallel with other checks ## How It Works **Two-pronged approach:** 1. **Snapshot Diff** - Compares current storage layout against committed snapshot using `forge inspect`. Catches unintended variable reordering, type changes, or gap modifications. 2. **Upgrade Simulation** - Foundry tests that populate state, perform a proxy upgrade, and verify all values survive: - `test_upgradePreservesState` - Verifies core state variables - `test_upgradePreservesValidatorMappings` - Verifies `validatorEthAddressToSolochainAddress` mapping - `test_upgradePreservesMultipleValidators` - Verifies `validatorsAllowlist` with multiple entries - `test_functionalityAfterUpgrade` - Verifies contract remains functional post-upgrade ## Normalization The snapshot comparison normalizes JSON to avoid false positives: - Removes `astId` (changes with compiler runs) - Removes `contract` (contains full file path) - Removes `.types` section (contains unstable AST IDs embedded in type keys) - Sorts by slot number ## Usage ```bash # Check storage layout against snapshot ./scripts/check-storage-layout.sh # Run upgrade simulation tests forge test --match-contract StorageLayoutTest -vvv # Update snapshot (when intentionally changing storage) forge inspect DataHavenServiceManager storage --json > storage-snapshots/DataHavenServiceManager.storage.json ``` ## Test Plan - ./scripts/check-storage-layout.sh passes - forge test --match-contract StorageLayoutTest -vvv passes (4 tests) - CI workflow runs successfully
2026-02-05 11:08:35 +00:00
// 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;
fix: 🩹 map validator address to operator address for rewards & slashes (#441) ## Summary Slashing and rewards submissions were submitted through the bridge with their **solochain address** , while EigenLayer expects the **ethereum operator address**, the addresses were not being translated, so the protocol was broken. This PR adds a **reverse mapping** (Solochain address → Eth address) and uses it in both the slashing and rewards paths so that: - `slashValidatorsOperator` accepts requests where `operator` is a Solochain address and translates it to the Eth operator before calling EigenLayer. - `submitRewards` translates each `operatorRewards[].operator` from Solochain to Eth before calling the RewardsCoordinator. - Unknown or unmapped solochain addresses cause a revert (`UnknownSolochainAddress`) instead of silently failing. ## What's changed ### DataHavenServiceManager - **Reverse mapping**: `mapping(address => address) public validatorSolochainAddressToEthAddress` (Solochain → Eth), with `__GAP` reduced by one slot for upgradeable layout. - **Helper**: `_ethOperatorFromSolochain(address)` – returns Eth operator for a Solochain address, reverts with `UnknownSolochainAddress()` if unmapped. - **Registration / lifecycle**: - `registerOperator`: populates both forward and reverse mappings; enforces uniqueness (one Solochain per operator) and clears old reverse entry when an operator re-registers with a new Solochain. - `deregisterOperator`: clears both forward and reverse entries. - `updateSolochainAddressForValidator`: updates both mappings, enforces uniqueness and clears the previous Solochain's reverse entry. - **Slashing**: `slashValidatorsOperator` uses `_ethOperatorFromSolochain(slashings[i].operator)` so requests keyed by Solochain address are translated before calling EigenLayer. - **Rewards**: `submitRewards` builds a translated copy of the submission with each `operatorRewards[].operator` set via `_ethOperatorFromSolochain(...)`; unmapped addresses revert. ### IDataHavenServiceManager - New getter: `validatorSolochainAddressToEthAddress(address solochain) external view returns (address)`. - New errors: `UnknownSolochainAddress()`, `SolochainAddressAlreadyAssigned()`. ### Storage and fixtures - Storage snapshot updated for the new state variable. - `DataHavenServiceManagerBadLayout.sol` updated (reverse mapping + gap) for layout negative tests. - Storage layout test extended to assert the reverse mapping is preserved across proxy upgrade. ### Tests - **Slashing.t.sol**: Slashing with Solochain address (translation and emit of Eth operator); negative test for unmapped Solochain reverting with `UnknownSolochainAddress()`. - **RewardsSubmitter.t.sol**: Rewards submission with Solochain addresses (translation to Eth in RewardsCoordinator calldata); negative test for unmapped Solochain. - **StorageLayout.t.sol**: Reverse mapping preserved after upgrade. - **OperatorAddressMappings.t.sol** (new): Uniqueness (Solochain already assigned to another operator), update/deregister clearing reverse mapping, and getter behaviour. ## Testing - **Unit tests**: `forge test` from `contracts/` (all existing and new tests pass). - **Storage**: - `./scripts/check-storage-layout.sh` - `./scripts/check-storage-layout-negative.sh` - **Coverage**: Slashing path (Solochain → Eth translation + revert), rewards path (translation + revert), registration/update/deregister (reverse mapping and uniqueness), and storage layout upgrade preservation. --------- Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
2026-02-18 19:38:13 +00:00
mapping(address => address) public validatorSolochainAddressToEthAddress;
test: ✨ Add storage layout checks for upgradeable contracts (#420) ## Summary Implements storage layout testing for the upgradeable `DataHavenServiceManager` contract to prevent state corruption during proxy upgrades. ## Changes ### New Files - **`contracts/storage-snapshots/DataHavenServiceManager.storage.json`** - Baseline storage layout snapshot - **`contracts/storage-snapshots/README.md`** - Documentation for updating snapshots and known limitations - **`contracts/scripts/check-storage-layout.sh`** - CI script that compares current layout against snapshot - **`contracts/test/storage/StorageLayout.t.sol`** - Upgrade simulation tests verifying state preservation - **`.github/workflows/task-storage-layout.yml`** - CI workflow for storage layout checks ### Modified Files - **`.github/workflows/CI.yml`** - Added `storage-layout` job to run in parallel with other checks ## How It Works **Two-pronged approach:** 1. **Snapshot Diff** - Compares current storage layout against committed snapshot using `forge inspect`. Catches unintended variable reordering, type changes, or gap modifications. 2. **Upgrade Simulation** - Foundry tests that populate state, perform a proxy upgrade, and verify all values survive: - `test_upgradePreservesState` - Verifies core state variables - `test_upgradePreservesValidatorMappings` - Verifies `validatorEthAddressToSolochainAddress` mapping - `test_upgradePreservesMultipleValidators` - Verifies `validatorsAllowlist` with multiple entries - `test_functionalityAfterUpgrade` - Verifies contract remains functional post-upgrade ## Normalization The snapshot comparison normalizes JSON to avoid false positives: - Removes `astId` (changes with compiler runs) - Removes `contract` (contains full file path) - Removes `.types` section (contains unstable AST IDs embedded in type keys) - Sorts by slot number ## Usage ```bash # Check storage layout against snapshot ./scripts/check-storage-layout.sh # Run upgrade simulation tests forge test --match-contract StorageLayoutTest -vvv # Update snapshot (when intentionally changing storage) forge inspect DataHavenServiceManager storage --json > storage-snapshots/DataHavenServiceManager.storage.json ``` ## Test Plan - ./scripts/check-storage-layout.sh passes - forge test --match-contract StorageLayoutTest -vvv passes (4 tests) - CI workflow runs successfully
2026-02-05 11:08:35 +00:00
// Keep the original gap size to mirror shape, despite the shift
fix: 🩹 map validator address to operator address for rewards & slashes (#441) ## Summary Slashing and rewards submissions were submitted through the bridge with their **solochain address** , while EigenLayer expects the **ethereum operator address**, the addresses were not being translated, so the protocol was broken. This PR adds a **reverse mapping** (Solochain address → Eth address) and uses it in both the slashing and rewards paths so that: - `slashValidatorsOperator` accepts requests where `operator` is a Solochain address and translates it to the Eth operator before calling EigenLayer. - `submitRewards` translates each `operatorRewards[].operator` from Solochain to Eth before calling the RewardsCoordinator. - Unknown or unmapped solochain addresses cause a revert (`UnknownSolochainAddress`) instead of silently failing. ## What's changed ### DataHavenServiceManager - **Reverse mapping**: `mapping(address => address) public validatorSolochainAddressToEthAddress` (Solochain → Eth), with `__GAP` reduced by one slot for upgradeable layout. - **Helper**: `_ethOperatorFromSolochain(address)` – returns Eth operator for a Solochain address, reverts with `UnknownSolochainAddress()` if unmapped. - **Registration / lifecycle**: - `registerOperator`: populates both forward and reverse mappings; enforces uniqueness (one Solochain per operator) and clears old reverse entry when an operator re-registers with a new Solochain. - `deregisterOperator`: clears both forward and reverse entries. - `updateSolochainAddressForValidator`: updates both mappings, enforces uniqueness and clears the previous Solochain's reverse entry. - **Slashing**: `slashValidatorsOperator` uses `_ethOperatorFromSolochain(slashings[i].operator)` so requests keyed by Solochain address are translated before calling EigenLayer. - **Rewards**: `submitRewards` builds a translated copy of the submission with each `operatorRewards[].operator` set via `_ethOperatorFromSolochain(...)`; unmapped addresses revert. ### IDataHavenServiceManager - New getter: `validatorSolochainAddressToEthAddress(address solochain) external view returns (address)`. - New errors: `UnknownSolochainAddress()`, `SolochainAddressAlreadyAssigned()`. ### Storage and fixtures - Storage snapshot updated for the new state variable. - `DataHavenServiceManagerBadLayout.sol` updated (reverse mapping + gap) for layout negative tests. - Storage layout test extended to assert the reverse mapping is preserved across proxy upgrade. ### Tests - **Slashing.t.sol**: Slashing with Solochain address (translation and emit of Eth operator); negative test for unmapped Solochain reverting with `UnknownSolochainAddress()`. - **RewardsSubmitter.t.sol**: Rewards submission with Solochain addresses (translation to Eth in RewardsCoordinator calldata); negative test for unmapped Solochain. - **StorageLayout.t.sol**: Reverse mapping preserved after upgrade. - **OperatorAddressMappings.t.sol** (new): Uniqueness (Solochain already assigned to another operator), update/deregister clearing reverse mapping, and getter behaviour. ## Testing - **Unit tests**: `forge test` from `contracts/` (all existing and new tests pass). - **Storage**: - `./scripts/check-storage-layout.sh` - `./scripts/check-storage-layout-negative.sh` - **Coverage**: Slashing path (Solochain → Eth translation + revert), rewards path (translation + revert), registration/update/deregister (reverse mapping and uniqueness), and storage layout upgrade preservation. --------- Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
2026-02-18 19:38:13 +00:00
uint256[45] private __GAP;
test: ✨ Add storage layout checks for upgradeable contracts (#420) ## Summary Implements storage layout testing for the upgradeable `DataHavenServiceManager` contract to prevent state corruption during proxy upgrades. ## Changes ### New Files - **`contracts/storage-snapshots/DataHavenServiceManager.storage.json`** - Baseline storage layout snapshot - **`contracts/storage-snapshots/README.md`** - Documentation for updating snapshots and known limitations - **`contracts/scripts/check-storage-layout.sh`** - CI script that compares current layout against snapshot - **`contracts/test/storage/StorageLayout.t.sol`** - Upgrade simulation tests verifying state preservation - **`.github/workflows/task-storage-layout.yml`** - CI workflow for storage layout checks ### Modified Files - **`.github/workflows/CI.yml`** - Added `storage-layout` job to run in parallel with other checks ## How It Works **Two-pronged approach:** 1. **Snapshot Diff** - Compares current storage layout against committed snapshot using `forge inspect`. Catches unintended variable reordering, type changes, or gap modifications. 2. **Upgrade Simulation** - Foundry tests that populate state, perform a proxy upgrade, and verify all values survive: - `test_upgradePreservesState` - Verifies core state variables - `test_upgradePreservesValidatorMappings` - Verifies `validatorEthAddressToSolochainAddress` mapping - `test_upgradePreservesMultipleValidators` - Verifies `validatorsAllowlist` with multiple entries - `test_functionalityAfterUpgrade` - Verifies contract remains functional post-upgrade ## Normalization The snapshot comparison normalizes JSON to avoid false positives: - Removes `astId` (changes with compiler runs) - Removes `contract` (contains full file path) - Removes `.types` section (contains unstable AST IDs embedded in type keys) - Sorts by slot number ## Usage ```bash # Check storage layout against snapshot ./scripts/check-storage-layout.sh # Run upgrade simulation tests forge test --match-contract StorageLayoutTest -vvv # Update snapshot (when intentionally changing storage) forge inspect DataHavenServiceManager storage --json > storage-snapshots/DataHavenServiceManager.storage.json ``` ## Test Plan - ./scripts/check-storage-layout.sh passes - forge test --match-contract StorageLayoutTest -vvv passes (4 tests) - CI workflow runs successfully
2026-02-05 11:08:35 +00:00
// Keep a compatible constructor signature for upgrade tests.
constructor(
address,
address
) {
_disableInitializers();
}
}