diff --git a/packages/protocol/contracts/L1/TaikoConfig.sol b/packages/protocol/contracts/L1/TaikoConfig.sol index 86778a24dff..4e118e2fa0e 100644 --- a/packages/protocol/contracts/L1/TaikoConfig.sol +++ b/packages/protocol/contracts/L1/TaikoConfig.sol @@ -33,7 +33,6 @@ library TaikoConfig { // transactions list calldata, 8K for the remaining tx fields. maxBytesPerTxList: 120000, proofCooldownPeriod: 30 minutes, - systemProofCooldownPeriod: 15 minutes, // Only need 1 real zkp per 10 blocks. // If block number is N, then only when N % 10 == 0, the real ZKP // is needed. For mainnet, this must be 0 or 1. @@ -42,7 +41,8 @@ library TaikoConfig { ethDepositMaxFee: 1 ether / 10, txListCacheExpiry: 0, adjustmentQuotient: 16, - relaySignalRoot: false + relaySignalRoot: false, + enableSgxProving: false }); } } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 44928070fe5..b55ab81c467 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -19,7 +19,6 @@ library TaikoData { uint64 maxBytesPerTxList; uint256 txListCacheExpiry; uint256 proofCooldownPeriod; - uint256 systemProofCooldownPeriod; uint256 realProofSkipSize; uint256 ethDepositGas; uint256 ethDepositMaxFee; @@ -29,6 +28,7 @@ library TaikoData { uint96 minEthDepositAmount; uint8 adjustmentQuotient; bool relaySignalRoot; + bool enableSgxProving; } struct StateVariables { @@ -71,6 +71,11 @@ library TaikoData { TaikoData.EthDeposit[] depositsProcessed; } + struct TypedProof { + uint16 verifierId; + bytes proof; + } + struct BlockEvidence { bytes32 metaHash; bytes32 parentHash; @@ -80,8 +85,7 @@ library TaikoData { address prover; uint32 parentGasUsed; uint32 gasUsed; - uint16 verifierId; - bytes proof; + TypedProof[] blockProofs; // Fixed size and pos to save gas. [0]: ZKP, [1]: SGX if enabled } // 4 slots diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index a94a128bc24..7bfe6fc3b25 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -19,12 +19,9 @@ abstract contract TaikoErrors { error L1_INVALID_METADATA(); error L1_INVALID_PARAM(); error L1_INVALID_PROOF(); - error L1_INVALID_PROOF_OVERWRITE(); - error L1_NOT_SPECIAL_PROVER(); - error L1_ORACLE_PROVER_DISABLED(); - error L1_SAME_PROOF(); - error L1_SYSTEM_PROVER_DISABLED(); - error L1_SYSTEM_PROVER_PROHIBITED(); + error L1_INVALID_SGX_SIGNATURE(); + error L1_NOT_ALL_REQ_PROOF_VERIFIED(); + error L1_NOTHING_TO_OVERWRITE(); error L1_TOO_MANY_BLOCKS(); error L1_TX_LIST_NOT_EXIST(); error L1_TX_LIST_HASH(); diff --git a/packages/protocol/contracts/L1/TaikoEvents.sol b/packages/protocol/contracts/L1/TaikoEvents.sol index a0d6cb864a9..7bac2b713a7 100644 --- a/packages/protocol/contracts/L1/TaikoEvents.sol +++ b/packages/protocol/contracts/L1/TaikoEvents.sol @@ -26,4 +26,6 @@ abstract contract TaikoEvents { event EthDeposited(TaikoData.EthDeposit deposit); event ProofTimeTargetChanged(uint64 proofTimeTarget); + + event BlockForkChoiceFailsafeRewrite(uint256 blockId); } diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 99502dcaf45..9d7a337e999 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -10,6 +10,8 @@ import {AddressResolver} from "../../common/AddressResolver.sol"; import {LibMath} from "../../libs/LibMath.sol"; import {LibUtils} from "./LibUtils.sol"; import {TaikoData} from "../../L1/TaikoData.sol"; +import {LibVerifyTrusted} from "./proofTypes/LibVerifyTrusted.sol"; +import {LibVerifyZKP} from "./proofTypes/LibVerifyZKP.sol"; library LibProving { using LibMath for uint256; @@ -29,13 +31,8 @@ library LibProving { error L1_EVIDENCE_MISMATCH(bytes32 expected, bytes32 actual); error L1_FORK_CHOICE_NOT_FOUND(); error L1_INVALID_EVIDENCE(); - error L1_INVALID_PROOF(); - error L1_INVALID_PROOF_OVERWRITE(); - error L1_NOT_SPECIAL_PROVER(); - error L1_ORACLE_PROVER_DISABLED(); - error L1_SAME_PROOF(); - error L1_SYSTEM_PROVER_DISABLED(); - error L1_SYSTEM_PROVER_PROHIBITED(); + error L1_NOT_ALL_REQ_PROOF_VERIFIED(); + error L1_NOTHING_TO_OVERWRITE(); function proveBlock( TaikoData.State storage state, @@ -62,100 +59,71 @@ library LibProving { revert L1_EVIDENCE_MISMATCH(blk.metaHash, evidence.metaHash); } - // Separate between oracle proof (which needs to be overwritten) - // and non-oracle but system proofs - address specialProver; - if (evidence.prover == address(0)) { - specialProver = resolver.resolve("oracle_prover", true); - if (specialProver == address(0)) { - revert L1_ORACLE_PROVER_DISABLED(); - } - } else if (evidence.prover == address(1)) { - specialProver = resolver.resolve("system_prover", true); - if (specialProver == address(0)) { - revert L1_SYSTEM_PROVER_DISABLED(); - } + if (msg.sender == resolver.resolve("forkchoice_failsafe", false)) { + // Special prover AKA SuperProver - if (config.realProofSkipSize <= 1 || blockId % config.realProofSkipSize == 0) { - revert L1_SYSTEM_PROVER_PROHIBITED(); - } - } + // We need to write or overwrite blk.forkChoices[1] because in LibUtils.sol: + // "if (blk.forkChoices[1].key == keyForForkChoice(parentHash, parentGasUsed))" + // So in case someone is hurried up, write the first forkchoice which for whatever reason + // there is a bug in the circuit and verifiable, the verifyBlock() will always return fcId == 1. + TaikoData.ForkChoice storage fc = blk.forkChoices[1]; - if (specialProver != address(0) && msg.sender != specialProver) { - if (evidence.proof.length != 64) { - revert L1_NOT_SPECIAL_PROVER(); - } else { - uint8 v = uint8(evidence.verifierId); - bytes32 r; - bytes32 s; - bytes memory data = evidence.proof; - assembly { - r := mload(add(data, 32)) - s := mload(add(data, 64)) - } + // In case it was 0 (unproven) - it's fine we prove it here otherwise + // it does not matter if 2 or 3, only fk idx 1 is valid in such a special case where prover is failsafe. + unchecked { + ++blk.nextForkChoiceId; + } - // clear the proof before hashing evidence - evidence.verifierId = 0; - evidence.proof = new bytes(0); + fc.key = LibUtils.keyForForkChoice(evidence.parentHash, evidence.parentGasUsed); - if (specialProver != ecrecover(keccak256(abi.encode(evidence)), v, r, s)) { - revert L1_NOT_SPECIAL_PROVER(); - } - } + fc.blockHash = evidence.blockHash; + fc.signalRoot = evidence.signalRoot; + fc.gasUsed = evidence.gasUsed; + fc.prover = msg.sender; // "special" prover, the failsafe one + fc.provenAt = blk.proposedAt + state.proofTimeTarget; } + else { + + if( + (config.enableSgxProving && evidence.blockProofs.length < 2) + || + (!config.enableSgxProving && evidence.blockProofs.length < 1) + ) { + // One of the proofs are def. missing + revert L1_NOT_ALL_REQ_PROOF_VERIFIED(); + } - TaikoData.ForkChoice storage fc; + TaikoData.ForkChoice storage fc; - uint256 fcId = - LibUtils.getForkChoiceId(state, blk, evidence.parentHash, evidence.parentGasUsed); + uint256 fcId = + LibUtils.getForkChoiceId(state, blk, evidence.parentHash, evidence.parentGasUsed); - if (fcId == 0) { - fcId = blk.nextForkChoiceId; + if (fcId == 0) { + fcId = blk.nextForkChoiceId; - unchecked { - ++blk.nextForkChoiceId; - } + unchecked { + ++blk.nextForkChoiceId; + } - fc = blk.forkChoices[fcId]; + fc = blk.forkChoices[fcId]; - if (fcId == 1) { - // We only write the key when fcId is 1. - fc.key = LibUtils.keyForForkChoice(evidence.parentHash, evidence.parentGasUsed); + if (fcId == 1) { + // We only write the key when fcId is 1. + fc.key = LibUtils.keyForForkChoice(evidence.parentHash, evidence.parentGasUsed); + } else { + state.forkChoiceIds[blk.blockId][evidence.parentHash][evidence.parentGasUsed] = fcId; + } } else { - state.forkChoiceIds[blk.blockId][evidence.parentHash][evidence.parentGasUsed] = fcId; - } - } else if (evidence.prover == address(0)) { - // This is the branch the oracle prover is trying to overwrite - fc = blk.forkChoices[fcId]; - if ( - fc.blockHash == evidence.blockHash && fc.signalRoot == evidence.signalRoot - && fc.gasUsed == evidence.gasUsed - ) revert L1_SAME_PROOF(); - } else { - // This is the branch provers trying to overwrite - fc = blk.forkChoices[fcId]; - if (fc.prover != address(0) && fc.prover != address(1)) { revert L1_ALREADY_PROVEN(); } - if ( - fc.blockHash != evidence.blockHash || fc.signalRoot != evidence.signalRoot - || fc.gasUsed != evidence.gasUsed - ) revert L1_INVALID_PROOF_OVERWRITE(); - } - - fc.blockHash = evidence.blockHash; - fc.signalRoot = evidence.signalRoot; - fc.gasUsed = evidence.gasUsed; - fc.prover = evidence.prover; - - if (evidence.prover == address(1)) { - fc.provenAt = uint64(block.timestamp.max(blk.proposedAt + state.proofTimeTarget)); - } else { + fc.blockHash = evidence.blockHash; + fc.signalRoot = evidence.signalRoot; + fc.gasUsed = evidence.gasUsed; + fc.prover = evidence.prover; fc.provenAt = uint64(block.timestamp); - } - if (evidence.prover != address(0) && evidence.prover != address(1)) { + // Put together the input for proof and signature verification uint256[10] memory inputs; inputs[0] = uint256(uint160(address(resolver.resolve("signal_service", false)))); @@ -181,20 +149,28 @@ library LibProving { instance := keccak256(inputs, mul(32, 10)) } - (bool verified, bytes memory ret) = resolver.resolve( - LibUtils.getVerifierName(evidence.verifierId), false - ).staticcall( - bytes.concat( - bytes16(0), - bytes16(instance), // left 16 bytes of the given instance - bytes16(0), - bytes16(uint128(uint256(instance))), // right 16 bytes of the given instance - evidence.proof - ) + // Check ZK proof + TaikoData.TypedProof memory proof = evidence.blockProofs[0]; + + // @dantaik: If we inline this function below and not outsourcing this into a library + // we save only 47 gas. If the LibVerifyTrusted also outsourced thats all in all approx 94 gwei save only. + LibVerifyZKP.verifyProof( + resolver, + proof.proof, + instance, + proof.verifierId ); - if (!verified || ret.length != 32 || bytes32(ret) != keccak256("taiko")) { - revert L1_INVALID_PROOF(); + // If enabled, check SGX proof + if(config.enableSgxProving) { + proof = evidence.blockProofs[1]; + + LibVerifyTrusted.verifyProof( + resolver, + proof.proof, + instance, + proof.verifierId + ); } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 57042b16a5c..d47063999fc 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -88,7 +88,7 @@ library LibVerifying { ++i; } - address systemProver = resolver.resolve("system_prover", true); + address failsafeProver = resolver.resolve("forkchoice_failsafe", true); while (i < state.numBlocks && processed < maxBlocks) { blk = state.blocks[i % config.ringBufferSize]; assert(blk.blockId == i); @@ -99,13 +99,8 @@ library LibVerifying { TaikoData.ForkChoice storage fc = blk.forkChoices[fcId]; - if (fc.prover == address(0)) break; - - uint256 proofCooldownPeriod = fc.prover == address(1) - ? config.systemProofCooldownPeriod - : config.proofCooldownPeriod; - - if (block.timestamp < fc.provenAt + proofCooldownPeriod) break; + if (block.timestamp < fc.provenAt + config.proofCooldownPeriod) + break; blockHash = fc.blockHash; gasUsed = fc.gasUsed; @@ -117,7 +112,7 @@ library LibVerifying { blk: blk, fcId: uint24(fcId), fc: fc, - systemProver: systemProver + failsafeProver: failsafeProver }); unchecked { @@ -147,7 +142,7 @@ library LibVerifying { TaikoData.Block storage blk, TaikoData.ForkChoice storage fc, uint24 fcId, - address systemProver + address failsafeProver ) private { uint64 proofTime; unchecked { @@ -164,19 +159,16 @@ library LibVerifying { state.accProposedAt -= blk.proposedAt; } - // reward the prover - if (reward != 0) { - address prover = fc.prover != address(1) ? fc.prover : systemProver; - - // systemProver may become address(0) after a block is proven - if (prover != address(0)) { - if (state.taikoTokenBalances[prover] == 0) { - // Reduce refund to 1 wei as a penalty if the proposer - // has 0 TKO outstanding balance. - state.taikoTokenBalances[prover] = 1; - } else { - state.taikoTokenBalances[prover] += reward; - } + // Now there is no such distinguishing as address(0) vs. address(1) + address prover = fc.prover; + // reward the prover in case it is not the failSafe prover (who overwrite FKs) + if (reward != 0 && prover != failsafeProver) { + if (state.taikoTokenBalances[prover] == 0) { + // Reduce refund to 1 wei as a penalty if the proposer + // has 0 TKO outstanding balance. + state.taikoTokenBalances[prover] = 1; + } else { + state.taikoTokenBalances[prover] += reward; } } diff --git a/packages/protocol/contracts/L1/libs/proofTypes/LibVerifyTrusted.sol b/packages/protocol/contracts/L1/libs/proofTypes/LibVerifyTrusted.sol new file mode 100644 index 00000000000..5d0242d439b --- /dev/null +++ b/packages/protocol/contracts/L1/libs/proofTypes/LibVerifyTrusted.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.18; + +import {AddressResolver} from "../../../common/AddressResolver.sol"; +import {LibUtils} from "../LibUtils.sol"; +import {TaikoData} from "../../TaikoData.sol"; + +library LibVerifyTrusted { + error L1_INVALID_SGX_SIGNATURE(); + + function verifyProof( + AddressResolver resolver, + bytes memory proof, + bytes32 signedMsghash, + uint16 verifierId + ) internal view { + address trustedVerifier = resolver.resolve( + LibUtils.getVerifierName(verifierId), + false + ); + + // The signature proof + bytes memory data = proof; + uint8 v; + bytes32 r; + bytes32 s; + assembly { + // Extract a uint8 + v := byte(0, mload(add(data, 32))) + // Extract the first 32-byte chunk (after the uint8) + r := mload(add(data, 33)) + // Extract the second 32-byte chunk + s := mload(add(data, 65)) + } + + if (ecrecover(signedMsghash, v, r, s) != trustedVerifier) + revert L1_INVALID_SGX_SIGNATURE(); + } +} diff --git a/packages/protocol/contracts/L1/libs/proofTypes/LibVerifyZKP.sol b/packages/protocol/contracts/L1/libs/proofTypes/LibVerifyZKP.sol new file mode 100644 index 00000000000..1e937fedc9d --- /dev/null +++ b/packages/protocol/contracts/L1/libs/proofTypes/LibVerifyZKP.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.18; + +import {AddressResolver} from "../../../common/AddressResolver.sol"; +import {LibUtils} from "../LibUtils.sol"; +import {TaikoData} from "../../TaikoData.sol"; + +library LibVerifyZKP { + + bytes32 internal constant TAIKO_HASH = keccak256("taiko"); + + error L1_INVALID_PROOF(); + + function verifyProof( + AddressResolver resolver, + bytes memory proof, + bytes32 inputHash, + uint16 verifierId + ) internal view { + (bool verified, bytes memory ret) = resolver + .resolve(LibUtils.getVerifierName(verifierId), false) + .staticcall(bytes.concat(inputHash, proof)); + + if (!verified || ret.length != 32 || bytes32(ret) != TAIKO_HASH) + revert L1_INVALID_PROOF(); + } +} diff --git a/packages/protocol/script/DeployOnL1.s.sol b/packages/protocol/script/DeployOnL1.s.sol index 1453df45ad8..37eea31455f 100644 --- a/packages/protocol/script/DeployOnL1.s.sol +++ b/packages/protocol/script/DeployOnL1.s.sol @@ -80,8 +80,6 @@ contract DeployOnL1 is Script { setAddress(l2ChainId, "taiko", taikoL2Address); setAddress(l2ChainId, "signal_service", l2SignalService); - setAddress("oracle_prover", oracleProver); - setAddress("system_prover", systemProver); setAddress(l2ChainId, "treasury", treasury); // TaikoToken diff --git a/packages/protocol/test/TaikoL1Oracle.t.sol b/packages/protocol/test/TaikoL1MultiProving.t.sol similarity index 50% rename from packages/protocol/test/TaikoL1Oracle.t.sol rename to packages/protocol/test/TaikoL1MultiProving.t.sol index fbb5281bc63..0407a4cea1f 100644 --- a/packages/protocol/test/TaikoL1Oracle.t.sol +++ b/packages/protocol/test/TaikoL1MultiProving.t.sol @@ -24,6 +24,7 @@ contract TaikoL1Oracle is TaikoL1 { config.ringBufferSize = 12; config.proofCooldownPeriod = 5 minutes; config.realProofSkipSize = 10; + config.enableSgxProving = true; // It means SGX proof is necessary } } @@ -41,61 +42,12 @@ contract TaikoL1OracleTest is TaikoL1TestBase { function setUp() public override { TaikoL1TestBase.setUp(); registerAddress(L1.getVerifierName(100), address(new Verifier())); - registerAddress("oracle_prover", Alice); - registerAddress("system_prover", Alice); } - function testOracleProverWithSignature() external { - depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); - depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); - - TaikoData.BlockMetadata memory meta = proposeBlock(Bob, 1000000, 1024); - proveBlock( - Bob, - Bob, - meta, - GENESIS_BLOCK_HASH, - 10000, - 10001, - bytes32(uint256(0x11)), - bytes32(uint256(0x12)) - ); - TaikoData.BlockEvidence memory evidence = TaikoData.BlockEvidence({ - metaHash: LibUtils.hashMetadata(meta), - parentHash: GENESIS_BLOCK_HASH, - blockHash: bytes32(uint256(0x11)), - signalRoot: bytes32(uint256(0x12)), - graffiti: 0x0, - prover: address(0), - parentGasUsed: 10000, - gasUsed: 40000, - verifierId: 0, - proof: new bytes(0) - }); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(AlicePK, keccak256(abi.encode(evidence))); - - evidence.verifierId = v; - evidence.proof = bytes.concat(r, s); - - vm.prank(Carol, Carol); - L1.proveBlock(meta.id, abi.encode(evidence)); - TaikoData.ForkChoice memory fc = L1.getForkChoice(1, GENESIS_BLOCK_HASH, 10000); - - assertEq(fc.blockHash, bytes32(uint256(0x11))); - assertEq(fc.signalRoot, bytes32(uint256(0x12))); - assertEq(fc.provenAt, block.timestamp); - assertEq(fc.prover, address(0)); - assertEq(fc.gasUsed, 40000); - } - - function testOracleProverCanAlwaysOverwriteIfNotSameProof() external { - // Carol is the oracle prover - registerAddress("oracle_prover", Carol); - registerAddress("system_prover", Carol); - - depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); - depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); - depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); + function testProvingWithSgx() external { + depositTaikoToken(Alice, 1E6 * 1E8, 100 ether); + depositTaikoToken(Bob, 1E6 * 1E8, 100 ether); + depositTaikoToken(Carol, 1E6 * 1E8, 100 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; uint256 blockId = 1; @@ -105,7 +57,7 @@ contract TaikoL1OracleTest is TaikoL1TestBase { uint32 parentGasUsed = uint32(10000 + i); // Bob proves the block - proveBlock( + proveBlockWithSgxSignature( Bob, Bob, meta, @@ -120,6 +72,10 @@ contract TaikoL1OracleTest is TaikoL1TestBase { TaikoData.ForkChoice memory fc = L1.getForkChoice(blockId, parentHash, parentGasUsed); + vm.warp(block.timestamp + 1); + vm.warp(block.timestamp + conf.proofCooldownPeriod); + verifyBlock(Carol, 1); + if (i == 0) { assertFalse(fc.key == 0); } else { @@ -130,126 +86,46 @@ contract TaikoL1OracleTest is TaikoL1TestBase { assertEq(fc.provenAt, provenAt); assertEq(fc.prover, Bob); assertEq(fc.gasUsed, 10001); - - // Carol - who is oracle prover - can overwrite with same proof - vm.warp(block.timestamp + 10 seconds); - proveBlock( - Carol, - address(0), - meta, - parentHash, - parentGasUsed, - 10002, - bytes32(uint256(0x11)), - bytes32(uint256(0x12)) - ); - - provenAt = block.timestamp; - - fc = L1.getForkChoice(blockId, parentHash, parentGasUsed); - - if (i == 0) { - assertFalse(fc.key == 0); - } else { - assertEq(fc.key, 0); - } - assertEq(fc.blockHash, bytes32(uint256(0x11))); - assertEq(fc.signalRoot, bytes32(uint256(0x12))); - assertEq(fc.provenAt, provenAt); - assertEq(fc.prover, address(0)); - assertEq(fc.gasUsed, 10002); } } - function testOracleProverCannotOverwriteIfSameProof() external { + /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' + function test_cooldown_more_blocks_than_ring_buffer_size() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); bytes32 parentHash = GENESIS_BLOCK_HASH; - uint256 blockId = 1; - TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - - for (uint256 i = 0; i < 5; ++i) { - uint32 parentGasUsed = uint32(10000 + i); - - // Bob proves the block - proveBlock( - Bob, - Bob, - meta, - parentHash, - parentGasUsed, - 10001, - bytes32(uint256(0x11)), - bytes32(uint256(0x12)) - ); - - uint256 provenAt = block.timestamp; - - TaikoData.ForkChoice memory fc = L1.getForkChoice(blockId, parentHash, parentGasUsed); - - if (i == 0) { - assertFalse(fc.key == 0); - } else { - assertEq(fc.key, 0); - } - assertEq(fc.blockHash, bytes32(uint256(0x11))); - assertEq(fc.signalRoot, bytes32(uint256(0x12))); - assertEq(fc.provenAt, provenAt); - assertEq(fc.prover, Bob); - assertEq(fc.gasUsed, 10001); + uint32 parentGasUsed = 0; + uint32 gasUsed = 1000000; - // Carol cannot prove the fork choice again - vm.warp(block.timestamp + 10 seconds); - vm.expectRevert(); - proveBlock( - Carol, - Carol, - meta, - parentHash, - parentGasUsed, - 10002, - bytes32(uint256(0x21)), - bytes32(uint256(0x22)) - ); + for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { + TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); + mine(1); - // Alice, the oracle prover, cannot overwrite with same parameters - vm.warp(block.timestamp + 10 seconds); + bytes32 blockHash = bytes32(1e10 + blockId); + bytes32 signalRoot = bytes32(1e9 + blockId); + proveBlockWithSgxSignature(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); - vm.expectRevert(TaikoErrors.L1_SAME_PROOF.selector); - proveBlock( - Alice, - address(0), - meta, - parentHash, - parentGasUsed, - 10001, - bytes32(uint256(0x11)), - bytes32(uint256(0x12)) - ); + uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; + vm.warp(block.timestamp + 4 minutes + 59 seconds); verifyBlock(Carol, 1); - fc = L1.getForkChoice(blockId, parentHash, parentGasUsed); + assertEq(lastVerifiedBlockId, L1.getStateVariables().lastVerifiedBlockId); - if (i == 0) { - assertFalse(fc.key == 0); - } else { - assertEq(fc.key, 0); - } - assertEq(fc.blockHash, bytes32(uint256(0x11))); - assertEq(fc.signalRoot, bytes32(uint256(0x12))); - assertEq(fc.provenAt, provenAt); - assertEq(fc.prover, Bob); - assertEq(fc.gasUsed, 10001); + vm.warp(block.timestamp + 1 seconds); + verifyBlock(Carol, 1); + assertFalse(lastVerifiedBlockId == L1.getStateVariables().lastVerifiedBlockId); - vm.warp(block.timestamp + 10 seconds); + parentHash = blockHash; + parentGasUsed = gasUsed; } + printVariables(""); } - /// @dev Test we can propose, prove, then verify more blocks than 'maxNumProposedBlocks' - function test_cooldown_more_blocks_than_ring_buffer_size() external { + /// @dev Test if system works with both proof submitted + function test_multi_proving_with_both_signatures() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -259,25 +135,24 @@ contract TaikoL1OracleTest is TaikoL1TestBase { uint32 gasUsed = 1000000; for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { - printVariables("before propose"); TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - proveBlock(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); + proveBlockWithSgxSignature(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - vm.warp(block.timestamp + 4 minutes + 59 seconds); + // Need to wait config.proofCooldownPeriod + vm.warp(block.timestamp + conf.proofCooldownPeriod); verifyBlock(Carol, 1); - assertEq(lastVerifiedBlockId, L1.getStateVariables().lastVerifiedBlockId); + // Check if shortly after proving (+verify) the last verify is not the same anymore + // no need to have a cooldown period + uint256 lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; - vm.warp(block.timestamp + 1 seconds); - verifyBlock(Carol, 1); - assertFalse(lastVerifiedBlockId == L1.getStateVariables().lastVerifiedBlockId); + assertFalse(lastVerifiedBlockIdNow == lastVerifiedBlockId); parentHash = blockHash; parentGasUsed = gasUsed; @@ -285,14 +160,8 @@ contract TaikoL1OracleTest is TaikoL1TestBase { printVariables(""); } - /// @dev So in case we have regular proving mechanism we shall check if still a cooldown happens - /// @dev when proving a block (in a normal way). - /// @notice In case both oracle_prover and system_prover is disbaled, there is no reason why - /// @notice cooldowns be above 0 min tho (!). - function test_if_oracle_is_disabled_cooldown_is_still_as_proofCooldownPeriod() external { - registerAddress("oracle_prover", address(0)); - registerAddress("system_prover", address(0)); - + /// @dev Not possible to verify below proof cooldown time + function test_if_fails_if_verify_before_proof_cooldown() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -303,25 +172,25 @@ contract TaikoL1OracleTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - proveBlock(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); + proveBlockWithSgxSignature(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - vm.warp(block.timestamp + 1 seconds); + // Need to wait config.proofCooldownPeriod + vm.warp(block.timestamp + 4 minutes); verifyBlock(Carol, 1); // Check if shortly after proving (+verify) the last verify is not the same anymore // no need to have a cooldown period uint256 lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; - assertEq(lastVerifiedBlockIdNow, lastVerifiedBlockId); - - vm.warp(block.timestamp + 5 minutes); + assertEq(lastVerifiedBlockIdNow,lastVerifiedBlockId); + // Mine 1 min 1 sec to be above the 5 mins + vm.warp(block.timestamp + 61); verifyBlock(Carol, 1); lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; @@ -331,17 +200,10 @@ contract TaikoL1OracleTest is TaikoL1TestBase { parentHash = blockHash; parentGasUsed = gasUsed; } - printVariables(""); } - /// @dev Test if oracle prover is the only prover it cannot be verified - function test_that_simple_oracle_prover_cannot_be_verified_only_if_normal_proof_comes_in() - external - { - // Bob is an oracle prover now - registerAddress("oracle_prover", Bob); - registerAddress("system_prover", Bob); - + /// @dev Not possible to prove if only ZKP is present + function test_if_fails_multi_proving_with_only_zk_proof() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -350,51 +212,39 @@ contract TaikoL1OracleTest is TaikoL1TestBase { uint32 parentGasUsed = 0; uint32 gasUsed = 1000000; - for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { + for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - proveBlock( - Bob, address(0), meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); + + // Try only with ZK proof + vm.expectRevert(TaikoErrors.L1_NOT_ALL_REQ_PROOF_VERIFIED.selector); + proveBlock(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - vm.warp(block.timestamp + 1 seconds); + // Need to wait config.proofCooldownPeriod + vm.warp(block.timestamp + 4 minutes); verifyBlock(Carol, 1); - // Check if shortly after proving (+verify) the last verify is the same (bc it is an oracle proof) + // Check if shortly after proving (+verify) the last verify is not the same anymore + // no need to have a cooldown period uint256 lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; - // Cannot be verified - assertEq(lastVerifiedBlockIdNow, lastVerifiedBlockId); - - proveBlock( - Carol, Carol, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); - - vm.warp(block.timestamp + 1 seconds); - vm.warp(block.timestamp + 5 minutes); + assertEq(lastVerifiedBlockIdNow,lastVerifiedBlockId); + // Mine 1 min 1 sec to be above the 5 mins + vm.warp(block.timestamp + 61); verifyBlock(Carol, 1); - lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; - - // Can be verified now bc regular user overwrote it - assertFalse(lastVerifiedBlockIdNow == lastVerifiedBlockId); - parentHash = blockHash; parentGasUsed = gasUsed; } - printVariables(""); } - /// @dev Test if system prover is the prover, cooldown is systemProofCooldownPeriod - function test_if_prover_is_system_prover_cooldown_is_systemProofCooldownPeriod() external { - registerAddress("system_prover", Bob); - + /// @dev Try with SGX only and fail + function test_if_fails_multi_proving_with_only_sgx_sig_proof() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -403,63 +253,39 @@ contract TaikoL1OracleTest is TaikoL1TestBase { uint32 parentGasUsed = 0; uint32 gasUsed = 1000000; - for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { + for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - uint256 realproof = blockId % conf.realProofSkipSize; - - if (realproof == 0) { - proveBlock( - Carol, Carol, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); - } else { - proveBlock( - Bob, address(1), meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); - } + // Try only with SGX sig proof + vm.expectRevert(TaikoErrors.L1_NOT_ALL_REQ_PROOF_VERIFIED.selector); + proveBlockWithSgxSignatureOnly(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - vm.warp(block.timestamp + 1 seconds); + // Need to wait config.proofCooldownPeriod + vm.warp(block.timestamp + 4 minutes); verifyBlock(Carol, 1); // Check if shortly after proving (+verify) the last verify is not the same anymore // no need to have a cooldown period uint256 lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; - // It would be true anyways, but better to separate things. - // If not real proof is necessary, also the proofCooldownPeriod needs to be elapsed to be true. - // So separating the check. - /// @notice: In case both system and oracle are disabled, we should set the cooldown time to 0 mins. - if (realproof != 0) { - assertEq(lastVerifiedBlockIdNow, lastVerifiedBlockId); - } - - vm.warp( - block.timestamp + L1.getStateVariables().proofTimeTarget - + conf.systemProofCooldownPeriod - ); + assertEq(lastVerifiedBlockIdNow,lastVerifiedBlockId); + // Mine 1 min 1 sec to be above the 5 mins + vm.warp(block.timestamp + 61); verifyBlock(Carol, 1); - lastVerifiedBlockIdNow = L1.getStateVariables().lastVerifiedBlockId; - - assertFalse(lastVerifiedBlockIdNow == lastVerifiedBlockId); - parentHash = blockHash; parentGasUsed = gasUsed; } - printVariables(""); } - /// @dev Test if system proofs can be verified - function test_if_system_proofs_can_be_verified_without_regular_proofs() external { - registerAddress("system_prover", Bob); - + /// @dev Test if regular prover cannot overwrite + function test_if_regular_prover_cannot_override() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -470,17 +296,19 @@ contract TaikoL1OracleTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - proveBlock(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); + proveBlockWithSgxSignature(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - // Need to wait config.systemProofCooldownPeriod - vm.warp(block.timestamp + conf.systemProofCooldownPeriod); + vm.expectRevert(TaikoErrors.L1_ALREADY_PROVEN.selector); + proveBlockWithSgxSignature(Carol, Carol, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); + + // Need to wait config.proofCooldownPeriod + vm.warp(block.timestamp + conf.proofCooldownPeriod); verifyBlock(Carol, 1); // Check if shortly after proving (+verify) the last verify is not the same anymore @@ -495,10 +323,8 @@ contract TaikoL1OracleTest is TaikoL1TestBase { printVariables(""); } - /// @dev Test if system prover cannot be overwritten - function test_if_systemProver_can_prove_but_regular_provers_can_overwrite() external { - registerAddress("system_prover", Bob); - + /// @dev At some point set forkchoice by the 'failsafe' mechanism + function test_if_failsafe_account_can_set_fork_choice() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -506,42 +332,42 @@ contract TaikoL1OracleTest is TaikoL1TestBase { bytes32 parentHash = GENESIS_BLOCK_HASH; uint32 parentGasUsed = 0; uint32 gasUsed = 1000000; - for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - uint256 realProof = blockId % conf.realProofSkipSize; - - if (realProof == 0) { - proveBlock( - Carol, Carol, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); - } else { - proveBlock( - Bob, address(1), meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); + // 'Prove' every second block with the failsafe mechanism + if(blockId % 2 == 0) { + TaikoData.TypedProof[] memory blockProofs; + + TaikoData.BlockEvidence memory evidence = TaikoData.BlockEvidence({ + metaHash: LibUtils.hashMetadata(meta), + parentHash: parentHash, + blockHash: blockHash, + signalRoot: signalRoot, + graffiti: 0x0, + prover: FailsafeProver, + parentGasUsed: parentGasUsed, + gasUsed: gasUsed, + blockProofs: blockProofs + }); + + vm.prank(FailsafeProver,FailsafeProver); + L1.proveBlock(meta.id,abi.encode(evidence)); + + // Wait enough because now provenAt set in the future - exactly at proofTimeTarget to not + // cause any 'damage' to the tokenomics + vm.warp(block.timestamp + (L1.getStateVariables().proofTimeTarget + 1) + conf.proofCooldownPeriod); } - - uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - - // Carol could overwrite it - if (realProof != 0) { - proveBlock( - Carol, Carol, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); + else { + proveBlockWithSgxSignature(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); + vm.warp(block.timestamp + (conf.proofCooldownPeriod + 1)); } - vm.warp(block.timestamp + 1 seconds); - vm.warp(block.timestamp + 5 minutes); - - TaikoData.ForkChoice memory fc = L1.getForkChoice(blockId, parentHash, parentGasUsed); - - if (realProof != 0) assertEq(fc.prover, Carol); + uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; verifyBlock(Carol, 1); @@ -557,11 +383,8 @@ contract TaikoL1OracleTest is TaikoL1TestBase { printVariables(""); } - /// @dev Test if there is no system/oracle proofs - function test_if_there_is_no_system_and_oracle_provers() external { - registerAddress("system_prover", address(0)); - registerAddress("oracle_prover", address(0)); - + /// @dev Kind of same as above but not setting the fork choice directly but overwriting it + function test_if_failsafe_account_can_overwrite_fork_choice() external { depositTaikoToken(Alice, 1e6 * 1e8, 100 ether); depositTaikoToken(Bob, 1e6 * 1e8, 100 ether); depositTaikoToken(Carol, 1e6 * 1e8, 100 ether); @@ -572,24 +395,37 @@ contract TaikoL1OracleTest is TaikoL1TestBase { for (uint256 blockId = 1; blockId < conf.maxNumProposedBlocks * 10; blockId++) { TaikoData.BlockMetadata memory meta = proposeBlock(Alice, 1000000, 1024); - printVariables("after propose"); mine(1); bytes32 blockHash = bytes32(1e10 + blockId); bytes32 signalRoot = bytes32(1e9 + blockId); - proveBlock(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); - uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; + proveBlockWithSgxSignature(Bob, Bob, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot); + + // Overwrite fk + TaikoData.TypedProof[] memory blockProofs; + + TaikoData.BlockEvidence memory evidence = TaikoData.BlockEvidence({ + metaHash: LibUtils.hashMetadata(meta), + parentHash: parentHash, + blockHash: blockHash, + signalRoot: signalRoot, + graffiti: 0x0, + prover: FailsafeProver, + parentGasUsed: parentGasUsed, + gasUsed: gasUsed, + blockProofs: blockProofs + }); + + vm.prank(FailsafeProver,FailsafeProver); + L1.proveBlock(meta.id,abi.encode(evidence)); + + // Wait enough because now provenAt set in the future - exactly at proofTimeTarget to not + // cause any 'damage' to the tokenomics + vm.warp(block.timestamp + (L1.getStateVariables().proofTimeTarget + 1) + conf.proofCooldownPeriod); - // Carol could not overwrite it - vm.expectRevert(TaikoErrors.L1_ALREADY_PROVEN.selector); - proveBlock( - Carol, Carol, meta, parentHash, parentGasUsed, gasUsed, blockHash, signalRoot - ); + uint256 lastVerifiedBlockId = L1.getStateVariables().lastVerifiedBlockId; - /// @notice: Based on the current codebase we still need to wait even if the system and oracle proofs are disbaled, which - /// @notice: in such case best to set 0 mins (cause noone could overwrite a valid fk). - vm.warp(block.timestamp + conf.proofCooldownPeriod); verifyBlock(Carol, 1); // Check if shortly after proving (+verify) the last verify is not the same anymore diff --git a/packages/protocol/test/TaikoL1TestBase.t.sol b/packages/protocol/test/TaikoL1TestBase.t.sol index 73dfe75b2ce..c9752bd987b 100644 --- a/packages/protocol/test/TaikoL1TestBase.t.sol +++ b/packages/protocol/test/TaikoL1TestBase.t.sol @@ -46,6 +46,7 @@ abstract contract TaikoL1TestBase is Test { address public constant Frank = 0x430c9b60e19634e12FC6d68B7fEa7bFB26c2e419; address public constant George = 0x520147C0eB43d8D71b2b03037bB7b31f8F78EF5f; address public constant Hilbert = 0x61081B12838240B1Ba02b3177153BcA678a86078; + address public constant FailsafeProver = 0x60081b12838240b1Ba02B3177153BCA678A86079; // Calculation shall be done in derived contracts - based on testnet or mainnet expected proof time uint64 public initProofTimeIssued; @@ -66,12 +67,18 @@ abstract contract TaikoL1TestBase is Test { registerAddress("signal_service", address(ss)); registerAddress("ether_vault", address(L1EthVault)); + + registerAddress("forkchoice_failsafe", address(FailsafeProver)); registerL2Address("treasury", L2Treasury); + registerL2Address("taiko", address(TaikoL2)); registerL2Address("signal_service", address(L2SS)); registerL2Address("taiko_l2", address(TaikoL2)); registerAddress(L1.getVerifierName(100), address(new Verifier())); registerAddress(L1.getVerifierName(0), address(new Verifier())); + // Register Alice as an SGX verifier - SGX verifiers be 10000 offsetted + // e.g.: 0..9999 ZK verifiers, 10000..19999 SGX verifier addresses + registerAddress(L1.getVerifierName(10000), Alice); tko = new TaikoToken(); registerAddress("taiko_token", address(tko)); @@ -149,6 +156,17 @@ abstract contract TaikoL1TestBase is Test { bytes32 blockHash, bytes32 signalRoot ) internal { + TaikoData.TypedProof memory zkpTypedProof = TaikoData.TypedProof({ + verifierId: 100, + proof: new bytes(100) + }); + + TaikoData.TypedProof[] memory blockProofs = new TaikoData.TypedProof[]( + 1 + ); + + blockProofs[0] = zkpTypedProof; + TaikoData.BlockEvidence memory evidence = TaikoData.BlockEvidence({ metaHash: LibUtils.hashMetadata(meta), parentHash: parentHash, @@ -158,10 +176,85 @@ abstract contract TaikoL1TestBase is Test { prover: prover, parentGasUsed: parentGasUsed, gasUsed: gasUsed, + blockProofs: blockProofs + }); + + vm.prank(msgSender, msgSender); + L1.proveBlock(meta.id, abi.encode(evidence)); + } + + function proveBlockWithSgxSignature( + address msgSender, + address prover, + TaikoData.BlockMetadata memory meta, + bytes32 parentHash, + uint32 parentGasUsed, + uint32 gasUsed, + bytes32 blockHash, + bytes32 signalRoot + ) internal { + TaikoData.TypedProof memory zkpTypedProof = TaikoData.TypedProof({ verifierId: 100, proof: new bytes(100) }); + TaikoData.TypedProof[] memory blockProofs = new TaikoData.TypedProof[]( + 2 + ); + + blockProofs[0] = zkpTypedProof; + + TaikoData.BlockEvidence memory evidence = TaikoData.BlockEvidence({ + metaHash: LibUtils.hashMetadata(meta), + parentHash: parentHash, + blockHash: blockHash, + signalRoot: signalRoot, + graffiti: 0x0, + prover: prover, + parentGasUsed: parentGasUsed, + gasUsed: gasUsed, + blockProofs: blockProofs + }); + + blockProofs[1] = createSgxSignature(evidence); + + evidence.blockProofs = blockProofs; + + vm.prank(msgSender, msgSender); + L1.proveBlock(meta.id, abi.encode(evidence)); + } + + function proveBlockWithSgxSignatureOnly( + address msgSender, + address prover, + TaikoData.BlockMetadata memory meta, + bytes32 parentHash, + uint32 parentGasUsed, + uint32 gasUsed, + bytes32 blockHash, + bytes32 signalRoot + ) internal { + + TaikoData.TypedProof[] memory blockProofs = new TaikoData.TypedProof[]( + 1 + ); + + TaikoData.BlockEvidence memory evidence = TaikoData.BlockEvidence({ + metaHash: LibUtils.hashMetadata(meta), + parentHash: parentHash, + blockHash: blockHash, + signalRoot: signalRoot, + graffiti: 0x0, + prover: prover, + parentGasUsed: parentGasUsed, + gasUsed: gasUsed, + blockProofs: blockProofs + }); + + blockProofs[0] = createSgxSignature(evidence); + + evidence.blockProofs = blockProofs; + vm.prank(msgSender, msgSender); L1.proveBlock(meta.id, abi.encode(evidence)); } @@ -220,4 +313,40 @@ abstract contract TaikoL1TestBase is Test { vm.warp(block.timestamp + 20 * counts); vm.roll(block.number + counts); } + + function createSgxSignature( + TaikoData.BlockEvidence memory evidence + ) internal view returns (TaikoData.TypedProof memory sgxProof) { + uint256[10] memory inputs; + + inputs[0] = uint256(uint160(address(ss))); + inputs[1] = uint256(uint160(address(L2SS))); + inputs[2] = uint256(uint160(address(TaikoL2))); + + inputs[3] = uint256(evidence.metaHash); + inputs[4] = uint256(evidence.parentHash); + inputs[5] = uint256(evidence.blockHash); + inputs[6] = uint256(evidence.signalRoot); + inputs[7] = uint256(evidence.graffiti); + inputs[8] = (uint256(uint160(evidence.prover)) << 96) + | (uint256(evidence.parentGasUsed) << 64) | (uint256(evidence.gasUsed) << 32); + + // Also hash configs that will be used by circuits + inputs[9] = uint256(conf.blockMaxGasLimit) << 192 + | uint256(conf.maxTransactionsPerBlock) << 128 + | uint256(conf.maxBytesPerTxList) << 64; + + bytes32 instance; + assembly { + instance := keccak256(inputs, mul(32, 10)) + } + + // console2.log("Instace:"); + // console2.logBytes(instance); + // Alice is a trusted SGX signer + (uint8 v, bytes32 r, bytes32 s) = vm.sign(AlicePK, instance); + + sgxProof.verifierId = 10000; + sgxProof.proof = bytes.concat(bytes1(v), r, s); + } } diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md index 6f0c1cd1204..65ad192a61c 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoData.md @@ -17,7 +17,6 @@ struct Config { uint64 maxBytesPerTxList; uint256 txListCacheExpiry; uint256 proofCooldownPeriod; - uint256 systemProofCooldownPeriod; uint256 realProofSkipSize; uint256 ethDepositGas; uint256 ethDepositMaxFee; @@ -27,6 +26,7 @@ struct Config { uint96 minEthDepositAmount; uint8 adjustmentQuotient; bool relaySignalRoot; + bool enableSgxProving; } ``` @@ -80,6 +80,15 @@ struct BlockMetadata { } ``` +### TypedProof + +```solidity +struct TypedProof { + uint16 verifierId; + bytes proof; +} +``` + ### BlockEvidence ```solidity @@ -92,8 +101,7 @@ struct BlockEvidence { address prover; uint32 parentGasUsed; uint32 gasUsed; - uint16 verifierId; - bytes proof; + struct TaikoData.TypedProof[] blockProofs; } ``` diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md index 85b66c7b9c7..ae6f130e111 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoErrors.md @@ -70,40 +70,22 @@ error L1_INVALID_PARAM() error L1_INVALID_PROOF() ``` -### L1_INVALID_PROOF_OVERWRITE +### L1_INVALID_SGX_SIGNATURE ```solidity -error L1_INVALID_PROOF_OVERWRITE() +error L1_INVALID_SGX_SIGNATURE() ``` -### L1_NOT_SPECIAL_PROVER +### L1_NOT_ALL_REQ_PROOF_VERIFIED ```solidity -error L1_NOT_SPECIAL_PROVER() +error L1_NOT_ALL_REQ_PROOF_VERIFIED() ``` -### L1_ORACLE_PROVER_DISABLED +### L1_NOTHING_TO_OVERWRITE ```solidity -error L1_ORACLE_PROVER_DISABLED() -``` - -### L1_SAME_PROOF - -```solidity -error L1_SAME_PROOF() -``` - -### L1_SYSTEM_PROVER_DISABLED - -```solidity -error L1_SYSTEM_PROVER_DISABLED() -``` - -### L1_SYSTEM_PROVER_PROHIBITED - -```solidity -error L1_SYSTEM_PROVER_PROHIBITED() +error L1_NOTHING_TO_OVERWRITE() ``` ### L1_TOO_MANY_BLOCKS diff --git a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md index c5b4964c07b..fea845fd46a 100644 --- a/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md +++ b/packages/website/pages/docs/reference/contract-documentation/L1/TaikoEvents.md @@ -33,3 +33,9 @@ event EthDeposited(struct TaikoData.EthDeposit deposit) ```solidity event ProofTimeTargetChanged(uint64 proofTimeTarget) ``` + +### BlockForkChoiceFailsafeRewrite + +```solidity +event BlockForkChoiceFailsafeRewrite(uint256 blockId) +```