This repository was archived by the owner on Jan 16, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 212
feat(derive): Payload Attribute Building #92
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
73c10c4
feat(derive): attributes building
refcell 0a03227
feat(derive): ecotone tx building setup
refcell 8c69bcc
fix(derive): attribute builder
refcell 7717aef
Merge branch 'main' into refcell/attributes-builder
refcell 18cd1e3
fix(derive): use chain provider
refcell 7c4299a
fix(derive): deposit tx work
refcell 2e64f1e
fix(derive): deposit tx work
refcell e361767
fix(derive): migrate deposit utility methods to types
refcell 1eea233
fix(derive): deposit unmarshalling test
refcell 9672a50
fix(derive): ecotone transactions
refcell c4e638f
fix(derive): ecotone transaction building
refcell e4847b6
fix(derive): op-alloy-consensus bump
refcell 1224977
fix(derive): point to clabby's branch
refcell 4701de6
fix(derive): remove lazy static
refcell bca50b7
chore(derive): Review changes
clabby File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| //! The [`AttributesBuilder`] and it's default implementation. | ||
|
|
||
| use super::derive_deposits; | ||
| use crate::{ | ||
| params::SEQUENCER_FEE_VAULT_ADDRESS, | ||
| traits::ChainProvider, | ||
| types::{ | ||
| BlockID, BuilderError, EcotoneTransactionBuilder, L2BlockInfo, PayloadAttributes, | ||
| RawTransaction, RollupConfig, SystemConfig, | ||
| }, | ||
| }; | ||
| use alloc::{boxed::Box, fmt::Debug, sync::Arc, vec, vec::Vec}; | ||
| use alloy_primitives::B256; | ||
| use async_trait::async_trait; | ||
|
|
||
| /// The [AttributesBuilder] is responsible for preparing [PayloadAttributes] | ||
| /// that can be used to construct an L2 Block containing only deposits. | ||
| #[async_trait] | ||
| pub trait AttributesBuilder { | ||
| /// Prepares a template [PayloadAttributes] that is ready to be used to build an L2 block. | ||
| /// The block will contain deposits only, on top of the given L2 parent, with the L1 origin | ||
| /// set to the given epoch. | ||
| /// By default, the [PayloadAttributes] template will have `no_tx_pool` set to true, | ||
| /// and no sequencer transactions. The caller has to modify the template to add transactions. | ||
| /// This can be done by either setting the `no_tx_pool` to false as sequencer, or by appending | ||
| /// batch transactions as the verifier. | ||
| async fn prepare_payload_attributes( | ||
| &mut self, | ||
| l2_parent: L2BlockInfo, | ||
| epoch: BlockID, | ||
| ) -> Result<PayloadAttributes, BuilderError>; | ||
| } | ||
|
|
||
| /// The [SystemConfigL2Fetcher] fetches the system config by L2 hash. | ||
| pub trait SystemConfigL2Fetcher { | ||
| /// Fetch the system config by L2 hash. | ||
| fn system_config_by_l2_hash(&self, hash: B256) -> anyhow::Result<SystemConfig>; | ||
| } | ||
|
|
||
| /// A stateful implementation of the [AttributesBuilder]. | ||
| #[derive(Debug, Default)] | ||
| pub struct StatefulAttributesBuilder<S, R> | ||
| where | ||
| S: SystemConfigL2Fetcher + Debug, | ||
| R: ChainProvider + Debug, | ||
| { | ||
| /// The rollup config. | ||
| rollup_cfg: Arc<RollupConfig>, | ||
| /// The system config fetcher. | ||
| config_fetcher: S, | ||
| /// The L1 receipts fetcher. | ||
| receipts_fetcher: R, | ||
| } | ||
|
|
||
| impl<S, R> StatefulAttributesBuilder<S, R> | ||
| where | ||
| S: SystemConfigL2Fetcher + Debug, | ||
| R: ChainProvider + Debug, | ||
| { | ||
| /// Create a new [StatefulAttributesBuilder] with the given epoch. | ||
| pub fn new(rcfg: Arc<RollupConfig>, cfg: S, receipts: R) -> Self { | ||
| Self { rollup_cfg: rcfg, config_fetcher: cfg, receipts_fetcher: receipts } | ||
| } | ||
| } | ||
|
|
||
| #[async_trait] | ||
| impl<S, R> AttributesBuilder for StatefulAttributesBuilder<S, R> | ||
| where | ||
| S: SystemConfigL2Fetcher + Send + Debug, | ||
| R: ChainProvider + Send + Debug, | ||
| { | ||
| async fn prepare_payload_attributes( | ||
| &mut self, | ||
| l2_parent: L2BlockInfo, | ||
| epoch: BlockID, | ||
| ) -> Result<PayloadAttributes, BuilderError> { | ||
| let l1_header; | ||
| let deposit_transactions: Vec<RawTransaction>; | ||
| // let mut sequence_number = 0u64; | ||
| let mut sys_config = | ||
| self.config_fetcher.system_config_by_l2_hash(l2_parent.block_info.hash)?; | ||
|
|
||
| // If the L1 origin changed in this block, then we are in the first block of the epoch. | ||
| // In this case we need to fetch all transaction receipts from the L1 origin block so | ||
| // we can scan for user deposits. | ||
| if l2_parent.l1_origin.number != epoch.number { | ||
| let header = self.receipts_fetcher.header_by_hash(epoch.hash).await?; | ||
| if l2_parent.l1_origin.hash != header.parent_hash { | ||
| return Err(BuilderError::BlockMismatchEpochReset( | ||
| epoch, | ||
| l2_parent.l1_origin, | ||
| header.parent_hash, | ||
| )); | ||
| } | ||
| let receipts = self.receipts_fetcher.receipts_by_hash(epoch.hash).await?; | ||
| sys_config.update_with_receipts(&receipts, &self.rollup_cfg, header.timestamp)?; | ||
| let deposits = | ||
| derive_deposits(epoch.hash, receipts, self.rollup_cfg.deposit_contract_address) | ||
| .await?; | ||
| l1_header = header; | ||
| deposit_transactions = deposits; | ||
| // sequence_number = 0; | ||
| } else { | ||
| #[allow(clippy::collapsible_else_if)] | ||
| if l2_parent.l1_origin.hash != epoch.hash { | ||
| return Err(BuilderError::BlockMismatch(epoch, l2_parent.l1_origin)); | ||
| } | ||
|
|
||
| let header = self.receipts_fetcher.header_by_hash(epoch.hash).await?; | ||
| l1_header = header; | ||
| deposit_transactions = vec![]; | ||
| // sequence_number = l2_parent.seq_num + 1; | ||
| } | ||
|
|
||
| // Sanity check the L1 origin was correctly selected to maintain the time invariant | ||
| // between L1 and L2. | ||
| let next_l2_time = l2_parent.block_info.timestamp + self.rollup_cfg.block_time; | ||
| if next_l2_time < l1_header.timestamp { | ||
| return Err(BuilderError::BrokenTimeInvariant( | ||
| l2_parent.l1_origin, | ||
| next_l2_time, | ||
| BlockID { hash: l1_header.hash_slow(), number: l1_header.number }, | ||
| l1_header.timestamp, | ||
| )); | ||
| } | ||
|
|
||
| let mut upgrade_transactions: Vec<RawTransaction> = vec![]; | ||
| if self.rollup_cfg.is_ecotone_active(next_l2_time) { | ||
| upgrade_transactions = | ||
| EcotoneTransactionBuilder::build_txs().map_err(BuilderError::Custom)?; | ||
| } | ||
|
|
||
| // TODO(clabby): `L1BlockInfo` parsing from calldata. | ||
| // let l1_info_tx = l1_info_deposit_bytes(self.rollup_cfg, sys_config, sequence_number, | ||
| // l1_info, next_l2_time)?; | ||
|
|
||
| let mut txs = | ||
| Vec::with_capacity(1 + deposit_transactions.len() + upgrade_transactions.len()); | ||
| // txs.push(l1_info_tx); | ||
| txs.extend(deposit_transactions); | ||
| txs.extend(upgrade_transactions); | ||
|
|
||
| let mut withdrawals = None; | ||
| if self.rollup_cfg.is_canyon_active(next_l2_time) { | ||
| withdrawals = Some(Vec::default()); | ||
| } | ||
|
|
||
| let mut parent_beacon_root = None; | ||
| if self.rollup_cfg.is_ecotone_active(next_l2_time) { | ||
| // if the parent beacon root is not available, default to zero hash | ||
| parent_beacon_root = Some(l1_header.parent_beacon_block_root.unwrap_or_default()); | ||
| } | ||
|
|
||
| Ok(PayloadAttributes { | ||
| timestamp: next_l2_time, | ||
| prev_randao: l1_header.mix_hash, | ||
| fee_recipient: SEQUENCER_FEE_VAULT_ADDRESS, | ||
| transactions: txs, | ||
| no_tx_pool: true, | ||
| gas_limit: Some(u64::from_be_bytes( | ||
| alloy_primitives::U64::from(sys_config.gas_limit).to_be_bytes(), | ||
| )), | ||
| withdrawals, | ||
| parent_beacon_block_root: parent_beacon_root, | ||
| }) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| //! Contains a helper method to derive deposit transactions from L1 Receipts. | ||
|
|
||
| use crate::{ | ||
| params::DEPOSIT_EVENT_ABI_HASH, | ||
| types::{decode_deposit, DepositError, RawTransaction}, | ||
| }; | ||
| use alloc::vec::Vec; | ||
| use alloy_consensus::Receipt; | ||
| use alloy_primitives::{Address, Log, B256}; | ||
|
|
||
| /// Derive deposits for transaction receipts. | ||
| /// | ||
| /// Successful deposits must be emitted by the deposit contract and have the correct event | ||
| /// signature. So the receipt address must equal the specified deposit contract and the first topic | ||
| /// must be the [DEPOSIT_EVENT_ABI_HASH]. | ||
| pub(crate) async fn derive_deposits( | ||
| block_hash: B256, | ||
| receipts: Vec<Receipt>, | ||
| deposit_contract: Address, | ||
| ) -> anyhow::Result<Vec<RawTransaction>> { | ||
| let receipts = receipts.into_iter().filter(|r| r.status).collect::<Vec<_>>(); | ||
| // Flatten the list of receipts into a list of logs. | ||
| let addr = |l: &Log| l.address == deposit_contract; | ||
| let topics = |l: &Log| l.data.topics().first().map_or(false, |i| *i == DEPOSIT_EVENT_ABI_HASH); | ||
| let filter_logs = | ||
| |r: Receipt| r.logs.into_iter().filter(|l| addr(l) && topics(l)).collect::<Vec<Log>>(); | ||
| let logs = receipts.into_iter().flat_map(filter_logs).collect::<Vec<Log>>(); | ||
| // TODO(refcell): are logs **and** receipts guaranteed to be _in order_? | ||
| // If not, we need to somehow get the index of each log in the block. | ||
| logs.iter() | ||
| .enumerate() | ||
| .map(|(i, l)| decode_deposit(block_hash, i, l)) | ||
| .collect::<Result<Vec<_>, DepositError>>() | ||
| .map_err(|e| anyhow::anyhow!(e)) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.