Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions snapshots/abi/AggregateVerifier.json
Original file line number Diff line number Diff line change
Expand Up @@ -623,12 +623,12 @@
},
{
"inputs": [],
"name": "parentIndex",
"name": "parentAddress",
"outputs": [
{
"internalType": "uint32",
"internalType": "address",
"name": "",
"type": "uint32"
"type": "address"
}
],
"stateMutability": "pure",
Expand Down
4 changes: 2 additions & 2 deletions snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@
"sourceCodeHash": "0x955bd0c9b47e43219865e4e92abf28d916c96de20cbdf2f94c8ab14d02083759"
},
"src/multiproof/AggregateVerifier.sol:AggregateVerifier": {
"initCodeHash": "0x911b368f15552ea645430ad3775ccfe8fe58b1b365a8e0c236d6ce53b5d2219a",
"sourceCodeHash": "0x032f11e23c188b939ba6cbdc446271b0caad2f4da00535b164a3489291162762"
"initCodeHash": "0x7b0764d9a89a1e79129c2a25851b7e44e6392eddb08b4993fce25d6a9bffc223",
"sourceCodeHash": "0x22e1947a7b289be69137262a8e76083e05987b86fc768932a3e5d7c658e07082"
},
"src/multiproof/tee/NitroEnclaveVerifier.sol:NitroEnclaveVerifier": {
"initCodeHash": "0x4210e4b415f18d50ec4b2d76f216af08f5943cf9a930262a8189c32e3b9ea49c",
Expand Down
37 changes: 19 additions & 18 deletions src/multiproof/AggregateVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
INTERMEDIATE_BLOCK_INTERVAL = intermediateBlockInterval;
PROOF_THRESHOLD = proofThreshold;

INITIALIZE_CALLDATA_SIZE = 0x7E + 0x20 * intermediateOutputRootsCount();
INITIALIZE_CALLDATA_SIZE = 0x8E + 0x20 * intermediateOutputRootsCount();
}

/// @notice Initializes the contract.
Expand All @@ -317,13 +317,13 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
// in the factory, but are not used by the game, which would allow for multiple dispute games for the same
// output proposal to be created.
//
// Expected length: 0x7E
// Expected length: 0x8E + 0x20 * intermediateOutputRootsCount()
// - 0x04 selector
// - 0x14 creator address
// - 0x20 root claim
// - 0x20 l1 head
// - 0x20 extraData (l2BlockNumber)
// - 0x04 extraData (parentIndex)
// - 0x14 extraData (parent game address)
// - 0x20 x (BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL) extraData (intermediate roots)
// - 0x02 CWIA bytes

Expand All @@ -347,10 +347,10 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
);
}

// The first game is initialized with a parent index of uint32.max.
if (parentIndex() != type(uint32).max) {
// The first game is initialized with a parent address of the AnchorStateRegistry.
if (parentAddress() != address(ANCHOR_STATE_REGISTRY)) {
// For subsequent games, get the parent game's information.
(,, IDisputeGame parentGame) = DISPUTE_GAME_FACTORY.gameAtIndex(parentIndex());
IDisputeGame parentGame = IDisputeGame(parentAddress());

// Parent game must be respected, not blacklisted, not retired, and not challenged.
if (!_isValidGame(parentGame)) revert InvalidParentGame();
Expand Down Expand Up @@ -713,24 +713,24 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {

/// @notice The intermediate output roots of the game.
function intermediateOutputRoots() public view returns (bytes memory) {
return _getArgBytes(0x78, 0x20 * intermediateOutputRootsCount());
return _getArgBytes(0x88, 0x20 * intermediateOutputRootsCount());
}

/// @notice The intermediate output root at the given index.
/// @param index The index of the intermediate output root.
function intermediateOutputRoot(uint256 index) public view returns (bytes32) {
if (index >= intermediateOutputRootsCount()) revert InvalidIntermediateRootIndex();
return _getArgBytes32(0x78 + 0x20 * index);
return _getArgBytes32(0x88 + 0x20 * index);
}

/// @notice Getter for the extra data.
function extraData() public view returns (bytes memory) {
// The extra data starts at the second word within the cwia calldata and
// is 36 + 32 x intermediateRootsCount() bytes long.
// is 52 + 32 x intermediateRootsCount() bytes long.
// 32 bytes are for the l2BlockNumber
// 4 bytes are for the parentIndex
// 20 bytes are for the parent address
// 32 bytes are for each intermediate root
return _getArgBytes(0x54, 0x24 + 0x20 * intermediateOutputRootsCount());
return _getArgBytes(0x54, 0x34 + 0x20 * intermediateOutputRootsCount());
}

/// @notice Getter for the creator of the dispute game.
Expand All @@ -754,9 +754,9 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
return _getArgUint256(0x54);
}

/// @notice The parent index of the game.
function parentIndex() public pure returns (uint32) {
return _getArgUint32(0x74);
/// @notice The parent UUID of the game.
function parentAddress() public pure returns (address) {
return _getArgAddress(0x74);
}

function _proofVerifiedUpdate(ProofType proofType, address proposer) internal {
Expand Down Expand Up @@ -932,17 +932,18 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
}

/// @notice Returns the status of the parent game.
/// @dev If the parent game index is `uint32.max`, then the parent game's status is considered as `DEFENDER_WINS`.
/// @dev If the parent game address is `address(ANCHOR_STATE_REGISTRY)`, then the parent game's status is considered
/// as `DEFENDER_WINS`.
function _getParentGameStatus() internal view returns (GameStatus) {
if (parentIndex() != type(uint32).max) {
(,, IDisputeGame parentGame) = DISPUTE_GAME_FACTORY.gameAtIndex(parentIndex());
if (parentAddress() != address(ANCHOR_STATE_REGISTRY)) {
IDisputeGame parentGame = IDisputeGame(parentAddress());
if (ANCHOR_STATE_REGISTRY.isGameBlacklisted(parentGame) || ANCHOR_STATE_REGISTRY.isGameRetired(parentGame))
{
return GameStatus.CHALLENGER_WINS;
}
return parentGame.status();
}
// If this is the first dispute game (i.e. parent game index is `uint32.max`), then the
// If this is the first dispute game (i.e. parent game address is `address(ANCHOR_STATE_REGISTRY)`), then the
// parent game's status is considered as `DEFENDER_WINS`.
return GameStatus.DEFENDER_WINS;
}
Expand Down
69 changes: 44 additions & 25 deletions test/multiproof/AggregateVerifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ contract AggregateVerifierTest is BaseTest {
Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber)));
bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE);

AggregateVerifier game =
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proof);
AggregateVerifier game = _createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof
);

assertEq(game.wasRespectedGameTypeWhenCreated(), true);
assertEq(address(game.teeProver()), TEE_PROVER);
assertEq(address(game.zkProver()), address(0));
assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS));
assertEq(game.l2SequenceNumber(), currentL2BlockNumber);
assertEq(game.rootClaim().raw(), rootClaim.raw());
assertEq(game.parentIndex(), type(uint32).max);
assertEq(game.parentAddress(), address(anchorStateRegistry));
assertEq(game.gameType().raw(), AGGREGATE_VERIFIER_GAME_TYPE.raw());
assertEq(game.gameCreator(), TEE_PROVER);
assertEq(
game.extraData(), abi.encodePacked(currentL2BlockNumber, type(uint32).max, game.intermediateOutputRoots())
game.extraData(),
abi.encodePacked(currentL2BlockNumber, address(anchorStateRegistry), game.intermediateOutputRoots())
);
assertEq(game.bondRecipient(), TEE_PROVER);
assertEq(anchorStateRegistry.isGameProper(IDisputeGame(address(game))), true);
Expand All @@ -45,20 +47,22 @@ contract AggregateVerifierTest is BaseTest {
Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber)));
bytes memory proof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK);

AggregateVerifier game =
_createAggregateVerifierGame(ZK_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proof);
AggregateVerifier game = _createAggregateVerifierGame(
ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof
);

assertEq(game.wasRespectedGameTypeWhenCreated(), true);
assertEq(address(game.teeProver()), address(0));
assertEq(address(game.zkProver()), ZK_PROVER);
assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS));
assertEq(game.l2SequenceNumber(), currentL2BlockNumber);
assertEq(game.rootClaim().raw(), rootClaim.raw());
assertEq(game.parentIndex(), type(uint32).max);
assertEq(game.parentAddress(), address(anchorStateRegistry));
assertEq(game.gameType().raw(), AGGREGATE_VERIFIER_GAME_TYPE.raw());
assertEq(game.gameCreator(), ZK_PROVER);
assertEq(
game.extraData(), abi.encodePacked(currentL2BlockNumber, type(uint32).max, game.intermediateOutputRoots())
game.extraData(),
abi.encodePacked(currentL2BlockNumber, address(anchorStateRegistry), game.intermediateOutputRoots())
);
assertEq(game.bondRecipient(), ZK_PROVER);
assertEq(anchorStateRegistry.isGameProper(IDisputeGame(address(game))), true);
Expand All @@ -84,8 +88,9 @@ contract AggregateVerifierTest is BaseTest {
Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber)));
bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE);

AggregateVerifier game =
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proof);
AggregateVerifier game = _createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof
);

// Cannot claim bond before game is over
vm.expectRevert(GameNotResolved.selector);
Expand Down Expand Up @@ -117,8 +122,9 @@ contract AggregateVerifierTest is BaseTest {
Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber)));
bytes memory proof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK);

AggregateVerifier game =
_createAggregateVerifierGame(ZK_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proof);
AggregateVerifier game = _createAggregateVerifierGame(
ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof
);

// Resolve after 7 days
vm.warp(block.timestamp + 7 days);
Expand Down Expand Up @@ -147,8 +153,9 @@ contract AggregateVerifierTest is BaseTest {
bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE);
bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK);

AggregateVerifier game =
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, teeProof);
AggregateVerifier game = _createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof
);

_provideProof(game, ZK_PROVER, zkProof);
assertEq(game.proofCount(), 2);
Expand Down Expand Up @@ -180,8 +187,9 @@ contract AggregateVerifierTest is BaseTest {
bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE);
bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK);

AggregateVerifier game =
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, teeProof);
AggregateVerifier game = _createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof
);

Timestamp originalExpectedResolution = game.expectedResolution();
assertEq(originalExpectedResolution.raw(), block.timestamp + 7 days);
Expand Down Expand Up @@ -210,16 +218,17 @@ contract AggregateVerifierTest is BaseTest {
bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE);
bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK);

AggregateVerifier game =
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, teeProof);
AggregateVerifier game = _createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof
);

Hash uuid = factory.getGameUUID(
AGGREGATE_VERIFIER_GAME_TYPE,
rootClaim,
abi.encodePacked(currentL2BlockNumber, type(uint32).max, game.intermediateOutputRoots())
abi.encodePacked(currentL2BlockNumber, address(anchorStateRegistry), game.intermediateOutputRoots())
);
vm.expectRevert(abi.encodeWithSelector(IDisputeGameFactory.GameAlreadyExists.selector, uuid));
_createAggregateVerifierGame(ZK_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, zkProof);
_createAggregateVerifierGame(ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), zkProof);
}

function testVerifyFailsWithL1OriginInFuture() public {
Expand All @@ -235,7 +244,9 @@ contract AggregateVerifierTest is BaseTest {
vm.expectRevert(
abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number)
);
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proofBytes);
_createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes
);
}

function testVerifyFailsWithL1OriginTooOld() public {
Expand All @@ -253,7 +264,9 @@ contract AggregateVerifierTest is BaseTest {
abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw());

vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number));
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proofBytes);
_createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes
);
}

function testVerifyFailsWithL1OriginHashMismatch() public {
Expand All @@ -267,7 +280,9 @@ contract AggregateVerifierTest is BaseTest {

bytes32 actualHash = blockhash(l1OriginNumber);
vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, actualHash));
_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proofBytes);
_createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes
);
}

function testVerifyWithBlockhashWindow() public {
Expand All @@ -284,7 +299,9 @@ contract AggregateVerifierTest is BaseTest {
bytes memory proofBytes =
abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw());

_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proofBytes);
_createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes
);
}

function testVerifyWithEIP2935Window() public {
Expand All @@ -308,7 +325,9 @@ contract AggregateVerifierTest is BaseTest {
bytes memory proofBytes =
abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), expectedHash, l1OriginNumber, rootClaim.raw());

_createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, type(uint32).max, proofBytes);
_createAggregateVerifierGame(
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes
);
}

function testDeployWithInvalidBlockIntervals() public {
Expand Down
4 changes: 2 additions & 2 deletions test/multiproof/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ contract BaseTest is Test {
address creator,
Claim rootClaim,
uint256 l2BlockNumber,
uint32 parentIndex,
address parentAddress,
bytes memory proof
)
internal
returns (AggregateVerifier game)
{
bytes memory intermediateRoots =
abi.encodePacked(_generateIntermediateRootsExceptLast(l2BlockNumber), rootClaim.raw());
bytes memory extraData = abi.encodePacked(uint256(l2BlockNumber), uint32(parentIndex), intermediateRoots);
bytes memory extraData = abi.encodePacked(uint256(l2BlockNumber), parentAddress, intermediateRoots);

vm.deal(creator, INIT_BOND);
vm.prank(creator);
Expand Down
Loading
Loading