Skip to content
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
8 changes: 1 addition & 7 deletions crates/engine-api/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
//! by the sequencer to produce new blocks.

use crate::EngineApiResult;
use alloy_primitives::B256;
use morph_payload_types::{AssembleL2BlockParams, ExecutableL2Data, GenericResponse, SafeL2Data};
use morph_primitives::MorphHeader;

Expand Down Expand Up @@ -66,16 +65,11 @@ pub trait MorphL2EngineApi: Send + Sync {
/// # Arguments
///
/// * `data` - The block data to import
/// * `batch_hash` - Optional batch hash if this block is a batch point
///
/// # Returns
///
/// Returns `Ok(())` on success.
async fn new_l2_block(
&self,
data: ExecutableL2Data,
batch_hash: Option<B256>,
) -> EngineApiResult<()>;
async fn new_l2_block(&self, data: ExecutableL2Data) -> EngineApiResult<()>;

/// Import a safe L2 block from derivation.
///
Expand Down
36 changes: 4 additions & 32 deletions crates/engine-api/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,16 +291,11 @@ where
Ok(GenericResponse { success: true })
}

async fn new_l2_block(
&self,
data: ExecutableL2Data,
batch_hash: Option<B256>,
) -> EngineApiResult<()> {
async fn new_l2_block(&self, data: ExecutableL2Data) -> EngineApiResult<()> {
tracing::info!(
target: "morph::engine",
block_number = data.number,
block_hash = %data.hash,
?batch_hash,
"importing new L2 block"
);

Expand Down Expand Up @@ -373,9 +368,8 @@ where
}

let executed = cached.executed.into_executed_payload();
let recovered_with_batch =
self.apply_batch_hash(executed.recovered_block().clone(), batch_hash);
let recovered_with_data = apply_executable_data_overrides(recovered_with_batch, &data)?;
let recovered_with_data =
apply_executable_data_overrides(executed.recovered_block().clone(), &data)?;
let computed_hash = recovered_with_data.hash();
if computed_hash != data.hash {
return Err(MorphEngineApiError::ValidationFailed(format!(
Expand Down Expand Up @@ -462,8 +456,7 @@ where
self.cache_built_payload(&built_payload);

// 3. Import the block
self.new_l2_block(executable_data.clone(), data.batch_hash)
.await?;
self.new_l2_block(executable_data.clone()).await?;

// 4. Return the persisted header.
let header = self
Expand Down Expand Up @@ -602,22 +595,6 @@ impl<Provider> RealMorphL2EngineApi<Provider> {
);
}

fn apply_batch_hash(
&self,
recovered_block: RecoveredBlock<Block>,
batch_hash: Option<B256>,
) -> RecoveredBlock<Block> {
let Some(batch_hash) = batch_hash else {
return recovered_block;
};
let (block, senders) = recovered_block.split();
let block = block.map_header(|mut header: MorphHeader| {
header.batch_hash = batch_hash;
header
});
RecoveredBlock::new_unhashed(block, senders)
}

fn current_head(&self) -> EngineApiResult<InMemoryHead>
where
Provider: BlockReader,
Expand Down Expand Up @@ -728,7 +705,6 @@ impl MorphL2EngineApi for StubMorphL2EngineApi {
async fn new_l2_block(
&self,
_data: morph_payload_types::ExecutableL2Data,
_batch_hash: Option<alloy_primitives::B256>,
) -> crate::EngineApiResult<()> {
tracing::warn!(target: "morph::engine", "new_l2_block called on stub implementation");
Err(crate::MorphEngineApiError::Internal(
Expand Down Expand Up @@ -790,7 +766,6 @@ mod tests {

let target_header = MorphHeader {
next_l1_msg_index: 42,
batch_hash: B256::ZERO,
inner: Header {
parent_hash: B256::from([0x11; 32]),
beneficiary: Address::from([0x22; 20]),
Expand Down Expand Up @@ -868,7 +843,6 @@ mod tests {
fn test_apply_executable_data_overrides_sets_header_fields_exactly() {
let source_header = MorphHeader {
next_l1_msg_index: 1,
batch_hash: B256::from([0xab; 32]),
inner: Header {
parent_hash: B256::from([0x01; 32]),
beneficiary: Address::from([0x02; 20]),
Expand All @@ -883,7 +857,6 @@ mod tests {
..Default::default()
},
};
let source_batch_hash = source_header.batch_hash;
let recovered = recovered_with_header(source_header);
let data = ExecutableL2Data {
parent_hash: B256::from([0x11; 32]),
Expand Down Expand Up @@ -920,7 +893,6 @@ mod tests {
);
assert_eq!(header.inner.logs_bloom.as_slice(), data.logs_bloom.as_ref());
assert_eq!(header.next_l1_msg_index, data.next_l1_message_index);
assert_eq!(header.batch_hash, source_batch_hash);
}

#[test]
Expand Down
22 changes: 6 additions & 16 deletions crates/engine-api/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
//! allowing the sequencer to interact with the execution layer via RPC.

use crate::{EngineApiResult, api::MorphL2EngineApi};
use alloy_primitives::B256;
use jsonrpsee::{RpcModule, core::RpcResult, proc_macros::rpc};
use morph_payload_types::{AssembleL2BlockParams, ExecutableL2Data, GenericResponse, SafeL2Data};
use morph_primitives::MorphHeader;
Expand Down Expand Up @@ -40,8 +39,7 @@ pub trait MorphL2EngineRpc {
///
/// `engine_newL2Block`
#[method(name = "newL2Block")]
async fn new_l2_block(&self, data: ExecutableL2Data, batch_hash: Option<B256>)
-> RpcResult<()>;
async fn new_l2_block(&self, data: ExecutableL2Data) -> RpcResult<()>;

/// Import a safe L2 block from derivation.
///
Expand Down Expand Up @@ -106,26 +104,18 @@ where
})
}

async fn new_l2_block(
&self,
data: ExecutableL2Data,
batch_hash: Option<B256>,
) -> RpcResult<()> {
async fn new_l2_block(&self, data: ExecutableL2Data) -> RpcResult<()> {
tracing::info!(
target: "morph::engine",
block_number = data.number,
block_hash = %data.hash,
?batch_hash,
"importing new L2 block"
);

self.inner
.new_l2_block(data, batch_hash)
.await
.map_err(|e| {
tracing::error!(target: "morph::engine", error = %e, "failed to import L2 block");
e.into()
})
self.inner.new_l2_block(data).await.map_err(|e| {
tracing::error!(target: "morph::engine", error = %e, "failed to import L2 block");
e.into()
})
}

async fn new_safe_l2_block(&self, data: SafeL2Data) -> RpcResult<MorphHeader> {
Expand Down
8 changes: 3 additions & 5 deletions crates/evm/src/assemble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::sync::Arc;
///
/// This assembler builds Morph blocks from the execution output.
/// Unlike `EthBlockAssembler`, it produces `MorphHeader` with proper
/// L2-specific fields (next_l1_msg_index, batch_hash).
/// L2-specific fields (next_l1_msg_index).
#[derive(Debug, Clone)]
pub struct MorphBlockAssembler {
/// Chain specification
Expand Down Expand Up @@ -48,12 +48,11 @@ impl BlockAssembler<MorphEvmConfig> for MorphBlockAssembler {
/// 2. **Build Logs Bloom**: Aggregates logs from all receipts
/// 3. **Check Hardforks**: Determines if EIP-1559 (London) is active for base fee
/// 4. **Build Header**: Creates standard Ethereum header fields
/// 5. **Wrap in MorphHeader**: Adds L2-specific fields (next_l1_msg_index, batch_hash)
/// 5. **Wrap in MorphHeader**: Adds L2-specific fields (next_l1_msg_index)
/// 6. **Build Block Body**: Combines transactions and empty ommers
///
/// # L2-Specific Fields
/// - `next_l1_msg_index`: Inherited from parent block
/// - `batch_hash`: Set to default (will be filled by payload builder)
///
/// # Arguments
/// * `input` - Contains execution context, transactions, receipts, and state root
Expand Down Expand Up @@ -117,11 +116,10 @@ impl BlockAssembler<MorphEvmConfig> for MorphBlockAssembler {
};

// Wrap in MorphHeader with L2-specific fields
// Note: next_l1_msg_index and batch_hash will be set by the payload builder
// Note: next_l1_msg_index will be updated by the payload builder
let header = MorphHeader {
inner,
next_l1_msg_index: parent.header().next_l1_msg_index,
batch_hash: Default::default(),
};

Ok(alloy_consensus::Block::new(
Expand Down
1 change: 0 additions & 1 deletion crates/evm/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ mod tests {
..Default::default()
},
next_l1_msg_index: 0,
batch_hash: B256::ZERO,
};

let body = BlockBody {
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
//! requiring a custom receipt builder.
//!
//! 4. **L2-Specific Header**: `MorphHeader` extends standard header with
//! `next_l1_msg_index` and `batch_hash` fields.
//! the `next_l1_msg_index` field.
//!
//! 5. **No Blob Transactions**: EIP-4844 blob transactions are not supported.

Expand Down
1 change: 0 additions & 1 deletion crates/payload/builder/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,6 @@ where
let (mut morph_block, senders) = block.split();
morph_block = morph_block.map_header(|mut header: MorphHeader| {
header.next_l1_msg_index = info.next_l1_message_index;
// batch_hash remains B256::ZERO - it will be set by the batch submitter
header
});
block = RecoveredBlock::new_unhashed(morph_block, senders);
Expand Down
15 changes: 1 addition & 14 deletions crates/payload/types/src/safe_l2_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! This type is used for NewSafeL2Block in the derivation pipeline.

use alloy_primitives::{B256, Bytes};
use alloy_primitives::Bytes;

/// Safe L2 block data, used for NewSafeL2Block (derivation).
///
Expand Down Expand Up @@ -38,10 +38,6 @@ pub struct SafeL2Data {
/// RLP-encoded transactions.
#[serde(default)]
pub transactions: Vec<Bytes>,

/// Optional batch hash for batch association.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub batch_hash: Option<B256>,
}

impl SafeL2Data {
Expand All @@ -59,11 +55,6 @@ impl SafeL2Data {
pub fn transaction_count(&self) -> usize {
self.transactions.len()
}

/// Returns true if this block is associated with a batch.
pub fn has_batch(&self) -> bool {
self.batch_hash.is_some()
}
}

#[cfg(test)]
Expand All @@ -77,7 +68,6 @@ mod tests {
assert_eq!(data.gas_limit, 0);
assert!(data.base_fee_per_gas.is_none());
assert!(!data.has_transactions());
assert!(!data.has_batch());
}

#[test]
Expand All @@ -88,7 +78,6 @@ mod tests {
base_fee_per_gas: Some(1_000_000_000),
timestamp: 1234567890,
transactions: vec![Bytes::from(vec![0x01, 0x02])],
batch_hash: Some(B256::random()),
};

let json = serde_json::to_string(&data).expect("serialize");
Expand All @@ -105,13 +94,11 @@ mod tests {
base_fee_per_gas: None,
timestamp: 1234567890,
transactions: vec![],
batch_hash: None,
};

let json = serde_json::to_string(&data).expect("serialize");
// Optional fields should not appear in JSON when None
assert!(!json.contains("baseFeePerGas"));
assert!(!json.contains("batchHash"));

let decoded: SafeL2Data = serde_json::from_str(&json).expect("deserialize");
assert_eq!(data, decoded);
Expand Down
Loading