Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ToB fixes #447

Merged
merged 11 commits into from
Sep 29, 2023
4 changes: 2 additions & 2 deletions contracts/src/challengeV2/libraries/ChallengeErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ error InvalidEndHeight(uint256 actualHeight, uint256 expectedHeight);
/// @dev The prefix proof is empty
error EmptyPrefixProof();
/// @dev The edge is not of type Block
error EdgeTypeNotBlock(uint256 eType);
error EdgeTypeNotBlock(uint256 level);
/// @dev The edge is not of type SmallStep
error EdgeTypeNotSmallStep(uint256 eType);
error EdgeTypeNotSmallStep(uint256 level);
/// @dev The first rival record is empty
error EmptyFirstRival();
/// @dev The difference between two heights is less than 2
Expand Down
7 changes: 3 additions & 4 deletions contracts/src/rollup/BOLDUpgradeAction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ contract BOLDUpgradeAction {
(ExecutionState memory genesisExecState, uint256 inboxMaxCount) = PREIMAGE_LOOKUP.get(latestConfirmedStateHash);
// double check the hash
require(
RollupLib.stateHashMem(genesisExecState, inboxMaxCount) == latestConfirmedStateHash,
PREIMAGE_LOOKUP.stateHash(genesisExecState, inboxMaxCount) == latestConfirmedStateHash,
"Invalid latest execution hash"
);

Expand Down Expand Up @@ -361,9 +361,8 @@ contract BOLDUpgradeAction {
}

function upgradeSurroundingContracts(address newRollupAddress) private {
// now we upgrade each of the contracts that a reference to the rollup address
// first we upgrade to an implementation which allows setting, then set the rollup address
// then we revert to the previous implementation since we dont require this functionality going forward
// upgrade each of these contracts to an implementation that allows
// the rollup address to be set to the new rollup address

TransparentUpgradeableProxy bridge = TransparentUpgradeableProxy(payable(BRIDGE));
address currentBridgeImpl = PROXY_ADMIN_BRIDGE.getProxyImplementation(bridge);
Expand Down
18 changes: 15 additions & 3 deletions contracts/src/rollup/RollupCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,11 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
require(assertion.beforeState.machineStatus == MachineStatus.FINISHED, "BAD_PREV_STATUS");

AssertionNode storage prevAssertion = getAssertionStorage(prevAssertionHash);
// Required inbox position through which the next assertion (the one after this new assertion) must consume
uint256 nextInboxPosition;
bytes32 sequencerBatchAcc;
{
// This new assertion consumes the messages from prevInboxPosition to afterInboxPosition
uint64 afterInboxPosition = assertion.afterState.globalState.getInboxPosition();
uint64 prevInboxPosition = assertion.beforeState.globalState.getInboxPosition();

Expand All @@ -413,6 +415,14 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
require(afterInboxPosition >= prevInboxPosition, "INBOX_BACKWARDS");
DZGoldman marked this conversation as resolved.
Show resolved Hide resolved
require(afterInboxPosition <= assertion.beforeStateData.configData.nextInboxPosition, "INBOX_TOO_FAR");

// SANITY CHECK: the next inbox position did indeed move forward
// this is enforced by code in a later section that artificially increases the nextInboxPosition
// even if there hadn't been any new messages since the last assertion;
// this ensures that assertions will continue to advance.
// It also means that below, where we check that afterInboxPosition equals prev.nextInboxPosition
// in the FINISHED state, we can be sure that it processed at least one message
require(assertion.beforeStateData.configData.nextInboxPosition > prevInboxPosition, "NEXT_INBOX_BACKWARDS");

// if the position in the message is > 0, then the afterInboxPosition cannot be the nextInboxPosition
// as this would be outside the range - this can only occur for ERRORED states
if (assertion.afterState.machineStatus == MachineStatus.ERRORED) {
Expand All @@ -422,7 +432,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
);
}
} else if (assertion.afterState.machineStatus == MachineStatus.FINISHED) {
// if the machine is FINISHED, then it should consume all messages in the inbox as seen at the time of prev
// if the machine is FINISHED, then it should consume all messages in the inbox as seen at the time of prev (minimum 1; see below)
require(
afterInboxPosition == assertion.beforeStateData.configData.nextInboxPosition,
"INVALID_FINISHED_INBOX"
Expand All @@ -433,7 +443,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
// we checked this above, but include a safety check here in case of refactoring
revert("INVALID_STATUS");
}

// Inbox position at the time of this assertion being created
uint256 currentInboxPosition = bridge.sequencerMessageCount();
// Cannot read more messages than currently exist in the inbox
require(afterInboxPosition <= currentInboxPosition, "INBOX_PAST_END");
Expand All @@ -452,7 +462,9 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
// No new messages have been added to the inbox since the last assertion
// In this case if we set the next inbox position to the current one we would be insisting that
// the next assertion process no messages. So instead we increment the next inbox position to current
// plus one, so that the next assertion will process exactly one message
// plus one, so that the next assertion will process exactly one message.
// Thus, no assertion can be empty (except the genesis assertion, which is created
// via a different codepath).
nextInboxPosition = currentInboxPosition + 1;
} else {
nextInboxPosition = currentInboxPosition;
Expand Down
3 changes: 2 additions & 1 deletion contracts/src/rollup/RollupCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ contract RollupCreator is Ownable {
deployed.proxyAdmin.transferOwnership(config.owner);

// Create the rollup proxy to figure out the address and initialize it later
deployed.rollup = new RollupProxy{salt: keccak256(abi.encode(config))}();
deployed.rollup =
new RollupProxy{salt: keccak256(abi.encode(config, _batchPoster, _validators, disableValidatorWhitelist, maxDataSize))}();

(deployed.bridge, deployed.sequencerInbox, deployed.inbox, deployed.rollupEventInbox, deployed.outbox) =
bridgeCreator.createBridge(
Expand Down
31 changes: 0 additions & 31 deletions contracts/src/rollup/RollupLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,6 @@ import "../challengeV2/EdgeChallengeManager.sol";
library RollupLib {
using GlobalStateLib for GlobalState;

function stateHash(ExecutionState calldata execState, uint256 inboxMaxCount)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked(
execState.globalState.hash(),
inboxMaxCount,
execState.machineStatus
)
);
}

/// @dev same as stateHash but expects execState in memory instead of calldata
function stateHashMem(ExecutionState memory execState, uint256 inboxMaxCount)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked(
execState.globalState.hash(),
inboxMaxCount,
execState.machineStatus
)
);
}

// Not the same as a machine hash for a given execution state
function executionStateHash(ExecutionState memory state) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(state.machineStatus, state.globalState.hash()));
Expand Down
28 changes: 28 additions & 0 deletions contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import "../src/libraries/Error.sol";
import "../src/mocks/TestWETH9.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts-upgradeable/utils/Create2Upgradeable.sol";

contract RollupTest is Test {
using GlobalStateLib for GlobalState;

address constant owner = address(1337);
address constant sequencer = address(7331);

Expand Down Expand Up @@ -129,6 +132,11 @@ contract RollupTest is Test {
vm.expectEmit(false, false, false, false);
emit RollupCreated(address(0), address(0), address(0), address(0), address(0));
address rollupAddr = rollupCreator.createRollup(config, address(0), new address[](0), false, MAX_DATA_SIZE);
bytes32 rollupSalt = keccak256(abi.encode(config, address(0), new address[](0), false, MAX_DATA_SIZE));
address expectedRollupAddress = Create2Upgradeable.computeAddress(
rollupSalt, keccak256(type(RollupProxy).creationCode), address(rollupCreator)
);
assertEq(expectedRollupAddress, rollupAddr, "Unexpected rollup address");

userRollup = RollupUserLogic(address(rollupAddr));
adminRollup = RollupAdminLogic(address(rollupAddr));
Expand Down Expand Up @@ -1184,4 +1192,24 @@ contract RollupTest is Test {
vm.expectRevert();
adminRollup.setChallengeManager(address(0xdeadbeef));
}

function testExecutionStateHash() public {
ExecutionState memory es = ExecutionState(
GlobalState([rand.hash(), rand.hash()], [uint64(uint256(rand.hash())), uint64(uint256(rand.hash()))]),
MachineStatus.FINISHED
);
bytes32 expectedHash = keccak256(abi.encodePacked(es.machineStatus, es.globalState.hash()));
assertEq(RollupLib.executionStateHash(es), expectedHash, "Unexpected hash");
}

function testAssertionHash() public {
bytes32 parentHash = rand.hash();
ExecutionState memory es = ExecutionState(
GlobalState([rand.hash(), rand.hash()], [uint64(uint256(rand.hash())), uint64(uint256(rand.hash()))]),
MachineStatus.FINISHED
);
bytes32 inboxAcc = rand.hash();
bytes32 expectedHash = keccak256(abi.encodePacked(parentHash, RollupLib.executionStateHash(es), inboxAcc));
assertEq(RollupLib.assertionHash(parentHash, es, inboxAcc), expectedHash, "Unexpected hash");
}
}
48 changes: 48 additions & 0 deletions contracts/test/challengeV2/EdgeChallengeManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,54 @@ contract EdgeChallengeManagerTest is Test {
);
}

function testRevertInvalidHash() public {
EdgeInitData memory ei = deployAndInit();

(bytes32[] memory states, bytes32[] memory exp) =
appendRandomStatesBetween(genesisStates(), StateToolsLib.mockMachineHash(ei.a1State), height1);

vm.expectRevert("INVALID_ASSERTION_HASH");
ei.challengeManager.createLayerZeroEdge(
CreateEdgeArgs({
level: 0,
endHistoryRoot: MerkleTreeLib.root(exp),
endHeight: height1,
claimId: ei.a2,
prefixProof: abi.encode(
ProofUtils.expansionFromLeaves(states, 0, 1),
ProofUtils.generatePrefixProof(1, ArrayUtilsLib.slice(states, 1, states.length))
),
proof: abi.encode(
ProofUtils.generateInclusionProof(ProofUtils.rehashed(states), 0), genesisStateData, ei.a1Data
)
})
);
}

function testRevertInvalidHashPrev() public {
EdgeInitData memory ei = deployAndInit();

(bytes32[] memory states, bytes32[] memory exp) =
appendRandomStatesBetween(genesisStates(), StateToolsLib.mockMachineHash(ei.a1State), height1);

vm.expectRevert("INVALID_ASSERTION_HASH");
ei.challengeManager.createLayerZeroEdge(
CreateEdgeArgs({
level: 0,
endHistoryRoot: MerkleTreeLib.root(exp),
endHeight: height1,
claimId: ei.a1,
prefixProof: abi.encode(
ProofUtils.expansionFromLeaves(states, 0, 1),
ProofUtils.generatePrefixProof(1, ArrayUtilsLib.slice(states, 1, states.length))
),
proof: abi.encode(
ProofUtils.generateInclusionProof(ProofUtils.rehashed(states), states.length - 1), ei.a2Data, ei.a1Data
)
})
);
}

function testCanCreateEdgeWithStake()
public
returns (EdgeInitData memory, bytes32[] memory, bytes32[] memory, bytes32)
Expand Down
13 changes: 4 additions & 9 deletions contracts/test/challengeV2/EdgeChallengeManagerLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1582,21 +1582,16 @@ contract EdgeChallengeManagerLibTest is Test {
struct ExecStateVars {
ExecutionState execState;
bytes32 machineHash;
bytes32 stateHash;
}

function randomExecutionState(IOneStepProofEntry os, uint256 inboxMaxCount)
private
returns (ExecStateVars memory)
{
function randomExecutionState(IOneStepProofEntry os) private returns (ExecStateVars memory) {
ExecutionState memory execState = ExecutionState(
GlobalState([rand.hash(), rand.hash()], [uint64(uint256(rand.hash())), uint64(uint256(rand.hash()))]),
MachineStatus.FINISHED
);

bytes32 machineHash = os.getMachineHash(execState);
bytes32 stateHash = RollupLib.stateHashMem(execState, inboxMaxCount);
return ExecStateVars(execState, machineHash, stateHash);
return ExecStateVars(execState, machineHash);
}

function createZeroBlockEdge(uint256 mode) internal {
Expand All @@ -1608,8 +1603,8 @@ contract EdgeChallengeManagerLibTest is Test {
revertArg = abi.encodeWithSelector(NotPowerOfTwo.selector, expectedEndHeight);
}

ExecStateVars memory startExec = randomExecutionState(entry, 10);
ExecStateVars memory endExec = randomExecutionState(entry, 20);
ExecStateVars memory startExec = randomExecutionState(entry);
ExecStateVars memory endExec = randomExecutionState(entry);
ExpsAndProofs memory roots = newRootsAndProofs(0, expectedEndHeight, startExec.machineHash, endExec.machineHash);
bytes32 claimId = rand.hash();
bytes32 endRoot;
Expand Down
Loading
Loading