Skip to content

Commit

Permalink
feat(protocol): only keep one latest copy of synced snippet (#15767)
Browse files Browse the repository at this point in the history
  • Loading branch information
dantaik authored Feb 13, 2024
1 parent 8535c7f commit 786cc9b
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 80 deletions.
9 changes: 2 additions & 7 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 3 additions & 5 deletions packages/protocol/contracts/L1/libs/LibUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,13 @@ library LibUtils {

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];
Expand All @@ -74,8 +73,7 @@ library LibUtils {
state.transitions[slot][blk.verifiedTransitionId];

return ICrossChainSync.Snippet({
remoteBlockId: blockId,
syncedInBlock: blk.proposedIn,
blockId: _blockId,
blockHash: transition.blockHash,
stateRoot: transition.stateRoot
});
Expand Down
43 changes: 16 additions & 27 deletions packages/protocol/contracts/L2/TaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ 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
uint64 public latestSyncedL1Height;
bytes32 public publicInputHash; // slot 2
uint64 public gasExcess; // slot 3

uint256[146] private __gap;
Snippet private _l1Snippet; // slot 4, 5, 6

uint256[144] private __gap;

event Anchored(bytes32 parentHash, uint64 gasExcess);

Expand Down Expand Up @@ -147,19 +147,14 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync {
ownerChainId, "state_root", l1StateRoot
);

emit CrossChainSynced(uint64(block.number), 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;

_l1Snippet.blockId = l1Height;
_l1Snippet.blockHash = l1BlockHash;
_l1Snippet.stateRoot = l1StateRoot;
emit CrossChainSynced(l1Height, l1BlockHash, l1StateRoot);

emit Anchored(blockhash(parentId), gasExcess);
}
Expand All @@ -175,14 +170,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 _l1Snippet;
}

/// @notice Gets the basefee and gas excess using EIP-1559 configuration for
Expand Down Expand Up @@ -274,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) {
Expand Down
13 changes: 3 additions & 10 deletions packages/protocol/contracts/common/ICrossChainSync.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
10 changes: 5 additions & 5 deletions packages/protocol/contracts/signal/SignalService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ 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
);

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();

Expand Down Expand Up @@ -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);
}

Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/test/HelperContracts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ contract SkipProofCheckSignal is SignalService {
contract DummyCrossChainSync is EssentialContract, ICrossChainSync {
Snippet private _snippet;

function setSyncedData(bytes32 blockHash, bytes32 stateRoot) external {
function setSnippet(uint64 blockId, bytes32 blockHash, bytes32 stateRoot) external {
_snippet.blockId = blockId;
_snippet.blockHash = blockHash;
_snippet.stateRoot = stateRoot;
}

function getSyncedSnippet(uint64) external view returns (Snippet memory) {
function getSyncedSnippet() public view returns (Snippet memory) {
return _snippet;
}
}
31 changes: 8 additions & 23 deletions packages/protocol/test/L1/TaikoL1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,11 @@ contract TaikoL1Test is TaikoL1TestBase {
}

/// @dev getCrossChainBlockHash tests
function test_L1_getCrossChainBlockHash0() external {
bytes32 genHash = L1.getSyncedSnippet(0).blockHash;
assertEq(GENESIS_BLOCK_HASH, genHash);

// Reverts if block is not yet verified!
vm.expectRevert(TaikoErrors.L1_BLOCK_MISMATCH.selector);
L1.getSyncedSnippet(1);
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
Expand Down Expand Up @@ -235,26 +233,13 @@ contract TaikoL1Test is TaikoL1TestBase {

verifyBlock(Carol, 1);

// Querying written blockhash
assertEq(L1.getSyncedSnippet(blockId).blockHash, 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(queriedBlockId).stateRoot);

// 2nd
queriedBlockId = 2;
expectedSR = bytes32(1e9 + uint256(queriedBlockId));
assertEq(expectedSR, L1.getSyncedSnippet(queriedBlockId).stateRoot);

// Not found -> reverts
vm.expectRevert(TaikoErrors.L1_BLOCK_MISMATCH.selector);
L1.getSyncedSnippet((count + 1));
}

function test_L1_deposit_hash_creation() external {
Expand Down
3 changes: 2 additions & 1 deletion packages/protocol/test/bridge/Bridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,8 @@ contract BridgeTest is TaikoTest {

addressManager.setAddress(dest, "signal_service", address(mockProofSignalService));

crossChainSync.setSyncedData(
crossChainSync.setSnippet(
123,
0xd5f5d8ac6bc37139c97389b00e9cf53e89c153ad8a5fc765ffe9f44ea9f3d31e,
0x631b214fb030d82847224f0b3d3b906a6764dded176ad3c7262630204867ba85
);
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/test/signal/SignalService.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ contract MockSignalService is SignalService {
address /*relay*/
)
internal
pure
override
returns (bytes32)
{
Expand Down

0 comments on commit 786cc9b

Please sign in to comment.