From bb582a40f0854a3c961caf0a9b076f7738ea0415 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 13 Apr 2024 17:38:43 -0400 Subject: [PATCH 1/3] feat(derive): `L2ChainProvider` w/ `op-alloy-consensus` Updates the `L2ChainProvider` implementation to use the proper OP Stack types, and completes the block info / payload by hash fetching. --- Cargo.lock | 2 +- crates/derive/Cargo.toml | 5 +- crates/derive/src/alloy_providers.rs | 20 ++-- crates/derive/src/stages/attributes_queue.rs | 14 +-- .../src/stages/test_utils/attributes_queue.rs | 2 +- crates/derive/src/traits/data_sources.rs | 4 +- .../src/traits/test_utils/data_sources.rs | 8 +- crates/derive/src/types/attributes.rs | 16 ++-- crates/derive/src/types/block.rs | 19 ++++ crates/derive/src/types/mod.rs | 6 +- crates/derive/src/types/payload.rs | 96 +++++++++++++------ 11 files changed, 127 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0396b541..671cc3f28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding#842bf916e8242a163cb4375cce56f3ee59a340e9" dependencies = [ "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=e3f2f07)", "alloy-primitives", diff --git a/crates/derive/Cargo.toml b/crates/derive/Cargo.toml index e382c87f0..51de8e9c8 100644 --- a/crates/derive/Cargo.toml +++ b/crates/derive/Cargo.toml @@ -19,6 +19,7 @@ alloy-primitives = { version = "0.7.0", default-features = false, features = ["r alloy-rlp = { version = "0.3.4", default-features = false, features = ["derive"] } alloy-sol-types = { version = "0.7.0", default-features = false } alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "e3f2f07", default-features = false } +op-alloy-consensus = { git = "https://github.com/clabby/op-alloy", branch = "refcell/consensus-port", default-features = false } alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "e3f2f07", default-features = false } async-trait = "0.1.77" hashbrown = "0.14.3" @@ -42,8 +43,8 @@ tracing-subscriber = "0.3.18" [features] default = ["serde", "k256"] -serde = ["dep:serde", "alloy-primitives/serde"] -k256 = ["alloy-primitives/k256", "alloy-consensus/k256"] +serde = ["dep:serde", "alloy-primitives/serde", "alloy-consensus/serde", "op-alloy-consensus/serde"] +k256 = ["alloy-primitives/k256", "alloy-consensus/k256", "op-alloy-consensus/k256"] online = [ "dep:alloy-provider", "dep:alloy-transport-http", diff --git a/crates/derive/src/alloy_providers.rs b/crates/derive/src/alloy_providers.rs index 9a1db6db6..75e11b7b6 100644 --- a/crates/derive/src/alloy_providers.rs +++ b/crates/derive/src/alloy_providers.rs @@ -3,7 +3,7 @@ use crate::{ traits::{ChainProvider, L2ChainProvider}, - types::{Block, BlockInfo, ExecutionPayloadEnvelope, L2BlockInfo, RollupConfig}, + types::{Block, BlockInfo, L2BlockInfo, L2ExecutionPayloadEnvelope, OpBlock, RollupConfig}, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_consensus::{Header, Receipt, ReceiptWithBloom, TxEnvelope, TxType}; @@ -149,26 +149,26 @@ impl>> ChainProvider for AlloyChainProvider } } -/// The [AlloyL2SafeHeadProvider] is a concrete implementation of the [L2ChainProvider] trait, +/// The [AlloyL2ChainProvider] is a concrete implementation of the [L2ChainProvider] trait, /// providing data over Ethereum JSON-RPC using an alloy provider as the backend. /// /// **Note**: /// This provider fetches data using the `debug_getRawBlock` method. The RPC must support this /// namespace. #[derive(Debug)] -pub struct AlloyL2SafeHeadProvider>> { +pub struct AlloyL2ChainProvider>> { /// The inner Ethereum JSON-RPC provider. inner: T, /// The rollup configuration. rollup_config: Arc, /// `payload_by_number` LRU cache. - payload_by_number_cache: LruCache, + payload_by_number_cache: LruCache, /// `l2_block_info_by_number` LRU cache. l2_block_info_by_number_cache: LruCache, } -impl>> AlloyL2SafeHeadProvider { - /// Creates a new [AlloyL2SafeHeadProvider] with the given alloy provider and [RollupConfig]. +impl>> AlloyL2ChainProvider { + /// Creates a new [AlloyL2ChainProvider] with the given alloy provider and [RollupConfig]. pub fn new(inner: T, rollup_config: Arc) -> Self { Self { inner, @@ -180,7 +180,7 @@ impl>> AlloyL2SafeHeadProvider { } #[async_trait] -impl>> L2ChainProvider for AlloyL2SafeHeadProvider { +impl>> L2ChainProvider for AlloyL2ChainProvider { async fn l2_block_info_by_number(&mut self, number: u64) -> Result { if let Some(l2_block_info) = self.l2_block_info_by_number_cache.get(&number) { return Ok(*l2_block_info); @@ -192,7 +192,7 @@ impl>> L2ChainProvider for AlloyL2SafeHeadProv Ok(l2_block_info) } - async fn payload_by_number(&mut self, number: u64) -> Result { + async fn payload_by_number(&mut self, number: u64) -> Result { if let Some(payload) = self.payload_by_number_cache.get(&number) { return Ok(payload.clone()); } @@ -203,8 +203,8 @@ impl>> L2ChainProvider for AlloyL2SafeHeadProv .request("debug_getRawBlock", [U64::from(number)]) .await .map_err(|e| anyhow!(e))?; - let block = Block::decode(&mut raw_block.as_ref()).map_err(|e| anyhow!(e))?; - let payload_envelope: ExecutionPayloadEnvelope = block.into(); + let block = OpBlock::decode(&mut raw_block.as_ref()).map_err(|e| anyhow!(e))?; + let payload_envelope: L2ExecutionPayloadEnvelope = block.into(); self.payload_by_number_cache.put(number, payload_envelope.clone()); Ok(payload_envelope) diff --git a/crates/derive/src/stages/attributes_queue.rs b/crates/derive/src/stages/attributes_queue.rs index f64992fe4..881d1a6cf 100644 --- a/crates/derive/src/stages/attributes_queue.rs +++ b/crates/derive/src/stages/attributes_queue.rs @@ -80,14 +80,14 @@ where pub async fn next_attributes( &mut self, parent: L2BlockInfo, - ) -> StageResult { + ) -> StageResult { // Load the batch. let batch = self.load_batch(parent).await?; // Construct the payload attributes from the loaded batch. let attributes = self.create_next_attributes(batch, parent).await?; let populated_attributes = - AttributesWithParent { attributes, parent, is_last_in_span: self.is_last_in_span }; + L2AttributesWithParent { attributes, parent, is_last_in_span: self.is_last_in_span }; // Clear out the local state once payload attributes are prepared. self.batch = None; @@ -101,7 +101,7 @@ where &mut self, batch: SingleBatch, parent: L2BlockInfo, - ) -> StageResult { + ) -> StageResult { // Sanity check parent hash if batch.parent_hash != parent.block_info.hash { return Err(StageError::Reset(ResetError::BadParentHash( @@ -164,7 +164,7 @@ where #[cfg(test)] mod tests { use super::{ - AttributesQueue, AttributesWithParent, BlockInfo, L2BlockInfo, PayloadAttributes, + AttributesQueue, BlockInfo, L2AttributesWithParent, L2BlockInfo, L2PayloadAttributes, RollupConfig, SingleBatch, StageError, StageResult, }; use crate::{ @@ -272,7 +272,7 @@ mod tests { async fn test_create_next_attributes_success() { let cfg = RollupConfig::default(); let mock = new_attributes_provider(None, vec![]); - let mut payload_attributes = PayloadAttributes::default(); + let mut payload_attributes = L2PayloadAttributes::default(); let mock_builder = MockAttributesBuilder { attributes: vec![Ok(payload_attributes.clone())] }; let mut aq = AttributesQueue::new(cfg, mock, mock_builder); @@ -298,7 +298,7 @@ mod tests { async fn test_next_attributes_load_batch_last_in_span() { let cfg = RollupConfig::default(); let mock = new_attributes_provider(None, vec![Ok(Default::default())]); - let mut pa = PayloadAttributes::default(); + let mut pa = L2PayloadAttributes::default(); let mock_builder = MockAttributesBuilder { attributes: vec![Ok(pa.clone())] }; let mut aq = AttributesQueue::new(cfg, mock, mock_builder); // If we load the batch, we should get the last in span. @@ -310,7 +310,7 @@ mod tests { // It should also reset the last in span flag and clear the batch. let attributes = aq.next_attributes(L2BlockInfo::default()).await.unwrap(); pa.no_tx_pool = true; - let populated_attributes = AttributesWithParent { + let populated_attributes = L2AttributesWithParent { attributes: pa, parent: L2BlockInfo::default(), is_last_in_span: true, diff --git a/crates/derive/src/stages/test_utils/attributes_queue.rs b/crates/derive/src/stages/test_utils/attributes_queue.rs index 1adaa0c2d..e8643e693 100644 --- a/crates/derive/src/stages/test_utils/attributes_queue.rs +++ b/crates/derive/src/stages/test_utils/attributes_queue.rs @@ -15,7 +15,7 @@ use async_trait::async_trait; #[derive(Debug, Default)] pub struct MockAttributesBuilder { /// The attributes to return. - pub attributes: Vec>, + pub attributes: Vec>, } #[async_trait] diff --git a/crates/derive/src/traits/data_sources.rs b/crates/derive/src/traits/data_sources.rs index c94b7b575..dd5c00f36 100644 --- a/crates/derive/src/traits/data_sources.rs +++ b/crates/derive/src/traits/data_sources.rs @@ -2,7 +2,7 @@ //! pipeline's stages. use crate::types::{ - Blob, BlockInfo, ExecutionPayloadEnvelope, IndexedBlobHash, L2BlockInfo, StageResult, + Blob, BlockInfo, IndexedBlobHash, L2BlockInfo, L2ExecutionPayloadEnvelope, StageResult, }; use alloc::{boxed::Box, fmt::Debug, vec::Vec}; use alloy_consensus::{Header, Receipt, TxEnvelope}; @@ -40,7 +40,7 @@ pub trait L2ChainProvider { /// Returns an execution payload for a given number. /// Errors if the execution payload does not exist. - async fn payload_by_number(&mut self, number: u64) -> Result; + async fn payload_by_number(&mut self, number: u64) -> Result; } /// The BlobProvider trait specifies the functionality of a data source that can provide blobs. diff --git a/crates/derive/src/traits/test_utils/data_sources.rs b/crates/derive/src/traits/test_utils/data_sources.rs index 75784652b..8f28f934f 100644 --- a/crates/derive/src/traits/test_utils/data_sources.rs +++ b/crates/derive/src/traits/test_utils/data_sources.rs @@ -2,7 +2,7 @@ use crate::{ traits::{ChainProvider, L2ChainProvider}, - types::{BlockInfo, ExecutionPayloadEnvelope, L2BlockInfo}, + types::{BlockInfo, L2BlockInfo, L2ExecutionPayloadEnvelope}, }; use alloc::{boxed::Box, vec::Vec}; use alloy_consensus::{Header, Receipt, TxEnvelope}; @@ -16,12 +16,12 @@ pub struct MockBlockFetcher { /// Blocks pub blocks: Vec, /// Payloads - pub payloads: Vec, + pub payloads: Vec, } impl MockBlockFetcher { /// Creates a new [MockBlockFetcher] with the given origin and batches. - pub fn new(blocks: Vec, payloads: Vec) -> Self { + pub fn new(blocks: Vec, payloads: Vec) -> Self { Self { blocks, payloads } } } @@ -36,7 +36,7 @@ impl L2ChainProvider for MockBlockFetcher { .ok_or_else(|| anyhow::anyhow!("Block not found")) } - async fn payload_by_number(&mut self, number: u64) -> Result { + async fn payload_by_number(&mut self, number: u64) -> Result { self.payloads .iter() .find(|p| p.execution_payload.block_number == number) diff --git a/crates/derive/src/types/attributes.rs b/crates/derive/src/types/attributes.rs index 60ca46e59..8384bd7bb 100644 --- a/crates/derive/src/types/attributes.rs +++ b/crates/derive/src/types/attributes.rs @@ -10,7 +10,7 @@ use alloy_primitives::{Address, B256}; /// Payload attributes. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct PayloadAttributes { +pub struct L2PayloadAttributes { /// Value for the timestamp field of the new payload. #[cfg_attr(feature = "serde", serde(rename = "timestamp"))] pub timestamp: u64, @@ -42,23 +42,27 @@ pub struct PayloadAttributes { /// Payload Attributes with parent block reference. #[derive(Debug, Clone, PartialEq)] -pub struct AttributesWithParent { +pub struct L2AttributesWithParent { /// The payload attributes. - pub attributes: PayloadAttributes, + pub attributes: L2PayloadAttributes, /// The parent block reference. pub parent: L2BlockInfo, /// Whether the current batch is the last in its span. pub is_last_in_span: bool, } -impl AttributesWithParent { +impl L2AttributesWithParent { /// Create a new [AttributesWithParent] instance. - pub fn new(attributes: PayloadAttributes, parent: L2BlockInfo, is_last_in_span: bool) -> Self { + pub fn new( + attributes: L2PayloadAttributes, + parent: L2BlockInfo, + is_last_in_span: bool, + ) -> Self { Self { attributes, parent, is_last_in_span } } /// Returns the payload attributes. - pub fn attributes(&self) -> &PayloadAttributes { + pub fn attributes(&self) -> &L2PayloadAttributes { &self.attributes } diff --git a/crates/derive/src/types/block.rs b/crates/derive/src/types/block.rs index a30d4b45f..71020c504 100644 --- a/crates/derive/src/types/block.rs +++ b/crates/derive/src/types/block.rs @@ -5,6 +5,7 @@ use alloy_consensus::{Header, TxEnvelope}; use alloy_primitives::{Address, BlockHash, BlockNumber, B256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; +use op_alloy_consensus::OpTxEnvelope; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -114,6 +115,24 @@ pub struct Block { pub withdrawals: Option>, } +/// OP Stack full block. +/// +/// Withdrawals can be optionally included at the end of the RLP encoded message. +/// +/// Taken from [reth-primitives](https://github.com/paradigmxyz/reth) +#[derive(Debug, Clone, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)] +#[rlp(trailing)] +pub struct OpBlock { + /// Block header. + pub header: Header, + /// Transactions in this block. + pub body: Vec, + /// Ommers/uncles header. + pub ommers: Vec
, + /// Block withdrawals. + pub withdrawals: Option>, +} + /// Withdrawal represents a validator withdrawal from the consensus layer. /// /// Taken from [reth-primitives](https://github.com/paradigmxyz/reth) diff --git a/crates/derive/src/types/mod.rs b/crates/derive/src/types/mod.rs index 24db731ad..cfc1297f8 100644 --- a/crates/derive/src/types/mod.rs +++ b/crates/derive/src/types/mod.rs @@ -9,7 +9,7 @@ use alloy_primitives::Bytes; use alloy_rlp::{Decodable, Encodable}; mod attributes; -pub use attributes::{AttributesWithParent, PayloadAttributes}; +pub use attributes::{L2AttributesWithParent, L2PayloadAttributes}; mod system_config; pub use system_config::{SystemAccounts, SystemConfig, SystemConfigUpdateType}; @@ -34,11 +34,11 @@ pub use ecotone::*; mod payload; pub use payload::{ - ExecutionPayload, ExecutionPayloadEnvelope, PAYLOAD_MEM_FIXED_COST, PAYLOAD_TX_MEM_OVERHEAD, + L2ExecutionPayload, L2ExecutionPayloadEnvelope, PAYLOAD_MEM_FIXED_COST, PAYLOAD_TX_MEM_OVERHEAD, }; mod block; -pub use block::{Block, BlockID, BlockInfo, BlockKind, L2BlockInfo, Withdrawal}; +pub use block::{Block, BlockID, BlockInfo, BlockKind, L2BlockInfo, OpBlock, Withdrawal}; mod l1_block_info; pub use l1_block_info::{L1BlockInfoBedrock, L1BlockInfoEcotone, L1BlockInfoTx}; diff --git a/crates/derive/src/types/payload.rs b/crates/derive/src/types/payload.rs index de0010350..f0484dd79 100644 --- a/crates/derive/src/types/payload.rs +++ b/crates/derive/src/types/payload.rs @@ -1,9 +1,9 @@ //! Contains the execution payload type. use alloc::vec::Vec; -use alloy_consensus::TxEnvelope; use alloy_primitives::{Address, Bloom, Bytes, B256}; use anyhow::Result; +use op_alloy_consensus::{OpTxEnvelope, OpTxType}; /// Fixed and variable memory costs for a payload. /// ~1000 bytes per payload, with some margin for overhead like map data. @@ -13,7 +13,7 @@ pub const PAYLOAD_MEM_FIXED_COST: u64 = 1000; /// 24 bytes per tx overhead (size of slice header in memory). pub const PAYLOAD_TX_MEM_OVERHEAD: u64 = 24; -use super::{Block, BlockInfo, L2BlockInfo, RollupConfig, Withdrawal}; +use super::{Block, BlockInfo, L2BlockInfo, OpBlock, RollupConfig, Withdrawal}; use alloy_rlp::{Decodable, Encodable}; #[cfg(feature = "serde")] @@ -22,16 +22,16 @@ use serde::{Deserialize, Serialize}; /// Envelope wrapping the [ExecutionPayload]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ExecutionPayloadEnvelope { +pub struct L2ExecutionPayloadEnvelope { /// Parent beacon block root. #[cfg_attr(feature = "serde", serde(rename = "parentBeaconBlockRoot"))] pub parent_beacon_block_root: Option, /// The inner execution payload. #[cfg_attr(feature = "serde", serde(rename = "executionPayload"))] - pub execution_payload: ExecutionPayload, + pub execution_payload: L2ExecutionPayload, } -impl ExecutionPayloadEnvelope { +impl L2ExecutionPayloadEnvelope { /// Returns the payload memory size. pub fn mem_size(&self) -> u64 { let mut out = PAYLOAD_MEM_FIXED_COST; @@ -45,7 +45,7 @@ impl ExecutionPayloadEnvelope { /// The execution payload. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ExecutionPayload { +pub struct L2ExecutionPayload { /// The parent hash. #[cfg_attr(feature = "serde", serde(rename = "parentHash"))] pub parent_hash: B256, @@ -111,32 +111,35 @@ pub struct ExecutionPayload { pub excess_blob_gas: Option, } -impl ExecutionPayloadEnvelope { +impl L2ExecutionPayloadEnvelope { /// Converts the [ExecutionPayloadEnvelope] to an [L2BlockInfo], by checking against the L1 /// information transaction or the genesis block. pub fn to_l2_block_ref(&self, rollup_config: &RollupConfig) -> Result { - let ExecutionPayloadEnvelope { execution_payload, .. } = self; + let L2ExecutionPayloadEnvelope { execution_payload, .. } = self; - let (l1_origin, sequence_number) = - if execution_payload.block_number == rollup_config.genesis.l2.number { - if execution_payload.block_hash != rollup_config.genesis.l2.hash { - anyhow::bail!("Invalid genesis hash"); - } - (&rollup_config.genesis.l1, 0) - } else { - if execution_payload.transactions.is_empty() { - anyhow::bail!( - "L2 block is missing L1 info deposit transaction, block hash: {}", - execution_payload.block_hash - ); - } - let _ = TxEnvelope::decode(&mut execution_payload.transactions[0].as_ref()) - .map_err(|e| anyhow::anyhow!(e))?; + let (l1_origin, sequence_number) = if execution_payload.block_number == + rollup_config.genesis.l2.number + { + if execution_payload.block_hash != rollup_config.genesis.l2.hash { + anyhow::bail!("Invalid genesis hash"); + } + (&rollup_config.genesis.l1, 0) + } else { + if execution_payload.transactions.is_empty() { + anyhow::bail!( + "L2 block is missing L1 info deposit transaction, block hash: {}", + execution_payload.block_hash + ); + } + let tx = OpTxEnvelope::decode(&mut execution_payload.transactions[0].as_ref()) + .map_err(|e| anyhow::anyhow!(e))?; - todo!( - "Need Deposit transaction variant - see 'PayloadToBlockRef' in 'payload_util.go'" - ); - }; + if !matches!(tx.tx_type(), OpTxType::Deposit) { + anyhow::bail!("First payload transaction has unexpected type: {:?}", tx.tx_type()); + } + + todo!("Parse L1 block info from info transaction"); + }; Ok(L2BlockInfo { block_info: BlockInfo { @@ -151,11 +154,46 @@ impl ExecutionPayloadEnvelope { } } -impl From for ExecutionPayloadEnvelope { +impl From for L2ExecutionPayloadEnvelope { fn from(block: Block) -> Self { let Block { header, body, withdrawals, .. } = block; Self { - execution_payload: ExecutionPayload { + execution_payload: L2ExecutionPayload { + parent_hash: header.parent_hash, + fee_recipient: header.beneficiary, + state_root: header.state_root, + receipts_root: header.receipts_root, + logs_bloom: header.logs_bloom, + prev_randao: header.difficulty.into(), + block_number: header.number, + gas_limit: header.gas_limit, + gas_used: header.gas_used, + timestamp: header.timestamp, + extra_data: header.extra_data.clone(), + base_fee_per_gas: header.base_fee_per_gas, + block_hash: header.hash_slow(), + transactions: body + .into_iter() + .map(|tx| { + let mut buf = Vec::with_capacity(tx.length()); + tx.encode(&mut buf); + buf.into() + }) + .collect(), + withdrawals, + blob_gas_used: header.blob_gas_used, + excess_blob_gas: header.excess_blob_gas, + }, + parent_beacon_block_root: header.parent_beacon_block_root, + } + } +} + +impl From for L2ExecutionPayloadEnvelope { + fn from(block: OpBlock) -> Self { + let OpBlock { header, body, withdrawals, .. } = block; + Self { + execution_payload: L2ExecutionPayload { parent_hash: header.parent_hash, fee_recipient: header.beneficiary, state_root: header.state_root, From 5d760daa6583c06d7deedfa5d945ef66c3702af2 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 14 Apr 2024 14:13:58 -0400 Subject: [PATCH 2/3] rebase --- Cargo.lock | 8 +++++++- crates/derive/Cargo.toml | 1 - crates/derive/src/stages/attributes_queue.rs | 8 ++++---- crates/derive/src/stages/attributes_queue/builder.rs | 12 ++++++------ .../derive/src/stages/test_utils/attributes_queue.rs | 6 +++--- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 671cc3f28..dd42596ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,13 +43,15 @@ dependencies = [ "alloy-eips 0.1.0 (git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding)", "alloy-primitives", "alloy-rlp", + "alloy-serde 0.1.0 (git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding)", + "serde", "sha2", ] [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding#842bf916e8242a163cb4375cce56f3ee59a340e9" +source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" dependencies = [ "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=e3f2f07)", "alloy-primitives", @@ -68,6 +70,8 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-serde 0.1.0 (git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding)", + "c-kzg", + "serde", ] [[package]] @@ -1582,6 +1586,8 @@ dependencies = [ "alloy-eips 0.1.0 (git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding)", "alloy-primitives", "alloy-rlp", + "alloy-serde 0.1.0 (git+https://github.com/clabby/alloy?branch=cl/consensus-expose-encoding)", + "serde", ] [[package]] diff --git a/crates/derive/Cargo.toml b/crates/derive/Cargo.toml index 51de8e9c8..06a16f289 100644 --- a/crates/derive/Cargo.toml +++ b/crates/derive/Cargo.toml @@ -19,7 +19,6 @@ alloy-primitives = { version = "0.7.0", default-features = false, features = ["r alloy-rlp = { version = "0.3.4", default-features = false, features = ["derive"] } alloy-sol-types = { version = "0.7.0", default-features = false } alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "e3f2f07", default-features = false } -op-alloy-consensus = { git = "https://github.com/clabby/op-alloy", branch = "refcell/consensus-port", default-features = false } alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "e3f2f07", default-features = false } async-trait = "0.1.77" hashbrown = "0.14.3" diff --git a/crates/derive/src/stages/attributes_queue.rs b/crates/derive/src/stages/attributes_queue.rs index 881d1a6cf..19e1d1a29 100644 --- a/crates/derive/src/stages/attributes_queue.rs +++ b/crates/derive/src/stages/attributes_queue.rs @@ -3,8 +3,8 @@ use crate::{ traits::{OriginProvider, ResettableStage}, types::{ - AttributesWithParent, BlockInfo, L2BlockInfo, PayloadAttributes, ResetError, RollupConfig, - SingleBatch, StageError, StageResult, SystemConfig, + BlockInfo, L2AttributesWithParent, L2BlockInfo, L2PayloadAttributes, ResetError, + RollupConfig, SingleBatch, StageError, StageResult, SystemConfig, }, }; use alloc::boxed::Box; @@ -29,7 +29,7 @@ pub trait AttributesProvider { } /// [AttributesQueue] accepts batches from the [BatchQueue] stage -/// and transforms them into [PayloadAttributes]. The outputted payload +/// and transforms them into [L2PayloadAttributes]. The outputted payload /// attributes cannot be buffered because each batch->attributes transformation /// pulls in data about the current L2 safe head. /// @@ -95,7 +95,7 @@ where Ok(populated_attributes) } - /// Creates the next attributes, transforming a [SingleBatch] into [PayloadAttributes]. + /// Creates the next attributes, transforming a [SingleBatch] into [L2PayloadAttributes]. /// This sets `no_tx_pool` and appends the batched txs to the attributes tx list. pub async fn create_next_attributes( &mut self, diff --git a/crates/derive/src/stages/attributes_queue/builder.rs b/crates/derive/src/stages/attributes_queue/builder.rs index 806e646d5..0ec7f8341 100644 --- a/crates/derive/src/stages/attributes_queue/builder.rs +++ b/crates/derive/src/stages/attributes_queue/builder.rs @@ -5,7 +5,7 @@ use crate::{ params::SEQUENCER_FEE_VAULT_ADDRESS, traits::ChainProvider, types::{ - BlockID, BuilderError, EcotoneTransactionBuilder, L2BlockInfo, PayloadAttributes, + BlockID, BuilderError, EcotoneTransactionBuilder, L2BlockInfo, L2PayloadAttributes, RawTransaction, RollupConfig, SystemConfig, }, }; @@ -17,10 +17,10 @@ use async_trait::async_trait; /// 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. + /// Prepares a template [L2PayloadAttributes] 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, + /// By default, the [L2PayloadAttributes] 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. @@ -28,7 +28,7 @@ pub trait AttributesBuilder { &mut self, l2_parent: L2BlockInfo, epoch: BlockID, - ) -> Result; + ) -> Result; } /// The [SystemConfigL2Fetcher] fetches the system config by L2 hash. @@ -73,7 +73,7 @@ where &mut self, l2_parent: L2BlockInfo, epoch: BlockID, - ) -> Result { + ) -> Result { let l1_header; let deposit_transactions: Vec; // let mut sequence_number = 0u64; @@ -151,7 +151,7 @@ where parent_beacon_root = Some(l1_header.parent_beacon_block_root.unwrap_or_default()); } - Ok(PayloadAttributes { + Ok(L2PayloadAttributes { timestamp: next_l2_time, prev_randao: l1_header.mix_hash, fee_recipient: SEQUENCER_FEE_VAULT_ADDRESS, diff --git a/crates/derive/src/stages/test_utils/attributes_queue.rs b/crates/derive/src/stages/test_utils/attributes_queue.rs index e8643e693..a630b9123 100644 --- a/crates/derive/src/stages/test_utils/attributes_queue.rs +++ b/crates/derive/src/stages/test_utils/attributes_queue.rs @@ -4,8 +4,8 @@ use crate::{ stages::attributes_queue::{AttributesBuilder, AttributesProvider}, traits::OriginProvider, types::{ - BlockID, BlockInfo, BuilderError, L2BlockInfo, PayloadAttributes, SingleBatch, StageError, - StageResult, + BlockID, BlockInfo, BuilderError, L2BlockInfo, L2PayloadAttributes, SingleBatch, + StageError, StageResult, }, }; use alloc::{boxed::Box, vec::Vec}; @@ -25,7 +25,7 @@ impl AttributesBuilder for MockAttributesBuilder { &mut self, _l2_parent: L2BlockInfo, _epoch: BlockID, - ) -> Result { + ) -> Result { match self.attributes.pop() { Some(Ok(attrs)) => Ok(attrs), Some(Err(err)) => Err(BuilderError::Custom(err)), From e1c6d6ce2a0c1f7b5079d972d95980acf05deced Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 14 Apr 2024 16:21:41 -0400 Subject: [PATCH 3/3] feat(derive): Complete `L2ChainProvider` --- crates/derive/src/types/l1_block_info.rs | 22 +++++++++++++++++++++- crates/derive/src/types/payload.rs | 15 ++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/crates/derive/src/types/l1_block_info.rs b/crates/derive/src/types/l1_block_info.rs index 432a8dcc0..af43c0749 100644 --- a/crates/derive/src/types/l1_block_info.rs +++ b/crates/derive/src/types/l1_block_info.rs @@ -1,6 +1,6 @@ //! This module contains the [L1BlockInfoTx] type, and various encoding / decoding methods for it. -use super::{DepositSourceDomain, L1InfoDepositSource, RollupConfig, SystemConfig}; +use super::{BlockID, DepositSourceDomain, L1InfoDepositSource, RollupConfig, SystemConfig}; use alloc::vec::Vec; use alloy_consensus::Header; use alloy_primitives::{address, Address, Bytes, TxKind, B256, U256}; @@ -230,6 +230,26 @@ impl L1BlockInfoTx { Self::Bedrock(bedrock_tx) => bedrock_tx.encode_calldata(), } } + + /// Returns the L1 [BlockID] for the info transaction. + pub fn id(&self) -> BlockID { + match self { + Self::Ecotone(L1BlockInfoEcotone { number, block_hash, .. }) => { + BlockID { number: *number, hash: *block_hash } + } + Self::Bedrock(L1BlockInfoBedrock { number, block_hash, .. }) => { + BlockID { number: *number, hash: *block_hash } + } + } + } + + /// Returns the sequence number for the info transaction + pub fn sequence_number(&self) -> u64 { + match self { + Self::Ecotone(L1BlockInfoEcotone { sequence_number, .. }) => *sequence_number, + Self::Bedrock(L1BlockInfoBedrock { sequence_number, .. }) => *sequence_number, + } + } } impl L1BlockInfoBedrock { diff --git a/crates/derive/src/types/payload.rs b/crates/derive/src/types/payload.rs index f0484dd79..34614e36f 100644 --- a/crates/derive/src/types/payload.rs +++ b/crates/derive/src/types/payload.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use alloy_primitives::{Address, Bloom, Bytes, B256}; use anyhow::Result; -use op_alloy_consensus::{OpTxEnvelope, OpTxType}; +use op_alloy_consensus::OpTxEnvelope; /// Fixed and variable memory costs for a payload. /// ~1000 bytes per payload, with some margin for overhead like map data. @@ -13,7 +13,7 @@ pub const PAYLOAD_MEM_FIXED_COST: u64 = 1000; /// 24 bytes per tx overhead (size of slice header in memory). pub const PAYLOAD_TX_MEM_OVERHEAD: u64 = 24; -use super::{Block, BlockInfo, L2BlockInfo, OpBlock, RollupConfig, Withdrawal}; +use super::{Block, BlockInfo, L1BlockInfoTx, L2BlockInfo, OpBlock, RollupConfig, Withdrawal}; use alloy_rlp::{Decodable, Encodable}; #[cfg(feature = "serde")] @@ -123,7 +123,7 @@ impl L2ExecutionPayloadEnvelope { if execution_payload.block_hash != rollup_config.genesis.l2.hash { anyhow::bail!("Invalid genesis hash"); } - (&rollup_config.genesis.l1, 0) + (rollup_config.genesis.l1, 0) } else { if execution_payload.transactions.is_empty() { anyhow::bail!( @@ -134,11 +134,12 @@ impl L2ExecutionPayloadEnvelope { let tx = OpTxEnvelope::decode(&mut execution_payload.transactions[0].as_ref()) .map_err(|e| anyhow::anyhow!(e))?; - if !matches!(tx.tx_type(), OpTxType::Deposit) { + let OpTxEnvelope::Deposit(tx) = tx else { anyhow::bail!("First payload transaction has unexpected type: {:?}", tx.tx_type()); - } + }; - todo!("Parse L1 block info from info transaction"); + let l1_info = L1BlockInfoTx::decode_calldata(tx.input.as_ref())?; + (l1_info.id(), l1_info.sequence_number()) }; Ok(L2BlockInfo { @@ -148,7 +149,7 @@ impl L2ExecutionPayloadEnvelope { parent_hash: execution_payload.parent_hash, timestamp: execution_payload.timestamp, }, - l1_origin: *l1_origin, + l1_origin, seq_num: sequence_number, }) }