Skip to content

Commit

Permalink
refactor(protocol): convert metadata from V2 to V1 only once (#17842)
Browse files Browse the repository at this point in the history
  • Loading branch information
dantaik committed Jul 25, 2024
1 parent d95cc36 commit 55ced31
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 112 deletions.
9 changes: 3 additions & 6 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,12 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents {
{
TaikoData.Config memory config = getConfig();

TaikoData.BlockMetadataV2 memory meta2;
(meta2, deposits_) = LibProposing.proposeBlock(state, config, this, _params, _txList);

if (meta2.id >= config.ontakeForkHeight) revert L1_FORK_ERROR();
(meta_,, deposits_) = LibProposing.proposeBlock(state, config, this, _params, _txList);
if (meta_.id >= config.ontakeForkHeight) revert L1_FORK_ERROR();

if (LibUtils.shouldVerifyBlocks(config, meta_.id, true) && !state.slotB.provingPaused) {
LibVerifying.verifyBlocks(state, config, this, config.maxBlocksToVerify);
}
meta_ = LibData.blockMetadataV2toV1(meta2);
}

function proposeBlockV2(
Expand All @@ -106,7 +103,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents {
{
TaikoData.Config memory config = getConfig();

(meta_,) = LibProposing.proposeBlock(state, config, this, _params, _txList);
(, meta_,) = LibProposing.proposeBlock(state, config, this, _params, _txList);
if (meta_.id < config.ontakeForkHeight) revert L1_FORK_ERROR();

if (LibUtils.shouldVerifyBlocks(config, meta_.id, true) && !state.slotB.provingPaused) {
Expand Down
20 changes: 2 additions & 18 deletions packages/protocol/contracts/L1/libs/LibData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ library LibData {
});
}

function metadataV1toV2(
TaikoData.BlockMetadata memory _v1,
uint96 _livenessBond
)
function blockMetadataV1toV2(TaikoData.BlockMetadata memory _v1)
internal
pure
returns (TaikoData.BlockMetadataV2 memory)
Expand All @@ -73,7 +70,7 @@ library LibData {
blobUsed: _v1.blobUsed,
parentMetaHash: _v1.parentMetaHash,
proposer: _v1.sender,
livenessBond: _livenessBond,
livenessBond: 0,
proposedAt: 0,
proposedIn: 0,
blobTxListOffset: 0,
Expand All @@ -84,17 +81,4 @@ library LibData {
blockGasIssuance: 0
});
}

function hashMetadata(
bool postFork,
TaikoData.BlockMetadataV2 memory _meta
)
internal
pure
returns (bytes32)
{
return postFork
? keccak256(abi.encode(_meta)) //
: keccak256(abi.encode(blockMetadataV2toV1(_meta)));
}
}
158 changes: 85 additions & 73 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ library LibProposing {

struct Local {
TaikoData.SlotB b;
TaikoData.BlockParamsV2 params;
address proposerAccess;
ITierProvider tierProvider;
bytes32 parentMetaHash;
bool postFork;
}
Expand Down Expand Up @@ -60,7 +63,9 @@ library LibProposing {
/// @param _resolver Address resolver interface.
/// @param _data Encoded data bytes containing the block params.
/// @param _txList Transaction list bytes (if not blob).
/// @return meta_ The constructed block's metadata.
/// @return metaV1_ The constructed block's metadata v1.
/// @return meta_ The constructed block's metadata v2.
/// @return deposits_ An empty ETH deposit array.
function proposeBlock(
TaikoData.State storage _state,
TaikoData.Config memory _config,
Expand All @@ -69,18 +74,23 @@ library LibProposing {
bytes calldata _txList
)
public
returns (TaikoData.BlockMetadataV2 memory meta_, TaikoData.EthDeposit[] memory deposits_)
returns (
TaikoData.BlockMetadata memory metaV1_,
TaikoData.BlockMetadataV2 memory meta_,
TaikoData.EthDeposit[] memory deposits_
)
{
// Checks proposer access.
{
address access = _resolver.resolve(LibStrings.B_PROPOSER_ACCESS, true);
if (access != address(0) && !IProposerAccess(access).isProposerEligible(msg.sender)) {
revert L1_INVALID_PROPOSER();
}
Local memory local;

local.proposerAccess = _resolver.resolve(LibStrings.B_PROPOSER_ACCESS, true);
if (
local.proposerAccess != address(0)
&& !IProposerAccess(local.proposerAccess).isProposerEligible(msg.sender)
) {
revert L1_INVALID_PROPOSER();
}

// Taiko, as a Based Rollup, enables permissionless block proposals.
Local memory local;
local.b = _state.slotB;
local.postFork = local.b.numBlocks >= _config.ontakeForkHeight;

Expand All @@ -90,93 +100,90 @@ library LibProposing {
revert L1_TOO_MANY_BLOCKS();
}

TaikoData.BlockParamsV2 memory params;

if (local.postFork) {
if (_data.length != 0) {
params = abi.decode(_data, (TaikoData.BlockParamsV2));
local.params = abi.decode(_data, (TaikoData.BlockParamsV2));
// otherwise use a default BlockParamsV2 with 0 values
}
} else {
params = LibData.blockParamsV1ToV2(abi.decode(_data, (TaikoData.BlockParams)));
local.params = LibData.blockParamsV1ToV2(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;
}

if (!local.postFork || params.anchorBlockId == 0) {
params.anchorBlockId = uint64(block.number - 1);
if (!local.postFork || local.params.anchorBlockId == 0) {
local.params.anchorBlockId = uint64(block.number - 1);
}

if (!local.postFork || params.timestamp == 0) {
params.timestamp = uint64(block.timestamp);
if (!local.postFork || local.params.timestamp == 0) {
local.params.timestamp = uint64(block.timestamp);
}

// Verify params against the parent block.
{
TaikoData.Block storage parentBlk =
_state.blocks[(local.b.numBlocks - 1) % _config.blockRingBufferSize];

if (local.postFork) {
// Verify the passed in L1 state block number.
// We only allow the L1 block to be 2 epochs old.
// The other constraint is that the L1 block number needs to be larger than or equal
// the one in the previous L2 block.
if (
params.anchorBlockId + _config.maxAnchorHeightOffset < block.number //
|| params.anchorBlockId >= block.number
|| params.anchorBlockId < parentBlk.proposedIn
) {
revert L1_INVALID_ANCHOR_BLOCK();
}

// Verify the passed in timestamp.
// We only allow the timestamp to be 2 epochs old.
// The other constraint is that the timestamp needs to be larger than or equal the
// one in the previous L2 block.
if (
params.timestamp + _config.maxAnchorHeightOffset * 12 < block.timestamp
|| params.timestamp > block.timestamp || params.timestamp < parentBlk.proposedAt
) {
revert L1_INVALID_TIMESTAMP();
}
TaikoData.Block storage parentBlk =
_state.blocks[(local.b.numBlocks - 1) % _config.blockRingBufferSize];

if (local.postFork) {
// Verify the passed in L1 state block number.
// We only allow the L1 block to be 2 epochs old.
// The other constraint is that the L1 block number needs to be larger than or equal
// the one in the previous L2 block.
if (
local.params.anchorBlockId + _config.maxAnchorHeightOffset < block.number //
|| local.params.anchorBlockId >= block.number
|| local.params.anchorBlockId < parentBlk.proposedIn
) {
revert L1_INVALID_ANCHOR_BLOCK();
}

// 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) {
params.parentMetaHash = parentBlk.metaHash;
} else if (params.parentMetaHash != parentBlk.metaHash) {
revert L1_UNEXPECTED_PARENT();
// Verify the passed in timestamp.
// We only allow the timestamp to be 2 epochs old.
// The other constraint is that the timestamp needs to be larger than or equal the
// one in the previous L2 block.
if (
local.params.timestamp + _config.maxAnchorHeightOffset * 12 < block.timestamp
|| local.params.timestamp > block.timestamp
|| local.params.timestamp < parentBlk.proposedAt
) {
revert L1_INVALID_TIMESTAMP();
}
}

// 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 (local.params.parentMetaHash == 0) {
local.params.parentMetaHash = parentBlk.metaHash;
} else if (local.params.parentMetaHash != parentBlk.metaHash) {
revert L1_UNEXPECTED_PARENT();
}

// Initialize metadata to compute a metaHash, which forms a part of
// the block data to be stored on-chain for future integrity checks.
// If we choose to persist all data fields in the metadata, it will
// require additional storage slots.
unchecked {
meta_ = TaikoData.BlockMetadataV2({
anchorBlockHash: blockhash(params.anchorBlockId),
anchorBlockHash: blockhash(local.params.anchorBlockId),
difficulty: keccak256(abi.encode("TAIKO_DIFFICULTY", local.b.numBlocks)),
blobHash: 0, // to be initialized below
extraData: params.extraData,
coinbase: params.coinbase,
extraData: local.params.extraData,
coinbase: local.params.coinbase,
id: local.b.numBlocks,
gasLimit: _config.blockMaxGasLimit,
timestamp: params.timestamp,
anchorBlockId: params.anchorBlockId,
timestamp: local.params.timestamp,
anchorBlockId: local.params.anchorBlockId,
minTier: 0, // to be initialized below
blobUsed: _txList.length == 0,
parentMetaHash: params.parentMetaHash,
parentMetaHash: local.params.parentMetaHash,
proposer: msg.sender,
livenessBond: _config.livenessBond,
proposedAt: uint64(block.timestamp),
proposedIn: uint64(block.number),
blobTxListOffset: params.blobTxListOffset,
blobTxListLength: params.blobTxListLength,
blobIndex: params.blobIndex,
blobTxListOffset: local.params.blobTxListOffset,
blobTxListLength: local.params.blobTxListLength,
blobIndex: local.params.blobIndex,
basefeeAdjustmentQuotient: _config.basefeeAdjustmentQuotient,
basefeeSharingPctg: _config.basefeeSharingPctg,
blockGasIssuance: _config.blockGasIssuance
Expand All @@ -191,29 +198,34 @@ library LibProposing {
// proposeBlock functions are called more than once in the same
// L1 transaction, these multiple L2 blocks will share the same
// blob.
meta_.blobHash = blobhash(params.blobIndex);
meta_.blobHash = blobhash(local.params.blobIndex);
if (meta_.blobHash == 0) revert L1_BLOB_NOT_FOUND();
} else {
meta_.blobHash = keccak256(_txList);
emit CalldataTxList(meta_.id, _txList);
}

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

// Use the difficulty as a random number
meta_.minTier = local.tierProvider.getMinTier(uint256(meta_.difficulty));

// Use the difficulty as a random number
meta_.minTier = tierProvider.getMinTier(uint256(meta_.difficulty));
if (!local.postFork) {
metaV1_ = LibData.blockMetadataV2toV1(meta_);
}

// Create the block that will be stored onchain
TaikoData.Block memory blk = TaikoData.Block({
metaHash: LibData.hashMetadata(local.postFork, meta_),
metaHash: local.postFork ? keccak256(abi.encode(meta_)) : keccak256(abi.encode(metaV1_)),
assignedProver: address(0),
livenessBond: local.postFork ? 0 : meta_.livenessBond,
blockId: local.b.numBlocks,
proposedAt: local.postFork ? params.timestamp : uint64(block.timestamp),
proposedIn: local.postFork ? params.anchorBlockId : uint64(block.number),
proposedAt: local.postFork ? local.params.timestamp : uint64(block.timestamp),
proposedIn: local.postFork ? local.params.anchorBlockId : uint64(block.number),
// For a new block, the next transition ID is always 1, not 0.
nextTransitionId: 1,
livenessBondReturned: false,
Expand All @@ -231,7 +243,7 @@ library LibProposing {

LibBonds.debitBond(_state, _resolver, msg.sender, _config.livenessBond);

// Bribe the block builder. Unlock 1559-tips, this tip is only made
// Bribe the block builder. Unlike 1559-tips, this tip is only made
// if this transaction succeeds.
if (msg.value != 0 && block.coinbase != address(0)) {
address(block.coinbase).sendEtherAndVerify(msg.value);
Expand All @@ -243,10 +255,10 @@ library LibProposing {
emit BlockProposedV2(meta_.id, meta_);
} else {
emit BlockProposed({
blockId: meta_.id,
blockId: metaV1_.id,
assignedProver: msg.sender,
livenessBond: _config.livenessBond,
meta: LibData.blockMetadataV2toV1(meta_),
meta: metaV1_,
depositsProcessed: deposits_
});
}
Expand Down
15 changes: 9 additions & 6 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,12 @@ library LibProving {
_input, (TaikoData.BlockMetadataV2, TaikoData.Transition, TaikoData.TierProof)
);
} else {
TaikoData.BlockMetadata memory meta1;
TaikoData.BlockMetadata memory metaV1;

(meta1, tran, proof) = abi.decode(
(metaV1, tran, proof) = abi.decode(
_input, (TaikoData.BlockMetadata, TaikoData.Transition, TaikoData.TierProof)
);
// Below, the liveness bond parameter must be 0 to force reading from block storage.
meta = LibData.metadataV1toV2(meta1, 0);
meta = LibData.blockMetadataV1toV2(metaV1);
}

if (_blockId != meta.id) revert LibUtils.L1_INVALID_BLOCK_ID();
Expand Down Expand Up @@ -197,8 +196,12 @@ 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.metaHash != LibData.hashMetadata(local.postFork, meta)) {
revert L1_BLOCK_MISMATCH();
{
bytes32 metaHash = local.postFork
? keccak256(abi.encode(meta))
: keccak256(abi.encode(LibData.blockMetadataV2toV1(meta)));

if (local.metaHash != metaHash) revert L1_BLOCK_MISMATCH();
}

// Each transition is uniquely identified by the parentHash, with the
Expand Down
8 changes: 4 additions & 4 deletions packages/protocol/contracts/L1/provers/GuardianProver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ contract GuardianProver is IVerifier, EssentialContract {
}

function approveV2(
TaikoData.BlockMetadataV2 calldata _meta2,
TaikoData.BlockMetadataV2 calldata _metaV2,
TaikoData.Transition calldata _tran,
TaikoData.TierProof calldata _proof
)
Expand All @@ -207,10 +207,10 @@ contract GuardianProver is IVerifier, EssentialContract {
returns (bool)
{
return _approve({
_blockId: _meta2.id,
_proofHash: keccak256(abi.encode(_meta2, _tran, _proof.data)),
_blockId: _metaV2.id,
_proofHash: keccak256(abi.encode(_metaV2, _tran, _proof.data)),
_blockHash: _tran.blockHash,
_data: abi.encode(_meta2, _tran, _proof),
_data: abi.encode(_metaV2, _tran, _proof),
_proofData: _proof.data
});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/mainnet/MainnetTierRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "../L1/tiers/ITierRouter.sol";
/// @custom:security-contact security@taiko.xyz
contract MainnetTierRouter is ITierRouter {
/// @inheritdoc ITierRouter
function getProvider(uint256 _blockId) external pure returns (address) {
function getProvider(uint256) external pure returns (address) {
return 0x4cffe56C947E26D07C14020499776DB3e9AE3a23; // TierProviderV2
}
}
Loading

0 comments on commit 55ced31

Please sign in to comment.