Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions icm-contracts/avalanche/validator-manager/ValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,17 @@ contract ValidatorManager is IValidatorManager, Initializable, OwnableUpgradeabl
revert InvalidInitializationStatus();
}

// Check that the blockchainID and validator manager address in the ConversionData correspond to this contract.
// Other validation checks are done by the P-Chain when converting the L1, so are not required here.
// Check that the subnetID, blockchainID and validator manager address in the ConversionData
// correspond to this contract. Other validation checks are done by the P-Chain when converting
// the L1, so are not required here.
//
// The subnetID check is required because the P-Chain places no restriction on which
// (blockchainID, address) pair a subnet names as its manager: anyone can convert their own
// subnet naming this contract, and the resulting SubnetToL1ConversionMessage would otherwise
// pass every check below with an attacker-chosen initial validator set.
if (conversionData.subnetID != $._subnetID) {
revert InvalidSubnetID(conversionData.subnetID);
}
if (conversionData.validatorManagerBlockchainID != WARP_MESSENGER.getBlockchainID()) {
revert InvalidValidatorManagerBlockchainID(conversionData.validatorManagerBlockchainID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface IValidatorManager is IACP99Manager {
error InvalidValidatorManagerAddress(address validatorManagerAddress);
error InvalidWarpOriginSenderAddress(address senderAddress);
error InvalidValidatorManagerBlockchainID(bytes32 blockchainID);
error InvalidSubnetID(bytes32 subnetID);
error InvalidWarpSourceChainID(bytes32 sourceChainID);
error InvalidInitializationStatus();
error InvalidMaximumChurnPercentage(uint8 maximumChurnPercentage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,46 @@ abstract contract ValidatorManagerTest is Test {
manager.initializeValidatorSet(conversionData, 0);
}

// The P-Chain lets any subnet name an arbitrary (blockchainID, address) pair as its manager, so a
// conversion of an unrelated subnet naming this contract is a genuinely signed P-Chain message.
// It must not be accepted as this manager's initial validator set.
function testInitializeValidatorSetForeignSubnet() public {
vm.prank(address(0x123));
IACP99Manager manager = _setUp();

bytes32 foreignSubnetID =
bytes32(hex"8765432187654321876543218765432187654321876543218765432187654321");
ConversionData memory conversionData = _defaultConversionData();
conversionData.subnetID = foreignSubnetID;

vm.mockCall(
WARP_PRECOMPILE_ADDRESS,
abi.encodeWithSelector(IWarpMessenger.getBlockchainID.selector),
abi.encode(DEFAULT_SOURCE_BLOCKCHAIN_ID)
);
vm.mockCall(
WARP_PRECOMPILE_ADDRESS,
abi.encodeWithSelector(IWarpMessenger.getVerifiedWarpMessage.selector, uint32(0)),
abi.encode(
WarpMessage({
sourceChainID: validatorManager.P_CHAIN_BLOCKCHAIN_ID(),
originSenderAddress: address(0),
payload: ValidatorMessages.packSubnetToL1ConversionMessage(
sha256(ValidatorMessages.packConversionData(conversionData))
)
}),
true
)
);

vm.expectRevert(
abi.encodeWithSelector(IValidatorManager.InvalidSubnetID.selector, foreignSubnetID)
);
manager.initializeValidatorSet(conversionData, 0);

assertFalse(validatorManager.isValidatorSetInitialized());
}

function testRemoveValidatorTotalWeight5() public {
// Use prank here, because otherwise each test will end up with a different contract address, leading to a different subnet conversion hash.
vm.prank(address(0x123));
Expand Down
Loading