From 849e784fb8c2a051317111f886e8730926c5ab8f Mon Sep 17 00:00:00 2001 From: apenzk Date: Wed, 19 Feb 2025 13:44:07 +0100 Subject: [PATCH] improving simpleStaking but postconfirmation does not work --- .../mcr/contracts/src/settlement/MCR.sol | 45 ++++-- .../contracts/src/staking/MovementStaking.sol | 18 ++- .../staking/interfaces/IMovementStaking.sol | 2 +- .../mcr/contracts/test/settlement/MCR.sol | 133 ++++++++++++++---- 4 files changed, 152 insertions(+), 46 deletions(-) diff --git a/protocol-units/settlement/mcr/contracts/src/settlement/MCR.sol b/protocol-units/settlement/mcr/contracts/src/settlement/MCR.sol index 15cf2c462..e73f6e6e5 100644 --- a/protocol-units/settlement/mcr/contracts/src/settlement/MCR.sol +++ b/protocol-units/settlement/mcr/contracts/src/settlement/MCR.sol @@ -152,7 +152,7 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { stakingContract.acceptGenesisCeremony(); } - function computeTotalStake( + function getTotalStake( uint256 epoch ) public view returns (uint256) { // we can either use the attesterStake or the custodianStake @@ -169,12 +169,12 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { } - function computeTotalStakeForAcceptingEpoch() + function getTotalStakeForAcceptingEpoch() public view returns (uint256) { - return computeTotalStake(getAcceptingEpoch()); + return getTotalStake(getAcceptingEpoch()); } // gets the total stake for the accepting epoch @@ -201,13 +201,14 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { } // Sets the last postconfirmed superBlock height. - function setLastPostconfirmedSuperBlockHeight(uint256 height) public { - require( - hasRole(COMMITMENT_ADMIN, msg.sender), - "SET_LAST_POSTCONFIRMED_SUPERBLOCK_HEIGHT_IS_COMMITMENT_ADMIN_ONLY" - ); - lastPostconfirmedSuperBlockHeight = height; - } + // TODO: i have commented this out (so reconfirm) as we do not want to allow the commitment admin to set the last postconfirmed superblock height. + // function setLastPostconfirmedSuperBlockHeight(uint256 height) public { + // require( + // hasRole(COMMITMENT_ADMIN, msg.sender), + // "SET_LAST_POSTCONFIRMED_SUPERBLOCK_HEIGHT_IS_COMMITMENT_ADMIN_ONLY" + // ); + // lastPostconfirmedSuperBlockHeight = height; + // } // Forces the latest attestation by setting the superBlock height // Note: this only safe when we are running with a single validator as it does not zero out follow-on commitments. @@ -287,7 +288,8 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { // if the current acceptor is live we should not accept postconfirmations from voluntary attesters // TODO: we probably have to apply this check somewhere else as (volunteer) attesters can only postconfirm and rollover an epoch in which they are staked. if (currentAcceptorIsLive()) { - if (attester != getCurrentAcceptor()) revert("NotAcceptorAndAcceptorIsLive"); + // TODO: for now everyone can postconfirm, but change this later + // if (attester != getCurrentAcceptor()) revert("NotAcceptorAndAcceptorIsLive"); } // keep ticking through postconfirmations and rollovers as long as the acceptor is permitted to do @@ -357,7 +359,7 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { // but since the operations we're doing are very cheap, the set actually adds overhead // TODO the supermajority is 2f+1 from 3f+1 nodes. Not 2f from 3f. - uint256 supermajority = (2 * computeTotalStake(superBlockEpoch)) / 3; + uint256 supermajority = (2 * getTotalStake(superBlockEpoch)) / 3; address[] memory attesters = getStakedAttestersForAcceptingEpoch(); // iterate over the attester set @@ -366,6 +368,10 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { for (uint256 i = 0; i < attesters.length; i++) { address attester = attesters[i]; SuperBlockCommitment memory superBlockCommitment = commitments[superBlockHeight][attester]; + // check if the commitment has committed to the correct superBlock height + // TODO: possibly this is not needed and we can remove the height from the commitment? + if (superBlockCommitment.height != superBlockHeight) continue; + // check the total stake on the commitment uint256 totalStakeOnCommitment = commitmentStakes[superBlockCommitment.height][superBlockCommitment.commitment]; if (totalStakeOnCommitment > supermajority) { @@ -480,4 +486,19 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR { // TODO: have an acceptor that can be set. currentAcceptor = attesters[acceptorIndex]; } + + /// @notice Gets the commitment submitted by an attester for a given height + function getCommitmentByAttester(uint256 height, address attester) public view returns (SuperBlockCommitment memory) { + return commitments[height][attester]; + } + + /// @notice Gets the height of the last postconfirmed superblock + function getLastPostconfirmedSuperBlockHeight() public view returns (uint256) { + return lastPostconfirmedSuperBlockHeight; + } + + /// @notice Gets the epoch assigned to a superblock height + function getSuperBlockHeightAssignedEpoch(uint256 height) public view returns (uint256) { + return superBlockHeightAssignedEpoch[height]; + } } diff --git a/protocol-units/settlement/mcr/contracts/src/staking/MovementStaking.sol b/protocol-units/settlement/mcr/contracts/src/staking/MovementStaking.sol index fee5accbd..a6bd7b71c 100644 --- a/protocol-units/settlement/mcr/contracts/src/staking/MovementStaking.sol +++ b/protocol-units/settlement/mcr/contracts/src/staking/MovementStaking.sol @@ -74,7 +74,7 @@ contract MovementStaking is uint256 activeAttesterCount = 0; for (uint256 i = 0; i < totalAttesters; i++) { address attester = registeredAttestersByDomain[domain].at(i); - if (computeAllStakeForCurrentAcceptingEpoch(attester) > 0) { + if (getAttesterStakeForAcceptingEpoch(domain, attester) > 0) { activeAttesterCount++; } } @@ -84,7 +84,7 @@ contract MovementStaking is uint256 activeIndex = 0; for (uint256 i = 0; i < totalAttesters; i++) { address attester = registeredAttestersByDomain[domain].at(i); - if (computeAllStakeForCurrentAcceptingEpoch(attester) > 0) { + if (getAttesterStakeForAcceptingEpoch(domain, attester) > 0) { activeAttesters[activeIndex] = attester; activeIndex++; } @@ -276,6 +276,18 @@ contract MovementStaking is getCustodianStake(domain, getAcceptingEpoch(domain), custodian); } + function getAttesterStake(address domain, uint256 epoch, address attester) public view returns (uint256) { + uint256 attesterStake = 0; + for (uint256 i = 0; i < registeredCustodiansByDomain[domain].length(); i++) { + attesterStake += getStake(domain, epoch, registeredCustodiansByDomain[domain].at(i), attester); + } + return attesterStake; + } + + function getAttesterStakeForAcceptingEpoch(address domain, address attester) public view returns (uint256) { + return getAttesterStake(domain, getAcceptingEpoch(domain), attester); + } + // stakes for the next epoch function stake( address domain, @@ -616,7 +628,7 @@ contract MovementStaking is /// @notice Computes total stake across all custodians and attesters for the current accepting epoch /// @param domain The domain to compute total stake for - function computeAllStakeForCurrentAcceptingEpoch( + function computeAllStakeForAcceptingEpoch( address domain ) public view returns (uint256) { return computeAllStake(domain, getAcceptingEpoch(domain)); diff --git a/protocol-units/settlement/mcr/contracts/src/staking/interfaces/IMovementStaking.sol b/protocol-units/settlement/mcr/contracts/src/staking/interfaces/IMovementStaking.sol index d9a7dd370..0d6849bc0 100644 --- a/protocol-units/settlement/mcr/contracts/src/staking/interfaces/IMovementStaking.sol +++ b/protocol-units/settlement/mcr/contracts/src/staking/interfaces/IMovementStaking.sol @@ -98,5 +98,5 @@ interface IMovementStaking { error GenesisAlreadyAccepted(); function getStakedAttestersForAcceptingEpoch(address domain) external view returns (address[] memory); - function computeAllStakeForCurrentAcceptingEpoch(address attester) external view returns (uint256); + function computeAllStakeForAcceptingEpoch(address attester) external view returns (uint256); } diff --git a/protocol-units/settlement/mcr/contracts/test/settlement/MCR.sol b/protocol-units/settlement/mcr/contracts/test/settlement/MCR.sol index 43079c6eb..04be7b0d1 100644 --- a/protocol-units/settlement/mcr/contracts/test/settlement/MCR.sol +++ b/protocol-units/settlement/mcr/contracts/test/settlement/MCR.sol @@ -117,15 +117,15 @@ contract MCRTest is Test, IMCR { assertNotEq(mcr.getCurrentAcceptor(), bob); // make a block commitment - MCRStorage.SuperBlockCommitment memory bc1 = MCRStorage.SuperBlockCommitment({ + MCRStorage.SuperBlockCommitment memory initCommitment = MCRStorage.SuperBlockCommitment({ height: 1, commitment: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))), blockId: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))) }); vm.prank(alice); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); vm.prank(bob); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); // TODO these tests need to be split up into different test functions (happy / unhappy path) // bob should not be the current acceptor @@ -149,6 +149,7 @@ contract MCRTest is Test, IMCR { } + /// @notice Test that the staking works as expected function testSimpleStaking() public { console.log("Starting testSimpleStaking"); @@ -185,55 +186,127 @@ contract MCRTest is Test, IMCR { console.log("Alice staking..."); vm.prank(alice); - staking.stake(address(mcr), moveToken, 34); - console.log("Alice staked amount:", mcr.getStakeForAcceptingEpoch(address(moveToken), alice)); + staking.stake(address(mcr), moveToken, 33); + uint256 aliceStake = mcr.getStakeForAcceptingEpoch(address(moveToken), alice); + console.log("Alice staked amount:", aliceStake); + assertEq(aliceStake, 33, "Alice's stake amount not correctly recorded"); + console.log("\nBob approving and staking..."); vm.prank(bob); moveToken.approve(address(staking), 100); vm.prank(bob); staking.stake(address(mcr), moveToken, 33); - console.log("Bob staked amount:", mcr.getStakeForAcceptingEpoch(address(moveToken), bob)); + uint256 bobStake = mcr.getStakeForAcceptingEpoch(address(moveToken), bob); + console.log("Bob staked amount:", bobStake); + assertEq(bobStake, 33, "Bob's stake amount not correctly recorded"); console.log("\nCarol approving and staking..."); vm.prank(carol); moveToken.approve(address(staking), 100); vm.prank(carol); - staking.stake(address(mcr), moveToken, 33); - console.log("Carol staked amount:", mcr.getStakeForAcceptingEpoch(address(moveToken), carol)); + staking.stake(address(mcr), moveToken, 34); + uint256 carolStake = mcr.getStakeForAcceptingEpoch(address(moveToken), carol); + console.log("Carol staked amount:", carolStake); + assertEq(carolStake, 34, "Carol's stake amount not correctly recorded"); + + // check that the total stake is 100 + assertEq(mcr.getTotalStakeForAcceptingEpoch(), 100, "Total stake should be 100"); + console.log("Total stake:", mcr.getTotalStakeForAcceptingEpoch()); + + // log the attester list + address[] memory attesters = staking.getStakedAttestersForAcceptingEpoch(address(mcr)); + console.log("Attesters:", attesters.length); + assertEq(attesters.length, 3, "There should be 3 attesters"); + for (uint256 i = 0; i < attesters.length; i++) { + console.log("Attester:", attesters[i]); + } // end the genesis ceremony console.log("\nAccepting genesis ceremony..."); mcr.acceptGenesisCeremony(); console.log("Genesis ceremony accepted"); - // make a block commitment - console.log("\nMaking block commitment..."); - MCRStorage.SuperBlockCommitment memory bc1 = MCRStorage.SuperBlockCommitment({ - height: 1, + + // check if there is a postconfirmed superblock + uint256 initHeight = mcr.getLastPostconfirmedSuperBlockHeight(); + console.log("Last postconfirmed superblock height:", initHeight); + // retrieve the postconfirmed superblock at that height + MCRStorage.SuperBlockCommitment memory retrievedCommitmentEmpty = mcr.getPostconfirmedCommitment(initHeight); + console.log("Retrieved commitment height:", retrievedCommitmentEmpty.height); + console.log("Retrieved commitment:", uint256(retrievedCommitmentEmpty.commitment)); + + // make a superBlock commitment + uint256 targetHeight = 1; + console.log("\nMaking superBlock commitment..."); + MCRStorage.SuperBlockCommitment memory initCommitment = MCRStorage.SuperBlockCommitment({ + height: targetHeight, commitment: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))), blockId: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))) }); console.log("Alice submitting commitment..."); vm.prank(alice); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); + // retrieve the commitment at height 1 (the one we submitted) + MCRStorage.SuperBlockCommitment memory retrievedCommitmentAlice = mcr.getCommitmentByAttester(targetHeight, alice); + console.log("Alice commitment :", uint256(retrievedCommitmentAlice.commitment)); + assert(retrievedCommitmentAlice.commitment == initCommitment.commitment); console.log("Bob submitting commitment..."); vm.prank(bob); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); + // retrieve the commitment + MCRStorage.SuperBlockCommitment memory retrievedCommitmentBob = mcr.getCommitmentByAttester(targetHeight, bob); + console.log("Bob commitment: ", uint256(retrievedCommitmentBob.commitment)); + assert(retrievedCommitmentBob.commitment == initCommitment.commitment); + + // who is the current acceptor? + // TODO: add acceptor role and check it is alice + // console.log("Current acceptor:", mcr.getCurrentAcceptor()); + + // check if acceptor is live, note that currentAcceptorIsLive() returns bool + console.log("Current acceptor is live:", mcr.currentAcceptorIsLive()); + assert(mcr.currentAcceptorIsLive()); + // check what is the assigned epoch for the height targetHeight + console.log("Assigned epoch for targetHeight:", mcr.getSuperBlockHeightAssignedEpoch(1)); + console.log("Accepting epoch:", mcr.getAcceptingEpoch()); + console.log("Present epoch:", mcr.getPresentEpoch()); + // in this setup the accepting epoch is the same as the assigned epoch for the targetHeight + assertEq(mcr.getSuperBlockHeightAssignedEpoch(1), mcr.getAcceptingEpoch()); + + + // alice postconfirms the superblock using attemptPostconfirm + console.log("Alice postconfirming..."); + vm.prank(alice); + // can we make this so we catch any errors from + mcr.postconfirmSuperBlocks(); + + // check that the commitment is postconfirmed at targetHeight + MCRStorage.SuperBlockCommitment memory retrievedCommitmentAlicePostconfirmed = mcr.getPostconfirmedCommitment(targetHeight); + console.log("Alice postconfirmed - height:", retrievedCommitmentAlicePostconfirmed.height); + console.log("Alice postconfirmed - commitment:", uint256(retrievedCommitmentAlicePostconfirmed.commitment)); + // check that the heights are correct + uint256 newHeight = mcr.getLastPostconfirmedSuperBlockHeight(); + console.log("Last postconfirmed superblock height:", newHeight); + assertEq(newHeight, initHeight + 1); + assertEq(targetHeight, newHeight); + + assert(retrievedCommitmentAlicePostconfirmed.commitment == initCommitment.commitment); + assert(retrievedCommitmentAlicePostconfirmed.height == targetHeight); + // now we move to block 2 and make some commitment just to trigger the epochRollover console.log("\nRetrieving commitment..."); - MCRStorage.SuperBlockCommitment memory retrievedCommitment = mcr.getPostconfirmedCommitment(1); + MCRStorage.SuperBlockCommitment memory retrievedCommitment = mcr.getPostconfirmedCommitment(targetHeight); console.log("Retrieved commitment height:", retrievedCommitment.height); - console.log("Expected height:", bc1.height); + console.log("Expected height:", initCommitment.height); console.log("Retrieved commitment:", toHexString(abi.encode(retrievedCommitment.commitment))); - console.log("Expected commitment:", toHexString(abi.encode(bc1.commitment))); + console.log("Expected commitment:", toHexString(abi.encode(initCommitment.commitment))); - assert(retrievedCommitment.commitment == bc1.commitment); - assert(retrievedCommitment.blockId == bc1.blockId); - assert(retrievedCommitment.height == 1); + assert(retrievedCommitment.commitment == initCommitment.commitment); + assert(retrievedCommitment.blockId == initCommitment.blockId); + assert(retrievedCommitment.height == targetHeight); } function testDishonestValidator() public { @@ -280,20 +353,20 @@ contract MCRTest is Test, IMCR { mcr.submitSuperBlockCommitment(dishonestCommitment); // make a block commitment - MCRStorage.SuperBlockCommitment memory bc1 = MCRStorage.SuperBlockCommitment({ + MCRStorage.SuperBlockCommitment memory initCommitment = MCRStorage.SuperBlockCommitment({ height: 1, commitment: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))), blockId: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))) }); vm.prank(alice); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); vm.prank(bob); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); MCRStorage.SuperBlockCommitment memory retrievedCommitment = mcr.getPostconfirmedCommitment(1); // now we move to block 2 and make some commitment just to trigger the epochRollover - assert(retrievedCommitment.commitment == bc1.commitment); - assert(retrievedCommitment.blockId == bc1.blockId); + assert(retrievedCommitment.commitment == initCommitment.commitment); + assert(retrievedCommitment.blockId == initCommitment.blockId); assert(retrievedCommitment.height == 1); } @@ -343,15 +416,15 @@ contract MCRTest is Test, IMCR { mcr.submitSuperBlockCommitment(dishonestCommitment); // make a block commitment - MCRStorage.SuperBlockCommitment memory bc1 = MCRStorage.SuperBlockCommitment({ + MCRStorage.SuperBlockCommitment memory initCommitment = MCRStorage.SuperBlockCommitment({ height: 1, commitment: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))), blockId: keccak256(abi.encodePacked(uint256(1), uint256(2), uint256(3))) }); vm.prank(alice); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); vm.prank(bob); - mcr.submitSuperBlockCommitment(bc1); + mcr.submitSuperBlockCommitment(initCommitment); // now we move to block 2 and make some commitment just to trigger the epochRollover vm.warp(310 seconds); @@ -371,8 +444,8 @@ contract MCRTest is Test, IMCR { assertEq(mcr.getStakeForAcceptingEpoch(address(moveToken), bob), 33); assertEq(mcr.getStakeForAcceptingEpoch(address(moveToken), carol), 33); MCRStorage.SuperBlockCommitment memory retrievedCommitment = mcr.getPostconfirmedCommitment(1); - assert(retrievedCommitment.commitment == bc1.commitment); - assert(retrievedCommitment.blockId == bc1.blockId); + assert(retrievedCommitment.commitment == initCommitment.commitment); + assert(retrievedCommitment.blockId == initCommitment.blockId); assert(retrievedCommitment.height == 1); }