From dacc0f466c5f93edc1ca7f34fe6d493ed26841a3 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 13 Feb 2024 17:33:16 +0800 Subject: [PATCH 1/2] only keep the latest snippet --- packages/protocol/contracts/L1/TaikoL1.sol | 9 ++--- .../protocol/contracts/L1/libs/LibUtils.sol | 14 +++++--- .../contracts/L1/libs/LibVerifying.sol | 4 +-- packages/protocol/contracts/L2/TaikoL2.sol | 33 ++++++++----------- .../contracts/common/ICrossChainSync.sol | 13 ++------ .../contracts/signal/SignalService.sol | 4 +-- packages/protocol/test/HelperContracts.sol | 13 ++++---- packages/protocol/test/L1/TaikoL1.t.sol | 12 +++---- packages/protocol/test/bridge/Bridge.t.sol | 3 +- 9 files changed, 45 insertions(+), 60 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 694de59b79a..921546c622e 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -167,13 +167,8 @@ contract TaikoL1 is /// @notice Important: as this contract doesn't send each block's state root as a signal when /// the block is verified, bridging developers should subscribe to CrossChainSynced events /// to ensure all synced state roots are verifiable using merkle proofs. - function getSyncedSnippet(uint64 blockId) - public - view - override - returns (ICrossChainSync.Snippet memory) - { - return LibUtils.getSyncedSnippet(state, getConfig(), blockId); + function getSyncedSnippet() public view override returns (ICrossChainSync.Snippet memory) { + return LibUtils.getSyncedSnippet(state, getConfig()); } /// @notice Gets the state variables of the TaikoL1 contract. diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 8824f4aa5b3..d443fd05ce0 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -53,16 +53,21 @@ library LibUtils { ts = state.transitions[slot][tid]; } + struct Snippet { + uint64 blockId; + bytes32 blockHash; + bytes32 stateRoot; + } + function getSyncedSnippet( TaikoData.State storage state, - TaikoData.Config memory config, - uint64 blockId + TaikoData.Config memory config ) external view returns (ICrossChainSync.Snippet memory) { - uint64 _blockId = blockId == 0 ? state.slotB.lastVerifiedBlockId : blockId; + uint64 _blockId = state.slotB.lastVerifiedBlockId; uint64 slot = _blockId % config.blockRingBufferSize; TaikoData.Block storage blk = state.blocks[slot]; @@ -74,8 +79,7 @@ library LibUtils { state.transitions[slot][blk.verifiedTransitionId]; return ICrossChainSync.Snippet({ - remoteBlockId: blockId, - syncedInBlock: blk.proposedIn, + blockId: _blockId, blockHash: transition.blockHash, stateRoot: transition.stateRoot }); diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index b88040edf4a..36a79abdd4b 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -247,9 +247,7 @@ library LibVerifying { config.chainId, "state_root", stateRoot ); - emit CrossChainSynced( - uint64(block.number), lastVerifiedBlockId, blockHash, stateRoot - ); + emit CrossChainSynced(uint64(block.number), lastVerifiedBlockId, blockHash, stateRoot); } } } diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 0fa9ad55537..15e4b9b6989 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -45,15 +45,16 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { // Mapping from L2 block numbers to their block hashes. // All L2 block hashes will be saved in this mapping. - mapping(uint256 blockId => bytes32 blockHash) public l2Hashes; - mapping(uint256 l1height => ICrossChainSync.Snippet) public snippets; + mapping(uint256 blockId => bytes32 blockHash) public l2Hashes; // slot 1 // A hash to check the integrity of public inputs. - bytes32 public publicInputHash; // slot 3 - uint64 public gasExcess; // slot 4 + bytes32 public publicInputHash; // slot 2 + uint64 public gasExcess; // slot 3 uint64 public latestSyncedL1Height; - uint256[146] private __gap; + ICrossChainSync.Snippet private _chainData; // slot 4, 5, 6 + + uint256[144] private __gap; event Anchored(bytes32 parentHash, uint64 gasExcess); @@ -147,20 +148,18 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { ownerChainId, "state_root", l1StateRoot ); - emit CrossChainSynced(uint64(block.number), l1Height, l1BlockHash, l1StateRoot); + emit CrossChainSynced(l1Height, l1BlockHash, l1StateRoot); // Update state variables l2Hashes[parentId] = blockhash(parentId); - snippets[l1Height] = ICrossChainSync.Snippet({ - remoteBlockId: l1Height, - syncedInBlock: uint64(block.number), - blockHash: l1BlockHash, - stateRoot: l1StateRoot - }); publicInputHash = publicInputHashNew; latestSyncedL1Height = l1Height; + _chainData.blockId = l1Height; + _chainData.blockHash = l1BlockHash; + _chainData.stateRoot = l1StateRoot; + emit Anchored(blockhash(parentId), gasExcess); } @@ -175,14 +174,8 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { } /// @inheritdoc ICrossChainSync - function getSyncedSnippet(uint64 blockId) - public - view - override - returns (ICrossChainSync.Snippet memory) - { - uint256 id = blockId == 0 ? latestSyncedL1Height : blockId; - return snippets[id]; + function getSyncedSnippet() public view returns (ICrossChainSync.Snippet memory) { + return _chainData; } /// @notice Gets the basefee and gas excess using EIP-1559 configuration for diff --git a/packages/protocol/contracts/common/ICrossChainSync.sol b/packages/protocol/contracts/common/ICrossChainSync.sol index 97d22e8af9a..43126b51312 100644 --- a/packages/protocol/contracts/common/ICrossChainSync.sol +++ b/packages/protocol/contracts/common/ICrossChainSync.sol @@ -22,25 +22,18 @@ pragma solidity 0.8.24; /// both chains remain consistent and can be cross-referenced with integrity. interface ICrossChainSync { struct Snippet { - uint64 remoteBlockId; - uint64 syncedInBlock; + uint64 blockId; bytes32 blockHash; bytes32 stateRoot; } /// @dev Emitted when a block has been synced across chains. - /// @param syncedInBlock The ID of this chain's block where the sync - /// happened. /// @param blockId The ID of the remote block whose block hash are synced. /// @param blockHash The hash of the synced block. /// @param stateRoot The block's state root. - event CrossChainSynced( - uint64 indexed syncedInBlock, uint64 indexed blockId, bytes32 blockHash, bytes32 stateRoot - ); + event CrossChainSynced(uint64 indexed blockId, bytes32 blockHash, bytes32 stateRoot); /// @notice Fetches the hash of a block from the opposite chain. - /// @param blockId The target block id. Specifying 0 retrieves the hash - /// of the latest block. /// @return snippet The block hash and signal root synced. - function getSyncedSnippet(uint64 blockId) external view returns (Snippet memory snippet); + function getSyncedSnippet() external view returns (Snippet memory snippet); } diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 8a53675f879..5581359b907 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -45,7 +45,7 @@ contract SignalService is EssentialContract, ISignalService { uint256[49] private __gap; event TrustedRelayUpdated(uint64 indexed hopChainId, uint64 indexed srcChainId, address hop); - event ChainDataRelayed( + event SnippetRelayed( uint64 indexed chainid, bytes32 indexed kind, bytes32 data, bytes32 signal ); @@ -211,7 +211,7 @@ contract SignalService is EssentialContract, ISignalService { returns (bytes32 slot) { bytes32 signal = signalForChainData(chainId, kind, data); - emit ChainDataRelayed(chainId, kind, data, signal); + emit SnippetRelayed(chainId, kind, data, signal); return _sendSignal(address(this), signal); } diff --git a/packages/protocol/test/HelperContracts.sol b/packages/protocol/test/HelperContracts.sol index f3ad1801960..3b4d7829716 100644 --- a/packages/protocol/test/HelperContracts.sol +++ b/packages/protocol/test/HelperContracts.sol @@ -53,14 +53,15 @@ contract SkipProofCheckSignal is SignalService { } contract DummyCrossChainSync is EssentialContract, ICrossChainSync { - Snippet private _snippet; + Snippet private _chainData; - function setSyncedData(bytes32 blockHash, bytes32 stateRoot) external { - _snippet.blockHash = blockHash; - _snippet.stateRoot = stateRoot; + function setSnippet(uint64 blockId, bytes32 blockHash, bytes32 stateRoot) external { + _chainData.blockId = blockId; + _chainData.blockHash = blockHash; + _chainData.stateRoot = stateRoot; } - function getSyncedSnippet(uint64) external view returns (Snippet memory) { - return _snippet; + function getSyncedSnippet() public view returns (Snippet memory) { + return _chainData; } } diff --git a/packages/protocol/test/L1/TaikoL1.t.sol b/packages/protocol/test/L1/TaikoL1.t.sol index 86ade3ceea4..881353a7305 100644 --- a/packages/protocol/test/L1/TaikoL1.t.sol +++ b/packages/protocol/test/L1/TaikoL1.t.sol @@ -193,12 +193,12 @@ contract TaikoL1Test is TaikoL1TestBase { /// @dev getCrossChainBlockHash tests function test_L1_getCrossChainBlockHash0() external { - bytes32 genHash = L1.getSyncedSnippet(0).blockHash; + bytes32 genHash = L1.getSyncedSnippet().blockHash; assertEq(GENESIS_BLOCK_HASH, genHash); // Reverts if block is not yet verified! vm.expectRevert(TaikoErrors.L1_BLOCK_MISMATCH.selector); - L1.getSyncedSnippet(1); + L1.getSyncedSnippet(); } /// @dev getSyncedSnippet tests @@ -236,7 +236,7 @@ contract TaikoL1Test is TaikoL1TestBase { verifyBlock(Carol, 1); // Querying written blockhash - assertEq(L1.getSyncedSnippet(blockId).blockHash, blockHash); + assertEq(L1.getSyncedSnippet().blockHash, blockHash); mine(5); parentHashes[blockId] = blockHash; @@ -245,16 +245,16 @@ contract TaikoL1Test is TaikoL1TestBase { uint64 queriedBlockId = 1; bytes32 expectedSR = bytes32(1e9 + uint256(queriedBlockId)); - assertEq(expectedSR, L1.getSyncedSnippet(queriedBlockId).stateRoot); + assertEq(expectedSR, L1.getSyncedSnippet().stateRoot); // 2nd queriedBlockId = 2; expectedSR = bytes32(1e9 + uint256(queriedBlockId)); - assertEq(expectedSR, L1.getSyncedSnippet(queriedBlockId).stateRoot); + assertEq(expectedSR, L1.getSyncedSnippet().stateRoot); // Not found -> reverts vm.expectRevert(TaikoErrors.L1_BLOCK_MISMATCH.selector); - L1.getSyncedSnippet((count + 1)); + L1.getSyncedSnippet(); } function test_L1_deposit_hash_creation() external { diff --git a/packages/protocol/test/bridge/Bridge.t.sol b/packages/protocol/test/bridge/Bridge.t.sol index c7dd95a4992..616ff7d72ad 100644 --- a/packages/protocol/test/bridge/Bridge.t.sol +++ b/packages/protocol/test/bridge/Bridge.t.sol @@ -643,7 +643,8 @@ contract BridgeTest is TaikoTest { addressManager.setAddress(dest, "signal_service", address(mockProofSignalService)); - crossChainSync.setSyncedData( + crossChainSync.setSnippet( + 123, 0xd5f5d8ac6bc37139c97389b00e9cf53e89c153ad8a5fc765ffe9f44ea9f3d31e, 0x631b214fb030d82847224f0b3d3b906a6764dded176ad3c7262630204867ba85 ); From e539427b374c6edebef533aa6a772a3143f0d940 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 13 Feb 2024 17:50:27 +0800 Subject: [PATCH 2/2] fix --- .../protocol/contracts/L1/libs/LibUtils.sol | 6 ---- .../contracts/L1/libs/LibVerifying.sol | 4 ++- packages/protocol/contracts/L2/TaikoL2.sol | 22 ++++++-------- .../contracts/signal/SignalService.sol | 6 ++-- packages/protocol/test/HelperContracts.sol | 10 +++---- packages/protocol/test/L1/TaikoL1.t.sol | 29 +++++-------------- .../protocol/test/signal/SignalService.t.sol | 1 + 7 files changed, 28 insertions(+), 50 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index d443fd05ce0..dd61849aba7 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -53,12 +53,6 @@ library LibUtils { ts = state.transitions[slot][tid]; } - struct Snippet { - uint64 blockId; - bytes32 blockHash; - bytes32 stateRoot; - } - function getSyncedSnippet( TaikoData.State storage state, TaikoData.Config memory config diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 36a79abdd4b..b88040edf4a 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -247,7 +247,9 @@ library LibVerifying { config.chainId, "state_root", stateRoot ); - emit CrossChainSynced(uint64(block.number), lastVerifiedBlockId, blockHash, stateRoot); + emit CrossChainSynced( + uint64(block.number), lastVerifiedBlockId, blockHash, stateRoot + ); } } } diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 15e4b9b6989..8f70d392cac 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -50,9 +50,8 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { // A hash to check the integrity of public inputs. bytes32 public publicInputHash; // slot 2 uint64 public gasExcess; // slot 3 - uint64 public latestSyncedL1Height; - ICrossChainSync.Snippet private _chainData; // slot 4, 5, 6 + Snippet private _l1Snippet; // slot 4, 5, 6 uint256[144] private __gap; @@ -148,17 +147,14 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { ownerChainId, "state_root", l1StateRoot ); - emit CrossChainSynced(l1Height, l1BlockHash, l1StateRoot); - // Update state variables l2Hashes[parentId] = blockhash(parentId); - publicInputHash = publicInputHashNew; - latestSyncedL1Height = l1Height; - _chainData.blockId = l1Height; - _chainData.blockHash = l1BlockHash; - _chainData.stateRoot = l1StateRoot; + _l1Snippet.blockId = l1Height; + _l1Snippet.blockHash = l1BlockHash; + _l1Snippet.stateRoot = l1StateRoot; + emit CrossChainSynced(l1Height, l1BlockHash, l1StateRoot); emit Anchored(blockhash(parentId), gasExcess); } @@ -175,7 +171,7 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { /// @inheritdoc ICrossChainSync function getSyncedSnippet() public view returns (ICrossChainSync.Snippet memory) { - return _chainData; + return _l1Snippet; } /// @notice Gets the basefee and gas excess using EIP-1559 configuration for @@ -267,10 +263,10 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync { // Calculate how much more gas to issue to offset gas excess. // after each L1 block time, config.gasTarget more gas is issued, // the gas excess will be reduced accordingly. - // Note that when latestSyncedL1Height is zero, we skip this step. + // Note that when _l1Snippet.blockId is zero, we skip this step. uint256 numL1Blocks; - if (latestSyncedL1Height > 0 && l1Height > latestSyncedL1Height) { - numL1Blocks = l1Height - latestSyncedL1Height; + if (_l1Snippet.blockId > 0 && l1Height > _l1Snippet.blockId) { + numL1Blocks = l1Height - _l1Snippet.blockId; } if (numL1Blocks > 0) { diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 5581359b907..db2f2bddb07 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -49,16 +49,16 @@ contract SignalService is EssentialContract, ISignalService { uint64 indexed chainid, bytes32 indexed kind, bytes32 data, bytes32 signal ); - error SS_INVALID_PARAMS(); - error SS_INVALID_PROOF(); error SS_EMPTY_PROOF(); error SS_INVALID_APP(); error SS_INVALID_HOP_PROOF(); error SS_INVALID_LAST_HOP_CHAINID(); error SS_INVALID_MID_HOP_CHAINID(); + error SS_INVALID_PARAMS(); + error SS_INVALID_PROOF(); error SS_INVALID_RELAY(); - error SS_INVALID_SIGNAL(); error SS_INVALID_STATE_ROOT(); + error SS_INVALID_SIGNAL(); error SS_LOCAL_CHAIN_DATA_NOT_FOUND(); error SS_UNSUPPORTED(); diff --git a/packages/protocol/test/HelperContracts.sol b/packages/protocol/test/HelperContracts.sol index 3b4d7829716..1b8e93cd1a2 100644 --- a/packages/protocol/test/HelperContracts.sol +++ b/packages/protocol/test/HelperContracts.sol @@ -53,15 +53,15 @@ contract SkipProofCheckSignal is SignalService { } contract DummyCrossChainSync is EssentialContract, ICrossChainSync { - Snippet private _chainData; + Snippet private _snippet; function setSnippet(uint64 blockId, bytes32 blockHash, bytes32 stateRoot) external { - _chainData.blockId = blockId; - _chainData.blockHash = blockHash; - _chainData.stateRoot = stateRoot; + _snippet.blockId = blockId; + _snippet.blockHash = blockHash; + _snippet.stateRoot = stateRoot; } function getSyncedSnippet() public view returns (Snippet memory) { - return _chainData; + return _snippet; } } diff --git a/packages/protocol/test/L1/TaikoL1.t.sol b/packages/protocol/test/L1/TaikoL1.t.sol index 881353a7305..92e3e43f436 100644 --- a/packages/protocol/test/L1/TaikoL1.t.sol +++ b/packages/protocol/test/L1/TaikoL1.t.sol @@ -192,13 +192,11 @@ contract TaikoL1Test is TaikoL1TestBase { } /// @dev getCrossChainBlockHash tests - function test_L1_getCrossChainBlockHash0() external { - bytes32 genHash = L1.getSyncedSnippet().blockHash; - assertEq(GENESIS_BLOCK_HASH, genHash); - - // Reverts if block is not yet verified! - vm.expectRevert(TaikoErrors.L1_BLOCK_MISMATCH.selector); - L1.getSyncedSnippet(); + function test_L1_getCrossChainSnippet_Genesis() external { + ICrossChainSync.Snippet memory snippet = L1.getSyncedSnippet(); + assertEq(snippet.blockId, 0); + assertEq(snippet.blockHash, GENESIS_BLOCK_HASH); + assertEq(snippet.stateRoot, 0); } /// @dev getSyncedSnippet tests @@ -235,26 +233,13 @@ contract TaikoL1Test is TaikoL1TestBase { verifyBlock(Carol, 1); - // Querying written blockhash + assertEq(L1.getSyncedSnippet().blockId, blockId); assertEq(L1.getSyncedSnippet().blockHash, blockHash); + assertEq(L1.getSyncedSnippet().stateRoot, stateRoot); mine(5); parentHashes[blockId] = blockHash; } - - uint64 queriedBlockId = 1; - bytes32 expectedSR = bytes32(1e9 + uint256(queriedBlockId)); - - assertEq(expectedSR, L1.getSyncedSnippet().stateRoot); - - // 2nd - queriedBlockId = 2; - expectedSR = bytes32(1e9 + uint256(queriedBlockId)); - assertEq(expectedSR, L1.getSyncedSnippet().stateRoot); - - // Not found -> reverts - vm.expectRevert(TaikoErrors.L1_BLOCK_MISMATCH.selector); - L1.getSyncedSnippet(); } function test_L1_deposit_hash_creation() external { diff --git a/packages/protocol/test/signal/SignalService.t.sol b/packages/protocol/test/signal/SignalService.t.sol index 2b218c871aa..9eb77b2c8b5 100644 --- a/packages/protocol/test/signal/SignalService.t.sol +++ b/packages/protocol/test/signal/SignalService.t.sol @@ -12,6 +12,7 @@ contract MockSignalService is SignalService { address /*relay*/ ) internal + pure override returns (bytes32) {