From 239665311df523576735dfe18ec0b492c03e8cba Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Mon, 24 Jul 2023 18:45:08 +0200 Subject: [PATCH 1/6] Adopt epoch validation --- src/lib/LibVoting.sol | 11 +++++++---- test/SubnetActorDiamond.t.sol | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/lib/LibVoting.sol b/src/lib/LibVoting.sol index c5f940e47..2a8a6e611 100644 --- a/src/lib/LibVoting.sol +++ b/src/lib/LibVoting.sol @@ -13,7 +13,9 @@ struct VotingStorage { uint64 lastVotingExecutedEpoch; /// @notice Initial epoch number uint64 genesisEpoch; + /// @notice Percentage of majority uint8 majorityPercentage; + /// @notice Checkpoint submission period uint64 submissionPeriod; /// @notice Contains the executable epochs that are ready to be executed, but has yet to be executed. /// This usually happens when previous submission epoch has not executed, but the next submission @@ -51,10 +53,11 @@ library LibVoting { if (epoch <= s.lastVotingExecutedEpoch) { revert EpochAlreadyExecuted(); } - if (epoch > s.genesisEpoch) { - if ((epoch - s.genesisEpoch) % s.submissionPeriod != 0) { - revert EpochNotVotable(); - } + if (epoch < s.genesisEpoch) { + revert EpochNotVotable(); + } + if ((epoch - s.genesisEpoch) % s.submissionPeriod != 0) { + revert EpochNotVotable(); } } diff --git a/test/SubnetActorDiamond.t.sol b/test/SubnetActorDiamond.t.sol index e60ea4c47..9bcfd0a17 100644 --- a/test/SubnetActorDiamond.t.sol +++ b/test/SubnetActorDiamond.t.sol @@ -749,6 +749,32 @@ contract SubnetActorDiamondTest is Test { require(saGetter.lastVotingExecutedEpoch() == 0); } + function testSubnetActorDiamond_SubmitCheckpoint_Works_EpochEqualToGenesisEpoch() public { + address validator = vm.addr(100); + + vm.roll(0); + + _assertDeploySubnetActor( + DEFAULT_NETWORK_NAME, + GATEWAY_ADDRESS, + ConsensusType.Mir, + DEFAULT_MIN_VALIDATOR_STAKE, + DEFAULT_MIN_VALIDATORS, + DEFAULT_CHECKPOINT_PERIOD, + GENESIS, + DEFAULT_MAJORITY_PERCENTAGE + ); + + _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); + + BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); + + _assertVote(validator, checkpoint); + + require(saManager.hasValidatorVotedForSubmission(checkpoint.epoch, validator) == true); + require(saGetter.lastVotingExecutedEpoch() == checkpoint.epoch); + } + function testSubnetActorDiamond_SubmitCheckpoint_Fails_NotAccount() public { address validator = address(1235); From 7c293517457f30d8385c3942588482e8006b2881 Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Mon, 24 Jul 2023 19:56:37 +0200 Subject: [PATCH 2/6] Fix epoch in ExecutableQueueHelper --- src/lib/ExecutableQueueHelper.sol | 6 +- src/lib/LibVoting.sol | 1 + src/structs/ExecutableQueue.sol | 1 + test/ExecutableQueueHelper.t.sol | 15 + test/Gateway.t.sol | 2202 ----------------------------- test/GatewayDiamond.t.sol | 2 +- test/SubnetActor.t.sol | 1048 -------------- test/SubnetActorDiamond.t.sol | 27 + 8 files changed, 48 insertions(+), 3254 deletions(-) delete mode 100644 test/Gateway.t.sol delete mode 100644 test/SubnetActor.t.sol diff --git a/src/lib/ExecutableQueueHelper.sol b/src/lib/ExecutableQueueHelper.sol index 31e499c00..9d0cf2868 100644 --- a/src/lib/ExecutableQueueHelper.sol +++ b/src/lib/ExecutableQueueHelper.sol @@ -5,14 +5,14 @@ import {ExecutableQueue} from "../structs/ExecutableQueue.sol"; library ExecutableQueueHelper { function push(ExecutableQueue storage queue, uint64 epoch) public { - if (epoch == 0) { + if (epoch == queue.genesisEpoch) { return; } - if (queue.first == 0 || queue.first > epoch) { + if (queue.first == queue.genesisEpoch || queue.first > epoch) { queue.first = epoch; } - if (queue.last == 0 || queue.last < epoch) { + if (queue.last == queue.genesisEpoch || queue.last < epoch) { queue.last = epoch; } diff --git a/src/lib/LibVoting.sol b/src/lib/LibVoting.sol index 2a8a6e611..32faaaf8e 100644 --- a/src/lib/LibVoting.sol +++ b/src/lib/LibVoting.sol @@ -76,6 +76,7 @@ library LibVoting { function initGenesisEpoch(uint64 genesisEpoch) internal { VotingStorage storage s = votingStorage(); s.genesisEpoch = genesisEpoch; + s.executableQueue.genesisEpoch = genesisEpoch; } /// @notice method that gives the epoch for a given block number and checkpoint period diff --git a/src/structs/ExecutableQueue.sol b/src/structs/ExecutableQueue.sol index df8c59019..380341627 100644 --- a/src/structs/ExecutableQueue.sol +++ b/src/structs/ExecutableQueue.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.19; struct ExecutableQueue { + uint64 genesisEpoch; // genesis epoch uint64 period; // number of blocks per epoch uint64 first; // next epoch uint64 last; // last epoch diff --git a/test/ExecutableQueueHelper.t.sol b/test/ExecutableQueueHelper.t.sol index 98486ed7a..9892a5cbf 100644 --- a/test/ExecutableQueueHelper.t.sol +++ b/test/ExecutableQueueHelper.t.sol @@ -19,6 +19,7 @@ contract ExecutableQueueHelperTest is Test { function setUp() public { queue.period = BLOCKS_PER_EPOCH; + queue.genesisEpoch = 0; } function test_Push_Works_ZeroEpoch() public { @@ -27,6 +28,20 @@ contract ExecutableQueueHelperTest is Test { require(queue.epochs[0] == false); } + function test_Push_Works_GenesisEpochInvalid() public { + queue.genesisEpoch = 10; + queue.push(10); + + require(queue.epochs[10] == false); + } + + function test_Push_Works_NonGenesisEpochValid() public { + queue.genesisEpoch = 10; + queue.push(11); + + require(queue.epochs[11]); + } + function test_Push_Works_OneEpoch() public { _assertPush(EPOCH_ONE); diff --git a/test/Gateway.t.sol b/test/Gateway.t.sol deleted file mode 100644 index 85f4de89a..000000000 --- a/test/Gateway.t.sol +++ /dev/null @@ -1,2202 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import "forge-std/StdInvariant.sol"; -import "../src/Gateway.sol"; -import "../src/SubnetActor.sol"; -import "../src/lib/SubnetIDHelper.sol"; -import "../src/lib/CheckpointHelper.sol"; -import "../src/lib/CrossMsgHelper.sol"; -import "../src/lib/FvmAddressHelper.sol"; -import "../src/structs/FvmAddress.sol"; - -contract GatewayDeploymentTest is StdInvariant, Test { - using SubnetIDHelper for SubnetID; - using CheckpointHelper for BottomUpCheckpoint; - using CrossMsgHelper for CrossMsg; - using StorableMsgHelper for StorableMsg; - using FvmAddressHelper for FvmAddress; - - uint64 constant MIN_COLLATERAL_AMOUNT = 1 ether; - uint64 constant MAX_NONCE = type(uint64).max; - address constant BLS_ACCOUNT_ADDREESS = address(0xfF000000000000000000000000000000bEefbEEf); - bytes32 private constant DEFAULT_NETWORK_NAME = bytes32("test"); - uint64 private constant DEFAULT_MIN_VALIDATORS = 1; - uint8 private constant DEFAULT_MAJORITY_PERCENTAGE = 70; - uint64 constant DEFAULT_CHECKPOINT_PERIOD = 10; - string private constant DEFAULT_NET_ADDR = "netAddr"; - bytes private constant GENESIS = EMPTY_BYTES; - uint256 constant CROSS_MSG_FEE = 10 gwei; - address constant CHILD_NETWORK_ADDRESS = address(10); - address constant CHILD_NETWORK_ADDRESS_2 = address(11); - uint64 constant EPOCH_ONE = 1 * DEFAULT_CHECKPOINT_PERIOD; - uint256 constant INITIAL_VALIDATOR_FUNDS = 1 ether; - - Gateway gw; - Gateway gw2; - SubnetActor sa; - - uint64 private constant ROOTNET_CHAINID = 123; - address public constant ROOTNET_ADDRESS = address(1); - - address TOPDOWN_VALIDATOR_1 = address(12); - - error NotSystemActor(); - error NotSignableAccount(); - error NotEnoughFee(); - error NotEnoughFunds(); - error NotEnoughFundsToRelease(); - error CannotReleaseZero(); - error NotEnoughBalance(); - error NotEnoughSubnetCircSupply(); - error NotInitialized(); - error NotValidator(); - error NotEmptySubnetCircSupply(); - error NotRegisteredSubnet(); - error AlreadyRegisteredSubnet(); - error AlreadyInitialized(); - error AlreadyCommittedCheckpoint(); - error InconsistentPrevCheckpoint(); - error InvalidCheckpointEpoch(); - error InvalidCheckpointSource(); - error InvalidCrossMsgNonce(); - error InvalidCrossMsgDestinationSubnet(); - error InvalidCrossMsgDestinationAddress(); - error InvalidCrossMsgsSortOrder(); - error InvalidCrossMsgFromSubnetId(); - error InvalidCrossMsgFromRawAddress(); - error CannotSendCrossMsgToItself(); - error SubnetNotActive(); - error PostboxNotExist(); - error ValidatorAlreadyVoted(); - error MessagesNotSorted(); - error ValidatorsAndWeightsLengthMismatch(); - error ValidatorWeightIsZero(); - error NotEnoughFundsForMembership(); - error EpochNotVotable(); - error EpochAlreadyExecuted(); - - function setUp() public { - address[] memory path = new address[](1); - path[0] = ROOTNET_ADDRESS; - - address[] memory path2 = new address[](2); - path2[0] = CHILD_NETWORK_ADDRESS; - path2[1] = CHILD_NETWORK_ADDRESS_2; - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - addValidator(TOPDOWN_VALIDATOR_1, 100); - - constructorParams.networkName = SubnetID({root: ROOTNET_CHAINID, route: path2}); - gw2 = new Gateway(constructorParams); - - SubnetActor.ConstructParams memory subnetConstructorParams = SubnetActor.ConstructParams({ - parentId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - name: DEFAULT_NETWORK_NAME, - ipcGatewayAddr: address(gw), - consensus: ConsensusType.Mir, - minActivationCollateral: MIN_COLLATERAL_AMOUNT, - minValidators: DEFAULT_MIN_VALIDATORS, - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, - genesis: GENESIS - }); - sa = new SubnetActor(subnetConstructorParams); - - targetContract(address(gw)); - } - - function invariant_CrossMsgFee() public view { - require(gw.crossMsgFee() == CROSS_MSG_FEE); - } - - function test_Deployment_Works_Root(uint64 checkpointPeriod) public { - vm.assume(checkpointPeriod >= DEFAULT_CHECKPOINT_PERIOD); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - bottomUpCheckPeriod: checkpointPeriod, - topDownCheckPeriod: checkpointPeriod, - msgFee: CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - SubnetID memory networkName = gw.getNetworkName(); - - require(networkName.isRoot(), "networkName.isRoot()"); - require(gw.initialized() == true, "gw.initialized() == true"); - - require(gw.minStake() == MIN_COLLATERAL_AMOUNT, "gw.minStake() == MIN_COLLATERAL_AMOUNT"); - require(gw.bottomUpCheckPeriod() == checkpointPeriod, "gw.bottomUpCheckPeriod() == checkpointPeriod"); - require(gw.topDownCheckPeriod() == checkpointPeriod, "gw.topDownCheckPeriod() == checkpointPeriod"); - require( - gw.majorityPercentage() == DEFAULT_MAJORITY_PERCENTAGE, - "gw.majorityPercentage() == DEFAULT_MAJORITY_PERCENTAGE" - ); - } - - function test_Deployment_Works_NotRoot(uint64 checkpointPeriod) public { - vm.assume(checkpointPeriod >= DEFAULT_CHECKPOINT_PERIOD); - address[] memory path = new address[](2); - path[0] = address(0); - path[1] = address(1); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: checkpointPeriod, - topDownCheckPeriod: checkpointPeriod, - msgFee: CROSS_MSG_FEE, - majorityPercentage: 100 - }); - gw = new Gateway(constructorParams); - - SubnetID memory networkName = gw.getNetworkName(); - - require(networkName.isRoot() == false, "networkName.isRoot()"); - require(gw.initialized() == false, "gw.initialized() == false"); - - require(gw.minStake() == MIN_COLLATERAL_AMOUNT, "gw.minStake() == MIN_COLLATERAL_AMOUNT"); - require(gw.bottomUpCheckPeriod() == checkpointPeriod, "gw.bottomUpCheckPeriod() == checkpointPeriod"); - require(gw.topDownCheckPeriod() == checkpointPeriod, "gw.topDownCheckPeriod() == checkpointPeriod"); - require(gw.majorityPercentage() == 100, "gw.majorityPercentage() == 100"); - } - - function test_Register_Works_SingleSubnet(uint256 subnetCollateral) public { - vm.assume(subnetCollateral >= MIN_COLLATERAL_AMOUNT && subnetCollateral < type(uint64).max); - address subnetAddress = vm.addr(100); - vm.prank(subnetAddress); - vm.deal(subnetAddress, subnetCollateral); - - registerSubnet(subnetCollateral, subnetAddress); - - require(gw.totalSubnets() == 1); - Subnet[] memory subnets = gw.listSubnets(); - require(subnets.length == 1, "subnets.length == 1"); - } - - function test_InitGenesisEpoch_Works() public { - vm.prank(FilAddress.SYSTEM_ACTOR); - gw2.initGenesisEpoch(50); - - require(gw2.initialized() == true); - require(gw2.getGenesisEpoch() == 50); - } - - function test_InitGenesisEpoch_Fails_NotSystemActor() public { - vm.expectRevert(NotSystemActor.selector); - gw.initGenesisEpoch(50); - } - - function test_InitGenesisEpoch_Fails_AlreadyInitialized() public { - vm.prank(FilAddress.SYSTEM_ACTOR); - vm.expectRevert(AlreadyInitialized.selector); - gw.initGenesisEpoch(50); - } - - function test_Register_Works_MultipleSubnets(uint8 numberOfSubnets) public { - vm.assume(numberOfSubnets > 0); - - for (uint256 i = 1; i <= numberOfSubnets; i++) { - address subnetAddress = vm.addr(i); - vm.prank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - } - - require(gw.totalSubnets() == numberOfSubnets); - Subnet[] memory subnets = gw.listSubnets(); - require(subnets.length == numberOfSubnets, "subnets.length == numberOfSubnets"); - } - - function test_Register_Fail_InsufficientCollateral(uint256 collateral) public { - vm.assume(collateral < MIN_COLLATERAL_AMOUNT); - vm.expectRevert(NotEnoughFunds.selector); - - gw.register{value: collateral}(); - } - - function test_Register_Fail_SubnetAlreadyExists() public { - registerSubnet(MIN_COLLATERAL_AMOUNT, address(this)); - - vm.expectRevert(AlreadyRegisteredSubnet.selector); - - gw.register{value: MIN_COLLATERAL_AMOUNT}(); - } - - function testAddStake_Works_SingleStaking(uint256 stakeAmount, uint256 registerAmount) public { - address subnetAddress = vm.addr(100); - vm.assume(registerAmount >= MIN_COLLATERAL_AMOUNT && registerAmount < type(uint64).max); - vm.assume(stakeAmount > 0 && stakeAmount < type(uint256).max - registerAmount); - - uint256 totalAmount = stakeAmount + registerAmount; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, totalAmount); - - registerSubnet(registerAmount, subnetAddress); - addStake(stakeAmount, subnetAddress); - - (, uint256 totalStaked, , , , ) = getSubnet(subnetAddress); - - require(totalStaked == totalAmount); - } - - function test_AddStake_Works_Reactivate() public { - address subnetAddress = vm.addr(100); - uint256 registerAmount = MIN_COLLATERAL_AMOUNT; - uint256 stakeAmount = MIN_COLLATERAL_AMOUNT; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, registerAmount); - - registerSubnet(registerAmount, subnetAddress); - gw.releaseStake(registerAmount); - - (, , , , , Status statusInactive) = getSubnet(subnetAddress); - require(statusInactive == Status.Inactive); - - vm.deal(subnetAddress, stakeAmount); - addStake(stakeAmount, subnetAddress); - - (, uint256 staked, , , , Status statusActive) = getSubnet(subnetAddress); - - require(staked == stakeAmount); - require(statusActive == Status.Active); - } - - function test_AddStake_Works_NotEnoughFundsToReactivate() public { - address subnetAddress = vm.addr(100); - uint256 registerAmount = MIN_COLLATERAL_AMOUNT; - uint256 stakeAmount = MIN_COLLATERAL_AMOUNT - 1; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, registerAmount); - - registerSubnet(registerAmount, subnetAddress); - gw.releaseStake(registerAmount); - - vm.deal(subnetAddress, stakeAmount); - addStake(stakeAmount, subnetAddress); - - (, uint256 staked, , , , Status status) = getSubnet(subnetAddress); - - require(staked == stakeAmount); - require(status == Status.Inactive); - } - - function testAddStake_Works_MultipleStakings(uint8 numberOfStakes) public { - vm.assume(numberOfStakes > 0); - - address subnetAddress = address(1); - uint256 singleStakeAmount = 1 ether; - uint256 registerAmount = MIN_COLLATERAL_AMOUNT; - uint256 expectedStakedAmount = registerAmount; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, registerAmount + singleStakeAmount * numberOfStakes); - - registerSubnet(registerAmount, subnetAddress); - - for (uint256 i = 0; i < numberOfStakes; i++) { - addStake(singleStakeAmount, subnetAddress); - - expectedStakedAmount += singleStakeAmount; - } - - (, uint256 totalStake, , , , ) = getSubnet(subnetAddress); - - require(totalStake == expectedStakedAmount); - } - - function testAddStake_Fail_ZeroAmount() public { - registerSubnet(MIN_COLLATERAL_AMOUNT, address(this)); - - vm.expectRevert(NotEnoughFunds.selector); - - gw.addStake{value: 0}(); - } - - function testAddStake_Fail_SubnetNotExists() public { - vm.expectRevert(NotRegisteredSubnet.selector); - - gw.addStake{value: 1}(); - } - - function test_ReleaseStake_Works_FullAmount(uint256 stakeAmount) public { - address subnetAddress = CHILD_NETWORK_ADDRESS; - uint256 registerAmount = MIN_COLLATERAL_AMOUNT; - - vm.assume(stakeAmount > 0 && stakeAmount < type(uint256).max - registerAmount); - - uint256 fullAmount = stakeAmount + registerAmount; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, fullAmount); - - registerSubnet(registerAmount, subnetAddress); - addStake(stakeAmount, subnetAddress); - - gw.releaseStake(fullAmount); - - (, uint256 stake, , , , Status status) = getSubnet(subnetAddress); - - require(stake == 0); - require(status == Status.Inactive); - require(subnetAddress.balance == fullAmount); - } - - function test_ReleaseStake_Works_SubnetInactive() public { - address subnetAddress = vm.addr(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - gw.releaseStake(MIN_COLLATERAL_AMOUNT / 2); - - (, uint256 stake, , , , Status status) = getSubnet(subnetAddress); - require(stake == MIN_COLLATERAL_AMOUNT / 2, "stake == MIN_COLLATERAL_AMOUNT / 2"); - require(status == Status.Inactive, "status == Status.Inactive"); - } - - function test_ReleaseStake_Works_PartialAmount(uint256 partialAmount) public { - address subnetAddress = CHILD_NETWORK_ADDRESS; - uint256 registerAmount = MIN_COLLATERAL_AMOUNT; - - vm.assume(partialAmount > registerAmount && partialAmount < type(uint256).max - registerAmount); - - uint256 totalAmount = partialAmount + registerAmount; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, totalAmount); - - registerSubnet(registerAmount, subnetAddress); - addStake(partialAmount, subnetAddress); - - gw.releaseStake(partialAmount); - - (, uint256 stake, , , , Status status) = getSubnet(subnetAddress); - - require(stake == registerAmount); - require(status == Status.Active); - require(subnetAddress.balance == partialAmount); - } - - function test_ReleaseStake_Fail_ZeroAmount() public { - registerSubnet(MIN_COLLATERAL_AMOUNT, address(this)); - - vm.expectRevert(CannotReleaseZero.selector); - - gw.releaseStake(0); - } - - function test_ReleaseStake_Fail_InsufficientSubnetBalance(uint256 releaseAmount, uint256 subnetBalance) public { - vm.assume(subnetBalance > MIN_COLLATERAL_AMOUNT); - vm.assume(releaseAmount > subnetBalance && releaseAmount < type(uint256).max - subnetBalance); - - address subnetAddress = vm.addr(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, releaseAmount); - - registerSubnet(subnetBalance, subnetAddress); - - vm.expectRevert(NotEnoughFundsToRelease.selector); - - gw.releaseStake(releaseAmount); - } - - function test_ReleaseStake_Fail_NotRegisteredSubnet() public { - vm.expectRevert(NotRegisteredSubnet.selector); - - gw.releaseStake(1); - } - - function test_ReleaseStake_Works_TransitionToInactive() public { - address subnetAddress = vm.addr(100); - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - gw.releaseStake(10); - - (, uint256 stake, , , , Status status) = getSubnet(subnetAddress); - - require(stake == MIN_COLLATERAL_AMOUNT - 10, "stake should be MIN_COLLATERAL_AMOUNT - 10"); - require(status == Status.Inactive, "status should be Inactive"); - } - - function test_ReleaseRewards_Fails_CannotReleaseZero() public { - vm.expectRevert(CannotReleaseZero.selector); - - gw.releaseRewards(0); - } - - function test_ReleaseRewards_Fails_NotRegisteredSubnet() public { - vm.expectRevert(NotRegisteredSubnet.selector); - vm.deal(address(gw), 1); - gw.releaseRewards(1); - } - - function test_ReleaseRewards_Works() public { - address subnetAddress = CHILD_NETWORK_ADDRESS; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - vm.stopPrank(); - vm.prank(subnetAddress); - vm.deal(address(gw), 1); - gw.releaseRewards(1); - } - - function test_Kill_Works() public { - address subnetAddress = CHILD_NETWORK_ADDRESS; - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - require(subnetAddress.balance == 0); - - gw.kill(); - - (SubnetID memory id, uint256 stake, uint256 nonce, , uint256 circSupply, Status status) = getSubnet( - subnetAddress - ); - - require(id.toHash() == SubnetID(0, new address[](0)).toHash()); - require(stake == 0); - require(nonce == 0); - require(circSupply == 0); - require(status == Status.Unset); - require(gw.totalSubnets() == 0); - require(subnetAddress.balance == MIN_COLLATERAL_AMOUNT); - } - - function test_Kill_Fail_SubnetNotExists() public { - vm.expectRevert(NotRegisteredSubnet.selector); - - gw.kill(); - } - - function test_Kill_Fail_CircSupplyMoreThanZero() public { - address validatorAddress = address(100); - _join(validatorAddress); - - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - fund(funderAddress, fundAmount); - - vm.stopPrank(); - vm.startPrank(address(sa)); - vm.expectRevert(NotEmptySubnetCircSupply.selector); - - gw.kill(); - } - - function test_CommitChildCheck_Works() public { - address subnetAddress = address(100); - - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - - gw.commitChildCheck(checkpoint); - - require(gw.bottomUpNonce() == 0); - } - - function test_CommitChildCheck_Works_WithCrossMsgs() public { - address subnetAddress = address(sa); - address validatorAddress = address(100); - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - _join(validatorAddress); - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - fund(funderAddress, fundAmount); - - vm.stopPrank(); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - SubnetID memory networkName = gw.getNetworkName(); - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - - checkpoint.fee = 1; - checkpoint.crossMsgs = new CrossMsg[](1); - checkpoint.crossMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: networkName.createSubnetId(subnetAddress), - rawAddress: FvmAddressHelper.from(address(1)) - }), - to: IPCAddress({subnetId: networkName, rawAddress: FvmAddressHelper.from(address(2))}), - value: 1, - nonce: 0, - method: METHOD_SEND, - params: EMPTY_BYTES - }), - wrapped: false - }); - - require(checkpoint.crossMsgs[0].message.applyType(networkName) == IPCMsgType.BottomUp); - - vm.expectCall(subnetAddress, 0, abi.encodeWithSelector(ISubnetActor.reward.selector, checkpoint.fee), 1); - - (, , , , uint256 circSupplyBefore, ) = getSubnet(subnetAddress); - - gw.commitChildCheck(checkpoint); - - (, , uint256 appliedBottomUpNonce, , uint256 circSupplyAfter, ) = getSubnet(subnetAddress); - - require(appliedBottomUpNonce == 1); - require(circSupplyAfter == circSupplyBefore - checkpoint.fee - checkpoint.crossMsgs[0].message.value); - } - - function test_CommitChildCheck_Works_SameSubnet(uint64 blockNumber) public { - address subnetAddress = address(100); - vm.assume(blockNumber < type(uint64).max / 2 - 11); - vm.roll(blockNumber); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - gw.commitChildCheck(checkpoint); - - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - BottomUpCheckpoint memory checkpoint2 = createCheckpoint(subnetAddress, 2 * DEFAULT_CHECKPOINT_PERIOD); - - gw.commitChildCheck(checkpoint2); - } - - function test_CommitChildCheck_Fails_InvalidCrossMsgNonce(uint64 blockNumber) public { - address subnetAddress = address(sa); - vm.assume(blockNumber < type(uint64).max / 2 - 11); - vm.roll(blockNumber); - - address validatorAddress = address(100); - - _join(validatorAddress); - - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - fund(funderAddress, fundAmount); - - vm.stopPrank(); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - SubnetID memory networkName = gw.getNetworkName(); - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - - checkpoint.crossMsgs = new CrossMsg[](1); - checkpoint.crossMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: networkName.createSubnetId(subnetAddress), - rawAddress: FvmAddressHelper.from(address(1)) - }), - to: IPCAddress({subnetId: networkName, rawAddress: FvmAddressHelper.from(address(2))}), - value: 1, - nonce: 1, - method: METHOD_SEND, - params: EMPTY_BYTES - }), - wrapped: false - }); - - require(checkpoint.crossMsgs[0].message.applyType(networkName) == IPCMsgType.BottomUp); - - vm.expectRevert(InvalidCrossMsgNonce.selector); - - gw.commitChildCheck(checkpoint); - } - - function test_CommitChildCheck_Fails_NotInitialized() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - vm.expectRevert(NotInitialized.selector); - gw2.commitChildCheck(checkpoint); - } - - function test_CommitChildCheck_Fails_InvalidCheckpointSource() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - address notSubnetAddress = address(101); - - SubnetID memory subnetId = gw.getNetworkName().createSubnetId(notSubnetAddress); - BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ - source: subnetId, - epoch: 0, - fee: 0, - crossMsgs: new CrossMsg[](0), - prevHash: EMPTY_HASH, - children: new ChildCheck[](0), - proof: new bytes(0) - }); - - vm.expectRevert(InvalidCheckpointSource.selector); - gw.commitChildCheck(checkpoint); - } - - function test_CommitChildCheck_Fails_InvalidCheckpointEpoch_PrevEpoch() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - gw.commitChildCheck(checkpoint); - - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - BottomUpCheckpoint memory checkpoint2 = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD / 2); - vm.expectRevert(InvalidCheckpointEpoch.selector); - gw.commitChildCheck(checkpoint2); - } - - function test_CommitChildCheck_Fails_InvalidCheckpointEpoch_CurrentEpoch() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - BottomUpCheckpoint memory checkpoint = createCheckpoint(subnetAddress, DEFAULT_CHECKPOINT_PERIOD); - gw.commitChildCheck(checkpoint); - - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - vm.expectRevert(InvalidCheckpointEpoch.selector); - gw.commitChildCheck(checkpoint); - } - - function test_CommitChildCheck_Fails_SubnetNotActive() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - - SubnetID memory subnetId = gw.getNetworkName().createSubnetId(subnetAddress); - BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ - source: subnetId, - epoch: DEFAULT_CHECKPOINT_PERIOD, - fee: 0, - crossMsgs: new CrossMsg[](0), - prevHash: EMPTY_HASH, - children: new ChildCheck[](0), - proof: new bytes(0) - }); - - vm.expectRevert(SubnetNotActive.selector); - gw.commitChildCheck(checkpoint); - } - - function test_CommitChildCheck_Fails_InconsistentPrevCheckpoint() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - SubnetID memory subnetId = gw.getNetworkName().createSubnetId(subnetAddress); - BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ - source: subnetId, - epoch: DEFAULT_CHECKPOINT_PERIOD, - fee: 0, - crossMsgs: new CrossMsg[](0), - prevHash: EMPTY_HASH, - children: new ChildCheck[](0), - proof: new bytes(0) - }); - gw.commitChildCheck(checkpoint); - - checkpoint.fee = 100; // mess with the checkpoint to get different hash - - BottomUpCheckpoint memory checkpoint2 = BottomUpCheckpoint({ - source: subnetId, - epoch: 2 * DEFAULT_CHECKPOINT_PERIOD, - fee: 0, - crossMsgs: new CrossMsg[](0), - prevHash: checkpoint.toHash(), - children: new ChildCheck[](0), - proof: new bytes(0) - }); - - vm.expectRevert(InconsistentPrevCheckpoint.selector); - gw.commitChildCheck(checkpoint2); - } - - function test_CommitChildCheck_Fails_NotEnoughSubnetCircSupply() public { - address subnetAddress = address(100); - vm.startPrank(subnetAddress); - vm.deal(subnetAddress, MIN_COLLATERAL_AMOUNT); - registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); - - SubnetID memory subnetId = gw.getNetworkName().createSubnetId(subnetAddress); - BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ - source: subnetId, - epoch: DEFAULT_CHECKPOINT_PERIOD, - fee: MIN_COLLATERAL_AMOUNT * 2, - crossMsgs: new CrossMsg[](0), - prevHash: EMPTY_HASH, - children: new ChildCheck[](0), - proof: new bytes(0) - }); - - vm.expectRevert(NotEnoughSubnetCircSupply.selector); - gw.commitChildCheck(checkpoint); - } - - function test_Fund_Works_ReactivatedSubnet() public { - address validatorAddress = address(100); - - _join(validatorAddress); - - vm.prank(validatorAddress); - sa.leave(); - - _join(validatorAddress); - - require(sa.status() == Status.Active); - - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - fund(funderAddress, fundAmount); - } - - function test_Fund_Works_EthAccountSingleFunding() public { - address validatorAddress = address(100); - - _join(validatorAddress); - - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - fund(funderAddress, fundAmount); - } - - function test_Fund_Works_BLSAccountSingleFunding() public { - address validatorAddress = address(100); - _join(validatorAddress); - - uint256 fundAmount = 1 ether; - vm.startPrank(BLS_ACCOUNT_ADDREESS); - vm.deal(BLS_ACCOUNT_ADDREESS, fundAmount + 1); - - fund(BLS_ACCOUNT_ADDREESS, fundAmount); - } - - function test_GetTopDownMsgs_Works_Empty() public view { - (SubnetID memory subnetId, , , , , ) = getSubnet(address(sa)); - - require(gw.getTopDownMsgs(subnetId, 0).length == 0, "td msgs length not 0"); - } - - function test_Fund_Works_MultipleFundings() public { - uint8 numberOfFunds = 5; - uint256 fundAmount = 1 ether; - - address validatorAddress = address(100); - address funderAddress = address(101); - - _join(validatorAddress); - - vm.startPrank(funderAddress); - vm.expectCall(address(sa), 0, abi.encodeWithSelector(sa.reward.selector, gw.crossMsgFee()), 5); - - for (uint256 i = 0; i < numberOfFunds; i++) { - vm.deal(funderAddress, fundAmount + 1); - - fund(funderAddress, fundAmount); - } - - (SubnetID memory subnetId, , , , , ) = getSubnet(address(sa)); - - require(gw.getTopDownMsgs(subnetId, 0).length == numberOfFunds, "td msgs length not correct"); - } - - function test_Fund_Fails_WrongSubnet() public { - address validatorAddress = address(100); - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - _join(validatorAddress); - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - address[] memory wrongPath = new address[](3); - wrongPath[0] = address(1); - wrongPath[1] = address(2); - - vm.expectRevert(NotRegisteredSubnet.selector); - - gw.fund{value: fundAmount}(SubnetID(ROOTNET_CHAINID, wrongPath), FvmAddressHelper.from(msg.sender)); - } - - function test_Fund_Fails_InvalidAccount() public { - address validatorAddress = address(100); - address invalidAccount = address(sa); - uint256 fundAmount = 1 ether; - - _join(validatorAddress); - - vm.startPrank(invalidAccount); - vm.deal(invalidAccount, fundAmount + 1); - - (SubnetID memory subnetId, , , , , ) = getSubnet(address(sa)); - - vm.expectRevert(NotSignableAccount.selector); - - gw.fund{value: fundAmount}(subnetId, FvmAddressHelper.from(msg.sender)); - } - - function test_Fund_Fails_NotRegistered() public { - address validatorAddress = address(100); - address funderAddress = address(101); - uint256 fundAmount = 1 ether; - - _join(validatorAddress); - - vm.startPrank(funderAddress); - vm.deal(funderAddress, fundAmount + 1); - - address[] memory wrongSubnetPath = new address[](2); - wrongSubnetPath[0] = vm.addr(102); - wrongSubnetPath[0] = vm.addr(103); - SubnetID memory wrongSubnetId = SubnetID({root: ROOTNET_CHAINID, route: wrongSubnetPath}); - - vm.expectRevert(NotRegisteredSubnet.selector); - gw.fund{value: fundAmount}(wrongSubnetId, FvmAddressHelper.from(msg.sender)); - } - - function test_Fund_Fails_InsufficientAmount() public { - address validatorAddress = address(100); - address funderAddress = address(101); - - _join(validatorAddress); - - vm.startPrank(funderAddress); - vm.deal(funderAddress, 1 ether); - - (SubnetID memory subnetId, , , , , ) = getSubnet(address(sa)); - - vm.expectRevert(NotEnoughFee.selector); - - gw.fund{value: 0}(subnetId, FvmAddressHelper.from(msg.sender)); - } - - function test_Release_Fails_InsufficientAmount() public { - address[] memory path = new address[](2); - path[0] = address(1); - path[1] = address(2); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - address callerAddress = address(100); - - vm.startPrank(callerAddress); - vm.deal(callerAddress, 1 ether); - vm.expectRevert(NotEnoughFee.selector); - - gw.release{value: 0}(FvmAddressHelper.from(msg.sender)); - } - - function test_Release_Fails_InvalidAccount() public { - address[] memory path = new address[](2); - path[0] = address(1); - path[1] = address(2); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - address invalidAccount = address(sa); - - vm.startPrank(invalidAccount); - vm.deal(invalidAccount, 1 ether); - vm.expectRevert(NotSignableAccount.selector); - - gw.release{value: 1 ether}(FvmAddressHelper.from(msg.sender)); - } - - function test_Release_Works_BLSAccount(uint256 releaseAmount, uint256 crossMsgFee) public { - vm.assume(releaseAmount > 0 && releaseAmount < type(uint256).max); - vm.assume(crossMsgFee > 0 && crossMsgFee < releaseAmount); - - address[] memory path = new address[](2); - path[0] = makeAddr("root"); - path[1] = makeAddr("subnet_one"); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: crossMsgFee, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - vm.roll(0); - vm.warp(0); - vm.startPrank(BLS_ACCOUNT_ADDREESS); - vm.deal(BLS_ACCOUNT_ADDREESS, releaseAmount + 1); - - release(releaseAmount, crossMsgFee, EPOCH_ONE); - } - - function test_Release_Works_EmptyCrossMsgMeta(uint256 releaseAmount, uint256 crossMsgFee) public { - vm.assume(releaseAmount > 0 && releaseAmount < type(uint256).max); - vm.assume(crossMsgFee > 0 && crossMsgFee < releaseAmount); - - address[] memory path = new address[](2); - path[0] = makeAddr("root"); - path[1] = makeAddr("subnet_one"); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: crossMsgFee, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - address callerAddress = address(100); - - vm.roll(0); - vm.warp(0); - vm.startPrank(callerAddress); - vm.deal(callerAddress, releaseAmount + 1); - - release(releaseAmount, crossMsgFee, EPOCH_ONE); - } - - function test_Release_Works_NonEmptyCrossMsgMeta(uint256 releaseAmount, uint256 crossMsgFee) public { - vm.assume(releaseAmount > 0 && releaseAmount < type(uint256).max / 2); - vm.assume(crossMsgFee > 0 && crossMsgFee < releaseAmount); - - address[] memory path = new address[](2); - path[0] = makeAddr("root"); - path[1] = makeAddr("subnet_one"); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: crossMsgFee, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - address callerAddress = address(100); - - vm.roll(0); - vm.warp(0); - vm.startPrank(callerAddress); - vm.deal(callerAddress, 2 * releaseAmount + 1); - - release(releaseAmount, crossMsgFee, EPOCH_ONE); - - release(releaseAmount, crossMsgFee, EPOCH_ONE); - } - - function test_SendCrossMessage_Fails_NoDestination() public { - address caller = vm.addr(100); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - - vm.expectRevert(InvalidCrossMsgDestinationSubnet.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({ - subnetId: SubnetID({root: 0, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: false - }) - ); - } - - function test_SendCrossMessage_Fails_NotSignableAccount() public { - address caller = address(sa); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - - vm.expectRevert(NotSignableAccount.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: false - }) - ); - } - - function test_SendCrossMessage_Fails_NoCurrentNetwork() public { - address caller = vm.addr(100); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - SubnetID memory destinationSubnet = gw.getNetworkName(); - vm.expectRevert(CannotSendCrossMsgToItself.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(caller)}), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }) - ); - } - - function test_SendCrossMessage_Fails_DifferentMessageValue() public { - address caller = vm.addr(100); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - SubnetID memory destinationSubnet = gw.getNetworkName().createSubnetId(caller); - vm.expectRevert(NotEnoughFunds.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(caller)}), - value: 5, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }) - ); - } - - function test_SendCrossMessage_Fails_EmptyNetwork() public { - address caller = vm.addr(100); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - SubnetID memory destinationSubnet = SubnetID(0, new address[](0)); - vm.expectRevert(InvalidCrossMsgDestinationSubnet.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(caller)}), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }) - ); - } - - function test_SendCrossMessage_Fails_InvalidCrossMsgFromSubnetId() public { - address caller = vm.addr(100); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - SubnetID memory destinationSubnet = gw.getNetworkName().createSubnetId(caller); - vm.expectRevert(InvalidCrossMsgFromSubnetId.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: 0, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(caller)}), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }) - ); - } - - function test_SendCrossMessage_Fails_NotEnoughGas() public { - address caller = vm.addr(100); - vm.startPrank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - SubnetID memory destinationSubnet = gw.getNetworkName().createSubnetId(caller); - vm.expectRevert(NotEnoughFee.selector); - gw.sendCrossMessage{value: CROSS_MSG_FEE - 1}( - CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(address(0))}), - value: 0, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }) - ); - } - - function test_SendCrossMessage_Works_TopDown_SameSubnet() public { - address caller = vm.addr(100); - vm.prank(caller); - vm.deal(caller, MIN_COLLATERAL_AMOUNT + CROSS_MSG_FEE + 2); - registerSubnet(MIN_COLLATERAL_AMOUNT, caller); - - address receiver = address(this); // callback to reward() method - vm.prank(receiver); - vm.deal(receiver, MIN_COLLATERAL_AMOUNT + 1); - registerSubnet(MIN_COLLATERAL_AMOUNT, receiver); - - SubnetID memory destinationSubnet = gw.getNetworkName().createSubnetId(receiver); - SubnetID memory from = gw.getNetworkName().createSubnetId(caller); - - CrossMsg memory crossMsg = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(caller)}), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(receiver)}), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }); - - vm.prank(caller); - vm.expectCall(receiver, 0, abi.encodeWithSelector(ISubnetActor.reward.selector, CROSS_MSG_FEE), 1); - - gw.sendCrossMessage{value: CROSS_MSG_FEE + 1}(crossMsg); - - (SubnetID memory id, , uint256 nonce, , uint256 circSupply, ) = getSubnet(address(this)); - - require(crossMsg.message.applyType(gw.getNetworkName()) == IPCMsgType.TopDown); - require(id.equals(destinationSubnet)); - require(nonce == 1); - require(circSupply == CROSS_MSG_FEE + 1); - require(gw.getNetworkName().equals(destinationSubnet.commonParent(from))); - require(gw.appliedTopDownNonce() == 1); - } - - function reward(uint256 amount) external view { - console.log("reward method called with %d", amount); - } - - function test_SendCrossMessage_Works_BottomUp_CurrentNetworkNotCommonParent() public { - address receiver = vm.addr(101); - address caller = vm.addr(100); - - vm.prank(receiver); - vm.deal(receiver, MIN_COLLATERAL_AMOUNT); - registerSubnetGW(MIN_COLLATERAL_AMOUNT, receiver, gw2); - - vm.prank(caller); - vm.deal(caller, CROSS_MSG_FEE + 2); - - SubnetID memory network2 = gw2.getNetworkName(); - address[] memory destinationPath = new address[](1); - destinationPath[0] = ROOTNET_ADDRESS; - SubnetID memory destinationSubnet = SubnetID({root: ROOTNET_CHAINID, route: destinationPath}); - - CrossMsg memory crossMsg = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: network2, rawAddress: FvmAddressHelper.from(caller)}), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(receiver)}), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }); - - vm.prank(caller); - gw2.sendCrossMessage{value: CROSS_MSG_FEE + 1}(crossMsg); - - require(crossMsg.message.applyType(gw2.getNetworkName()) == IPCMsgType.BottomUp); - require(gw2.appliedTopDownNonce() == 0); - require(gw2.bottomUpNonce() == 1); - } - - function test_SendCrossMessage_Works_BottomUp_CurrentNetworkCommonParent() public { - // the receiver is a network 1 address, but we are declaring it is network2 so we can use it in the tests - address receiver = vm.addr(101); - address caller = vm.addr(100); - - vm.prank(caller); - vm.deal(caller, CROSS_MSG_FEE + 2); - SubnetID memory network2 = gw2.getNetworkName(); - address[] memory rootnetPath = new address[](1); - rootnetPath[0] = ROOTNET_ADDRESS; - SubnetID memory destinationSubnet = SubnetID({root: ROOTNET_CHAINID, route: rootnetPath}); - - CrossMsg memory crossMsg = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: network2, rawAddress: FvmAddressHelper.from(caller)}), - to: IPCAddress({subnetId: destinationSubnet, rawAddress: FvmAddressHelper.from(receiver)}), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: true - }); - - require(crossMsg.message.applyType(gw2.getNetworkName()) == IPCMsgType.BottomUp); - - vm.prank(caller); - - gw2.sendCrossMessage{value: CROSS_MSG_FEE + 1}(crossMsg); - - require(gw2.appliedTopDownNonce() == 0); - } - - function test_Propagate_Works_WithFeeRemainder() external { - address[] memory validators = setupValidators(); - address caller = validators[0]; - - bytes32 postboxId = setupWhiteListMethod(caller); - - vm.deal(caller, 1 ether); - vm.expectCall(caller, 1 ether - gw.crossMsgFee(), new bytes(0), 1); - vm.expectCall(address(this), 0, abi.encodeWithSelector(ISubnetActor.reward.selector, gw.crossMsgFee()), 1); - - vm.prank(caller); - gw.propagate{value: 1 ether}(postboxId); - - require(caller.balance == 1 ether - gw.crossMsgFee()); - } - - function test_Propagate_Works_NoFeeReminder() external { - address[] memory validators = setupValidators(); - address caller = validators[0]; - - uint256 fee = gw.crossMsgFee(); - - bytes32 postboxId = setupWhiteListMethod(caller); - - vm.deal(caller, fee); - vm.prank(caller); - - vm.expectCall(caller, 0, EMPTY_BYTES, 0); - - gw.propagate{value: fee}(postboxId); - require(caller.balance == 0, "caller.balance == 0"); - } - - function test_Propagate_Fails_NotEnoughFee() public { - address caller = vm.addr(100); - vm.deal(caller, 1 ether); - - vm.expectRevert(NotEnoughFee.selector); - gw.propagate(bytes32("")); - } - - function setupWhiteListMethod(address caller) internal returns (bytes32) { - address[] memory validators = setupValidators(); - - registerSubnet(MIN_COLLATERAL_AMOUNT, address(this)); - - CrossMsg memory crossMsg = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: gw.getNetworkName().createSubnetId(caller), - rawAddress: FvmAddressHelper.from(caller) - }), - to: IPCAddress({ - subnetId: gw.getNetworkName().createSubnetId(address(this)), - rawAddress: FvmAddressHelper.from(address(this)) - }), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: false - }); - - // we add a validator with 10 times as much weight as the default validator. - // This way we have 10/11 votes and we reach majority, setting the message in postbox - // addValidator(caller, 1000); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = crossMsg; - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[2]); - gw.submitTopDownCheckpoint(checkpoint); - - return crossMsg.toHash(); - } - - function test_SetMembership_Fails_NotSystemActor() public { - address caller = vm.addr(100); - - address[] memory validators = new address[](1); - validators[0] = caller; - uint256[] memory weights = new uint256[](1); - weights[0] = 100; - - vm.prank(caller); - vm.expectRevert(NotSystemActor.selector); - gw.setMembership(validators, weights); - } - - function test_SetMembership_Fails_ValidatorsAndWeightsNotEqual() public { - address[] memory validators = new address[](1); - validators[0] = vm.addr(100); - uint256[] memory weights = new uint256[](2); - weights[0] = 100; - weights[1] = 130; - - vm.prank(FilAddress.SYSTEM_ACTOR); - vm.expectRevert(ValidatorsAndWeightsLengthMismatch.selector); - gw.setMembership(validators, weights); - } - - function test_SetMembership_Fails_ZeroWeight() public { - address[] memory validators = new address[](1); - validators[0] = vm.addr(100); - uint256[] memory weights = new uint256[](1); - weights[0] = 0; - - vm.prank(FilAddress.SYSTEM_ACTOR); - vm.expectRevert(ValidatorWeightIsZero.selector); - gw.setMembership(validators, weights); - } - - function test_SetMembership_Works_MultipleValidators() public { - address[] memory validators = new address[](2); - validators[0] = vm.addr(100); - validators[1] = vm.addr(101); - uint256[] memory weights = new uint256[](2); - weights[0] = 100; - weights[1] = 150; - - vm.prank(FilAddress.SYSTEM_ACTOR); - gw.setMembership(validators, weights); - - require(gw.totalWeight() == 250); - } - - function test_SetMembership_Works_NewValidators() public { - addValidator(vm.addr(100), 100); - - require(gw.totalWeight() == 100); - - addValidator(vm.addr(101), 1000); - - require(gw.totalWeight() == 1000); - } - - function test_SubmitTopDownCheckpoint_Fails_NotSignableAccount() public { - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - - address validator = vm.addr(400); - vm.prank(validator); - vm.expectRevert(NotSignableAccount.selector); - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_EpochAlreadyExecuted() public { - address validator = address(100); - - addValidator(validator, 100); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - - vm.prank(validator); - vm.deal(validator, 1 ether); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validator); - vm.expectRevert(EpochAlreadyExecuted.selector); - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_EpochNotVotable() public { - address validator = vm.addr(100); - - addValidator(validator); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD + 1, - topDownMsgs: new CrossMsg[](0) - }); - - vm.prank(validator); - vm.expectRevert(EpochNotVotable.selector); - - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_ValidatorAlreadyVoted() public { - address[] memory validators = setupValidators(); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[0]); - vm.expectRevert(ValidatorAlreadyVoted.selector); - - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_NotInitialized() public { - address[] memory path = new address[](2); - path[0] = address(0); - path[1] = address(1); - - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: path}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - address validator = vm.addr(100); - - addValidator(validator, 100); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - - vm.prank(validator); - vm.deal(validator, 1); - vm.expectRevert(NotInitialized.selector); - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_NotValidator() public { - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - - address nonValidator = vm.addr(400); - vm.prank(nonValidator); - vm.deal(nonValidator, 1); - vm.expectRevert(NotValidator.selector); - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_MessagesNotSorted() public { - address[] memory validators = setupValidators(); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](2); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 10, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - topDownMsgs[1] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 0, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validators[0]); - vm.expectRevert(MessagesNotSorted.selector); - - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_NotEnoughBalance() public { - address validator = address(100); - - addValidator(validator, 100); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 100 ether, - nonce: 10, - method: METHOD_SEND, - params: EMPTY_BYTES - }), - wrapped: false - }); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validator); - vm.expectRevert(NotEnoughBalance.selector); - - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_InvalidCrossMsgDestinationSubnet() public { - address validator = address(100); - - addValidator(validator, 100); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({ - subnetId: SubnetID(0, new address[](0)), - rawAddress: FvmAddressHelper.from(address(this)) - }), - value: 0, - nonce: 10, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validator); - vm.deal(validator, 1); - vm.expectRevert(InvalidCrossMsgDestinationSubnet.selector); - - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Fails_TopDownInvalidCrossMsgNonce() public { - address validator = address(100); - - addValidator(validator, 100); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - // apply type = topdown - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 10, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validator); - vm.deal(validator, 1); - vm.expectRevert(InvalidCrossMsgNonce.selector); - - gw.submitTopDownCheckpoint(checkpoint); - } - - function test_SubmitTopDownCheckpoint_Works_ConsensusReachedAndExecuted() public { - address[] memory validators = setupValidators(); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(validators[0])}), - value: address(gw).balance, - nonce: 0, - method: METHOD_SEND, - params: EMPTY_BYTES - }), - wrapped: false - }); - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[2]); - - gw.submitTopDownCheckpoint(checkpoint); - - (, uint256 first, uint256 last) = gw.executableQueue(); - - require(gw.lastVotingExecutedEpoch() == checkpoint.epoch); - require(first == 0); - require(last == 0); - } - - // FIXME: This test was written by Limechain and is flaky so we disabled - // until we figure out what it does and the best way to fix it. - // function test_SubmitTopDownCheckpoint_FuzzNumberOfMessages(uint256 n) public { - // vm.assume(n < 19594); // TODO: test with different memory limit - // address[] memory validators = new address[](1); - // validators[0] = vm.addr(100); - // vm.deal(validators[0], 1); - // uint256[] memory weights = new uint[](1); - // weights[0] = 100; - - // vm.prank(FilAddress.SYSTEM_ACTOR); - // gw.setMembership(validators, weights); - - // CrossMsg[] memory topDownMsgs = new CrossMsg[](n); - // for (uint64 i = 0; i < n; i++) { - // topDownMsgs[i] = CrossMsg({ - // message: StorableMsg({ - // from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - // to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - // value: 0, - // nonce: i, - // method: this.callback.selector, - // params: EMPTY_BYTES - // }), - // wrapped: false - // }); - // } - - // TopDownCheckpoint memory checkpoint = TopDownCheckpoint({ - // epoch: DEFAULT_CHECKPOINT_PERIOD, - // topDownMsgs: topDownMsgs - // }); - - // vm.prank(validators[0]); - // gw.submitTopDownCheckpoint(checkpoint); - // } - - function test_SubmitTopDownCheckpoint_Works_ConsensusReachedAndAddedToQueue() public { - address[] memory validators = setupValidators(); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 0, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - uint64 nextEpoch = DEFAULT_CHECKPOINT_PERIOD + 10; - TopDownCheckpoint memory checkpoint = TopDownCheckpoint({epoch: nextEpoch, topDownMsgs: topDownMsgs}); - - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(checkpoint); - - vm.prank(validators[2]); - // should not call - vm.expectCall(address(this), abi.encodeWithSelector(this.callback.selector), 0); - gw.submitTopDownCheckpoint(checkpoint); - - (, uint256 first, uint256 last) = gw.executableQueue(); - - require(gw.lastVotingExecutedEpoch() == 0); - require(first == nextEpoch); - require(last == nextEpoch); - } - - function test_SubmitTopDownCheckpoint_Works_ConsensusReachedAndExecuteNext() public { - address[] memory validators = setupValidators(); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 0, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - - TopDownCheckpoint memory currentCheckpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - TopDownCheckpoint memory futureCheckpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD + 10, - topDownMsgs: topDownMsgs - }); - - // reaching consensus for the future checkpoint, so it should be added to the queue - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(futureCheckpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(futureCheckpoint); - - vm.prank(validators[2]); - gw.submitTopDownCheckpoint(futureCheckpoint); - - // reaching consensus for the current checkpoint, but since it contains 0 cross msgs - // it should execute the first checkpoint from the queue - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(currentCheckpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(currentCheckpoint); - - vm.prank(validators[2]); - // should not call - vm.expectCall(address(this), abi.encodeWithSelector(this.callback.selector), 1); - gw.submitTopDownCheckpoint(currentCheckpoint); - - (, uint256 first, uint256 last) = gw.executableQueue(); - - require(gw.lastVotingExecutedEpoch() == futureCheckpoint.epoch); - require(first == 0); - require(last == 0); - } - - function test_SubmitTopDownCheckpoint_Works_ConsensusReachedButNextEpochInFuture() public { - address[] memory validators = setupValidators(); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 0, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - - uint64 nextEpoch = DEFAULT_CHECKPOINT_PERIOD + 50; - TopDownCheckpoint memory currentCheckpoint = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - TopDownCheckpoint memory futureCheckpoint = TopDownCheckpoint({epoch: nextEpoch, topDownMsgs: topDownMsgs}); - - // reaching consensus for the future checkpoint, so it should be added to the queue - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(futureCheckpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(futureCheckpoint); - - vm.prank(validators[2]); - gw.submitTopDownCheckpoint(futureCheckpoint); - - // reaching consensus for the current checkpoint, but since it contains 0 cross msgs - // it should execute the first checkpoint from the queue, but since it's far in the future - // it should not execute anything - - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(currentCheckpoint); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(currentCheckpoint); - - vm.prank(validators[2]); - // should not call - vm.expectCall(address(this), abi.encodeWithSelector(this.callback.selector), 0); - gw.submitTopDownCheckpoint(currentCheckpoint); - - (, uint256 first, uint256 last) = gw.executableQueue(); - - require(gw.lastVotingExecutedEpoch() == DEFAULT_CHECKPOINT_PERIOD); - require(first == nextEpoch); - require(last == nextEpoch); - } - - function test_SubmitTopDownCheckpoint_Works_RoundAbort() public { - address[] memory validators = setupValidators(); - - CrossMsg[] memory topDownMsgs = new CrossMsg[](1); - topDownMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: gw.getNetworkName(), rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: 0, - method: this.callback.selector, - params: EMPTY_BYTES - }), - wrapped: false - }); - - TopDownCheckpoint memory checkpoint1 = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: new CrossMsg[](0) - }); - TopDownCheckpoint memory checkpoint2 = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - topDownMsgs[0].wrapped = true; - TopDownCheckpoint memory checkpoint3 = TopDownCheckpoint({ - epoch: DEFAULT_CHECKPOINT_PERIOD, - topDownMsgs: topDownMsgs - }); - - vm.prank(validators[0]); - gw.submitTopDownCheckpoint(checkpoint1); - - vm.prank(validators[1]); - gw.submitTopDownCheckpoint(checkpoint2); - - vm.prank(validators[2]); - gw.submitTopDownCheckpoint(checkpoint3); - - (, uint256 first, uint256 last) = gw.executableQueue(); - - require(gw.lastVotingExecutedEpoch() == 0); - require(first == 0); - require(last == 0); - } - - function setupValidators() internal returns (address[] memory) { - address validator1 = vm.addr(100); - address validator2 = vm.addr(200); - address validator3 = vm.addr(300); - address[] memory validators = new address[](3); - uint256[] memory weights = new uint256[](3); - - vm.deal(validator1, 1); - vm.deal(validator2, 1); - vm.deal(validator3, 1); - - validators[0] = validator1; - validators[1] = validator2; - validators[2] = validator3; - - weights[0] = 100; - weights[1] = 100; - weights[2] = 100; - - vm.prank(FilAddress.SYSTEM_ACTOR); - gw.setMembership(validators, weights); - - return validators; - } - - function addValidator(address validator) internal { - addValidator(validator, 100); - } - - function addValidator(address validator, uint256 weight) internal { - address[] memory validators = new address[](1); - validators[0] = validator; - uint256[] memory weights = new uint256[](1); - weights[0] = weight; - - vm.deal(validator, 1); - vm.prank(FilAddress.SYSTEM_ACTOR); - gw.setMembership(validators, weights); - } - - function callback() public view { - console.log("callback called"); - } - - function fund(address funderAddress, uint256 fundAmount) internal { - uint256 fundAmountWithSubtractedFee = fundAmount - gw.crossMsgFee(); - - (SubnetID memory subnetId, , uint256 nonceBefore, , uint256 circSupplyBefore, ) = getSubnet(address(sa)); - - uint256 expectedTopDownMsgsLenght = gw.getSubnetTopDownMsgsLength(subnetId) + 1; - uint256 expectedNonce = nonceBefore + 1; - uint256 expectedCircSupply = circSupplyBefore + fundAmountWithSubtractedFee; - - require(gw.crossMsgFee() > 0, "crossMsgFee is 0"); - - // vm.expectCall(address(sa), gw.crossMsgFee(), abi.encodeWithSelector(sa.reward.selector), 1); - - gw.fund{value: fundAmount}(subnetId, FvmAddressHelper.from(funderAddress)); - - (, , uint256 nonce, , uint256 circSupply, ) = getSubnet(address(sa)); - - require(gw.getSubnetTopDownMsgsLength(subnetId) == expectedTopDownMsgsLenght, "td nonce not same"); - - require(nonce == expectedNonce, "nonce not equal"); - require(circSupply == expectedCircSupply, "circ supply not equal"); - - for (uint256 msgIndex = 0; msgIndex < expectedTopDownMsgsLenght; msgIndex++) { - CrossMsg memory topDownMsg = gw.getSubnetTopDownMsg(subnetId, msgIndex); - - require(topDownMsg.message.nonce == msgIndex, "msg Index not euqla"); - require(topDownMsg.message.value == fundAmountWithSubtractedFee, "value not equal"); - require( - keccak256(abi.encode(topDownMsg.message.to)) == - keccak256( - abi.encode(IPCAddress({subnetId: subnetId, rawAddress: FvmAddressHelper.from(funderAddress)})) - ), - "td to not match" - ); - require( - keccak256(abi.encode(topDownMsg.message.from)) == - keccak256( - abi.encode( - IPCAddress({ - subnetId: subnetId.getParentSubnet(), - rawAddress: FvmAddressHelper.from(funderAddress) - }) - ) - ), - "td from not match" - ); - } - } - - function _join(address validatorAddress) internal { - vm.prank(validatorAddress); - vm.deal(validatorAddress, MIN_COLLATERAL_AMOUNT + 1); - sa.join{value: MIN_COLLATERAL_AMOUNT}(DEFAULT_NET_ADDR, FvmAddress({addrType: 1, payload: new bytes(20)})); - - require(sa.status() == Status.Active); - } - - function release(uint256 releaseAmount, uint256 crossMsgFee, uint64 epoch) internal { - (, , uint256 feeBefore, , ) = gw.bottomUpCheckpoints(epoch); - - uint256 expectedNonce = gw.bottomUpNonce() + 1; - uint256 expectedCheckpointDataFee = feeBefore + crossMsgFee; - - gw.release{value: releaseAmount}(FvmAddressHelper.from(msg.sender)); - - (, , uint256 fee, , ) = gw.bottomUpCheckpoints(epoch); - console.log("fee %d", fee); - console.log("expectedCheckpointDataFee: %d", expectedCheckpointDataFee); - - require(fee == expectedCheckpointDataFee, "cpDataAfter.fee == expectedCheckpointDataFee"); - require(gw.bottomUpNonce() == expectedNonce, "gw.bottomUpNonce() == expectedNonce"); - } - - function createCheckpoint( - address subnetAddress, - uint64 blockNumber - ) internal view returns (BottomUpCheckpoint memory) { - SubnetID memory subnetId = gw.getNetworkName().createSubnetId(subnetAddress); - BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ - source: subnetId, - epoch: blockNumber, - fee: 0, - crossMsgs: new CrossMsg[](0), - prevHash: EMPTY_HASH, - children: new ChildCheck[](0), - proof: new bytes(0) - }); - - return checkpoint; - } - - function addStake(uint256 stakeAmount, address subnetAddress) internal { - uint256 balanceBefore = subnetAddress.balance; - (, uint256 stakedBefore, , , , ) = getSubnet(subnetAddress); - - gw.addStake{value: stakeAmount}(); - - uint256 balanceAfter = subnetAddress.balance; - (, uint256 stakedAfter, , , , ) = getSubnet(subnetAddress); - - require(balanceAfter == balanceBefore - stakeAmount); - require(stakedAfter == stakedBefore + stakeAmount); - } - - function registerSubnetGW(uint256 collateral, address subnetAddress, Gateway gateway) internal { - gateway.register{value: collateral}(); - - (SubnetID memory id, uint256 stake, uint256 topDownNonce, , uint256 circSupply, Status status) = getSubnetGW( - subnetAddress, - gateway - ); - - SubnetID memory parentNetwork = gateway.getNetworkName(); - - require( - id.toHash() == parentNetwork.createSubnetId(subnetAddress).toHash(), - "id.toHash() == parentNetwork.createSubnetId(subnetAddress).toHash()" - ); - require(stake == collateral, "stake == collateral"); - require(topDownNonce == 0, "nonce == 0"); - require(circSupply == 0, "circSupply == 0"); - require(status == Status.Active, "status == Status.Active"); - } - - function registerSubnet(uint256 collateral, address subnetAddress) internal { - registerSubnetGW(collateral, subnetAddress, gw); - } - - function getSubnetGW( - address subnetAddress, - Gateway gateway - ) internal view returns (SubnetID memory, uint256, uint256, uint256, uint256, Status) { - SubnetID memory subnetId = gateway.getNetworkName().createSubnetId(subnetAddress); - - ( - Status status, - uint64 topDownNonce, - uint256 appliedBottomUpNonce, - uint256 stake, - , - uint256 circSupply, - SubnetID memory id, - - ) = gateway.subnets(subnetId.toHash()); - - return (id, stake, topDownNonce, appliedBottomUpNonce, circSupply, status); - } - - function getSubnet( - address subnetAddress - ) internal view returns (SubnetID memory, uint256, uint256, uint256, uint256, Status) { - return getSubnetGW(subnetAddress, gw); - } -} diff --git a/test/GatewayDiamond.t.sol b/test/GatewayDiamond.t.sol index ae804b481..d73bc40c6 100644 --- a/test/GatewayDiamond.t.sol +++ b/test/GatewayDiamond.t.sol @@ -428,7 +428,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(gwGetter2.initialized() == true); require(gwGetter2.getGenesisEpoch() == 50); } - + function testGatewayDiamond_GatewayDiamond_InitGenesisEpoch_Fails_NotSystemActor() public { vm.expectRevert(NotSystemActor.selector); gwManager.initGenesisEpoch(50); diff --git a/test/SubnetActor.t.sol b/test/SubnetActor.t.sol deleted file mode 100644 index 875fba348..000000000 --- a/test/SubnetActor.t.sol +++ /dev/null @@ -1,1048 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import {EMPTY_BYTES} from "../src/constants/Constants.sol"; - -import "../src/SubnetActor.sol"; -import "../src/Gateway.sol"; -import "../src/enums/Status.sol"; -import "../src/structs/Subnet.sol"; -import "../src/structs/FvmAddress.sol"; -import "../src/lib/SubnetIDHelper.sol"; -import "../src/lib/CheckpointHelper.sol"; -import "../src/lib/FvmAddressHelper.sol"; - -contract SubnetActorTest is Test { - using SubnetIDHelper for SubnetID; - using CheckpointHelper for BottomUpCheckpoint; - - SubnetActor sa; - Gateway gw; - - address private constant DEFAULT_IPC_GATEWAY_ADDR = address(1024); - uint64 constant DEFAULT_CHECKPOINT_PERIOD = 10; - bytes32 private constant DEFAULT_NETWORK_NAME = bytes32("test"); - uint256 private constant DEFAULT_MIN_VALIDATOR_STAKE = 1 ether; - uint64 private constant DEFAULT_MIN_VALIDATORS = 1; - string private constant DEFAULT_NET_ADDR = "netAddr"; - bytes private constant GENESIS = EMPTY_BYTES; - uint256 constant CROSS_MSG_FEE = 10 gwei; - uint8 private constant DEFAULT_MAJORITY_PERCENTAGE = 70; - uint64 private constant ROOTNET_CHAINID = 123; - address GATEWAY_ADDRESS; - - error NotGateway(); - error NotAccount(); - error CollateralIsZero(); - error CallerHasNoStake(); - error SubnetAlreadyKilled(); - error NotAllValidatorsHaveLeft(); - error NotValidator(); - error SubnetNotActive(); - error WrongCheckpointSource(); - error CheckpointNotChained(); - error NoRewardsSentForDistribution(); - error NoValidatorsInSubnet(); - error NotEnoughBalanceForRewards(); - error EpochAlreadyExecuted(); - error EpochNotVotable(); - error ValidatorAlreadyVoted(); - error MessagesNotSorted(); - error NoRewardToWithdraw(); - error GatewayCannotBeZero(); - - function setUp() public { - Gateway.ConstructorParams memory constructorParams = Gateway.ConstructorParams({ - networkName: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - msgFee: CROSS_MSG_FEE, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE - }); - gw = new Gateway(constructorParams); - - GATEWAY_ADDRESS = address(gw); - - _assertDeploySubnetActor( - DEFAULT_NETWORK_NAME, - GATEWAY_ADDRESS, - ConsensusType.Mir, - DEFAULT_MIN_VALIDATOR_STAKE, - DEFAULT_MIN_VALIDATORS, - DEFAULT_CHECKPOINT_PERIOD, - GENESIS, - DEFAULT_MAJORITY_PERCENTAGE - ); - } - - function test_Deployment_Works( - bytes32 _networkName, - address _ipcGatewayAddr, - uint256 _minActivationCollateral, - uint64 _minValidators, - uint64 _checkPeriod, - bytes calldata _genesis, - uint8 _majorityPercentage - ) public { - vm.assume(_minActivationCollateral > DEFAULT_MIN_VALIDATOR_STAKE); - vm.assume(_checkPeriod > DEFAULT_CHECKPOINT_PERIOD); - vm.assume(_majorityPercentage <= 100); - vm.assume(_ipcGatewayAddr != address(0)); - - _assertDeploySubnetActor( - _networkName, - _ipcGatewayAddr, - ConsensusType.Mir, - _minActivationCollateral, - _minValidators, - _checkPeriod, - _genesis, - _majorityPercentage - ); - - SubnetID memory parent = sa.getParent(); - require(parent.isRoot(), "parent.isRoot()"); - } - - function test_Deployments_Fail_GatewayCannotBeZero() public { - vm.expectRevert(GatewayCannotBeZero.selector); - - new SubnetActor( - SubnetActor.ConstructParams({ - parentId: SubnetID(ROOTNET_CHAINID, new address[](0)), - name: DEFAULT_NETWORK_NAME, - ipcGatewayAddr: address(0), - consensus: ConsensusType.Mir, - minActivationCollateral: DEFAULT_MIN_VALIDATOR_STAKE, - minValidators: DEFAULT_MIN_VALIDATORS, - bottomUpCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - topDownCheckPeriod: DEFAULT_CHECKPOINT_PERIOD, - majorityPercentage: DEFAULT_MAJORITY_PERCENTAGE, - genesis: EMPTY_BYTES - }) - ); - } - - function test_Receive_Fail_NotGateway() public { - vm.expectRevert(NotGateway.selector); - (bool success, ) = payable(address(sa)).call{value: 1}(""); - require(success); - } - - function test_Receive_Works() public { - vm.prank(GATEWAY_ADDRESS); - vm.deal(GATEWAY_ADDRESS, 1); - (bool success, ) = payable(address(sa)).call{value: 1}(""); - require(success); - } - - function test_Join_Fail_NoMinColalteral() public { - address validator = vm.addr(100); - - vm.deal(validator, 1 gwei); - vm.prank(validator); - vm.expectRevert(CollateralIsZero.selector); - - sa.join(DEFAULT_NET_ADDR, FvmAddress({addrType: 1, payload: new bytes(20)})); - } - - function test_Join_Fail_NotAccount() public { - address contractAddress = address(sa); - vm.deal(contractAddress, 1 gwei); - vm.prank(contractAddress); - vm.expectRevert(NotAccount.selector); - - sa.join(DEFAULT_NET_ADDR, FvmAddress({addrType: 1, payload: new bytes(20)})); - } - - function test_Join_Fail_AlreadyKilled() public { - address validator = vm.addr(1235); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - _assertLeave(validator, DEFAULT_MIN_VALIDATOR_STAKE); - _assertKill(validator); - - vm.expectRevert(SubnetAlreadyKilled.selector); - vm.prank(validator); - vm.deal(validator, DEFAULT_MIN_VALIDATOR_STAKE + 1); - - sa.join{value: DEFAULT_MIN_VALIDATOR_STAKE}( - DEFAULT_NET_ADDR, - FvmAddress({addrType: 1, payload: new bytes(20)}) - ); - } - - function test_Join_Works_CallAddStake() public { - address validator = vm.addr(1235); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - vm.expectCall(GATEWAY_ADDRESS, DEFAULT_MIN_VALIDATOR_STAKE, abi.encodeWithSelector(gw.addStake.selector), 1); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - require(sa.validatorCount() == 1); - require(sa.validatorAt(0) == validator); - } - - function test_Join_Works_CallRegister() public { - address validator = vm.addr(1235); - - vm.expectCall(GATEWAY_ADDRESS, DEFAULT_MIN_VALIDATOR_STAKE, abi.encodeWithSelector(gw.register.selector), 1); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - } - - function test_Join_Works_LessThanMinStake() public { - address validator = vm.addr(1235); - uint256 amount = DEFAULT_MIN_VALIDATOR_STAKE / 2; - vm.deal(validator, amount + 1); - vm.prank(validator); - vm.expectCall(GATEWAY_ADDRESS, amount, abi.encodeWithSelector(gw.register.selector), 0); - vm.expectCall(GATEWAY_ADDRESS, amount, abi.encodeWithSelector(gw.addStake.selector), 0); - sa.join{value: amount}(DEFAULT_NET_ADDR, FvmAddress({addrType: 1, payload: new bytes(20)})); - - require(sa.validatorCount() == 0); - } - - function test_Join_Works_MultipleNewValidators() public { - _assertJoin(vm.addr(1234), DEFAULT_MIN_VALIDATOR_STAKE); - _assertJoin(vm.addr(1235), DEFAULT_MIN_VALIDATOR_STAKE); - - require(sa.validatorCount() == 2); - } - - function test_Join_Works_NoNewValidator_CollateralNotEnough() public { - address validator = vm.addr(1235); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE - 1); - - require(sa.validatorCount() == 0); - require(sa.status() == Status.Instantiated); - } - - function test_Join_Works_ReactivateSubnet() public { - _assertJoin(vm.addr(1234), DEFAULT_MIN_VALIDATOR_STAKE); - _assertLeave(vm.addr(1234), DEFAULT_MIN_VALIDATOR_STAKE); - - require(sa.totalStake() == 0); - require(sa.validatorCount() == 0); - require(sa.status() == Status.Inactive); - - _assertJoin(vm.addr(1235), DEFAULT_MIN_VALIDATOR_STAKE); - - require(sa.validatorCount() == 1); - require(sa.status() == Status.Active); - } - - function test_Leave_Works_NoValidatorsLeft() public payable { - address validator = address(1235); - uint256 amount = DEFAULT_MIN_VALIDATOR_STAKE; - - _assertJoin(validator, amount); - - _assertLeave(validator, amount); - - require(sa.totalStake() == 0); - require(sa.validatorCount() == 0); - require(sa.status() == Status.Inactive); - } - - function test_Leave_Works_StillActive() public payable { - address validator1 = address(1234); - address validator2 = address(1235); - uint256 amount = DEFAULT_MIN_VALIDATOR_STAKE; - - _assertJoin(validator1, amount); - _assertJoin(validator2, amount); - - _assertLeave(validator1, amount); - - require(sa.totalStake() == amount); - require(sa.validatorCount() == 1); - require(sa.status() == Status.Active); - } - - function test_Leave_Fail_NotAccount() public payable { - address contractAddress = address(sa); - uint256 amount = DEFAULT_MIN_VALIDATOR_STAKE; - - vm.prank(contractAddress); - vm.deal(contractAddress, amount); - vm.expectRevert(NotAccount.selector); - - sa.leave(); - } - - function test_Leave_Fail_AlreadyKilled() public payable { - address validator = address(1235); - uint256 amount = DEFAULT_MIN_VALIDATOR_STAKE; - - _assertJoin(validator, amount); - - _assertLeave(validator, amount); - _assertKill(validator); - - vm.prank(validator); - vm.deal(validator, amount); - vm.expectRevert(SubnetAlreadyKilled.selector); - - sa.leave(); - } - - function test_Leave_Fail_NoStake() public payable { - address caller = address(1235); - - vm.prank(caller); - vm.deal(caller, 1 ether); - - vm.expectRevert(NotValidator.selector); - - sa.leave(); - } - - function test_Kill_Works() public payable { - address validator = address(1235); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - _assertLeave(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - _assertKill(validator); - - require(GATEWAY_ADDRESS.balance == 0); - require(gw.totalSubnets() == 0); - } - - function test_Kill_Fails_NotAccount() public payable { - address contractAddress = address(sa); - - vm.prank(contractAddress); - vm.expectRevert(NotAccount.selector); - sa.kill(); - } - - function test_Kill_Fails_NotAllValidatorsLeft() public payable { - address validator1 = address(1235); - address validator2 = address(1236); - - _assertJoin(validator1, DEFAULT_MIN_VALIDATOR_STAKE); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - - _assertLeave(validator1, DEFAULT_MIN_VALIDATOR_STAKE); - - vm.prank(validator1); - vm.expectRevert(NotAllValidatorsHaveLeft.selector); - sa.kill(); - } - - function test_Kill_Fails_AlreadyTerminating() public { - address validator = vm.addr(1235); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - _assertLeave(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - _assertKill(validator); - - vm.prank(validator); - vm.expectRevert(SubnetAlreadyKilled.selector); - - sa.kill(); - } - - function test_SubmitCheckpoint_Works_Executed() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - address validator2 = vm.addr(101); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - address validator3 = vm.addr(102); - _assertJoin(validator3, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint); - _assertVote(validator2, checkpoint); - - vm.expectCall( - GATEWAY_ADDRESS, - abi.encodeWithSelector(IGateway(GATEWAY_ADDRESS).commitChildCheck.selector, checkpoint) - ); - - _assertVote(validator3, checkpoint); - - (SubnetID memory source, uint64 epoch, uint256 fee, bytes32 prevHash, bytes memory proof) = sa - .committedCheckpoints(checkpoint.epoch); - - require(sa.prevExecutedCheckpointHash() == checkpoint.toHash()); - require(sa.lastVotingExecutedEpoch() == checkpoint.epoch); - require(source.toHash() == checkpoint.source.toHash()); - require(epoch == checkpoint.epoch); - require(fee == checkpoint.fee); - require(prevHash == checkpoint.prevHash); - require(proof.length == 0); - } - - function test_SubnetCheckpoint_Works_ExecutedFromQueue() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - address validator2 = vm.addr(101); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - address validator3 = vm.addr(102); - _assertJoin(validator3, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint1 = _createBottomUpCheckpoint(); - BottomUpCheckpoint memory checkpoint2 = _createBottomUpCheckpoint(); - BottomUpCheckpoint memory checkpoint3 = _createBottomUpCheckpoint(); - BottomUpCheckpoint memory checkpoint4 = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint1); - _assertVote(validator2, checkpoint1); - _assertVote(validator3, checkpoint1); - - require(sa.lastVotingExecutedEpoch() == checkpoint1.epoch); - require(sa.prevExecutedCheckpointHash() == checkpoint1.toHash()); - - // next epochs - checkpoint2.epoch = checkpoint1.epoch + DEFAULT_CHECKPOINT_PERIOD; - checkpoint3.epoch = checkpoint2.epoch + DEFAULT_CHECKPOINT_PERIOD; - checkpoint4.epoch = checkpoint3.epoch + DEFAULT_CHECKPOINT_PERIOD; - checkpoint2.crossMsgs[0].message.nonce = checkpoint1.crossMsgs[0].message.nonce + 1; - checkpoint3.crossMsgs[0].message.nonce = checkpoint2.crossMsgs[0].message.nonce + 1; - checkpoint4.crossMsgs[0].message.nonce = checkpoint3.crossMsgs[0].message.nonce + 1; - checkpoint2.prevHash = checkpoint1.toHash(); - checkpoint3.prevHash = checkpoint2.toHash(); - checkpoint4.prevHash = checkpoint3.toHash(); - - // add checkpoint 3 to the queue - _assertVote(validator, checkpoint3); - _assertVote(validator2, checkpoint3); - _assertVote(validator3, checkpoint3); - - // ensures no execution is triggered - require(sa.lastVotingExecutedEpoch() == checkpoint1.epoch); - require(sa.prevExecutedCheckpointHash() == checkpoint1.toHash()); - - // trigger execution of checkpoint 2 - _assertVote(validator, checkpoint2); - _assertVote(validator2, checkpoint2); - _assertVote(validator3, checkpoint2); - - require(sa.lastVotingExecutedEpoch() == checkpoint2.epoch); - require(sa.prevExecutedCheckpointHash() == checkpoint2.toHash()); - - // vote for checkpoint 4 and trigger execution of checkpoint 3 from the queue - _assertVote(validator, checkpoint4); - - (SubnetID memory source, uint64 epoch, uint256 fee, bytes32 prevHash, bytes memory proof) = sa - .committedCheckpoints(checkpoint3.epoch); - - require(sa.lastVotingExecutedEpoch() == checkpoint3.epoch); - require(sa.prevExecutedCheckpointHash() == checkpoint3.toHash()); - require(source.toHash() == checkpoint3.source.toHash()); - require(epoch == checkpoint3.epoch); - require(fee == checkpoint3.fee); - require(prevHash == checkpoint3.prevHash); - require(proof.length == 0); - } - - function test_SubmitCheckpoint_Works_RoundAbort() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - address validator2 = vm.addr(101); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint1 = _createBottomUpCheckpoint(); - BottomUpCheckpoint memory checkpoint2 = _createBottomUpCheckpoint(); - - checkpoint2.fee = 1; - - _assertVote(validator, checkpoint1); - - vm.prank(validator2); - - // should reset votes - sa.submitCheckpoint(checkpoint2); - - require(sa.hasValidatorVotedForSubmission(checkpoint1.epoch, validator) == false); - require(sa.hasValidatorVotedForSubmission(checkpoint2.epoch, validator2) == false); - require(sa.lastVotingExecutedEpoch() == 0); - - (, uint256 first, uint256 last) = sa.executableQueue(); - - require(first == 0); - require(last == 0); - } - - function test_SubmitCheckpoint_Fails_MessagesNotSorted() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - SubnetID memory subnetId = gw.getNetworkName().createSubnetId(address(sa)); - CrossMsg[] memory crossMsgs = new CrossMsg[](2); - crossMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(address(this)) - }), - to: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(address(this)) - }), - value: CROSS_MSG_FEE + 1, - nonce: 1, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: false - }); - crossMsgs[1] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(address(this)) - }), - to: IPCAddress({ - subnetId: SubnetID({root: ROOTNET_CHAINID, route: new address[](0)}), - rawAddress: FvmAddressHelper.from(address(this)) - }), - value: CROSS_MSG_FEE + 1, - nonce: 0, - method: METHOD_SEND, - params: new bytes(0) - }), - wrapped: false - }); - BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ - source: subnetId, - epoch: DEFAULT_CHECKPOINT_PERIOD, - fee: 0, - crossMsgs: crossMsgs, - prevHash: EMPTY_HASH, - children: new ChildCheck[](0), - proof: new bytes(0) - }); - - vm.expectRevert(MessagesNotSorted.selector); - vm.prank(validator); - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Works_CheckpointNotChained() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - address validator2 = vm.addr(101); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - address validator3 = vm.addr(102); - _assertJoin(validator3, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint1 = _createBottomUpCheckpoint(); - BottomUpCheckpoint memory checkpoint2 = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint1); - _assertVote(validator2, checkpoint1); - _assertVote(validator3, checkpoint1); - - require(sa.lastVotingExecutedEpoch() == checkpoint1.epoch); - require(sa.prevExecutedCheckpointHash() == checkpoint1.toHash()); - - // set next epoch - checkpoint2.epoch = checkpoint1.epoch + DEFAULT_CHECKPOINT_PERIOD; - - _assertVote(validator, checkpoint2); - _assertVote(validator2, checkpoint2); - - // not committed - vm.expectCall( - GATEWAY_ADDRESS, - abi.encodeWithSelector(IGateway(GATEWAY_ADDRESS).commitChildCheck.selector, checkpoint2), - 0 - ); - vm.prank(validator3); - - // should reset votes - sa.submitCheckpoint(checkpoint2); - - require(sa.hasValidatorVotedForSubmission(checkpoint2.epoch, validator) == false); - require(sa.hasValidatorVotedForSubmission(checkpoint2.epoch, validator2) == false); - require(sa.hasValidatorVotedForSubmission(checkpoint2.epoch, validator3) == false); - require(sa.lastVotingExecutedEpoch() == checkpoint1.epoch); - require(sa.prevExecutedCheckpointHash() == checkpoint1.toHash()); - - (, uint256 first, uint256 last) = sa.executableQueue(); - - require(first == 0); - require(last == 0); - } - - function callback() public view { - console.log("callback called"); - } - - function test_SubmitCheckpoint_Works_MostVotedWeightEqualToThreshold_Abort() public { - uint8 majorityPercentage = 50; - - _assertDeploySubnetActor( - DEFAULT_NETWORK_NAME, - GATEWAY_ADDRESS, - ConsensusType.Mir, - DEFAULT_MIN_VALIDATOR_STAKE, - DEFAULT_MIN_VALIDATORS, - DEFAULT_CHECKPOINT_PERIOD, - GENESIS, - majorityPercentage - ); - - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - address validator2 = vm.addr(101); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint1 = _createBottomUpCheckpoint(); - BottomUpCheckpoint memory checkpoint2 = _createBottomUpCheckpoint(); - - checkpoint2.crossMsgs[0].message.value = 1; - - _assertVote(validator, checkpoint1); - - vm.prank(validator2); - - // should reset votes - sa.submitCheckpoint(checkpoint2); - - require(sa.hasValidatorVotedForSubmission(checkpoint1.epoch, validator) == false); - require(sa.hasValidatorVotedForSubmission(checkpoint2.epoch, validator2) == false); - require(sa.lastVotingExecutedEpoch() == 0); - } - - function test_SubmitCheckpoint_Works_VoteForCheckpoint() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - address validator2 = vm.addr(101); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint); - - require(sa.hasValidatorVotedForSubmission(checkpoint.epoch, validator) == true); - require(sa.hasValidatorVotedForSubmission(checkpoint.epoch, validator2) == false); - require(sa.lastVotingExecutedEpoch() == 0); - } - - function test_SubmitCheckpoint_Works_EpochEqualToGenesisEpoch() public { - address validator = vm.addr(100); - - vm.roll(0); - - _assertDeploySubnetActor( - DEFAULT_NETWORK_NAME, - GATEWAY_ADDRESS, - ConsensusType.Mir, - DEFAULT_MIN_VALIDATOR_STAKE, - DEFAULT_MIN_VALIDATORS, - DEFAULT_CHECKPOINT_PERIOD, - GENESIS, - DEFAULT_MAJORITY_PERCENTAGE - ); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint); - - require(sa.hasValidatorVotedForSubmission(checkpoint.epoch, validator) == true); - require(sa.lastVotingExecutedEpoch() == checkpoint.epoch); - } - - function test_SubmitCheckpoint_Fails_NotAccount() public { - address validator = address(1235); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - vm.prank(validator); - vm.expectRevert(NotAccount.selector); - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Fails_SubnetNotActive() public { - address validator = address(1235); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - _assertLeave(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - vm.prank(validator); - vm.expectRevert(SubnetNotActive.selector); - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Fails_InvalidValidator() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - address notValidator = vm.addr(200); - vm.prank(notValidator); - vm.deal(notValidator, 1); - vm.expectRevert(NotValidator.selector); - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Fails_WrongSource() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - checkpoint.source = SubnetID(0, new address[](0)); - - vm.prank(validator); - vm.expectRevert(WrongCheckpointSource.selector); - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Fails_EpochAlreadyExecuted() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint); - - checkpoint.epoch = 1; - - vm.prank(validator); - vm.expectRevert(EpochAlreadyExecuted.selector); - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Fails_EpochNotVotable() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - checkpoint.epoch = 11; - - vm.prank(validator); - vm.expectRevert(EpochNotVotable.selector); - - sa.submitCheckpoint(checkpoint); - } - - function test_SubmitCheckpoint_Fails_ValidatorAlreadyVoted() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - address validator2 = vm.addr(200); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - _assertVote(validator, checkpoint); - - vm.expectRevert(ValidatorAlreadyVoted.selector); - vm.prank(validator); - - sa.submitCheckpoint(checkpoint); - } - - function test_Reward_Works_SingleValidator() public { - address validator = vm.addr(100); - - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - vm.startPrank(GATEWAY_ADDRESS); - vm.deal(GATEWAY_ADDRESS, 1 ether); - - sa.reward(1 ether); - - require(sa.accumulatedRewards(validator) == 1 ether); - // sa.reward{value: 1}(); - - // require(validator.balance == balanceBefore + 1); - } - - function test_Reward_Works_MultipleValidators() public { - address validator1 = vm.addr(100); - address validator2 = vm.addr(101); - - _assertJoin(validator1, DEFAULT_MIN_VALIDATOR_STAKE); - _assertJoin(validator2, DEFAULT_MIN_VALIDATOR_STAKE); - - uint256 validator1BalanceBefore = sa.accumulatedRewards(validator1); - uint256 validator2BalanceBefore = sa.accumulatedRewards(validator2); - - vm.startPrank(GATEWAY_ADDRESS); - vm.deal(GATEWAY_ADDRESS, 1 ether); - - sa.reward(110); - - require(sa.accumulatedRewards(validator1) - validator1BalanceBefore == 55); - require(sa.accumulatedRewards(validator2) - validator2BalanceBefore == 55); - } - - function test_Reward_Fails_NoValidatorsInSubnet() public { - vm.startPrank(GATEWAY_ADDRESS); - vm.deal(GATEWAY_ADDRESS, 1 ether); - vm.expectRevert(NoValidatorsInSubnet.selector); - - sa.reward(1 ether); - } - - function test_Reward_Fails_NotGateway() public { - address notGatewayAddr = vm.addr(101); - - vm.startPrank(notGatewayAddr); - vm.deal(notGatewayAddr, 1 ether); - vm.expectRevert(NotGateway.selector); - - sa.reward(1 ether); - } - - function test_Reward_Fails_NotEnoughBalanceForRewards() public { - _assertJoin(vm.addr(100), DEFAULT_MIN_VALIDATOR_STAKE); - _assertJoin(vm.addr(101), DEFAULT_MIN_VALIDATOR_STAKE); - - vm.startPrank(GATEWAY_ADDRESS); - vm.deal(GATEWAY_ADDRESS, 1 ether); - vm.expectRevert(NotEnoughBalanceForRewards.selector); - - sa.reward(1); - } - - function test_Withdraw_Fails_NotAccount() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - vm.prank(address(this)); - vm.expectRevert(NotAccount.selector); - - sa.withdraw(); - } - - function test_Withdraw_Fails_NoRewardToWithdraw() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - vm.prank(validator); - vm.expectRevert(NoRewardToWithdraw.selector); - - sa.withdraw(); - } - - function test_Withdraw_Works() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - vm.deal(GATEWAY_ADDRESS, 1 ether + 1); - vm.prank(GATEWAY_ADDRESS); - sa.reward(1 ether); - - uint256 balanceBefore = validator.balance; - vm.prank(validator); - sa.withdraw(); - - require(validator.balance == balanceBefore + 1 ether); - require(sa.accumulatedRewards(validator) == 0); - } - - function _assertJoin(address validator, uint256 amount) internal { - vm.startPrank(validator); - vm.deal(validator, amount + 1); - - uint256 balanceBefore = validator.balance; - uint256 stakeBefore = sa.stake(validator); - uint256 totalStakeBefore = sa.totalStake(); - - sa.join{value: amount}(DEFAULT_NET_ADDR, FvmAddress({addrType: 1, payload: new bytes(20)})); - - require(sa.stake(validator) == stakeBefore + amount); - require(sa.totalStake() == totalStakeBefore + amount); - require(validator.balance == balanceBefore - amount); - - vm.stopPrank(); - } - - function _assertLeave(address validator, uint256 amount) internal { - uint256 validatorBalanceBefore = validator.balance; - uint256 validatorsCountBefore = sa.validatorCount(); - uint256 totalStakeBefore = sa.totalStake(); - - vm.prank(validator); - vm.expectCall(GATEWAY_ADDRESS, abi.encodeWithSelector(gw.releaseStake.selector, amount)); - vm.expectCall(validator, amount, EMPTY_BYTES); - - sa.leave(); - - require(sa.stake(validator) == 0); - require(sa.totalStake() == totalStakeBefore - amount); - require(sa.validatorCount() == validatorsCountBefore - 1); - require(validator.balance == validatorBalanceBefore + amount); - } - - function _assertKill(address validator) internal { - vm.startPrank(validator); - vm.deal(validator, 1 ether); - vm.expectCall(GATEWAY_ADDRESS, abi.encodeWithSelector(gw.kill.selector)); - - sa.kill(); - - require(sa.totalStake() == 0); - require(sa.validatorCount() == 0); - require(sa.status() == Status.Killed); - - vm.stopPrank(); - } - - function _assertVote(address validator, BottomUpCheckpoint memory checkpoint) internal { - vm.prank(validator); - sa.submitCheckpoint(checkpoint); - - require(sa.hasValidatorVotedForSubmission(checkpoint.epoch, validator) == true, "validator not voted"); - } - - function _assertDeploySubnetActor( - bytes32 _name, - address _ipcGatewayAddr, - ConsensusType _consensus, - uint256 _minActivationCollateral, - uint64 _minValidators, - uint64 _checkPeriod, - bytes memory _genesis, - uint8 _majorityPercentage - ) public { - SubnetID memory _parentId = gw.getNetworkName(); - - sa = new SubnetActor( - SubnetActor.ConstructParams({ - parentId: _parentId, - name: _name, - ipcGatewayAddr: _ipcGatewayAddr, - consensus: _consensus, - minActivationCollateral: _minActivationCollateral, - minValidators: _minValidators, - bottomUpCheckPeriod: _checkPeriod, - topDownCheckPeriod: _checkPeriod, - majorityPercentage: _majorityPercentage, - genesis: _genesis - }) - ); - - require( - keccak256(abi.encodePacked(sa.name())) == keccak256(abi.encodePacked(_name)), - "keccak256(abi.encodePacked(sa.name())) == keccak256(abi.encodePacked(_networkName))" - ); - require(sa.ipcGatewayAddr() == _ipcGatewayAddr, "sa.ipcGatewayAddr() == _ipcGatewayAddr"); - require( - sa.minActivationCollateral() == _minActivationCollateral, - "sa.minActivationCollateral() == _minActivationCollateral" - ); - require(sa.minValidators() == _minValidators, "sa.minValidators() == _minValidators"); - require(sa.topDownCheckPeriod() == _checkPeriod, "sa.topDownCheckPeriod() == _checkPeriod"); - require(keccak256(sa.genesis()) == keccak256(_genesis), "keccak256(sa.genesis()) == keccak256(_genesis)"); - require(sa.majorityPercentage() == _majorityPercentage, "sa.majorityPercentage() == _majorityPercentage"); - require(sa.consensus() == _consensus); - require( - sa.getParent().toHash() == _parentId.toHash(), - "parent.toHash() == SubnetID({root: ROOTNET_CHAINID, route: path}).toHash()" - ); - } - - function _createBottomUpCheckpointWithConfig( - uint64 epoch, - uint64 nonce, - bytes32 prevHash, - bytes memory proof - ) internal view returns (BottomUpCheckpoint memory checkpoint) { - SubnetID memory subnetActorId = sa.getParent().createSubnetId(address(sa)); - CrossMsg[] memory crossMsgs = new CrossMsg[](1); - - crossMsgs[0] = CrossMsg({ - message: StorableMsg({ - from: IPCAddress({subnetId: subnetActorId, rawAddress: FvmAddressHelper.from(address(this))}), - to: IPCAddress({subnetId: subnetActorId, rawAddress: FvmAddressHelper.from(address(this))}), - value: 0, - nonce: nonce, - method: this.callback.selector, - params: new bytes(0) - }), - wrapped: false - }); - - checkpoint = BottomUpCheckpoint({ - source: subnetActorId, - epoch: epoch, - fee: 0, - crossMsgs: crossMsgs, - prevHash: prevHash, - children: new ChildCheck[](0), - proof: proof - }); - } - - function _createBottomUpCheckpoint() internal view returns (BottomUpCheckpoint memory checkpoint) { - return _createBottomUpCheckpointWithConfig(DEFAULT_CHECKPOINT_PERIOD, 0, EMPTY_HASH, new bytes(0)); - } - - function invariant_BalanceEqualsTotalStake() public { - assertEq(address(gw).balance, sa.totalStake()); - assertEq(address(sa).balance, 0); - } - - function test_SubmitCheckpoint_Works_TwoRounds() public { - address validator = vm.addr(100); - _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); - - BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); - - vm.expectCall( - GATEWAY_ADDRESS, - abi.encodeWithSelector(IGateway(GATEWAY_ADDRESS).commitChildCheck.selector, checkpoint) - ); - _assertVote(validator, checkpoint); - - (SubnetID memory source, uint64 epoch, uint256 fee, bytes32 prevHash, bytes memory proof) = sa - .committedCheckpoints(checkpoint.epoch); - - require(sa.prevExecutedCheckpointHash() == checkpoint.toHash()); - require(sa.lastVotingExecutedEpoch() == checkpoint.epoch); - require(source.toHash() == checkpoint.source.toHash()); - require(epoch == checkpoint.epoch); - require(fee == checkpoint.fee); - require(prevHash == checkpoint.prevHash); - require(proof.length == 0); - - bytes memory testProof = abi.encodePacked("testProof"); - - BottomUpCheckpoint memory checkpoint2 = _createBottomUpCheckpointWithConfig( - 2 * DEFAULT_CHECKPOINT_PERIOD, - 1, - checkpoint.toHash(), - testProof - ); - - vm.expectCall( - GATEWAY_ADDRESS, - abi.encodeWithSelector(IGateway(GATEWAY_ADDRESS).commitChildCheck.selector, checkpoint2) - ); - _assertVote(validator, checkpoint2); - - (SubnetID memory source2, uint64 epoch2, uint256 fee2, bytes32 prevHash2, bytes memory proof2) = sa - .committedCheckpoints(checkpoint2.epoch); - - require(sa.prevExecutedCheckpointHash() == checkpoint2.toHash()); - require(sa.lastVotingExecutedEpoch() == checkpoint2.epoch); - require(source2.toHash() == checkpoint2.source.toHash()); - require(epoch2 == checkpoint2.epoch); - require(fee2 == checkpoint2.fee); - require(prevHash2 == checkpoint2.prevHash); - require(keccak256(proof2) == keccak256(checkpoint2.proof)); - } -} diff --git a/test/SubnetActorDiamond.t.sol b/test/SubnetActorDiamond.t.sol index 9bcfd0a17..ca187235f 100644 --- a/test/SubnetActorDiamond.t.sol +++ b/test/SubnetActorDiamond.t.sol @@ -775,6 +775,33 @@ contract SubnetActorDiamondTest is Test { require(saGetter.lastVotingExecutedEpoch() == checkpoint.epoch); } + function testSubnetActorDiamond_SubmitCheckpoint_Fails_EpochNotEqualToGenesisEpoch() public { + address validator = vm.addr(100); + + vm.roll(0); + + _assertDeploySubnetActor( + DEFAULT_NETWORK_NAME, + GATEWAY_ADDRESS, + ConsensusType.Mir, + DEFAULT_MIN_VALIDATOR_STAKE, + DEFAULT_MIN_VALIDATORS, + DEFAULT_CHECKPOINT_PERIOD, + GENESIS, + DEFAULT_MAJORITY_PERCENTAGE + ); + + _assertJoin(validator, DEFAULT_MIN_VALIDATOR_STAKE); + + BottomUpCheckpoint memory checkpoint = _createBottomUpCheckpoint(); + checkpoint.epoch = 0; + + vm.prank(validator); + vm.expectRevert(EpochAlreadyExecuted.selector); + saManager.submitCheckpoint(checkpoint); + + } + function testSubnetActorDiamond_SubmitCheckpoint_Fails_NotAccount() public { address validator = address(1235); From 33d2c27108b33a5c05678873beeb75b1be26077f Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Mon, 24 Jul 2023 19:59:52 +0200 Subject: [PATCH 3/6] fix linting --- test/GatewayDiamond.t.sol | 2 +- test/SubnetActorDiamond.t.sol | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/GatewayDiamond.t.sol b/test/GatewayDiamond.t.sol index d73bc40c6..ae804b481 100644 --- a/test/GatewayDiamond.t.sol +++ b/test/GatewayDiamond.t.sol @@ -428,7 +428,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(gwGetter2.initialized() == true); require(gwGetter2.getGenesisEpoch() == 50); } - + function testGatewayDiamond_GatewayDiamond_InitGenesisEpoch_Fails_NotSystemActor() public { vm.expectRevert(NotSystemActor.selector); gwManager.initGenesisEpoch(50); diff --git a/test/SubnetActorDiamond.t.sol b/test/SubnetActorDiamond.t.sol index ca187235f..e02c0dfac 100644 --- a/test/SubnetActorDiamond.t.sol +++ b/test/SubnetActorDiamond.t.sol @@ -799,7 +799,6 @@ contract SubnetActorDiamondTest is Test { vm.prank(validator); vm.expectRevert(EpochAlreadyExecuted.selector); saManager.submitCheckpoint(checkpoint); - } function testSubnetActorDiamond_SubmitCheckpoint_Fails_NotAccount() public { From 67c10ec32ce2d2614aabbe94d873e58f80b6e703 Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Mon, 24 Jul 2023 20:36:49 +0200 Subject: [PATCH 4/6] Add tests for libvoring --- test/LibVoting.t.sol | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/LibVoting.t.sol diff --git a/test/LibVoting.t.sol b/test/LibVoting.t.sol new file mode 100644 index 000000000..e9f2d0dde --- /dev/null +++ b/test/LibVoting.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import "../src/lib/LibVoting.sol"; + +contract LibVotingTest is Test { + function test_basic() public { + LibVoting.initVoting(50, 10); + require(LibVoting.MIN_CHECKPOINT_PERIOD == 10, "checkpoint period correct"); + + require(LibVoting.getSubmissionPeriod() == 10, "submission period correct"); + + LibVoting.initGenesisEpoch(15); + require(LibVoting.getGenesisEpoch() == 15, "genesis epoch correct"); + + vm.expectRevert(EpochNotVotable.selector); + LibVoting.applyValidEpochOnly(5); + } + + function test_fails_EpochAlreadyExecuted() public { + LibVoting.initVoting(50, 10); + require(LibVoting.getSubmissionPeriod() == 10, "submission period correct"); + LibVoting.initGenesisEpoch(15); + vm.expectRevert(EpochAlreadyExecuted.selector); + LibVoting.applyValidEpochOnly(0); + } + + function test_fails_EpochNotVotable() public { + LibVoting.initVoting(50, 10); + require(LibVoting.getSubmissionPeriod() == 10, "submission period correct"); + LibVoting.initGenesisEpoch(15); + vm.expectRevert(EpochNotVotable.selector); + LibVoting.applyValidEpochOnly(16); + } + + function test_valid_epoch() public { + LibVoting.initVoting(50, 10); + require(LibVoting.getSubmissionPeriod() == 10, "submission period correct"); + LibVoting.initGenesisEpoch(15); + LibVoting.applyValidEpochOnly(25); + } + + function test_fails_epoch_100() public { + LibVoting.initVoting(50, 10); + require(LibVoting.getSubmissionPeriod() == 10, "submission period correct"); + LibVoting.initGenesisEpoch(100); + vm.expectRevert(EpochNotVotable.selector); + LibVoting.applyValidEpochOnly(99); + } + + function test_works_epoch_100() public { + LibVoting.initVoting(50, 10); + require(LibVoting.getSubmissionPeriod() == 10, "submission period correct"); + LibVoting.initGenesisEpoch(100); + LibVoting.applyValidEpochOnly(100); + } +} From a751b71020f91499b2217cb41db4d3a479b42c34 Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Tue, 25 Jul 2023 13:45:58 +0200 Subject: [PATCH 5/6] add listSubnets --- src/gateway/GatewayGetterFacet.sol | 15 +++++++++++++++ src/gateway/GatewayManagerFacet.sol | 5 ++++- src/lib/LibGatewayActorStorage.sol | 2 ++ test/GatewayDiamond.t.sol | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/gateway/GatewayGetterFacet.sol b/src/gateway/GatewayGetterFacet.sol index 6c2b8a7a8..b22f6bfab 100644 --- a/src/gateway/GatewayGetterFacet.sol +++ b/src/gateway/GatewayGetterFacet.sol @@ -157,4 +157,19 @@ contract GatewayGetterFacet { function majorityPercentage() public view returns (uint64) { return LibVoting.majorityPercentage(); } + + /// @notice returns the list of registered subnets in IPC + /// @return subnet - the list of subnets + function listSubnets() external view returns (Subnet[] memory) { + uint256 size = s.subnetKeys.length; + Subnet[] memory out = new Subnet[](size); + for (uint256 i = 0; i < size; ) { + bytes32 key = s.subnetKeys[i]; + out[i] = s.subnets[key]; + unchecked { + ++i; + } + } + return out; + } } diff --git a/src/gateway/GatewayManagerFacet.sol b/src/gateway/GatewayManagerFacet.sol index 76427cc2a..af6d370ce 100644 --- a/src/gateway/GatewayManagerFacet.sol +++ b/src/gateway/GatewayManagerFacet.sol @@ -31,7 +31,7 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard { s.initialized = true; } - /// @notice register a subnet in the gateway. called by a subnet when it reaches the threshold stake + /// @notice register a subnet in the gateway. It is called by a subnet when it reaches the threshold stake function register() external payable { if (msg.value < s.minStake) { revert NotEnoughFunds(); @@ -49,6 +49,9 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard { subnet.stake = msg.value; subnet.status = Status.Active; subnet.genesisEpoch = block.number; + + s.subnetKeys.push(subnetId.toHash()); + s.totalSubnets += 1; } diff --git a/src/lib/LibGatewayActorStorage.sol b/src/lib/LibGatewayActorStorage.sol index 1360809c4..8c44f744f 100644 --- a/src/lib/LibGatewayActorStorage.sol +++ b/src/lib/LibGatewayActorStorage.sol @@ -28,6 +28,8 @@ struct GatewayActorStorage { /// @notice List of subnets /// SubnetID => Subnet mapping(bytes32 => Subnet) subnets; + /// @notice Keys of the registered subnets. Useful to iterate through them + bytes32[] subnetKeys; /// @notice bottom-up period in number of epochs for the subnet uint64 bottomUpCheckPeriod; /// @notice Postbox keeps track of all the cross-net messages triggered by diff --git a/test/GatewayDiamond.t.sol b/test/GatewayDiamond.t.sol index ae804b481..0b57fac35 100644 --- a/test/GatewayDiamond.t.sol +++ b/test/GatewayDiamond.t.sol @@ -451,6 +451,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); } + require(gwGetter.listSubnets().length == numberOfSubnets); require(gwGetter.totalSubnets() == numberOfSubnets); } From 78d95424663297c910cc0d108a802f5bc96eef92 Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Tue, 25 Jul 2023 13:53:16 +0200 Subject: [PATCH 6/6] Fix tests --- test/GatewayDiamond.t.sol | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/test/GatewayDiamond.t.sol b/test/GatewayDiamond.t.sol index 0b57fac35..3f5398d7e 100644 --- a/test/GatewayDiamond.t.sol +++ b/test/GatewayDiamond.t.sol @@ -282,7 +282,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(gwGetter.crossMsgFee() == CROSS_MSG_FEE); } - function testGatewayDiamond_GatewayDiamond_Constructor() public view { + function testGatewayDiamond_Constructor() public view { require(gwGetter.totalSubnets() == 0, "totalSubnets"); require(gwGetter.bottomUpNonce() == 0, "bottomUpNonce"); require(gwGetter.minStake() == MIN_COLLATERAL_AMOUNT, "minStake"); @@ -297,11 +297,11 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(gwGetter.majorityPercentage() == DEFAULT_MAJORITY_PERCENTAGE, "majorityPercentage"); } - function testGatewayDiamond_GatewayDiamond_LoupeFunction() public view { + function testGatewayDiamond_LoupeFunction() public view { require(louper.facets().length == 4); } - function testGatewayDiamond_GatewayDiamond_Deployment_Works_Root(uint64 checkpointPeriod) public { + function testGatewayDiamond_Deployment_Works_Root(uint64 checkpointPeriod) public { vm.assume(checkpointPeriod >= DEFAULT_CHECKPOINT_PERIOD); GatewayDiamond dep; @@ -335,7 +335,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { ); } - function testGatewayDiamond_GatewayDiamond_Deployment_Works_NotRoot(uint64 checkpointPeriod) public { + function testGatewayDiamond_Deployment_Works_NotRoot(uint64 checkpointPeriod) public { vm.assume(checkpointPeriod >= DEFAULT_CHECKPOINT_PERIOD); address[] memory path = new address[](2); @@ -397,7 +397,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(depGetter.majorityPercentage() == 100, "gw.majorityPercentage() == 100"); } - function testGatewayDiamond_GatewayDiamond_Register_Works_SingleSubnet(uint256 subnetCollateral) public { + function testGatewayDiamond_Register_Works_SingleSubnet(uint256 subnetCollateral) public { vm.assume(subnetCollateral >= MIN_COLLATERAL_AMOUNT && subnetCollateral < type(uint64).max); address subnetAddress = vm.addr(100); vm.prank(subnetAddress); @@ -405,6 +405,8 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { registerSubnet(subnetCollateral, subnetAddress); require(gwGetter.totalSubnets() == 1); + Subnet[] memory subnets = gw.listSubnets(); + require(subnets.length == 1, "subnets.length == 1"); SubnetID memory subnetId = gwGetter.getNetworkName().createSubnetId(subnetAddress); @@ -421,7 +423,7 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(id.equals(subnetId)); } - function testGatewayDiamond_GatewayDiamond_InitGenesisEpoch_Works() public { + function testGatewayDiamond_InitGenesisEpoch_Works() public { vm.prank(FilAddress.SYSTEM_ACTOR); gwManager2.initGenesisEpoch(50); @@ -429,18 +431,18 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { require(gwGetter2.getGenesisEpoch() == 50); } - function testGatewayDiamond_GatewayDiamond_InitGenesisEpoch_Fails_NotSystemActor() public { + function testGatewayDiamond_InitGenesisEpoch_Fails_NotSystemActor() public { vm.expectRevert(NotSystemActor.selector); gwManager.initGenesisEpoch(50); } - function testGatewayDiamond_GatewayDiamond_InitGenesisEpoch_Fails_AlreadyInitialized() public { + function testGatewayDiamond_InitGenesisEpoch_Fails_AlreadyInitialized() public { vm.prank(FilAddress.SYSTEM_ACTOR); vm.expectRevert(AlreadyInitialized.selector); gwManager.initGenesisEpoch(50); } - function testGatewayDiamond_GatewayDiamond_Register_Works_MultipleSubnets(uint8 numberOfSubnets) public { + function testGatewayDiamond_Register_Works_MultipleSubnets(uint8 numberOfSubnets) public { vm.assume(numberOfSubnets > 0); for (uint256 i = 1; i <= numberOfSubnets; i++) { @@ -451,18 +453,19 @@ contract GatewayDiamondDeploymentTest is StdInvariant, Test { registerSubnet(MIN_COLLATERAL_AMOUNT, subnetAddress); } - require(gwGetter.listSubnets().length == numberOfSubnets); require(gwGetter.totalSubnets() == numberOfSubnets); + Subnet[] memory subnets = gw.listSubnets(); + require(subnets.length == numberOfSubnets, "subnets.length == numberOfSubnets"); } - function testGatewayDiamond_GatewayDiamond_Register_Fail_InsufficientCollateral(uint256 collateral) public { + function testGatewayDiamond_Register_Fail_InsufficientCollateral(uint256 collateral) public { vm.assume(collateral < MIN_COLLATERAL_AMOUNT); vm.expectRevert(NotEnoughFunds.selector); gwManager.register{value: collateral}(); } - function testGatewayDiamond_GatewayDiamond_Register_Fail_SubnetAlreadyExists() public { + function testGatewayDiamond_Register_Fail_SubnetAlreadyExists() public { registerSubnet(MIN_COLLATERAL_AMOUNT, address(this)); vm.expectRevert(AlreadyRegisteredSubnet.selector);