From 2b2a36ea5928f09909943366594856ceb2182c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Keszey=20D=C3=A1niel?= Date: Thu, 4 Jul 2024 18:56:49 +0200 Subject: [PATCH 1/3] Test proving/verifying logic --- .../protocol/contracts/L1/BasedOperator.sol | 22 ++++++------ packages/protocol/contracts/L1/TaikoData.sol | 3 +- .../protocol/contracts/L1/TaikoErrors.sol | 1 + packages/protocol/contracts/L1/TaikoL1.sol | 30 +++++++++++----- packages/protocol/test/L1/TaikoL1.t.sol | 20 ++++++----- packages/protocol/test/L1/TaikoL1TestBase.sol | 34 ++++++++----------- 6 files changed, 61 insertions(+), 49 deletions(-) diff --git a/packages/protocol/contracts/L1/BasedOperator.sol b/packages/protocol/contracts/L1/BasedOperator.sol index cdb877851540..7b307585db9c 100644 --- a/packages/protocol/contracts/L1/BasedOperator.sol +++ b/packages/protocol/contracts/L1/BasedOperator.sol @@ -108,13 +108,13 @@ contract BasedOperator is EssentialContract, TaikoErrors { } VerifierRegistry verifierRegistry = VerifierRegistry(resolve("verifier_registry", false)); - TaikoL1 taiko = TaikoL1(resolve("taiko", false)); + TaikoL1 taiko = TaikoL1(resolve("taiko", false)); // Verify the proofs uint160 prevVerifier = uint160(0); for (uint256 i = 0; i < proofBatch.proofs.length; i++) { IVerifier verifier = proofBatch.proofs[i].verifier; // Make sure each verifier is unique - if(prevVerifier >= uint160(address(verifier))) { + if (prevVerifier >= uint160(address(verifier))) { revert L1_INVALID_OR_DUPLICATE_VERIFIER(); } // Make sure it's a valid verifier @@ -140,18 +140,16 @@ contract BasedOperator is EssentialContract, TaikoErrors { proofBatch.blockMetadata.l2BlockNumber, proofBatch.transition.parentHash ); - console2.log("What is stored:"); - console2.logBytes32(storedTransition.blockHash); - - console2.log("What we are trying to prove:"); - console2.logBytes32(proofBatch.transition.blockHash); - // Brecht: SO we set the blockHash in proposeBlock(). - // But we need to prove it too (the same one), so somehow we need to check if this is proven already and IF NOT, then revert with "block already proven", no ? So i set the verifiableAfter in propseBlock to 0, because this is just a "proposed state". - if (storedTransition.isProven == true && storedTransition.blockHash == proofBatch.transition.blockHash) { + // But we need to prove it too (the same one), so somehow we need to check if this is proven + // already and IF NOT, then revert with "block already proven", no ? So i set the + // verifiableAfter in propseBlock to 0, because this is just a "proposed state". + if ( + storedTransition.isProven == true + && storedTransition.blockHash == proofBatch.transition.blockHash + ) { revert("block already proven"); - } - else { + } else { // TODO(Brecht): Check that one of the verifiers is now poissoned } diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index c6c50c66de66..703142f0834b 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -65,7 +65,8 @@ library TaikoData { /// @dev Struct holding the state variables for the {TaikoL1} contract. struct State { mapping(uint256 blockId => Block) blocks; - mapping(uint256 blockId => mapping(bytes32 parentBlockHash => TransitionState)) transitions; + // Todo (Brecht): please check which one to use here (?) metaHash or blockHash + mapping(uint256 blockId => mapping(bytes32 parentMetaHash => TransitionState)) transitions; uint64 genesisHeight; uint64 genesisTimestamp; uint64 numBlocks; diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index 551d45af64a2..545a9843ab41 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -20,6 +20,7 @@ abstract contract TaikoErrors { error L1_BLOB_NOT_FOUND(); error L1_BLOB_NOT_REUSEABLE(); error L1_BLOCK_MISMATCH(); + error L1_INCORRECT_BLOCK(); error L1_INSUFFICIENT_TOKEN(); error L1_INVALID_ADDRESS(); error L1_INVALID_AMOUNT(); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 2e55b2181271..ebd69dded85a 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -58,7 +58,7 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { payable nonReentrant whenNotPaused - onlyFromNamed("based_operator") + onlyFromNamed("operator") returns (TaikoData.BlockMetadata memory _block) { TaikoData.Config memory config = getConfig(); @@ -95,7 +95,10 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { TaikoData.Block storage parentBlock = state.blocks[(state.numBlocks - 1)]; - require(_block.parentMetaHash == parentBlock.blockHash, "invalid parentMetaHash"); + console2.log("Mi a faszom"); + console2.logBytes32(_block.parentMetaHash); + console2.logBytes32(parentBlock.metaHash); + require(_block.parentMetaHash == parentBlock.metaHash, "invalid parentHash"); // Verify the passed in L1 state block number. // We only allow the L1 block to be 4 epochs old. @@ -132,7 +135,7 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { // Store the block state.blocks[state.numBlocks] = blk; - // Store the passed in block hash as in + // Store the passed in block hash as is state.transitions[blk.blockId][_block.parentMetaHash].blockHash = _block.blockHash; // For now it does not matter - we are not going to prove anyways state.transitions[blk.blockId][_block.parentMetaHash].verifiableAfter = @@ -156,8 +159,12 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { external nonReentrant whenNotPaused - onlyFromNamed("based_operator") + onlyFromNamed("operator") { + console2.log("Miafasz van mar"); + console2.log(_block.l2BlockNumber); + console2.log(state.lastVerifiedBlockId); + console2.log(state.numBlocks); // Check that the block has been proposed but has not yet been verified. if ( _block.l2BlockNumber <= state.lastVerifiedBlockId @@ -169,7 +176,9 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { TaikoData.Block storage blk = state.blocks[_block.l2BlockNumber]; // Make sure the correct block was proven - require(blk.metaHash != keccak256(abi.encode(_block)), "incorrect block"); + if(blk.metaHash != keccak256(abi.encode(_block))) { + revert L1_INCORRECT_BLOCK(); + } // Store the transition TaikoData.TransitionState storage storedTransition = @@ -177,6 +186,7 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { storedTransition.blockHash = transition.blockHash; storedTransition.prover = prover; storedTransition.verifiableAfter = uint32(block.timestamp + SECURITY_DELAY_AFTER_PROVEN); + storedTransition.isProven = true; emit TransitionProved({ blockId: _block.l2BlockNumber, tran: transition, prover: prover }); } @@ -187,26 +197,30 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { external nonReentrant whenNotPaused - onlyFromNamed("based_operator") + onlyFromNamed("operator") { // Get the last verified blockhash TaikoData.Block storage blk = state.blocks[state.lastVerifiedBlockId]; - bytes32 blockHash = blk.blockHash; + // Brecht: i think this was a bug, or indeed TaikoData's Transition mapping was misleading.. Now i'm using metaHash as a path/key in the mapping, so i guess we shall use it here to query data, right ? But somehow it is not working :D // Go to the first unverified block + bytes32 blockHash = blk.metaHash; + //blockHash = blk.blockHash; uint256 blockId = uint256(state.lastVerifiedBlockId) + 1; uint256 numBlocksVerified; + console2.log("V1"); while (blockId < state.numBlocks && numBlocksVerified < maxBlocksToVerify) { blk = state.blocks[blockId]; - // Check if the parent block hash matches the actual block hash of the parent // Check if the timestamp is older than required if ( + // Brecht: I think here is also not good, or a mismatch.. How we use/save transitions. Is it blockHash or metaHash?? state.transitions[blockId][blockHash].blockHash == bytes32(0) || block.timestamp < state.transitions[blockId][blockHash].verifiableAfter ) { break; } + console2.log("V3"); // Copy the blockhash to the block blk.blockHash = state.transitions[blockId][blockHash].blockHash; // Update latest block hash diff --git a/packages/protocol/test/L1/TaikoL1.t.sol b/packages/protocol/test/L1/TaikoL1.t.sol index 9880ba4d7f28..f7726049f442 100644 --- a/packages/protocol/test/L1/TaikoL1.t.sol +++ b/packages/protocol/test/L1/TaikoL1.t.sol @@ -19,7 +19,7 @@ contract TaikoL1Test is TaikoL1TestBase { // console2.log(block.number); // meta.blockHash = randBytes32(); - // meta.parentMetaHash = GENESIS_BLOCK_HASH; + // meta.parentHash = GENESIS_BLOCK_HASH; // meta.l1Hash = blockhash(block.number - 1); // meta.difficulty = block.prevrandao; // meta.blobHash = randBytes32(); @@ -32,22 +32,24 @@ contract TaikoL1Test is TaikoL1TestBase { // meta.txListByteOffset = 0; // meta.txListByteSize = 0; // meta.blobUsed = true; - + bytes32 parentMetaHash; for (uint64 blockId = 1; blockId <= 1; blockId++) { printVariables("before propose"); - meta = createBlockMetaData(Alice, blockId, 1, true); + // Create metadata and propose the block + meta = createBlockMetaData(parentMetaHash, Alice, blockId, 1, true); proposeBlock(Alice, Alice, meta); - printVariables("after propose"); + // Create proofs and prove a block BasedOperator.ProofBatch memory blockProofs = createProofs(meta, Alice, true); - proveBlock(Alice, abi.encode(blockProofs)); - // bytes32 blockHash = bytes32(1e10 + blockId); - // bytes32 stateRoot = bytes32(1e9 + blockId); + //Wait enought time and verify block + vm.warp(uint32(block.timestamp + L1.SECURITY_DELAY_AFTER_PROVEN() + 1)); + vm.roll(block.number + 10); + verifyBlock(1); + printVariables("after verify"); - // proveBlock(Alice, meta, parentHash, blockHash, stateRoot, meta.minTier, ""); - // parentHash = blockHash; + parentMetaHash = keccak256(abi.encode(meta)); } } } diff --git a/packages/protocol/test/L1/TaikoL1TestBase.sol b/packages/protocol/test/L1/TaikoL1TestBase.sol index ac0d1c230488..540604242201 100644 --- a/packages/protocol/test/L1/TaikoL1TestBase.sol +++ b/packages/protocol/test/L1/TaikoL1TestBase.sol @@ -57,7 +57,7 @@ abstract contract TaikoL1TestBase is TaikoTest { basedOperator = BasedOperator( deployProxy({ - name: "based_operator", + name: "operator", impl: address(new BasedOperator()), data: abi.encodeCall(BasedOperator.init, (address(addressManager))) }) @@ -72,7 +72,7 @@ abstract contract TaikoL1TestBase is TaikoTest { ); registerAddress("taiko", address(L1)); - registerAddress("based_operator", address(basedOperator)); + registerAddress("operator", address(basedOperator)); registerAddress("verifier_registry", address(vr)); // ss = SignalService( @@ -93,7 +93,8 @@ abstract contract TaikoL1TestBase is TaikoTest { // ); address sgxImpl = address(new SgxVerifier()); - //Naming is like: 3, 1, 2, is because we need to have incremental order of addresses in BasedOperator, so figured out this is actually the way + //Naming is like: 3, 1, 2, is because we need to have incremental order of addresses in + // BasedOperator, so figured out this is actually the way sv3 = SgxVerifier( deployProxy({ name: "sgx1", //Name does not matter now, since we check validity via @@ -279,7 +280,7 @@ abstract contract TaikoL1TestBase is TaikoTest { // } // meta.blockHash = blockHash; - // meta.parentMetaHash = parentMetaHash; + // meta.parentHash = parentHash; // meta.timestamp = uint64(block.timestamp); // meta.l1Height = uint64(block.number - 1); @@ -303,20 +304,13 @@ abstract contract TaikoL1TestBase is TaikoTest { return meta; } - function proveBlock( - address prover, - bytes memory blockProof - ) - internal - { + function proveBlock(address prover, bytes memory blockProof) internal { vm.prank(prover, prover); - basedOperator.proveBlock( - blockProof - ); + basedOperator.proveBlock(blockProof); } - function verifyBlock(address, uint64 count) internal { - L1.verifyBlocks(count); + function verifyBlock(uint64 count) internal { + basedOperator.verifyBlocks(count); } // function setupGuardianProverMultisig() internal { @@ -425,6 +419,7 @@ abstract contract TaikoL1TestBase is TaikoTest { } function createBlockMetaData( + bytes32 parentMetaHash, address coinbase, uint64 l2BlockNumber, uint32 belowBlockTipHeight, // How many blocks (negatived direction) away from block.id @@ -435,9 +430,9 @@ abstract contract TaikoL1TestBase is TaikoTest { { meta.blockHash = randBytes32(); - meta.parentMetaHash = randBytes32(); + meta.parentMetaHash = parentMetaHash; if (l2BlockNumber == 1) { - meta.parentMetaHash = GENESIS_BLOCK_HASH; + meta.parentMetaHash = hex"0000000000000000000000000000000000000000000000000000000000000000"; } meta.l1Hash = blockhash(block.number - belowBlockTipHeight); @@ -476,7 +471,7 @@ abstract contract TaikoL1TestBase is TaikoTest { // Set transition TaikoData.Transition memory transition; - transition.parentHash = meta.parentMetaHash; + transition.parentHash = L1.getBlock(meta.l2BlockNumber).blockHash; transition.blockHash = meta.blockHash; proofBatch.transition = transition; @@ -509,7 +504,8 @@ abstract contract TaikoL1TestBase is TaikoTest { proofs[2].verifier = sv3; proofs[2].proof = bytes.concat(bytes4(0), bytes20(newInstance), signature); } else { - //Todo(dani): Implement more proof and verifiers when needed/available but for now, not to change the code in BasedOperator, maybe best to mock those 3 + //Todo(dani): Implement more proof and verifiers when needed/available but for now, not + // to change the code in BasedOperator, maybe best to mock those 3 } proofBatch.proofs = proofs; From ef000d9b86b2ad577d6b37b0546b81aba57bd6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Keszey=20D=C3=A1niel?= Date: Fri, 5 Jul 2024 10:38:33 +0200 Subject: [PATCH 2/3] add some tests --- .../protocol/contracts/L1/BasedOperator.sol | 2 +- packages/protocol/contracts/L1/TaikoData.sol | 8 +- packages/protocol/contracts/L1/TaikoL1.sol | 43 +++++------ .../contracts/L1/VerifierBattleRoyale.sol | 10 ++- packages/protocol/test/L1/TaikoL1.t.sol | 76 ++++++++++++++----- packages/protocol/test/L1/TaikoL1TestBase.sol | 35 +++++---- 6 files changed, 106 insertions(+), 68 deletions(-) diff --git a/packages/protocol/contracts/L1/BasedOperator.sol b/packages/protocol/contracts/L1/BasedOperator.sol index 7b307585db9c..181cb41cb272 100644 --- a/packages/protocol/contracts/L1/BasedOperator.sol +++ b/packages/protocol/contracts/L1/BasedOperator.sol @@ -137,7 +137,7 @@ contract BasedOperator is EssentialContract, TaikoErrors { // invalid // Get the currently stored transition TaikoData.TransitionState memory storedTransition = taiko.getTransition( - proofBatch.blockMetadata.l2BlockNumber, proofBatch.transition.parentHash + proofBatch.blockMetadata.l2BlockNumber, proofBatch.transition.parentBlockHash ); // Brecht: SO we set the blockHash in proposeBlock(). diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 703142f0834b..be787595fa6f 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -23,6 +23,7 @@ library TaikoData { /// @dev Struct containing data only required for proving a block struct BlockMetadata { bytes32 blockHash; + bytes32 parentBlockHash; bytes32 parentMetaHash; bytes32 l1Hash; uint256 difficulty; @@ -40,13 +41,13 @@ library TaikoData { /// @dev Struct representing transition to be proven. struct Transition { - bytes32 parentHash; + bytes32 parentBlockHash; bytes32 blockHash; } /// @dev Struct representing state transition data. struct TransitionState { - bytes32 blockHash; + bytes32 blockHash; //Might be removed.. uint64 timestamp; address prover; uint64 verifiableAfter; @@ -65,8 +66,7 @@ library TaikoData { /// @dev Struct holding the state variables for the {TaikoL1} contract. struct State { mapping(uint256 blockId => Block) blocks; - // Todo (Brecht): please check which one to use here (?) metaHash or blockHash - mapping(uint256 blockId => mapping(bytes32 parentMetaHash => TransitionState)) transitions; + mapping(uint256 blockId => mapping(bytes32 parentBlockHash => TransitionState)) transitions; uint64 genesisHeight; uint64 genesisTimestamp; uint64 numBlocks; diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index ebd69dded85a..4d0663bba089 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -9,7 +9,6 @@ pragma solidity ^0.8.20; import "../common/EssentialContract.sol"; import "./TaikoErrors.sol"; import "./TaikoEvents.sol"; -import "forge-std/console2.sol"; /// @title TaikoL1 contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { @@ -69,6 +68,7 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { // Verify L1 data // TODO(Brecht): needs to be more configurable for preconfirmations require(_block.l1Hash == blockhash(_block.l1StateBlockNumber), "INVALID_L1_BLOCKHASH"); + require(_block.blockHash != 0x0, "INVALID_L2_BLOCKHASH"); require(_block.difficulty == block.prevrandao, "INVALID_DIFFICULTY"); // Verify misc data require(_block.gasLimit == config.blockMaxGasLimit, "INVALID_GAS_LIMIT"); @@ -95,9 +95,6 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { TaikoData.Block storage parentBlock = state.blocks[(state.numBlocks - 1)]; - console2.log("Mi a faszom"); - console2.logBytes32(_block.parentMetaHash); - console2.logBytes32(parentBlock.metaHash); require(_block.parentMetaHash == parentBlock.metaHash, "invalid parentHash"); // Verify the passed in L1 state block number. @@ -136,10 +133,9 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { state.blocks[state.numBlocks] = blk; // Store the passed in block hash as is - state.transitions[blk.blockId][_block.parentMetaHash].blockHash = _block.blockHash; - // For now it does not matter - we are not going to prove anyways - state.transitions[blk.blockId][_block.parentMetaHash].verifiableAfter = - uint64(block.timestamp) + 365 days; + state.transitions[blk.blockId][_block.parentBlockHash].blockHash = _block.blockHash; + // Big enough number so that we are sure we don't hit that deadline in the future. + state.transitions[blk.blockId][_block.parentBlockHash].verifiableAfter = type(uint64).max; // Increment the counter (cursor) by 1. state.numBlocks++; @@ -161,10 +157,6 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { whenNotPaused onlyFromNamed("operator") { - console2.log("Miafasz van mar"); - console2.log(_block.l2BlockNumber); - console2.log(state.lastVerifiedBlockId); - console2.log(state.numBlocks); // Check that the block has been proposed but has not yet been verified. if ( _block.l2BlockNumber <= state.lastVerifiedBlockId @@ -176,13 +168,13 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { TaikoData.Block storage blk = state.blocks[_block.l2BlockNumber]; // Make sure the correct block was proven - if(blk.metaHash != keccak256(abi.encode(_block))) { + if (blk.metaHash != keccak256(abi.encode(_block))) { revert L1_INCORRECT_BLOCK(); } // Store the transition TaikoData.TransitionState storage storedTransition = - state.transitions[_block.l2BlockNumber][transition.parentHash]; + state.transitions[_block.l2BlockNumber][transition.parentBlockHash]; storedTransition.blockHash = transition.blockHash; storedTransition.prover = prover; storedTransition.verifiableAfter = uint32(block.timestamp + SECURITY_DELAY_AFTER_PROVEN); @@ -201,26 +193,23 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { { // Get the last verified blockhash TaikoData.Block storage blk = state.blocks[state.lastVerifiedBlockId]; - // Brecht: i think this was a bug, or indeed TaikoData's Transition mapping was misleading.. Now i'm using metaHash as a path/key in the mapping, so i guess we shall use it here to query data, right ? But somehow it is not working :D - // Go to the first unverified block - bytes32 blockHash = blk.metaHash; - //blockHash = blk.blockHash; + bytes32 blockHash = blk.blockHash; uint256 blockId = uint256(state.lastVerifiedBlockId) + 1; uint256 numBlocksVerified; - console2.log("V1"); + while (blockId < state.numBlocks && numBlocksVerified < maxBlocksToVerify) { blk = state.blocks[blockId]; - // Check if the parent block hash matches the actual block hash of the parent // Check if the timestamp is older than required if ( - // Brecht: I think here is also not good, or a mismatch.. How we use/save transitions. Is it blockHash or metaHash?? - state.transitions[blockId][blockHash].blockHash == bytes32(0) - || block.timestamp < state.transitions[blockId][blockHash].verifiableAfter + block + // Genesis is already verified with initialization so if we do not allow to set + // blockHash = bytes32(0), then we can remove the bytes32(0) check. + /*state.transitions[blockId][blockHash].blockHash == bytes32(0) + || */ + .timestamp < state.transitions[blockId][blockHash].verifiableAfter ) { break; } - - console2.log("V3"); // Copy the blockhash to the block blk.blockHash = state.transitions[blockId][blockHash].blockHash; // Update latest block hash @@ -272,6 +261,10 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors { return uint256(state.lastVerifiedBlockId); } + function getNumOfBlocks() public view returns (uint256) { + return uint256(state.numBlocks); + } + /// @notice Gets the configuration of the TaikoL1 contract. /// @return Config struct containing configuration parameters. function getConfig() public view virtual returns (TaikoData.Config memory) { diff --git a/packages/protocol/contracts/L1/VerifierBattleRoyale.sol b/packages/protocol/contracts/L1/VerifierBattleRoyale.sol index 607926014b34..b0a99dca6e29 100644 --- a/packages/protocol/contracts/L1/VerifierBattleRoyale.sol +++ b/packages/protocol/contracts/L1/VerifierBattleRoyale.sol @@ -84,7 +84,10 @@ contract VerifierBattleRoyale is EssentialContract { TaikoData.Transition memory transitionA = proofBatch.proofs[0].transition; TaikoData.Transition memory transitionB = proofBatch.proofs[1].transition; - require(transitionA.parentHash == transitionB.parentHash, "parentHash not the same"); + require( + transitionA.parentBlockHash == transitionB.parentBlockHash, + "parentHash not the same" + ); require(transitionA.blockHash != transitionB.blockHash, "blockhash the same"); } else if (proofBatch.proofs.length == 3) { /* Multiple verifiers in a consensus show that another verifier is faulty */ @@ -105,7 +108,10 @@ contract VerifierBattleRoyale is EssentialContract { for (uint256 i = 0; i < proofBatch.proofs.length - 1; i++) { TaikoData.Transition memory transitionA = proofBatch.proofs[i].transition; TaikoData.Transition memory transitionB = proofBatch.proofs[i + 1].transition; - require(transitionA.parentHash == transitionB.parentHash, "parentHash not the same"); + require( + transitionA.parentBlockHash == transitionB.parentBlockHash, + "parentHash not the same" + ); if (i < proofBatch.proofs.length - 2) { require(transitionA.blockHash == transitionB.blockHash, "blockhash the same"); } else { diff --git a/packages/protocol/test/L1/TaikoL1.t.sol b/packages/protocol/test/L1/TaikoL1.t.sol index f7726049f442..1d29b4fd0063 100644 --- a/packages/protocol/test/L1/TaikoL1.t.sol +++ b/packages/protocol/test/L1/TaikoL1.t.sol @@ -9,7 +9,7 @@ contract TaikoL1Test is TaikoL1TestBase { TaikoL1(payable(deployProxy({ name: "taiko", impl: address(new TaikoL1()), data: "" }))); } - function test_L1_proposeBlock() external { + function test_L1_propose_prove_and_verify_blocks_sequentially() external { giveEthAndTko(Alice, 100 ether, 100 ether); TaikoData.BlockMetadata memory meta; @@ -17,27 +17,12 @@ contract TaikoL1Test is TaikoL1TestBase { vm.roll(block.number + 1); vm.warp(block.timestamp + 12); - // console2.log(block.number); - // meta.blockHash = randBytes32(); - // meta.parentHash = GENESIS_BLOCK_HASH; - // meta.l1Hash = blockhash(block.number - 1); - // meta.difficulty = block.prevrandao; - // meta.blobHash = randBytes32(); - // meta.coinbase = Alice; - // meta.l2BlockNumber = 1; - // meta.gasLimit = L1.getConfig().blockMaxGasLimit; - // meta.l1StateBlockNumber = uint32(block.number-1); - // meta.timestamp = uint64(block.timestamp - 12); // 1 block behind - - // meta.txListByteOffset = 0; - // meta.txListByteSize = 0; - // meta.blobUsed = true; bytes32 parentMetaHash; - for (uint64 blockId = 1; blockId <= 1; blockId++) { - printVariables("before propose"); + for (uint64 blockId = 1; blockId <= 20; blockId++) { + printVariables("before propose & prove & verify"); // Create metadata and propose the block - meta = createBlockMetaData(parentMetaHash, Alice, blockId, 1, true); - proposeBlock(Alice, Alice, meta); + meta = createBlockMetaData(Alice, blockId, 1, true); + proposeBlock(Alice, Alice, meta, ""); // Create proofs and prove a block BasedOperator.ProofBatch memory blockProofs = createProofs(meta, Alice, true); @@ -47,9 +32,58 @@ contract TaikoL1Test is TaikoL1TestBase { vm.warp(uint32(block.timestamp + L1.SECURITY_DELAY_AFTER_PROVEN() + 1)); vm.roll(block.number + 10); verifyBlock(1); + parentMetaHash = keccak256(abi.encode(meta)); printVariables("after verify"); + } + } - parentMetaHash = keccak256(abi.encode(meta)); + function test_L1_propose_some_blocks_in_a_row_then_prove_and_verify() external { + giveEthAndTko(Alice, 100 ether, 100 ether); + + TaikoData.BlockMetadata[] memory blockMetaDatas = new TaikoData.BlockMetadata[](20); + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 12); + + bytes32 parentMetaHash; + for (uint64 blockId = 1; blockId <= 20; blockId++) { + printVariables("before propose & prove & verify"); + // Create metadata and propose the block + blockMetaDatas[blockId - 1] = createBlockMetaData(Alice, blockId, 1, true); + proposeBlock(Alice, Alice, blockMetaDatas[blockId - 1], ""); + vm.roll(block.number + 1); + vm.warp(block.timestamp + 12); + } + + for (uint64 blockId = 1; blockId <= 20; blockId++) { + // Create proofs and prove a block + BasedOperator.ProofBatch memory blockProofs = + createProofs(blockMetaDatas[blockId - 1], Alice, true); + proveBlock(Alice, abi.encode(blockProofs)); + + //Wait enought time and verify block (currently we simply just "wait enough" from latest + // block and not time it perfectly) + vm.warp(uint32(block.timestamp + L1.SECURITY_DELAY_AFTER_PROVEN() + 1)); + vm.roll(block.number + 10); + verifyBlock(1); + parentMetaHash = keccak256(abi.encode(blockMetaDatas[blockId - 1])); + printVariables("after verify 1"); } } + + function test_L1_propose_block_outside_the_4_epoch_window() external { + giveEthAndTko(Alice, 100 ether, 100 ether); + + TaikoData.BlockMetadata memory meta; + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 12); + + // Create metadata and propose the block 129 blocks later only + meta = createBlockMetaData(Alice, 1, 1, true); + vm.roll(block.number + 129); + vm.warp(block.timestamp + 129 * 12); + + proposeBlock(Alice, Alice, meta, TaikoErrors.L1_INVALID_L1_STATE_BLOCK.selector); + } } diff --git a/packages/protocol/test/L1/TaikoL1TestBase.sol b/packages/protocol/test/L1/TaikoL1TestBase.sol index 540604242201..02278334205a 100644 --- a/packages/protocol/test/L1/TaikoL1TestBase.sol +++ b/packages/protocol/test/L1/TaikoL1TestBase.sol @@ -236,7 +236,8 @@ abstract contract TaikoL1TestBase is TaikoTest { function proposeBlock( address proposer, address prover, - TaikoData.BlockMetadata memory meta + TaikoData.BlockMetadata memory meta, + bytes4 revertReason ) internal returns (TaikoData.BlockMetadata memory) @@ -296,10 +297,18 @@ abstract contract TaikoL1TestBase is TaikoTest { hex"0000000000000000000000000000000000000000000000000000000000000001"; bytes memory emptyTxList; - vm.prank(proposer, proposer); - meta = basedOperator.proposeBlock{ value: 1 ether / 10 }( - abi.encode(meta), meta.blobUsed == true ? emptyTxList : dummyTxList, prover - ); + if (revertReason == "") { + vm.prank(proposer, proposer); + meta = basedOperator.proposeBlock{ value: 1 ether / 10 }( + abi.encode(meta), meta.blobUsed == true ? emptyTxList : dummyTxList, prover + ); + } else { + vm.prank(proposer, proposer); + vm.expectRevert(revertReason); + meta = basedOperator.proposeBlock{ value: 1 ether / 10 }( + abi.encode(meta), meta.blobUsed == true ? emptyTxList : dummyTxList, prover + ); + } return meta; } @@ -400,13 +409,12 @@ abstract contract TaikoL1TestBase is TaikoTest { } function printVariables(string memory comment) internal { - (,,, uint64 numBlock,,) = L1.state(); string memory str = string.concat( Strings.toString(logCount++), ":[", Strings.toString(L1.getLastVerifiedBlockId()), unicode"→", - Strings.toString(numBlock), + Strings.toString(L1.getNumOfBlocks()), "] // ", comment ); @@ -419,10 +427,9 @@ abstract contract TaikoL1TestBase is TaikoTest { } function createBlockMetaData( - bytes32 parentMetaHash, address coinbase, uint64 l2BlockNumber, - uint32 belowBlockTipHeight, // How many blocks (negatived direction) away from block.id + uint32 belowBlockTipHeight, // How many blocks below from current tip (block.id) bool blobUsed ) internal @@ -430,11 +437,9 @@ abstract contract TaikoL1TestBase is TaikoTest { { meta.blockHash = randBytes32(); - meta.parentMetaHash = parentMetaHash; - if (l2BlockNumber == 1) { - meta.parentMetaHash = hex"0000000000000000000000000000000000000000000000000000000000000000"; - } - + TaikoData.Block memory parentBlock = L1.getBlock(l2BlockNumber - 1); + meta.parentMetaHash = parentBlock.metaHash; + meta.parentBlockHash = parentBlock.blockHash; meta.l1Hash = blockhash(block.number - belowBlockTipHeight); meta.difficulty = block.prevrandao; meta.blobHash = randBytes32(); @@ -471,7 +476,7 @@ abstract contract TaikoL1TestBase is TaikoTest { // Set transition TaikoData.Transition memory transition; - transition.parentHash = L1.getBlock(meta.l2BlockNumber).blockHash; + transition.parentBlockHash = L1.getBlock(meta.l2BlockNumber - 1).blockHash; transition.blockHash = meta.blockHash; proofBatch.transition = transition; From 3f31d5cfd7de97a4d2c9e20d43c50a242ce1f8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Keszey=20D=C3=A1niel?= Date: Fri, 5 Jul 2024 10:40:45 +0200 Subject: [PATCH 3/3] remove comment --- packages/protocol/contracts/L1/BasedOperator.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/protocol/contracts/L1/BasedOperator.sol b/packages/protocol/contracts/L1/BasedOperator.sol index 181cb41cb272..a8014dfab5f0 100644 --- a/packages/protocol/contracts/L1/BasedOperator.sol +++ b/packages/protocol/contracts/L1/BasedOperator.sol @@ -140,10 +140,8 @@ contract BasedOperator is EssentialContract, TaikoErrors { proofBatch.blockMetadata.l2BlockNumber, proofBatch.transition.parentBlockHash ); - // Brecht: SO we set the blockHash in proposeBlock(). - // But we need to prove it too (the same one), so somehow we need to check if this is proven - // already and IF NOT, then revert with "block already proven", no ? So i set the - // verifiableAfter in propseBlock to 0, because this is just a "proposed state". + // Somehow we need to check if this is proven already and IF YES and transition is trying to + // prove the same, then revert with "block already proven". if ( storedTransition.isProven == true && storedTransition.blockHash == proofBatch.transition.blockHash