From dd587c9adc84a768eb540a88ef479275c5db97e9 Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Wed, 25 Mar 2026 14:05:40 -0400 Subject: [PATCH 1/8] If the parent is invalid, the child is automatically invalid (Finding 1) --- src/multiproof/AggregateVerifier.sol | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/multiproof/AggregateVerifier.sol b/src/multiproof/AggregateVerifier.sol index 704ea205..f6fc3f96 100644 --- a/src/multiproof/AggregateVerifier.sol +++ b/src/multiproof/AggregateVerifier.sol @@ -437,25 +437,24 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { // The parent game must have resolved. if (parentGameStatus == GameStatus.IN_PROGRESS) revert ParentGameNotResolved(); - bool isChallenged = counteredByIntermediateRootIndexPlusOne > 0; - // If the parent game's claim is invalid, blacklisted, or retired, then the current game's claim is invalid. + // We don't care about what happens in this game once the parent is invalid. if (parentGameStatus == GameStatus.CHALLENGER_WINS) { status = GameStatus.CHALLENGER_WINS; } else { - // Game must be completed with a valid proof. + // Game must be completed with a valid proof and enough proofs. if (!gameOver()) revert GameNotOver(); - // If the game is challenged, status is CHALLENGER_WINS. - // If the game is not challenged, status is DEFENDER_WINS. - status = isChallenged ? GameStatus.CHALLENGER_WINS : GameStatus.DEFENDER_WINS; + if (proofCount < PROOF_THRESHOLD) revert NotEnoughProofs(); + + // If the game is challenged, reward the challenger. + if (counteredByIntermediateRootIndexPlusOne > 0) { + status = GameStatus.CHALLENGER_WINS; + bondRecipient = proofTypeToProver[ProofType.ZK]; + } else { + status = GameStatus.DEFENDER_WINS; + } } - if (proofCount < PROOF_THRESHOLD) revert NotEnoughProofs(); - - // Default bond recipient is the creator. We only change if successfully challenged. - if (isChallenged) { - bondRecipient = proofTypeToProver[ProofType.ZK]; - } // Mark the game as resolved. resolvedAt = Timestamp.wrap(uint64(block.timestamp)); emit Resolved(status); From eb82db789553e32566b6653ece48508c3ef5a2ea Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Wed, 25 Mar 2026 14:09:04 -0400 Subject: [PATCH 2/8] Update NitroEnclaveVerifier events (Finding 2) --- src/multiproof/tee/NitroEnclaveVerifier.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/multiproof/tee/NitroEnclaveVerifier.sol b/src/multiproof/tee/NitroEnclaveVerifier.sol index 17d4e004..dcdcd9a6 100644 --- a/src/multiproof/tee/NitroEnclaveVerifier.sol +++ b/src/multiproof/tee/NitroEnclaveVerifier.sol @@ -140,10 +140,10 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier { event ZkRouteWasFrozen(ZkCoProcessorType indexed zkCoProcessor, bytes4 indexed selector); /// @dev Emitted when the proof of attestation has been successfully verified - event AttestationSubmitted(VerificationResult result, ZkCoProcessorType zkCoProcessor, bytes output); + event AttestationSubmitted(VerificationResult result, ZkCoProcessorType indexed zkCoProcessor, bytes output); /// @dev Emitted when a batched proof has been successfully verified; encodedBatched = abi.encode(VerifierJournal[]) - event BatchAttestationSubmitted(bytes32 verifierId, ZkCoProcessorType zkCoProcessor, bytes encodedBatch); + event BatchAttestationSubmitted(bytes32 verifierId, ZkCoProcessorType indexed zkCoProcessor, bytes encodedBatch); /// @dev Event emitted when the proof submitter address is changed event ProofSubmitterChanged(address newProofSubmitter); @@ -554,7 +554,7 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier { _verifyZk(zkCoprocessor, programId, output, proofBytes); journal = abi.decode(output, (VerifierJournal)); journal = _verifyJournal(journal); - emit AttestationSubmitted(journal.result, zkCoprocessor, output); + emit AttestationSubmitted(journal.result, zkCoprocessor, abi.encode(journal)); } /** From 488feae7e25a6f869dff2e1174987a68f992f941 Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Wed, 25 Mar 2026 14:15:28 -0400 Subject: [PATCH 3/8] Update challenge comment (Finding 3) --- src/multiproof/AggregateVerifier.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/multiproof/AggregateVerifier.sol b/src/multiproof/AggregateVerifier.sol index f6fc3f96..225de966 100644 --- a/src/multiproof/AggregateVerifier.sol +++ b/src/multiproof/AggregateVerifier.sol @@ -515,6 +515,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { // This is only in case the ZK proof is nullified, which would lower the proof count. // If the ZK is nullified, we allow the remaining TEE proof to resolve. // The expected resolution time can no longer be increased as both proof types have been submitted. + // The exception is if the ZK proof is nullified, in which case the expected resolution will be + // increased by SLOW_FINALIZATION_DELAY from the time of nullification. proofCount += 1; // We purposely increase the resolution to allow for a ZK nullification. From 2007b3126c2e8b6bb8ff607ecfeac8b8f5a53129 Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Wed, 25 Mar 2026 14:23:45 -0400 Subject: [PATCH 4/8] Refactor duplicate code (Finding 4) --- src/multiproof/AggregateVerifier.sol | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/multiproof/AggregateVerifier.sol b/src/multiproof/AggregateVerifier.sol index 225de966..5770cc16 100644 --- a/src/multiproof/AggregateVerifier.sol +++ b/src/multiproof/AggregateVerifier.sol @@ -760,13 +760,9 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice Decreases the expected resolution timestamp. function _decreaseExpectedResolution() internal { - uint64 delay; + uint64 delay = _getDelay(); - if (proofCount >= 2) { - delay = FAST_FINALIZATION_DELAY; - } else if (proofCount == 1) { - delay = SLOW_FINALIZATION_DELAY; - } else { + if (delay == type(uint64).max) { // If there are no proofs, don't allow the game to resolve. expectedResolution = Timestamp.wrap(type(uint64).max); return; @@ -791,13 +787,9 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } function _increaseExpectedResolution() internal { - uint64 delay; + uint64 delay = _getDelay(); - if (proofCount >= 2) { - delay = FAST_FINALIZATION_DELAY; - } else if (proofCount == 1) { - delay = SLOW_FINALIZATION_DELAY; - } else { + if (delay == type(uint64).max) { // If there are no proofs, don't allow the game to resolve. expectedResolution = Timestamp.wrap(type(uint64).max); return; @@ -809,6 +801,16 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { expectedResolution = Timestamp.wrap(uint64(block.timestamp) + delay); } + function _getDelay() internal view returns (uint64) { + if (proofCount >= 2) { + return FAST_FINALIZATION_DELAY; + } else if (proofCount == 1) { + return SLOW_FINALIZATION_DELAY; + } else { + return type(uint64).max; + } + } + function _verifyProof( bytes calldata proofBytes, ProofType proofType, From 8cbc65bbed1e84e6c484c18e89e5076c7bce0bab Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Wed, 25 Mar 2026 14:33:59 -0400 Subject: [PATCH 5/8] semver lock --- snapshots/semver-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 5c7164c0..dd58c4ff 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -240,12 +240,12 @@ "sourceCodeHash": "0x955bd0c9b47e43219865e4e92abf28d916c96de20cbdf2f94c8ab14d02083759" }, "src/multiproof/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0xe631e0c2e8e86711f83cb97884065ce38d1ff519e0d2dbf8704e7e4c183a56cc", - "sourceCodeHash": "0xfa0464c07c06fddc98ba20e9a362ba10ecf94496556d0f7ac88d1986f79a8a6b" + "initCodeHash": "0x73f657f5df4491ba169a48becd1b4ef6902cf6221fa509e10efff0407c460c61", + "sourceCodeHash": "0xfc7d78c3ad110de584c8e1c34cda9547e09e9368a1dd8823680f5e734156a532" }, "src/multiproof/AggregateVerifier.sol:AggregateVerifier:dispute": { - "initCodeHash": "0x89f15344142ede7ca9d6af3c998fb3ffec84293105b28daa8427aa0608dd1d9e", - "sourceCodeHash": "0xfa0464c07c06fddc98ba20e9a362ba10ecf94496556d0f7ac88d1986f79a8a6b" + "initCodeHash": "0x01de5ce7cb737465f4e413e959c60d68184c50b31423bfe769fc129ca776d6e9", + "sourceCodeHash": "0xfc7d78c3ad110de584c8e1c34cda9547e09e9368a1dd8823680f5e734156a532" }, "src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": { "initCodeHash": "0x4c89ecad0d48b6da64ef7f489326aae63b7fbcd33f4fed949a496afd1be49009", From e276326d9cc3867951d799352a5dc34da1f86da1 Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Mon, 30 Mar 2026 16:34:41 -0400 Subject: [PATCH 6/8] add nullify event --- src/multiproof/Verifier.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/multiproof/Verifier.sol b/src/multiproof/Verifier.sol index a792f11d..e1f4e6c9 100644 --- a/src/multiproof/Verifier.sol +++ b/src/multiproof/Verifier.sol @@ -13,6 +13,10 @@ abstract contract Verifier is IVerifier { /// @dev This is used to prevent further proof verification after a soundness issue is found. bool public nullified; + /// @notice Emitted when the verifier is nullified. + /// @param game The game that nullified this verifier. + event Nullified(IDisputeGame game); + /// @notice Thrown when the verifier has been nullified. error Nullified(); @@ -38,5 +42,7 @@ abstract contract Verifier is IVerifier { || !ANCHOR_STATE_REGISTRY.isGameRespected(IDisputeGame(msg.sender)) ) revert NotProperGame(); nullified = true; + + emit Nullified(IDisputeGame(msg.sender)); } } From c2df30855c27e302db850f7dcf26ebc2d4df1f5f Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Mon, 30 Mar 2026 16:36:12 -0400 Subject: [PATCH 7/8] update event name --- src/multiproof/Verifier.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/multiproof/Verifier.sol b/src/multiproof/Verifier.sol index e1f4e6c9..d9505f9b 100644 --- a/src/multiproof/Verifier.sol +++ b/src/multiproof/Verifier.sol @@ -15,7 +15,7 @@ abstract contract Verifier is IVerifier { /// @notice Emitted when the verifier is nullified. /// @param game The game that nullified this verifier. - event Nullified(IDisputeGame game); + event VerifierNullified(IDisputeGame game); /// @notice Thrown when the verifier has been nullified. error Nullified(); @@ -43,6 +43,6 @@ abstract contract Verifier is IVerifier { ) revert NotProperGame(); nullified = true; - emit Nullified(IDisputeGame(msg.sender)); + emit VerifierNullified(IDisputeGame(msg.sender)); } } From 8f744f746599b17cc3d4a3498495b6be060f3301 Mon Sep 17 00:00:00 2001 From: Roger Bai Date: Mon, 30 Mar 2026 16:37:05 -0400 Subject: [PATCH 8/8] update snaptshots --- snapshots/abi/MockVerifier.json | 13 +++++++++++++ snapshots/abi/NitroEnclaveVerifier.json | 4 ++-- snapshots/abi/TEEVerifier.json | 13 +++++++++++++ snapshots/semver-lock.json | 10 +++++----- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/snapshots/abi/MockVerifier.json b/snapshots/abi/MockVerifier.json index 7129f561..2866b3c5 100644 --- a/snapshots/abi/MockVerifier.json +++ b/snapshots/abi/MockVerifier.json @@ -72,6 +72,19 @@ "stateMutability": "view", "type": "function" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IDisputeGame", + "name": "game", + "type": "address" + } + ], + "name": "VerifierNullified", + "type": "event" + }, { "inputs": [], "name": "NotProperGame", diff --git a/snapshots/abi/NitroEnclaveVerifier.json b/snapshots/abi/NitroEnclaveVerifier.json index 9f25f420..3a86b910 100644 --- a/snapshots/abi/NitroEnclaveVerifier.json +++ b/snapshots/abi/NitroEnclaveVerifier.json @@ -742,7 +742,7 @@ "type": "uint8" }, { - "indexed": false, + "indexed": true, "internalType": "enum ZkCoProcessorType", "name": "zkCoProcessor", "type": "uint8" @@ -767,7 +767,7 @@ "type": "bytes32" }, { - "indexed": false, + "indexed": true, "internalType": "enum ZkCoProcessorType", "name": "zkCoProcessor", "type": "uint8" diff --git a/snapshots/abi/TEEVerifier.json b/snapshots/abi/TEEVerifier.json index f7e53ced..b491ef7a 100644 --- a/snapshots/abi/TEEVerifier.json +++ b/snapshots/abi/TEEVerifier.json @@ -103,6 +103,19 @@ "stateMutability": "pure", "type": "function" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IDisputeGame", + "name": "game", + "type": "address" + } + ], + "name": "VerifierNullified", + "type": "event" + }, { "inputs": [ { diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 312dc49a..4e7af342 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -232,19 +232,19 @@ "sourceCodeHash": "0x955bd0c9b47e43219865e4e92abf28d916c96de20cbdf2f94c8ab14d02083759" }, "src/multiproof/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x720595c92e78d68910ee2c853ffafa828e390f0db87285bd5f6fc2efc46cef23", - "sourceCodeHash": "0x829e926bac073d6754e7dd7003c25d562d6281eb4155b47e2538cfc1b2e39481" + "initCodeHash": "0x911b368f15552ea645430ad3775ccfe8fe58b1b365a8e0c236d6ce53b5d2219a", + "sourceCodeHash": "0x032f11e23c188b939ba6cbdc446271b0caad2f4da00535b164a3489291162762" }, "src/multiproof/tee/NitroEnclaveVerifier.sol:NitroEnclaveVerifier": { - "initCodeHash": "0x64ca7a3c66eeae2278cdfffd2a1feaa8ada470403dd1fce23d9157c093f6320e", - "sourceCodeHash": "0x5bd92e57f25881aed184a9c7408676191bab858bf908ca7f8993896019e127b2" + "initCodeHash": "0x8cae758a4b1005bf2904e376fff49c2fb44e8d9d7bef1a33e18a85ee23d540a7", + "sourceCodeHash": "0x78d6c704d56ae8de255be71f5cc94e73e0bea1abd5b7e6dfcb4132174f89202b" }, "src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": { "initCodeHash": "0x2f9be34a6a0ed105a272193b6cec2b47321e1b468fac887c6c7cadd870949ffe", "sourceCodeHash": "0xf1ec1f02f540da659a204b26acf986fdce7d7d63bba67a87923f52453fb92ccb" }, "src/multiproof/tee/TEEVerifier.sol:TEEVerifier": { - "initCodeHash": "0xeab3964cb2e6bbf3b660a8dedcd01286b74894bf76cf979d0b415fc7ca140ade", + "initCodeHash": "0x9f65f2d6e2c43512594f7a08fc0ea475d06f48d230690c3c85cc85dee3a9679a", "sourceCodeHash": "0xd5e01b4f4e69313e56e51f2f46b7bbe699ef8dc24b8a6385b8ebac4162e05353" }, "src/revenue-share/FeeDisburser.sol:FeeDisburser": {