Skip to content

Commit

Permalink
feat(protocol)!: improve signal service and remove ICrossChainSync (#…
Browse files Browse the repository at this point in the history
…15859)

Co-authored-by: d1onys1us <13951458+d1onys1us@users.noreply.github.com>
Co-authored-by: Brecht Devos <Brechtp.Devos@gmail.com>
  • Loading branch information
3 people committed Feb 17, 2024
1 parent 3d9f222 commit 58ffe10
Show file tree
Hide file tree
Showing 21 changed files with 991 additions and 418 deletions.
3 changes: 3 additions & 0 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ library TaikoData {
uint256 ethDepositGas;
// The maximum fee allowed for an ETH deposit.
uint256 ethDepositMaxFee;
// The max number of L2 blocks that can stay unsynced on L1 (a value of zero disables
// syncing)
uint8 blockSyncThreshold;
}

/// @dev Struct representing prover assignment
Expand Down
25 changes: 3 additions & 22 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,7 @@ import "./TaikoEvents.sol";
/// layers"). The contract also handles the deposit and withdrawal of Taiko
/// tokens and Ether.
/// This contract doesn't hold any Ether. Ether deposited to L2 are held by the Bridge contract.
contract TaikoL1 is
EssentialContract,
ITaikoL1,
ICrossChainSync,
ITierProvider,
TaikoEvents,
TaikoErrors
{
contract TaikoL1 is EssentialContract, ITaikoL1, ITierProvider, TaikoEvents, TaikoErrors {
TaikoData.State public state;
uint256[100] private __gap;

Expand Down Expand Up @@ -168,19 +161,6 @@ contract TaikoL1 is
return LibUtils.getTransition(state, getConfig(), blockId, parentHash);
}

/// @inheritdoc ICrossChainSync
/// @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(), AddressResolver(this), blockId);
}

/// @notice Gets the state variables of the TaikoL1 contract.
function getStateVariables()
public
Expand Down Expand Up @@ -247,7 +227,8 @@ contract TaikoL1 is
ethDepositMinAmount: 1 ether,
ethDepositMaxAmount: 10_000 ether,
ethDepositGas: 21_000,
ethDepositMaxFee: 1 ether / 10
ethDepositMaxFee: 1 ether / 10,
blockSyncThreshold: 16
});
}

Expand Down
35 changes: 0 additions & 35 deletions packages/protocol/contracts/L1/libs/LibUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
pragma solidity 0.8.24;

import "../../common/AddressResolver.sol";
import "../../common/ICrossChainSync.sol";
import "../../signal/ISignalService.sol";
import "../../signal/LibSignals.sol";
import "../TaikoData.sol";
Expand Down Expand Up @@ -57,40 +56,6 @@ library LibUtils {
ts = state.transitions[slot][tid];
}

function getSyncedSnippet(
TaikoData.State storage state,
TaikoData.Config memory config,
AddressResolver resolver,
uint64 blockId
)
external
view
returns (ICrossChainSync.Snippet memory)
{
uint64 _blockId = blockId == 0 ? state.slotB.lastVerifiedBlockId : blockId;
uint64 slot = _blockId % config.blockRingBufferSize;

TaikoData.Block storage blk = state.blocks[slot];

if (blk.blockId != _blockId) revert L1_BLOCK_MISMATCH();
if (blk.verifiedTransitionId == 0) revert L1_TRANSITION_NOT_FOUND();

TaikoData.TransitionState storage ts = state.transitions[slot][blk.verifiedTransitionId];

// bool relayed = ISignalService(resolver.resolve("signal_service",
// false)).isChainDataRelayed(
// config.chainId, LibSignals.STATE_ROOT, ts.stateRoot
// );
// if (!relayed) revert L1_CHAIN_DATA_NOT_RELAYED();

return ICrossChainSync.Snippet({
syncedInBlock: blk.proposedIn,
blockId: blockId,
blockHash: ts.blockHash,
stateRoot: ts.stateRoot
});
}

/// @dev Retrieves a block based on its ID.
function getBlock(
TaikoData.State storage state,
Expand Down
40 changes: 23 additions & 17 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ library LibVerifying {
uint8 contestations
);

event CrossChainSynced(
uint64 indexed syncedInBlock, uint64 indexed blockId, bytes32 blockHash, bytes32 stateRoot
);

// Warning: Any errors defined here must also be defined in TaikoErrors.sol.
error L1_BLOCK_MISMATCH();
error L1_INVALID_CONFIG();
Expand Down Expand Up @@ -242,20 +238,30 @@ library LibVerifying {
// Update protocol level state variables
state.slotB.lastVerifiedBlockId = lastVerifiedBlockId;

// Store the L2's state root as a signal to the local signal
// service to allow for multi-hop bridging.
//
// This also means if we verified more than one block, only the last one's stateRoot
// is sent as a signal and verifiable with merkle proofs, all other blocks'
// stateRoot are not.
ISignalService(resolver.resolve("signal_service", false)).relayChainData(
config.chainId, LibSignals.STATE_ROOT, stateRoot
);

emit CrossChainSynced(
uint64(block.number), lastVerifiedBlockId, blockHash, stateRoot
);
// sync chain data
_syncChainData(config, resolver, lastVerifiedBlockId, stateRoot);
}
}
}

function _syncChainData(
TaikoData.Config memory config,
AddressResolver resolver,
uint64 lastVerifiedBlockId,
bytes32 stateRoot
)
private
{
ISignalService signalService = ISignalService(resolver.resolve("signal_service", false));

(uint64 lastSyncedBlock,) = signalService.getSyncedChainData(
config.chainId, LibSignals.STATE_ROOT, 0 /* latest block Id*/
);

if (lastVerifiedBlockId > lastSyncedBlock + config.blockSyncThreshold) {
signalService.syncChainData(
config.chainId, LibSignals.STATE_ROOT, lastVerifiedBlockId, stateRoot
);
}
}
}
74 changes: 28 additions & 46 deletions packages/protocol/contracts/L2/TaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ pragma solidity 0.8.24;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../common/ICrossChainSync.sol";
import "../signal/ISignalService.sol";
import "../signal/LibSignals.sol";
import "../libs/LibAddress.sol";
import "../libs/LibMath.sol";
import "../signal/ISignalService.sol";
import "../signal/LibSignals.sol";
import "./Lib1559Math.sol";
import "./CrossChainOwned.sol";

Expand All @@ -31,7 +30,7 @@ import "./CrossChainOwned.sol";
/// It is used to anchor the latest L1 block details to L2 for cross-layer
/// communication, manage EIP-1559 parameters for gas pricing, and store
/// verified L1 block information.
contract TaikoL2 is CrossChainOwned, ICrossChainSync {
contract TaikoL2 is CrossChainOwned {
using LibAddress for address;
using LibMath for uint256;
using SafeERC20 for IERC20;
Expand All @@ -43,18 +42,18 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync {

// Golden touch address
address public constant GOLDEN_TOUCH_ADDRESS = 0x0000777735367b36bC9B61C50022d9D0700dB4Ec;
uint8 public constant BLOCK_SYNC_THRESHOLD = 5;

// 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;

// 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
uint64 public lastSyncedBlock;

uint256[146] private __gap;
uint256[147] private __gap;

event Anchored(bytes32 parentHash, uint64 gasExcess);

Expand Down Expand Up @@ -102,19 +101,19 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync {
/// @param l1BlockHash The latest L1 block hash when this block was
/// proposed.
/// @param l1StateRoot The latest L1 block's state root.
/// @param l1Height The latest L1 block height when this block was proposed.
/// @param l1BlockId The latest L1 block height when this block was proposed.
/// @param parentGasUsed The gas used in the parent block.
function anchor(
bytes32 l1BlockHash,
bytes32 l1StateRoot,
uint64 l1Height,
uint64 l1BlockId,
uint32 parentGasUsed
)
external
nonReentrant
{
if (
l1BlockHash == 0 || l1StateRoot == 0 || l1Height == 0
l1BlockHash == 0 || l1StateRoot == 0 || l1BlockId == 0
|| (block.number != 1 && parentGasUsed == 0)
) {
revert L2_INVALID_PARAM();
Expand All @@ -137,29 +136,23 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync {

// Verify the base fee per gas is correct
uint256 basefee;
(basefee, gasExcess) = _calc1559BaseFee(config, l1Height, parentGasUsed);
(basefee, gasExcess) = _calc1559BaseFee(config, l1BlockId, parentGasUsed);
if (!skipFeeCheck() && block.basefee != basefee) {
revert L2_BASEFEE_MISMATCH();
}

// Store the L1's state root as a signal to the local signal service to
// allow for multi-hop bridging.
ISignalService(resolve("signal_service", false)).relayChainData(
ownerChainId, LibSignals.STATE_ROOT, l1StateRoot
);

emit CrossChainSynced(uint64(block.number), l1Height, l1BlockHash, l1StateRoot);

if (l1BlockId > lastSyncedBlock + BLOCK_SYNC_THRESHOLD) {
// Store the L1's state root as a signal to the local signal service to
// allow for multi-hop bridging.
ISignalService(resolve("signal_service", false)).syncChainData(
ownerChainId, LibSignals.STATE_ROOT, l1BlockId, l1StateRoot
);
lastSyncedBlock = l1BlockId;
}
// Update state variables
l2Hashes[parentId] = blockhash(parentId);
snippets[l1Height] = ICrossChainSync.Snippet({
syncedInBlock: uint64(block.number),
blockId: l1Height,
blockHash: l1BlockHash,
stateRoot: l1StateRoot
});
publicInputHash = publicInputHashNew;
latestSyncedL1Height = l1Height;

emit Anchored(blockhash(parentId), gasExcess);
}

Expand All @@ -181,31 +174,20 @@ 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];
}

/// @notice Gets the basefee and gas excess using EIP-1559 configuration for
/// the given parameters.
/// @param l1Height The synced L1 height in the next Taiko block
/// @param l1BlockId The synced L1 height in the next Taiko block
/// @param parentGasUsed Gas used in the parent block.
/// @return basefee The calculated EIP-1559 base fee per gas.
function getBasefee(
uint64 l1Height,
uint64 l1BlockId,
uint32 parentGasUsed
)
public
view
returns (uint256 basefee)
{
(basefee,) = _calc1559BaseFee(getConfig(), l1Height, parentGasUsed);
(basefee,) = _calc1559BaseFee(getConfig(), l1BlockId, parentGasUsed);
}

/// @notice Retrieves the block hash for the given L2 block number.
Expand Down Expand Up @@ -265,7 +247,7 @@ contract TaikoL2 is CrossChainOwned, ICrossChainSync {

function _calc1559BaseFee(
Config memory config,
uint64 l1Height,
uint64 l1BlockId,
uint32 parentGasUsed
)
private
Expand All @@ -281,13 +263,13 @@ 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 lastSyncedBlock is zero, we skip this step
// because that means this is the first time calculating the basefee
// and the difference between the L1 height would be extremely big,
// reverting the initial gas excess value back to 0.
uint256 numL1Blocks;
if (latestSyncedL1Height > 0 && l1Height > latestSyncedL1Height) {
numL1Blocks = l1Height - latestSyncedL1Height;
if (lastSyncedBlock > 0 && l1BlockId > lastSyncedBlock) {
numL1Blocks = l1BlockId - lastSyncedBlock;
}

if (numL1Blocks > 0) {
Expand Down
46 changes: 0 additions & 46 deletions packages/protocol/contracts/common/ICrossChainSync.sol

This file was deleted.

Loading

0 comments on commit 58ffe10

Please sign in to comment.