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

feat: Remove L2 Execution Payload #542

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
5 changes: 0 additions & 5 deletions Cargo.lock

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

11 changes: 5 additions & 6 deletions bin/client/src/l2/chain_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ use alloy_primitives::{Address, Bytes, B256};
use alloy_rlp::Decodable;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use kona_derive::traits::L2ChainProvider;
use kona_derive::{block::OpBlock, traits::L2ChainProvider};
use kona_mpt::{OrderedListWalker, TrieDBFetcher, TrieDBHinter};
use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType};
use kona_primitives::{L2ExecutionPayloadEnvelope, OpBlock};
use op_alloy_consensus::OpTxEnvelope;
use op_alloy_genesis::{RollupConfig, SystemConfig};
use op_alloy_protocol::L2BlockInfo;
Expand Down Expand Up @@ -73,13 +72,13 @@ impl<T: CommsClient + Send + Sync> L2ChainProvider for OracleL2ChainProvider<T>

async fn l2_block_info_by_number(&mut self, number: u64) -> Result<L2BlockInfo> {
// Get the payload at the given block number.
let payload = self.payload_by_number(number).await?;
let payload = self.block_by_number(number).await?;

// Construct the system config from the payload.
payload.to_l2_block_ref(&self.boot_info.rollup_config).map_err(Into::into)
}

async fn payload_by_number(&mut self, number: u64) -> Result<L2ExecutionPayloadEnvelope> {
async fn block_by_number(&mut self, number: u64) -> Result<OpBlock> {
// Fetch the header for the given block number.
let header @ Header { transactions_root, timestamp, .. } =
self.header_by_number(number).await?;
Expand All @@ -104,7 +103,7 @@ impl<T: CommsClient + Send + Sync> L2ChainProvider for OracleL2ChainProvider<T>
withdrawals: self.boot_info.rollup_config.is_canyon_active(timestamp).then(Vec::new),
..Default::default()
};
Ok(optimism_block.into())
Ok(optimism_block)
}

async fn system_config_by_number(
Expand All @@ -113,7 +112,7 @@ impl<T: CommsClient + Send + Sync> L2ChainProvider for OracleL2ChainProvider<T>
rollup_config: Arc<RollupConfig>,
) -> Result<SystemConfig> {
// Get the payload at the given block number.
let payload = self.payload_by_number(number).await?;
let payload = self.block_by_number(number).await?;

// Construct the system config from the payload.
payload.to_system_config(rollup_config.as_ref()).map_err(Into::into)
Expand Down
52 changes: 26 additions & 26 deletions crates/derive/src/batch/span_batch/batch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! The Span Batch Type

use alloc::vec::Vec;
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::FixedBytes;
use op_alloy_consensus::OpTxType;
use op_alloy_genesis::RollupConfig;
Expand Down Expand Up @@ -277,20 +278,18 @@ impl SpanBatch {
if self.timestamp() < next_timestamp {
for i in 0..(l2_safe_head.block_info.number - parent_num) {
let safe_block_num = parent_num + i + 1;
let safe_block_payload = match fetcher.payload_by_number(safe_block_num).await {
let safe_block_payload = match fetcher.block_by_number(safe_block_num).await {
Ok(p) => p,
Err(e) => {
warn!("failed to fetch payload for block number {safe_block_num}: {e}");
return BatchValidity::Undecided;
}
};
let safe_block_txs = &safe_block_payload.execution_payload.transactions;
let safe_block_txs = &safe_block_payload.body;
let batch_txs = &self.batches[i as usize].transactions;
// Execution payload has deposit txs but batch does not.
let deposit_count: usize = safe_block_txs
.iter()
.map(|tx| if tx.0[0] == OpTxType::Deposit as u8 { 1 } else { 0 })
.sum();
let deposit_count: usize =
safe_block_txs.iter().map(|tx| if tx.is_deposit() { 1 } else { 0 }).sum();
if safe_block_txs.len() - deposit_count != batch_txs.len() {
warn!(
"overlapped block's tx count does not match, safe_block_txs: {}, batch_txs: {}",
Expand All @@ -300,15 +299,17 @@ impl SpanBatch {
return BatchValidity::Drop;
}
for j in 0..batch_txs.len() {
if safe_block_txs[j + deposit_count] != batch_txs[j].0 {
let mut buf = Vec::new();
safe_block_txs[j + deposit_count].encode_2718(&mut buf);
if buf != batch_txs[j].0 {
warn!("overlapped block's transaction does not match");
return BatchValidity::Drop;
}
}
let safe_block_ref = match safe_block_payload.to_l2_block_ref(cfg) {
Ok(r) => r,
Err(e) => {
warn!("failed to extract L2BlockInfo from execution payload, hash: {}, err: {e}", safe_block_payload.execution_payload.block_hash);
warn!("failed to extract L2BlockInfo from execution payload, hash: {}, err: {e}", safe_block_payload.header.hash_slow());
return BatchValidity::Drop;
}
};
Expand Down Expand Up @@ -417,13 +418,14 @@ impl SpanBatch {
mod tests {
use super::*;
use crate::{
block::OpBlock,
stages::test_utils::{CollectingLayer, TraceStorage},
traits::test_utils::TestL2ChainProvider,
};
use alloc::vec;
use alloy_consensus::Header;
use alloy_eips::BlockNumHash;
use alloy_primitives::{b256, Bytes, B256};
use kona_primitives::{L2ExecutionPayload, L2ExecutionPayloadEnvelope};
use op_alloy_consensus::OpTxType;
use op_alloy_genesis::ChainGenesis;
use tracing::Level;
Expand Down Expand Up @@ -1407,13 +1409,11 @@ mod tests {
l1_origin: BlockNumHash { number: 9, ..Default::default() },
..Default::default()
};
let payload = L2ExecutionPayloadEnvelope {
parent_beacon_block_root: None,
execution_payload: L2ExecutionPayload { block_number: 41, ..Default::default() },
};
let block =
OpBlock { header: Header { number: 41, ..Default::default() }, ..Default::default() };
let mut fetcher = TestL2ChainProvider {
blocks: vec![l2_block],
payloads: vec![payload],
op_blocks: vec![block],
..Default::default()
};
let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
Expand Down Expand Up @@ -1477,17 +1477,17 @@ mod tests {
l1_origin: BlockNumHash { number: 9, ..Default::default() },
..Default::default()
};
let payload = L2ExecutionPayloadEnvelope {
parent_beacon_block_root: None,
execution_payload: L2ExecutionPayload {
block_number: 41,
block_hash: payload_block_hash,
let block = OpBlock {
header: Header {
number: 41,
// TODO: correct hash
..Default::default()
},
..Default::default()
};
let mut fetcher = TestL2ChainProvider {
blocks: vec![l2_block],
payloads: vec![payload],
op_blocks: vec![block],
..Default::default()
};
let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
Expand Down Expand Up @@ -1553,17 +1553,17 @@ mod tests {
l1_origin: BlockNumHash { number: 9, ..Default::default() },
..Default::default()
};
let payload = L2ExecutionPayloadEnvelope {
parent_beacon_block_root: None,
execution_payload: L2ExecutionPayload {
block_number: 41,
block_hash: payload_block_hash,
let block = OpBlock {
header: Header {
number: 41,
// TODO: correct hash
..Default::default()
},
..Default::default()
};
let mut fetcher = TestL2ChainProvider {
blocks: vec![l2_block],
payloads: vec![payload],
op_blocks: vec![block],
..Default::default()
};
let first = SpanBatchElement { epoch_num: 10, timestamp: 10, ..Default::default() };
Expand Down
166 changes: 166 additions & 0 deletions crates/derive/src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//! This module contains the various Block types.

use alloc::vec::Vec;
use alloy_consensus::{Header, TxEnvelope};
use alloy_eips::eip4895::Withdrawal;
use alloy_primitives::B256;
use alloy_rlp::{RlpDecodable, RlpEncodable};
use op_alloy_consensus::OpTxEnvelope;
use op_alloy_genesis::{RollupConfig, SystemConfig};
use op_alloy_protocol::{
block_info::DecodeError, BlockInfo, L1BlockInfoBedrock, L1BlockInfoEcotone, L1BlockInfoTx,
L2BlockInfo,
};
use thiserror::Error;

/// Ethereum 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 Block {
/// Block header.
pub header: Header,
/// Transactions in this block.
pub body: Vec<TxEnvelope>,
/// Ommers/uncles header.
pub ommers: Vec<Header>,
/// Block withdrawals.
pub withdrawals: Option<Vec<Withdrawal>>,
}

/// An error encountered during [OpBlock] conversion.
#[derive(Error, Debug)]
pub enum OpBlockConversionError {
/// Invalid genesis hash.
#[error("Invalid genesis hash. Expected {0}, got {1}")]
InvalidGenesisHash(B256, B256),
/// Invalid transaction type.
#[error("First payload transaction has unexpected type: {0}")]
InvalidTxType(u8),
/// L1 Info error
#[error(transparent)]
L1InfoError(#[from] DecodeError),
/// Missing system config in genesis block.
#[error("Missing system config in genesis block")]
MissingSystemConfigGenesis,
/// Empty transactions.
#[error("Empty transactions in payload. Block hash: {0}")]
EmptyTransactions(B256),
}

/// 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<OpTxEnvelope>,
/// Ommers/uncles header.
pub ommers: Vec<Header>,
/// Block withdrawals.
pub withdrawals: Option<Vec<Withdrawal>>,
}

impl OpBlock {
/// Converts the [OpBlock] 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<L2BlockInfo, OpBlockConversionError> {
let (l1_origin, sequence_number) = if self.header.number == rollup_config.genesis.l2.number
{
if self.header.hash_slow() != rollup_config.genesis.l2.hash {
return Err(OpBlockConversionError::InvalidGenesisHash(
rollup_config.genesis.l2.hash,
self.header.hash_slow(),
));
}
(rollup_config.genesis.l1, 0)
} else {
if self.body.is_empty() {
return Err(OpBlockConversionError::EmptyTransactions(self.header.hash_slow()));
}

let OpTxEnvelope::Deposit(ref tx) = self.body[0] else {
return Err(OpBlockConversionError::InvalidTxType(self.body[0].tx_type() as u8));
};

let l1_info = L1BlockInfoTx::decode_calldata(tx.input.as_ref())?;
(l1_info.id(), l1_info.sequence_number())
};

Ok(L2BlockInfo {
block_info: BlockInfo {
hash: self.header.hash_slow(),
number: self.header.number,
parent_hash: self.header.parent_hash,
timestamp: self.header.timestamp,
},
l1_origin,
seq_num: sequence_number,
})
}

/// Converts the [OpBlock] to a partial [SystemConfig].
pub fn to_system_config(
&self,
rollup_config: &RollupConfig,
) -> Result<SystemConfig, OpBlockConversionError> {
if self.header.number == rollup_config.genesis.l2.number {
if self.header.hash_slow() != rollup_config.genesis.l2.hash {
return Err(OpBlockConversionError::InvalidGenesisHash(
rollup_config.genesis.l2.hash,
self.header.hash_slow(),
));
}
return rollup_config
.genesis
.system_config
.ok_or(OpBlockConversionError::MissingSystemConfigGenesis);
}

if self.body.is_empty() {
return Err(OpBlockConversionError::EmptyTransactions(self.header.hash_slow()));
}
let OpTxEnvelope::Deposit(ref tx) = self.body[0] else {
return Err(OpBlockConversionError::InvalidTxType(self.body[0].tx_type() as u8));
};

let l1_info = L1BlockInfoTx::decode_calldata(tx.input.as_ref())?;
let l1_fee_scalar = match l1_info {
L1BlockInfoTx::Bedrock(L1BlockInfoBedrock { l1_fee_scalar, .. }) => l1_fee_scalar,
L1BlockInfoTx::Ecotone(L1BlockInfoEcotone {
base_fee_scalar,
blob_base_fee_scalar,
..
}) => {
// Translate Ecotone values back into encoded scalar if needed.
// We do not know if it was derived from a v0 or v1 scalar,
// but v1 is fine, a 0 blob base fee has the same effect.
let mut buf = B256::ZERO;
buf[0] = 0x01;
buf[24..28].copy_from_slice(blob_base_fee_scalar.to_be_bytes().as_ref());
buf[28..32].copy_from_slice(base_fee_scalar.to_be_bytes().as_ref());
buf.into()
}
};

Ok(SystemConfig {
batcher_address: l1_info.batcher_address(),
overhead: l1_info.l1_fee_overhead(),
scalar: l1_fee_scalar,
gas_limit: self.header.gas_limit as u64,
base_fee_scalar: None,
blob_base_fee_scalar: None,
})
}
}
1 change: 1 addition & 0 deletions crates/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern crate alloc;
mod macros;

pub mod batch;
pub mod block;
pub mod errors;
pub mod params;
pub mod pipeline;
Expand Down
Loading
Loading