Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): only keep one latest copy of synced snippet #15767

Merged
merged 2 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading