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

refactor(protocol): refactor TaikoL1 contract #17678

Merged
merged 2 commits into from
Jun 26, 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
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