datahaven/contracts/test/ValidatorSetSubmitter.t.sol
undercover-cactus 6d323385d8
refactor: rename rewardsInitiator to snowbridgeInitiator (#476)
## Summary
Renames the rewardsInitiator state variable, modifier, internal check
function, setRewardsInitiator function, and RewardsInitiatorSet event in
DataHavenServiceManager to their snowbridgeInitiator-prefixed
equivalents, to better reflect the role of this address.

## Motivation
The previous rewardsInitiator naming was misleading — the address
filling this role is specifically the Snowbridge relayer/gateway.
Renaming it end-to-end clarifies intent and aligns the codebase with the
actual architecture.

## Changes
* DataHavenServiceManager.sol: renamed state variable, modifier,
internal check, setRewardsInitiator → -> setSnowbridgeInitiator,
RewardsInitiatorSet -> SnowbridgeInitiatorSet
* IDataHavenServiceManager.sol: updated event, function signature, and
NatSpec
* Deploy scripts & configs: updated field names across all environments
(anvil, testnet, stagenet, mainnet)
2026-03-24 12:41:36 +01:00

288 lines
11 KiB
Solidity

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
/* solhint-disable func-name-mixedcase */
import {SnowbridgeAndAVSDeployer} from "./utils/SnowbridgeAndAVSDeployer.sol";
import {
IDataHavenServiceManagerErrors,
IDataHavenServiceManagerEvents
} from "../src/interfaces/IDataHavenServiceManager.sol";
import {DataHavenServiceManager} from "../src/DataHavenServiceManager.sol";
import {
TransparentUpgradeableProxy
} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {
IRewardsCoordinatorTypes
} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
contract ValidatorSetSubmitterTest is SnowbridgeAndAVSDeployer {
address public submitterA = address(uint160(uint256(keccak256("submitterA"))));
address public submitterB = address(uint160(uint256(keccak256("submitterB"))));
address public nonOwner = address(uint160(uint256(keccak256("nonOwner"))));
function setUp() public {
_deployMockAllContracts();
}
function beforeTestSetup(
bytes4 testSelector
) public pure returns (bytes[] memory beforeTestCalldata) {
if (
testSelector == this.test_sendNewValidatorSetForEra_success.selector
|| testSelector
== this.test_buildNewValidatorSetMessageForEra_encodesTargetEra.selector
|| testSelector == this.test_fuzz_sendNewValidatorSetForEra.selector
|| testSelector
== this.test_buildNewValidatorSetMessageForEra_exactEncoding.selector
) {
beforeTestCalldata = new bytes[](1);
beforeTestCalldata[0] =
abi.encodeWithSelector(this.setupValidatorsAsOperatorsWithAllocations.selector);
}
}
// ============ setValidatorSetSubmitter ============
function test_setValidatorSetSubmitter() public {
// After initialization, validatorSetSubmitter is already set to avsOwner
assertEq(
serviceManager.validatorSetSubmitter(),
avsOwner,
"validatorSetSubmitter should be set to avsOwner after init"
);
cheats.expectEmit();
emit IDataHavenServiceManagerEvents.ValidatorSetSubmitterUpdated(avsOwner, submitterA);
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
assertEq(
serviceManager.validatorSetSubmitter(),
submitterA,
"validatorSetSubmitter should be set"
);
}
function test_setValidatorSetSubmitter_revertsIfNotOwner() public {
cheats.prank(nonOwner);
cheats.expectRevert();
serviceManager.setValidatorSetSubmitter(submitterA);
}
function test_setValidatorSetSubmitter_revertsOnZeroAddress() public {
cheats.prank(avsOwner);
cheats.expectRevert(
abi.encodeWithSelector(IDataHavenServiceManagerErrors.ZeroAddress.selector)
);
serviceManager.setValidatorSetSubmitter(address(0));
}
function test_setValidatorSetSubmitter_rotation() public {
// Set submitter A (rotating from avsOwner set during init)
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
assertEq(serviceManager.validatorSetSubmitter(), submitterA);
// Rotate to submitter B
cheats.expectEmit();
emit IDataHavenServiceManagerEvents.ValidatorSetSubmitterUpdated(submitterA, submitterB);
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterB);
assertEq(serviceManager.validatorSetSubmitter(), submitterB);
// Old submitter A can no longer submit
vm.deal(submitterA, 10 ether);
cheats.prank(submitterA);
cheats.expectRevert(
abi.encodeWithSelector(
IDataHavenServiceManagerErrors.OnlyValidatorSetSubmitter.selector
)
);
serviceManager.sendNewValidatorSetForEra{value: 2 ether}(1, 1 ether, 1 ether);
}
// ============ sendNewValidatorSetForEra ============
function test_sendNewValidatorSetForEra_revertsIfNotSubmitter() public {
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
vm.deal(nonOwner, 10 ether);
cheats.prank(nonOwner);
cheats.expectRevert(
abi.encodeWithSelector(
IDataHavenServiceManagerErrors.OnlyValidatorSetSubmitter.selector
)
);
serviceManager.sendNewValidatorSetForEra{value: 2 ether}(1, 1 ether, 1 ether);
}
function test_sendNewValidatorSetForEra_success() public {
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
uint64 targetEra = 42;
vm.deal(submitterA, 1000000 ether);
bytes memory message = serviceManager.buildNewValidatorSetMessageForEra(targetEra);
bytes32 expectedHash = keccak256(message);
cheats.expectEmit();
emit IDataHavenServiceManagerEvents.ValidatorSetMessageSubmitted(
targetEra, expectedHash, submitterA
);
cheats.prank(submitterA);
serviceManager.sendNewValidatorSetForEra{value: 2 ether}(targetEra, 1 ether, 1 ether);
}
function test_sendNewValidatorSetForEra_revertsOnEmptyValidatorSet() public {
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
vm.deal(submitterA, 10 ether);
cheats.prank(submitterA);
cheats.expectRevert(
abi.encodeWithSelector(IDataHavenServiceManagerErrors.EmptyValidatorSet.selector)
);
serviceManager.sendNewValidatorSetForEra{value: 2 ether}(1, 1 ether, 1 ether);
}
function test_ownerCannotCallSendNewValidatorSetForEra() public {
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
vm.deal(avsOwner, 10 ether);
cheats.prank(avsOwner);
cheats.expectRevert(
abi.encodeWithSelector(
IDataHavenServiceManagerErrors.OnlyValidatorSetSubmitter.selector
)
);
serviceManager.sendNewValidatorSetForEra{value: 2 ether}(1, 1 ether, 1 ether);
}
// ============ buildNewValidatorSetMessageForEra ============
function test_buildNewValidatorSetMessageForEra_encodesTargetEra() public view {
bytes memory messageEra1 = serviceManager.buildNewValidatorSetMessageForEra(1);
bytes memory messageEra2 = serviceManager.buildNewValidatorSetMessageForEra(2);
bytes memory messageEra100 = serviceManager.buildNewValidatorSetMessageForEra(100);
// Different era values must produce different encoded output
assertTrue(
keccak256(messageEra1) != keccak256(messageEra2),
"Messages for different eras should differ"
);
assertTrue(
keccak256(messageEra1) != keccak256(messageEra100),
"Messages for different eras should differ"
);
}
function test_sendNewValidatorSetForEra_revertsWhenSubmitterIsZeroAddress() public {
// Deploy a fresh proxy with address(0) as the submitter
IRewardsCoordinatorTypes.StrategyAndMultiplier[] memory emptyStrategies =
new IRewardsCoordinatorTypes.StrategyAndMultiplier[](0);
cheats.startPrank(regularDeployer);
DataHavenServiceManager zeroSubmitterSM = DataHavenServiceManager(
address(
new TransparentUpgradeableProxy(
address(serviceManagerImplementation),
address(proxyAdmin),
abi.encodeWithSelector(
DataHavenServiceManager.initialize.selector,
avsOwner,
snowbridgeInitiator,
emptyStrategies,
address(snowbridgeGatewayMock),
address(0),
"v-test"
)
)
)
);
cheats.stopPrank();
assertEq(
zeroSubmitterSM.validatorSetSubmitter(),
address(0),
"validatorSetSubmitter should be address(0)"
);
vm.deal(submitterA, 10 ether);
cheats.prank(submitterA);
cheats.expectRevert(
abi.encodeWithSelector(
IDataHavenServiceManagerErrors.OnlyValidatorSetSubmitter.selector
)
);
zeroSubmitterSM.sendNewValidatorSetForEra{value: 2 ether}(1, 1 ether, 1 ether);
}
function test_fuzz_sendNewValidatorSetForEra(
uint64 targetEra
) public {
cheats.prank(avsOwner);
serviceManager.setValidatorSetSubmitter(submitterA);
vm.deal(submitterA, 1000000 ether);
bytes memory message = serviceManager.buildNewValidatorSetMessageForEra(targetEra);
bytes32 expectedHash = keccak256(message);
cheats.expectEmit();
emit IDataHavenServiceManagerEvents.ValidatorSetMessageSubmitted(
targetEra, expectedHash, submitterA
);
cheats.prank(submitterA);
serviceManager.sendNewValidatorSetForEra{value: 2 ether}(targetEra, 1 ether, 1 ether);
}
function test_buildNewValidatorSetMessageForEra_exactEncoding() public view {
uint64 targetEra = 42;
bytes memory message = serviceManager.buildNewValidatorSetMessageForEra(targetEra);
// Total: 4 (EL_MESSAGE_ID) + 1 (V0) + 1 (ReceiveValidators)
// + 1 (compact 10) + 10*20 (validators) + 8 (era) = 215
assertEq(message.length, 215, "Message length should be 215 bytes");
// First 4 bytes: EL_MESSAGE_ID = 0x70150038
assertEq(uint8(message[0]), 0x70, "EL_MESSAGE_ID byte 0");
assertEq(uint8(message[1]), 0x15, "EL_MESSAGE_ID byte 1");
assertEq(uint8(message[2]), 0x00, "EL_MESSAGE_ID byte 2");
assertEq(uint8(message[3]), 0x38, "EL_MESSAGE_ID byte 3");
// Byte 4: V0 = 0x00
assertEq(uint8(message[4]), 0x00, "V0 byte mismatch");
// Byte 5: ReceiveValidators = 0x00
assertEq(uint8(message[5]), 0x00, "ReceiveValidators byte mismatch");
// Byte 6: SCALE compact encoding of 10 validators = 10 << 2 = 40 = 0x28
assertEq(uint8(message[6]), 0x28, "Compact encoding of 10 validators");
// Last 8 bytes: era 42 in SCALE little-endian = 0x2A00000000000000
assertEq(uint8(message[207]), 0x2A, "Era LE byte 0");
assertEq(uint8(message[208]), 0x00, "Era LE byte 1");
assertEq(uint8(message[209]), 0x00, "Era LE byte 2");
assertEq(uint8(message[210]), 0x00, "Era LE byte 3");
assertEq(uint8(message[211]), 0x00, "Era LE byte 4");
assertEq(uint8(message[212]), 0x00, "Era LE byte 5");
assertEq(uint8(message[213]), 0x00, "Era LE byte 6");
assertEq(uint8(message[214]), 0x00, "Era LE byte 7");
}
// ============ Legacy function removed ============
function test_legacySendNewValidatorSet_removed() public {
// The old sendNewValidatorSet(uint128,uint128) selector should not be callable
bytes memory callData =
abi.encodeWithSelector(bytes4(keccak256("sendNewValidatorSet(uint128,uint128)")), 1, 1);
vm.deal(avsOwner, 10 ether);
cheats.prank(avsOwner);
(bool success,) = address(serviceManager).call{value: 2 ether}(callData);
assertFalse(success, "Legacy sendNewValidatorSet should not be callable");
}
}