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": { diff --git a/src/multiproof/AggregateVerifier.sol b/src/multiproof/AggregateVerifier.sol index cc774544..c00f13c7 100644 --- a/src/multiproof/AggregateVerifier.sol +++ b/src/multiproof/AggregateVerifier.sol @@ -447,25 +447,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); @@ -526,6 +525,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. @@ -769,13 +770,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; @@ -800,13 +797,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; @@ -818,6 +811,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, diff --git a/src/multiproof/Verifier.sol b/src/multiproof/Verifier.sol index a792f11d..d9505f9b 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 VerifierNullified(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 VerifierNullified(IDisputeGame(msg.sender)); } } diff --git a/src/multiproof/tee/NitroEnclaveVerifier.sol b/src/multiproof/tee/NitroEnclaveVerifier.sol index d6f7069b..9b7bfcfa 100644 --- a/src/multiproof/tee/NitroEnclaveVerifier.sol +++ b/src/multiproof/tee/NitroEnclaveVerifier.sol @@ -123,10 +123,10 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver { 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); @@ -451,7 +451,7 @@ contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver { _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)); } /**