Skip to content

Commit

Permalink
add assertion version to contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
MoonShiesty committed Nov 8, 2023
1 parent b94b8bd commit 4d09f5c
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 34 deletions.
22 changes: 17 additions & 5 deletions contracts/src/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pragma solidity ^0.8.0;
interface IRollup {
event ConfigChanged();

event AssertionCreated(uint256 assertionID, address asserterAddr, bytes32 vmHash);
event AssertionCreated(uint256 assertionID, address asserterAddr);

event AssertionChallenged(uint256 assertionID, address challengeAddr);

Expand Down Expand Up @@ -126,6 +126,12 @@ interface IRollup {
/// @dev Thrown when an attempt is made to grant a role to an account that already possesses the role.
error NoRoleToRevoke();

/// @dev Thrown when assertionData has incorrect encoding
error AssertionDataUnderflow();

/// @dev Thrown when a assertion data has an incorrect version
error AssertionVersionIncorrect();

struct Staker {
bool isStaked;
uint256 amountStaked;
Expand All @@ -134,7 +140,7 @@ interface IRollup {
}

struct Assertion {
bytes32 stateHash; // Hash of execution state associated with assertion. Currently equiv to `vmHash`.
bytes assertionData; // Hash of execution state associated with assertion. Currently equiv to `vmHash`.
uint256 blockNum; // Block number this assertion advanced to
uint256 parent; // Parent assertion ID
uint256 deadline; // Dispute deadline (L1 block number)
Expand Down Expand Up @@ -308,10 +314,10 @@ interface IRollup {
*
* Emits: `AssertionCreated` and `StakerStaked` events.
*
* @param vmHash New VM hash.
* @param inboxSize Size of inbox corresponding to assertion (number of transactions).
* @param blockNum Block number this assertion advances to.
* @param assertionData Versioned assertion data
*/
function createAssertion(bytes32 vmHash, uint256 inboxSize) external;
function createAssertion(uint256 blockNum, bytes calldata assertionData) external;

/**
* @notice Initiates a dispute between a defender and challenger on an unconfirmed DA.
Expand Down Expand Up @@ -349,4 +355,10 @@ interface IRollup {
* assertion is not the last confirmed assertion.
*/
function rejectFirstUnresolvedAssertion(address stakerAddress) external;

/**
* @notice Decode the vm hash from assertion data
* @param assertionData assertion data to decode
*/
function getAssertionVmHash(bytes memory assertionData) external pure returns (bytes32);
}
66 changes: 53 additions & 13 deletions contracts/src/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ abstract contract RollupBase is
IDAProvider public daProvider;
IVerifier public verifier;

// Current txBatch serialization version
uint8 public constant currentAssertionVersion = 0;

struct AssertionState {
mapping(address => bool) stakers; // all stakers that have ever staked on this assertion.
mapping(bytes32 => bool) childStateHashes; // child assertion vm hashes
mapping(bytes => bool) childStateHashes; // child assertion vm hashes
}

struct Zombie {
Expand Down Expand Up @@ -136,14 +139,19 @@ contract Rollup is RollupBase {
grantRole(VALIDATOR_ROLE, _validators[i]);
}

// Prepend version to vm hash
bytes memory assertionData = new bytes(1);
assertionData[0] = bytes1(currentAssertionVersion);
assertionData = bytes.concat(assertionData, _initialVMhash);

createAssertionHelper(
_initialAssertionID, // assertionID
_initialVMhash,
assertionData,
_initialBlockNum, // blockNum (genesis)
_initialAssertionID, // parentID (doesn't matter, since unchallengeable)
block.number // deadline (unchallengeable)
);
emit AssertionCreated(lastCreatedAssertionID, msg.sender, _initialVMhash);
emit AssertionCreated(lastCreatedAssertionID, msg.sender);
}

/// @custom:oz-upgrades-unsafe-allow constructor
Expand Down Expand Up @@ -415,13 +423,22 @@ contract Rollup is RollupBase {
}

/// @inheritdoc IRollup
function createAssertion(bytes32 vmHash, uint256 blockNum)
function createAssertion(uint256 blockNum, bytes calldata assertionData)
external
override
stakedOnly
whenNotPaused
validatorOnly
{
if (assertionData.length == 0) {
revert AssertionDataUnderflow();
}

uint8 assertionVersion = uint8(assertionData[0]);
if (assertionVersion != currentAssertionVersion) {
revert AssertionVersionIncorrect();
}

uint256 parentID = stakers[msg.sender].assertionID;
Assertion storage parent = assertions[parentID];
// Require that enough time has passed since the last assertion.
Expand All @@ -435,8 +452,8 @@ contract Rollup is RollupBase {

// Initialize assertion.
lastCreatedAssertionID++;
emit AssertionCreated(lastCreatedAssertionID, msg.sender, vmHash);
createAssertionHelper(lastCreatedAssertionID, vmHash, blockNum, parentID, newAssertionDeadline());
emit AssertionCreated(lastCreatedAssertionID, msg.sender);
createAssertionHelper(lastCreatedAssertionID, assertionData, blockNum, parentID, newAssertionDeadline());

// Update stake.
stakeOnAssertion(msg.sender, lastCreatedAssertionID);
Expand Down Expand Up @@ -478,9 +495,9 @@ contract Rollup is RollupBase {
// Initialize challenge.
SymChallenge challenge = new SymChallenge();
address challengeAddr = address(challenge);
bytes32 startStateHash = assertions[parentID].stateHash;
bytes32 endStateDefenderHash = assertions[defenderAssertionID].stateHash;
bytes32 endStateChallengerHash = assertions[challengerAssertionID].stateHash;
bytes32 startStateHash = getAssertionVmHash(assertions[parentID].assertionData);
bytes32 endStateDefenderHash = getAssertionVmHash(assertions[defenderAssertionID].assertionData);
bytes32 endStateChallengerHash = getAssertionVmHash(assertions[challengerAssertionID].assertionData);
stakers[challenger].currentChallenge = challengeAddr;
stakers[defender].currentChallenge = challengeAddr;
emit AssertionChallenged(defenderAssertionID, challengeAddr);
Expand Down Expand Up @@ -567,7 +584,7 @@ contract Rollup is RollupBase {
*/
function createAssertionHelper(
uint256 assertionID,
bytes32 stateHash,
bytes memory assertionData,
uint256 blockNum,
uint256 parentID,
uint256 deadline
Expand All @@ -580,12 +597,12 @@ contract Rollup is RollupBase {
parentAssertion.blockNum = blockNum;
} else if (blockNum != parentBlockNum) {
revert InvalidInboxSize();
} else if (parentAssertionState.childStateHashes[stateHash]) {
} else if (parentAssertionState.childStateHashes[assertionData]) {
revert DuplicateAssertion();
}
parentAssertionState.childStateHashes[stateHash] = true;
parentAssertionState.childStateHashes[assertionData] = true;
assertions[assertionID] = Assertion(
stateHash,
assertionData,
blockNum,
parentID,
deadline,
Expand All @@ -595,6 +612,29 @@ contract Rollup is RollupBase {
);
}


/**
* @notice Decode the vm hash from assertion data
* @param assertionData assertion data to decode
*/
function getAssertionVmHash(bytes memory assertionData) public pure returns (bytes32) {
if (assertionData.length == 0) {
revert AssertionDataUnderflow();
}

uint8 assertionVersion = uint8(assertionData[0]);
if (assertionVersion != currentAssertionVersion) {
revert AssertionVersionIncorrect();
}

bytes32 vmHash;
// Decode the next 32 bytes as vmHash
assembly {
vmHash := mload(add(assertionData, 33)) // skip version byte
}
return vmHash;
}

/**
* @notice Deletes the staker from global state. Does not touch assertion staker state.
* @param stakerAddress Address of the staker to delete
Expand Down
12 changes: 7 additions & 5 deletions contracts/src/bridge/L1Portal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ contract L1Portal is
IRollup.Assertion memory assertion = rollup.getAssertion(assertionID);

// Ensure that the assertion is confirmed.
require(_isAssertionConfirmed(assertionID, assertion.stateHash), "L1Portal: assertion not confirmed");
require(_isAssertionConfirmed(assertionID, assertion.assertionData), "L1Portal: assertion not confirmed");

// All withdrawals have a unique hash, we'll use this as the identifier for the withdrawal
// and to prevent replay attacks.
Expand All @@ -172,7 +172,8 @@ contract L1Portal is
// require(blockHash == assertion.stateHash, "L1Portal: invalid block");

// Verify the account proof.
bytes32 storageRoot = _verifyAccountInclusion(l2PortalAddress, assertion.stateHash, withdrawalAccountProof);
bytes32 stateHash = rollup.getAssertionVmHash(assertion.assertionData);
bytes32 storageRoot = _verifyAccountInclusion(l2PortalAddress, stateHash, withdrawalAccountProof);

// Verify that the hash of this withdrawal was stored in the L2Portal contract on L2.
// If this is true, then we know that this withdrawal was actually triggered on L2
Expand Down Expand Up @@ -213,7 +214,7 @@ contract L1Portal is
/// @inheritdoc IL1Portal
function isAssertionConfirmed(uint256 assertionID) public view override returns (bool) {
IRollup.Assertion memory assertion = rollup.getAssertion(assertionID);
return _isAssertionConfirmed(assertionID, assertion.stateHash);
return _isAssertionConfirmed(assertionID, assertion.assertionData);
}

/// @inheritdoc IL1Portal
Expand Down Expand Up @@ -256,14 +257,15 @@ contract L1Portal is
* @notice Determine if a given assertion is finalized and confirmed.
*
* @param assertionID The ID of the assertion.
* @param stateHash The stateHash field of the assertion.
* @param assertionData The assertion data
*/
function _isAssertionConfirmed(uint256 assertionID, bytes32 stateHash) internal view returns (bool) {
function _isAssertionConfirmed(uint256 assertionID, bytes memory assertionData) internal view returns (bool) {
// Must be finalized.
if (assertionID > rollup.getLastConfirmedAssertionID()) {
return false;
}

bytes32 stateHash = rollup.getAssertionVmHash(assertionData);
// Must be confirmed.
if (stateHash == bytes32(0)) {
return false;
Expand Down
22 changes: 11 additions & 11 deletions contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ contract RollupTest is RollupBaseSetup {

_appendTxBatch();

bytes32 mockVmHash = bytes32("");
bytes memory mockVmHash = bytes(hex"00");
uint256 mockInboxSize = 1;

// To avoid the MinimumAssertionPeriodNotPassed error, increase block.number
Expand All @@ -733,7 +733,7 @@ contract RollupTest is RollupBaseSetup {
assertEq(assertionIDInitial, 0);

vm.prank(alice);
rollup.createAssertion(mockVmHash, mockInboxSize);
rollup.createAssertion(mockInboxSize, mockVmHash);

// The assertionID of alice should change after she called `createAssertion`
(,, uint256 assertionIDFinal,) = rollup.stakers(address(alice));
Expand Down Expand Up @@ -889,11 +889,11 @@ contract RollupTest is RollupBaseSetup {

_appendTxBatch();

bytes32 mockVmHash = bytes32("");
bytes memory mockVmHash = bytes(hex"00");
uint256 mockInboxSize = 1;

vm.prank(alice);
rollup.createAssertion(mockVmHash, mockInboxSize);
rollup.createAssertion(mockInboxSize, mockVmHash);

// The assertionID of alice should change after she called `createAssertion`
(,, uint256 assertionIDFinal,) = rollup.stakers(address(alice));
Expand Down Expand Up @@ -981,7 +981,7 @@ contract RollupTest is RollupBaseSetup {
// Increase the sequencerInbox inboxSize with mock transactions we can assert on.
_appendTxBatch();

bytes32 mockVmHash = bytes32("");
bytes memory mockVmHash = bytes(hex"00");
uint256 mockInboxSize = 1;

// To avoid the MinimumAssertionPeriodNotPassed error, increase block.number
Expand All @@ -990,7 +990,7 @@ contract RollupTest is RollupBaseSetup {
assertEq(rollup.lastCreatedAssertionID(), 0, "The lastCreatedAssertionID should be 0 (genesis)");

vm.prank(alice);
rollup.createAssertion(mockVmHash, mockInboxSize);
rollup.createAssertion(mockInboxSize, mockVmHash);

// A successful assertion should bump the lastCreatedAssertionID to 1.
assertEq(rollup.lastCreatedAssertionID(), 1, "LastCreatedAssertionID not updated correctly");
Expand Down Expand Up @@ -1047,7 +1047,7 @@ contract RollupTest is RollupBaseSetup {
// Increase the sequencerInbox inboxSize with mock transactions we can assert on.
_appendTxBatch();

bytes32 mockVmHash = bytes32("");
bytes memory mockVmHash = bytes(hex"00");
uint256 mockInboxSize = 1;

// To avoid the MinimumAssertionPeriodNotPassed error, increase block.number
Expand All @@ -1062,15 +1062,15 @@ contract RollupTest is RollupBaseSetup {
// try as alice
vm.expectRevert("Pausable: paused");
vm.prank(alice);
rollup.createAssertion(mockVmHash, mockInboxSize);
rollup.createAssertion(mockInboxSize, mockVmHash);

// unpause and continue setup
vm.prank(deployer);
rollup.unpause();

// try again now that pause is over
vm.prank(alice);
rollup.createAssertion(mockVmHash, mockInboxSize);
rollup.createAssertion(mockInboxSize, mockVmHash);

// A successful assertion should bump the lastCreatedAssertionID to 1.
assertEq(rollup.lastCreatedAssertionID(), 1, "LastCreatedAssertionID not updated correctly");
Expand Down Expand Up @@ -1194,14 +1194,14 @@ contract RollupTest is RollupBaseSetup {

_appendTxBatch();

bytes32 mockVmHash = bytes32("");
bytes memory mockVmHash = bytes(hex"00");
uint256 mockInboxSize = 1;

// To avoid the MinimumAssertionPeriodNotPassed error, increase block.number
vm.roll(block.number + rollup.minimumAssertionPeriod());

vm.prank(alice);
rollup.createAssertion(mockVmHash, mockInboxSize);
rollup.createAssertion(mockInboxSize, mockVmHash);
}

uint256 defenderAssertionID = lastConfirmedAssertionID; //would be 0 in this case. cannot assign anything lower
Expand Down

0 comments on commit 4d09f5c

Please sign in to comment.