Skip to content

Commit

Permalink
testSimpleStaker + testStakeActivationAndPostconfirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
apenzk committed Feb 25, 2025
1 parent 69ee71b commit 8c2144a
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 132 deletions.
37 changes: 19 additions & 18 deletions protocol-units/settlement/mcr/contracts/src/settlement/MCR.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR {
}

// gets the next epoch
function getNextAcceptingEpoch() public view returns (uint256) {
return stakingContract.getNextAcceptingEpoch(address(this));
function getNextAcceptingEpochWithException() public view returns (uint256) {
return stakingContract.getNextAcceptingEpochWithException(address(this));
}

/// @notice Gets the stake for a given tuple (custodian, attester) at a given epoch
Expand Down Expand Up @@ -349,32 +349,33 @@ contract MCR is Initializable, BaseSettlement, MCRStorage, IMCR {
// TODO : Suggestion: move to the next epoch and count votes there
function attemptPostconfirmOrRollover(uint256 superBlockHeight) internal returns (bool) {
uint256 superBlockEpoch = superBlockHeightAssignedEpoch[superBlockHeight];
// ensure that the superBlock height is equal or above the lastPostconfirmedSuperBlockHeight
uint256 previousSuperBlockEpoch = superBlockHeightAssignedEpoch[superBlockHeight-1];
if (superBlockEpoch < previousSuperBlockEpoch ) {
// if there is a commitment at the superBlock height, we need to set the assigned epoch to the previous epoch.
address[] memory stakedAttesters = getStakedAttestersForAcceptingEpoch();
for (uint256 i = 0; i < stakedAttesters.length; i++) {
if (commitments[superBlockHeight][stakedAttesters[i]].height != 0) {
superBlockHeightAssignedEpoch[superBlockHeight] = previousSuperBlockEpoch;
break;
if (getLastPostconfirmedSuperBlockHeight() == 0) {
console.log("[attemptPostconfirmOrRollover] genesis");
// if there is no postconfirmed superblock we are at genesis
} else {
// ensure that the superBlock height is equal or above the lastPostconfirmedSuperBlockHeight
uint256 previousSuperBlockEpoch = superBlockHeightAssignedEpoch[superBlockHeight-1];
if (superBlockEpoch < previousSuperBlockEpoch ) {
address[] memory stakedAttesters = getStakedAttestersForAcceptingEpoch();
// if there is at least one commitment at this superBlock height, we need to update once
for (uint256 i = 0; i < stakedAttesters.length; i++) {
if (commitments[superBlockHeight][stakedAttesters[i]].height != 0) {
superBlockHeightAssignedEpoch[superBlockHeight] = previousSuperBlockEpoch;
break;
}
}
superBlockEpoch = previousSuperBlockEpoch;
}
superBlockEpoch = previousSuperBlockEpoch;
}

// if the accepting epoch is far behind the superBlockEpoch (which is determined by commitments measured in L1 block time), then the protocol was not live for a while
// We keep rolling over the epoch (i.e. update stakes) until we catch up with the present epoch
while (getAcceptingEpoch() < superBlockEpoch) {
// only permit rollover if the attester has stake, as this is related to the reward model (rollovers should be rewarded)
if (getAttesterStakeForAcceptingEpoch(msg.sender) == 0) return false;
// TODO only permit rollover after some liveness criteria for the acceptor, as this is related to the reward model (rollovers should be rewarded)
rollOverEpoch();
}

// only permit postconfirmation and rollover if the attester has stake
// this is related to the reward model (rollover and postconfirmation should be rewarded)
// as long as there is a single attester with stake, the protocol will keep rolling over the epoch
if (getAttesterStakeForAcceptingEpoch(msg.sender) == 0) return false;
// TODO only permit postconfirmation after some liveness criteria for the acceptor, as this is related to the reward model (postconfirmation should be rewarded)

uint256 supermajority = (2 * getTotalStake(superBlockEpoch)) / 3 + 1;
address[] memory attesters = getStakedAttestersForAcceptingEpoch();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,16 +211,15 @@ contract MovementStaking is
/// @notice Gets the next accepting epoch number
/// @dev Special handling for genesis state (epoch 0):
/// @dev If getAcceptingEpoch(domain) == 0, returns 0 to stay in genesis until ceremony completes
function getNextAcceptingEpoch(address domain) public view returns (uint256) {
function getNextAcceptingEpochWithException(address domain) public view returns (uint256) {
return getAcceptingEpoch(domain) == 0 ? 0 : getAcceptingEpoch(domain) + 1;
}

/// @notice Gets the next present epoch number
function getNextPresentEpoch(
address domain
) public view returns (uint256) {
return
getEpochByL1BlockTime(domain) + 1;
/// @dev Special handling for genesis state (accepting epoch 0):
/// @dev If getAcceptingEpoch(domain) == 0, returns 0 to stay in genesis until ceremony completes
function getNextPresentEpochWithException(address domain) public view returns (uint256) {
return getAcceptingEpoch(domain) == 0 ? 0 : getEpochByL1BlockTime(domain) + 1;
}

/// @dev gets the stake for a given epoch for a given {attester,custodian} tuple
Expand Down Expand Up @@ -329,55 +328,12 @@ contract MovementStaking is
if (token.balanceOf(address(this)) != balanceBefore + amount)
revert CustodianTransferAmountMismatch();

// set the attester to stake for the next epoch
_addStake(
domain,
getNextPresentEpoch(domain),
address(custodian),
msg.sender,
amount
);

// Let the world know that the attester has staked
emit AttesterStaked(
domain,
getNextPresentEpoch(domain),
address(custodian),
msg.sender,
amount
);
}


/// @notice Stakes at genesis
function stakeAtGenesis(
address domain,
IERC20 custodian,
uint256 amount
) external onlyRole(WHITELIST_ROLE) nonReentrant {
// add the attester to the list of attesters
registeredAttestersByDomain[domain].add(msg.sender);

// add the custodian to the list of custodians
// registeredCustodiansByDomain[domain].add(address(custodian)); // Note: we don't want this to take place by default as it opens up an opportunity for a gas attack by generating a large number of custodians for the domain contract to track

// check the balance of the token before transfer
uint256 balanceBefore = token.balanceOf(address(this));

// transfer the stake to the contract
// if the transfer is not using a custodian, the custodian is the token itself
// hence this works
// ! In general with this pattern, the custodian must be careful about not over-approving the token.
custodian.transferFrom(msg.sender, address(this), amount);

// require that the balance of the actual token has increased by the amount
if (token.balanceOf(address(this)) != balanceBefore + amount)
revert CustodianTransferAmountMismatch();

// set the attester to stake for the next epoch
// set the attester to stake for the next accepting epoch
_addStake(
domain,
0,
// TODO should this not be getNextAcceptingEpochWithException(domain)?
// getNextPresentEpochWithException(domain),
getNextAcceptingEpochWithException(domain),
address(custodian),
msg.sender,
amount
Expand All @@ -386,7 +342,7 @@ contract MovementStaking is
// Let the world know that the attester has staked
emit AttesterStaked(
domain,
0,
getNextAcceptingEpochWithException(domain),
address(custodian),
msg.sender,
amount
Expand All @@ -404,15 +360,17 @@ contract MovementStaking is
// note: by tracking in the next epoch we need to make sure when we roll over an epoch we check the amount rolled over from stake by the unstake in the next epoch
_addUnstake(
domain,
getNextPresentEpoch(domain),
// TODO should this not be getNextAcceptingEpochWithException(domain)?
// getNextPresentEpochWithException(domain),
getNextAcceptingEpochWithException(domain),
custodian,
msg.sender,
amount
);

emit AttesterUnstaked(
domain,
getNextAcceptingEpoch(domain),
getNextAcceptingEpochWithException(domain),
custodian,
msg.sender,
amount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ interface IMovementStaking {
function acceptGenesisCeremony() external;
function getEpochByL1BlockTime(address) external view returns (uint256);
function getAcceptingEpoch(address) external view returns (uint256);
function getNextAcceptingEpoch(address) external view returns (uint256);
function getNextPresentEpoch(address) external view returns (uint256);
function getNextAcceptingEpochWithException(address) external view returns (uint256);
function getNextPresentEpochWithException(address) external view returns (uint256);
function getStake(
address domain,
uint256 epoch,
Expand Down Expand Up @@ -45,7 +45,6 @@ interface IMovementStaking {
address custodian
) external view returns (uint256);
function stake(address domain, IERC20 custodian, uint256 amount) external;
function stakeAtGenesis(address domain, IERC20 custodian, uint256 amount) external;
function unstake(
address domain,
address custodian,
Expand Down
Loading

0 comments on commit 8c2144a

Please sign in to comment.