Skip to content

Commit

Permalink
refactor(protocol): refactor TaikoL1 contract (#17678)
Browse files Browse the repository at this point in the history
  • Loading branch information
dantaik authored Jun 26, 2024
1 parent e99a860 commit db6ccdf
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 57 deletions.
12 changes: 2 additions & 10 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,12 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
nonReentrant
emitEventForClient
{
(
TaikoData.BlockMetadata memory meta,
TaikoData.Transition memory tran,
TaikoData.TierProof memory proof
) = abi.decode(_input, (TaikoData.BlockMetadata, TaikoData.Transition, TaikoData.TierProof));

if (_blockId != meta.id) revert L1_INVALID_BLOCK_ID();

TaikoData.Config memory config = getConfig();
TaikoToken tko = TaikoToken(resolve(LibStrings.B_TAIKO_TOKEN, false));

LibProving.proveBlock(state, tko, config, this, meta, tran, proof);
LibProving.proveBlock(state, tko, config, this, _blockId, _input);

if (LibUtils.shouldVerifyBlocks(config, meta.id, false)) {
if (LibUtils.shouldVerifyBlocks(config, _blockId, false)) {
LibVerifying.verifyBlocks(state, tko, config, this, config.maxBlocksToVerify);
}
}
Expand Down
43 changes: 26 additions & 17 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ library LibProposing {
bytes32 private constant _EMPTY_ETH_DEPOSIT_HASH =
0x569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd;

struct Local {
TaikoData.SlotB b;
TaikoData.BlockParams params;
bytes32 parentMetaHash;
}

// Warning: Any events defined here must also be defined in TaikoEvents.sol.
/// @notice Emitted when a block is proposed.
/// @param blockId The ID of the proposed block.
Expand Down Expand Up @@ -64,28 +70,30 @@ library LibProposing {
internal
returns (TaikoData.BlockMetadata memory meta_, TaikoData.EthDeposit[] memory deposits_)
{
TaikoData.BlockParams memory params = abi.decode(_data, (TaikoData.BlockParams));
Local memory local;
local.params = abi.decode(_data, (TaikoData.BlockParams));

if (params.coinbase == address(0)) {
params.coinbase = msg.sender;
if (local.params.coinbase == address(0)) {
local.params.coinbase = msg.sender;
}

// Taiko, as a Based Rollup, enables permissionless block proposals.
TaikoData.SlotB memory b = _state.slotB;
local.b = _state.slotB;

// It's essential to ensure that the ring buffer for proposed blocks
// still has space for at least one more block.
if (b.numBlocks >= b.lastVerifiedBlockId + _config.blockMaxProposals + 1) {
if (local.b.numBlocks >= local.b.lastVerifiedBlockId + _config.blockMaxProposals + 1) {
revert L1_TOO_MANY_BLOCKS();
}

bytes32 parentMetaHash =
_state.blocks[(b.numBlocks - 1) % _config.blockRingBufferSize].metaHash;
local.parentMetaHash =
_state.blocks[(local.b.numBlocks - 1) % _config.blockRingBufferSize].metaHash;
// assert(parentMetaHash != 0);

// Check if parent block has the right meta hash. This is to allow the proposer to make sure
// the block builds on the expected latest chain state.
if (params.parentMetaHash != 0 && parentMetaHash != params.parentMetaHash) {
if (local.params.parentMetaHash != 0 && local.parentMetaHash != local.params.parentMetaHash)
{
revert L1_UNEXPECTED_PARENT();
}

Expand All @@ -98,16 +106,16 @@ library LibProposing {
l1Hash: blockhash(block.number - 1),
difficulty: 0, // to be initialized below
blobHash: 0, // to be initialized below
extraData: params.extraData,
extraData: local.params.extraData,
depositsHash: _EMPTY_ETH_DEPOSIT_HASH,
coinbase: params.coinbase,
id: b.numBlocks,
coinbase: local.params.coinbase,
id: local.b.numBlocks,
gasLimit: _config.blockMaxGasLimit,
timestamp: uint64(block.timestamp),
l1Height: uint64(block.number - 1),
minTier: 0, // to be initialized below
blobUsed: _txList.length == 0,
parentMetaHash: parentMetaHash,
parentMetaHash: local.parentMetaHash,
sender: msg.sender
});
}
Expand All @@ -131,7 +139,7 @@ library LibProposing {
// 7645: Alias ORIGIN to SENDER
if (
_config.checkEOAForCalldataDA
&& ECDSA.recover(meta_.blobHash, params.signature) != msg.sender
&& ECDSA.recover(meta_.blobHash, local.params.signature) != msg.sender
) {
revert L1_INVALID_SIG();
}
Expand All @@ -144,11 +152,12 @@ library LibProposing {
// of multiple Taiko blocks being proposed within a single
// Ethereum block, we choose to introduce a salt to this random
// number as the L2 mixHash.
meta_.difficulty = keccak256(abi.encodePacked(block.prevrandao, b.numBlocks, block.number));
meta_.difficulty =
keccak256(abi.encodePacked(block.prevrandao, local.b.numBlocks, block.number));

{
ITierRouter tierRouter = ITierRouter(_resolver.resolve(LibStrings.B_TIER_ROUTER, false));
ITierProvider tierProvider = ITierProvider(tierRouter.getProvider(b.numBlocks));
ITierProvider tierProvider = ITierProvider(tierRouter.getProvider(local.b.numBlocks));

// Use the difficulty as a random number
meta_.minTier = tierProvider.getMinTier(uint256(meta_.difficulty));
Expand All @@ -162,7 +171,7 @@ library LibProposing {
// block's proposal but before it has been proven or verified.
assignedProver: address(0),
livenessBond: _config.livenessBond,
blockId: b.numBlocks,
blockId: local.b.numBlocks,
proposedAt: meta_.timestamp,
proposedIn: uint64(block.number),
// For a new block, the next transition ID is always 1, not 0.
Expand All @@ -172,7 +181,7 @@ library LibProposing {
});

// Store the block in the ring buffer
_state.blocks[b.numBlocks % _config.blockRingBufferSize] = blk;
_state.blocks[local.b.numBlocks % _config.blockRingBufferSize] = blk;

// Increment the counter (cursor) by 1.
unchecked {
Expand Down
68 changes: 38 additions & 30 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,24 +92,32 @@ library LibProving {
/// @param _tko The taiko token.
/// @param _config Actual TaikoData.Config.
/// @param _resolver Address resolver interface.
/// @param _meta The block's metadata.
/// @param _tran The transition data.
/// @param _proof The proof.
/// @param _blockId The index of the block to prove. This is also used to
/// select the right implementation version.
/// @param _input An abi-encoded (TaikoData.BlockMetadata, TaikoData.Transition,
/// TaikoData.TierProof) tuple.
function proveBlock(
TaikoData.State storage _state,
TaikoToken _tko,
TaikoData.Config memory _config,
IAddressResolver _resolver,
TaikoData.BlockMetadata memory _meta,
TaikoData.Transition memory _tran,
TaikoData.TierProof memory _proof
uint64 _blockId,
bytes calldata _input
)
internal
{
(
TaikoData.BlockMetadata memory meta,
TaikoData.Transition memory tran,
TaikoData.TierProof memory proof
) = abi.decode(_input, (TaikoData.BlockMetadata, TaikoData.Transition, TaikoData.TierProof));

if (_blockId != meta.id) revert L1_INVALID_BLOCK_ID();

// Make sure parentHash is not zero
// To contest an existing transition, simply use any non-zero value as
// the blockHash and stateRoot.
if (_tran.parentHash == 0 || _tran.blockHash == 0 || _tran.stateRoot == 0) {
if (tran.parentHash == 0 || tran.blockHash == 0 || tran.stateRoot == 0) {
revert L1_INVALID_TRANSITION();
}

Expand All @@ -118,22 +126,22 @@ library LibProving {
local.b = _state.slotB;

// Check that the block has been proposed but has not yet been verified.
if (_meta.id <= local.b.lastVerifiedBlockId || _meta.id >= local.b.numBlocks) {
if (meta.id <= local.b.lastVerifiedBlockId || meta.id >= local.b.numBlocks) {
revert L1_INVALID_BLOCK_ID();
}

local.slot = _meta.id % _config.blockRingBufferSize;
local.slot = meta.id % _config.blockRingBufferSize;
TaikoData.Block storage blk = _state.blocks[local.slot];

local.blockId = blk.blockId;

if (LibUtils.shouldSyncStateRoot(_config.stateRootSyncInternal, local.blockId)) {
local.stateRoot = _tran.stateRoot;
local.stateRoot = tran.stateRoot;
}

local.assignedProver = blk.assignedProver;
if (local.assignedProver == address(0)) {
local.assignedProver = _meta.sender;
local.assignedProver = meta.sender;
}

local.livenessBond = blk.livenessBond;
Expand All @@ -142,7 +150,7 @@ library LibProving {
// Check the integrity of the block data. It's worth noting that in
// theory, this check may be skipped, but it's included for added
// caution.
if (local.blockId != _meta.id || local.metaHash != LibUtils.hashMetadata(_meta)) {
if (local.blockId != meta.id || local.metaHash != LibUtils.hashMetadata(meta)) {
revert L1_BLOCK_MISMATCH();
}

Expand All @@ -151,11 +159,11 @@ library LibProving {
// become available. In cases where a transition with the specified
// parentHash does not exist, the transition ID (tid) will be set to 0.
TaikoData.TransitionState memory ts;
(local.tid, ts) = _fetchOrCreateTransition(_state, blk, _tran, local);
(local.tid, ts) = _fetchOrCreateTransition(_state, blk, tran, local);

// The new proof must meet or exceed the minimum tier required by the
// block or the previous proof; it cannot be on a lower tier.
if (_proof.tier == 0 || _proof.tier < _meta.minTier || _proof.tier < ts.tier) {
if (proof.tier == 0 || proof.tier < meta.minTier || proof.tier < ts.tier) {
revert L1_INVALID_TIER();
}

Expand All @@ -165,8 +173,8 @@ library LibProving {
ITierRouter tierRouter = ITierRouter(_resolver.resolve(LibStrings.B_TIER_ROUTER, false));
ITierProvider tierProvider = ITierProvider(tierRouter.getProvider(local.blockId));

local.tier = tierProvider.getTier(_proof.tier);
local.minTier = tierProvider.getTier(_meta.minTier);
local.tier = tierProvider.getTier(proof.tier);
local.minTier = tierProvider.getTier(meta.minTier);
}

local.inProvingWindow = !LibUtils.isPostDeadline({
Expand Down Expand Up @@ -200,38 +208,38 @@ library LibProving {
// Taiko's core protocol.
if (local.tier.verifierName != "") {
address verifier = _resolver.resolve(local.tier.verifierName, false);
bool isContesting = _proof.tier == ts.tier && local.tier.contestBond != 0;
bool isContesting = proof.tier == ts.tier && local.tier.contestBond != 0;

IVerifier.Context memory ctx = IVerifier.Context({
metaHash: local.metaHash,
blobHash: _meta.blobHash,
blobHash: meta.blobHash,
// Separate msgSender to allow the prover to be any address in the future.
prover: msg.sender,
msgSender: msg.sender,
blockId: local.blockId,
isContesting: isContesting,
blobUsed: _meta.blobUsed
blobUsed: meta.blobUsed
});

IVerifier(verifier).verifyProof(ctx, _tran, _proof);
IVerifier(verifier).verifyProof(ctx, tran, proof);
}

local.isTopTier = local.tier.contestBond == 0;

local.sameTransition = _tran.blockHash == ts.blockHash && local.stateRoot == ts.stateRoot;
local.sameTransition = tran.blockHash == ts.blockHash && local.stateRoot == ts.stateRoot;

if (_proof.tier > ts.tier) {
if (proof.tier > ts.tier) {
// Handles the case when an incoming tier is higher than the current transition's tier.
// Reverts when the incoming proof tries to prove the same transition
// (L1_ALREADY_PROVED).
_overrideWithHigherProof(blk, ts, _tran, _proof, local);
_overrideWithHigherProof(blk, ts, tran, proof, local);

emit TransitionProved({
blockId: local.blockId,
tran: _tran,
tran: tran,
prover: msg.sender,
validityBond: local.tier.validityBond,
tier: _proof.tier
tier: proof.tier
});
} else {
// New transition and old transition on the same tier - and if this transaction tries to
Expand All @@ -244,15 +252,15 @@ library LibProving {
assert(ts.validityBond == 0 && ts.contester == address(0));

ts.prover = msg.sender;
ts.blockHash = _tran.blockHash;
ts.blockHash = tran.blockHash;
ts.stateRoot = local.stateRoot;

emit TransitionProved({
blockId: local.blockId,
tran: _tran,
tran: tran,
prover: msg.sender,
validityBond: 0,
tier: _proof.tier
tier: proof.tier
});
} else {
// Contesting but not on the highest tier
Expand Down Expand Up @@ -283,10 +291,10 @@ library LibProving {

emit TransitionContested({
blockId: local.blockId,
tran: _tran,
tran: tran,
contester: msg.sender,
contestBond: local.tier.contestBond,
tier: _proof.tier
tier: proof.tier
});
}
}
Expand Down

0 comments on commit db6ccdf

Please sign in to comment.