Skip to content

Commit

Permalink
feat(derive): L2ChainProvider w/ op-alloy-consensus (#98)
Browse files Browse the repository at this point in the history
* 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.

* rebase

* feat(derive): Complete `L2ChainProvider`
  • Loading branch information
clabby authored Apr 14, 2024
1 parent ce4fab6 commit 3d9e394
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 78 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,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",
Expand Down
20 changes: 10 additions & 10 deletions crates/derive/src/alloy_providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -149,26 +149,26 @@ impl<T: Provider<Http<reqwest::Client>>> ChainProvider for AlloyChainProvider<T>
}
}

/// 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<T: Provider<Http<reqwest::Client>>> {
pub struct AlloyL2ChainProvider<T: Provider<Http<reqwest::Client>>> {
/// The inner Ethereum JSON-RPC provider.
inner: T,
/// The rollup configuration.
rollup_config: Arc<RollupConfig>,
/// `payload_by_number` LRU cache.
payload_by_number_cache: LruCache<u64, ExecutionPayloadEnvelope>,
payload_by_number_cache: LruCache<u64, L2ExecutionPayloadEnvelope>,
/// `l2_block_info_by_number` LRU cache.
l2_block_info_by_number_cache: LruCache<u64, L2BlockInfo>,
}

impl<T: Provider<Http<reqwest::Client>>> AlloyL2SafeHeadProvider<T> {
/// Creates a new [AlloyL2SafeHeadProvider] with the given alloy provider and [RollupConfig].
impl<T: Provider<Http<reqwest::Client>>> AlloyL2ChainProvider<T> {
/// Creates a new [AlloyL2ChainProvider] with the given alloy provider and [RollupConfig].
pub fn new(inner: T, rollup_config: Arc<RollupConfig>) -> Self {
Self {
inner,
Expand All @@ -180,7 +180,7 @@ impl<T: Provider<Http<reqwest::Client>>> AlloyL2SafeHeadProvider<T> {
}

#[async_trait]
impl<T: Provider<Http<reqwest::Client>>> L2ChainProvider for AlloyL2SafeHeadProvider<T> {
impl<T: Provider<Http<reqwest::Client>>> L2ChainProvider for AlloyL2ChainProvider<T> {
async fn l2_block_info_by_number(&mut self, number: u64) -> Result<L2BlockInfo> {
if let Some(l2_block_info) = self.l2_block_info_by_number_cache.get(&number) {
return Ok(*l2_block_info);
Expand All @@ -192,7 +192,7 @@ impl<T: Provider<Http<reqwest::Client>>> L2ChainProvider for AlloyL2SafeHeadProv
Ok(l2_block_info)
}

async fn payload_by_number(&mut self, number: u64) -> Result<ExecutionPayloadEnvelope> {
async fn payload_by_number(&mut self, number: u64) -> Result<L2ExecutionPayloadEnvelope> {
if let Some(payload) = self.payload_by_number_cache.get(&number) {
return Ok(payload.clone());
}
Expand All @@ -203,8 +203,8 @@ impl<T: Provider<Http<reqwest::Client>>> 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)
Expand Down
22 changes: 11 additions & 11 deletions crates/derive/src/stages/attributes_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
///
Expand Down Expand Up @@ -80,28 +80,28 @@ where
pub async fn next_attributes(
&mut self,
parent: L2BlockInfo,
) -> StageResult<AttributesWithParent> {
) -> StageResult<L2AttributesWithParent> {
// 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;
self.is_last_in_span = false;
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,
batch: SingleBatch,
parent: L2BlockInfo,
) -> StageResult<PayloadAttributes> {
) -> StageResult<L2PayloadAttributes> {
// Sanity check parent hash
if batch.parent_hash != parent.block_info.hash {
return Err(StageError::Reset(ResetError::BadParentHash(
Expand Down Expand Up @@ -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::{
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand All @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions crates/derive/src/stages/attributes_queue/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
Expand All @@ -17,18 +17,18 @@ 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.
async fn prepare_payload_attributes(
&mut self,
l2_parent: L2BlockInfo,
epoch: BlockID,
) -> Result<PayloadAttributes, BuilderError>;
) -> Result<L2PayloadAttributes, BuilderError>;
}

/// The [SystemConfigL2Fetcher] fetches the system config by L2 hash.
Expand Down Expand Up @@ -73,7 +73,7 @@ where
&mut self,
l2_parent: L2BlockInfo,
epoch: BlockID,
) -> Result<PayloadAttributes, BuilderError> {
) -> Result<L2PayloadAttributes, BuilderError> {
let l1_header;
let deposit_transactions: Vec<RawTransaction>;
// let mut sequence_number = 0u64;
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions crates/derive/src/stages/test_utils/attributes_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -15,7 +15,7 @@ use async_trait::async_trait;
#[derive(Debug, Default)]
pub struct MockAttributesBuilder {
/// The attributes to return.
pub attributes: Vec<anyhow::Result<PayloadAttributes>>,
pub attributes: Vec<anyhow::Result<L2PayloadAttributes>>,
}

#[async_trait]
Expand All @@ -25,7 +25,7 @@ impl AttributesBuilder for MockAttributesBuilder {
&mut self,
_l2_parent: L2BlockInfo,
_epoch: BlockID,
) -> Result<PayloadAttributes, BuilderError> {
) -> Result<L2PayloadAttributes, BuilderError> {
match self.attributes.pop() {
Some(Ok(attrs)) => Ok(attrs),
Some(Err(err)) => Err(BuilderError::Custom(err)),
Expand Down
4 changes: 2 additions & 2 deletions crates/derive/src/traits/data_sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<ExecutionPayloadEnvelope>;
async fn payload_by_number(&mut self, number: u64) -> Result<L2ExecutionPayloadEnvelope>;
}

/// The BlobProvider trait specifies the functionality of a data source that can provide blobs.
Expand Down
8 changes: 4 additions & 4 deletions crates/derive/src/traits/test_utils/data_sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -16,12 +16,12 @@ pub struct MockBlockFetcher {
/// Blocks
pub blocks: Vec<L2BlockInfo>,
/// Payloads
pub payloads: Vec<ExecutionPayloadEnvelope>,
pub payloads: Vec<L2ExecutionPayloadEnvelope>,
}

impl MockBlockFetcher {
/// Creates a new [MockBlockFetcher] with the given origin and batches.
pub fn new(blocks: Vec<L2BlockInfo>, payloads: Vec<ExecutionPayloadEnvelope>) -> Self {
pub fn new(blocks: Vec<L2BlockInfo>, payloads: Vec<L2ExecutionPayloadEnvelope>) -> Self {
Self { blocks, payloads }
}
}
Expand All @@ -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<ExecutionPayloadEnvelope> {
async fn payload_by_number(&mut self, number: u64) -> Result<L2ExecutionPayloadEnvelope> {
self.payloads
.iter()
.find(|p| p.execution_payload.block_number == number)
Expand Down
16 changes: 10 additions & 6 deletions crates/derive/src/types/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

Expand Down
Loading

0 comments on commit 3d9e394

Please sign in to comment.