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): convert metadata from V2 to V1 only once #17842

Merged
merged 8 commits into from
Jul 25, 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: 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 [email protected]
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