diff --git a/.gitignore b/.gitignore index 4f024599..ddc787f3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /scripts/benchmark-results.* /test/ /integration_logs +genesis.json # editors .code diff --git a/Cargo.lock b/Cargo.lock index 542b3b7a..92b62b33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6525,6 +6525,7 @@ dependencies = [ "jsonrpsee 0.24.7", "metrics", "op-alloy-consensus", + "op-alloy-network", "op-alloy-rpc-types-engine", "rand 0.8.5", "reth", diff --git a/crates/op-rbuilder/Cargo.toml b/crates/op-rbuilder/Cargo.toml index d4978dc4..01e8ab5e 100644 --- a/crates/op-rbuilder/Cargo.toml +++ b/crates/op-rbuilder/Cargo.toml @@ -53,6 +53,7 @@ alloy-network.workspace = true # op op-alloy-consensus.workspace = true op-alloy-rpc-types-engine.workspace = true +op-alloy-network.workspace = true revm.workspace = true diff --git a/crates/op-rbuilder/src/integration/integration_test.rs b/crates/op-rbuilder/src/integration/integration_test.rs index 93069937..65f2221c 100644 --- a/crates/op-rbuilder/src/integration/integration_test.rs +++ b/crates/op-rbuilder/src/integration/integration_test.rs @@ -4,6 +4,9 @@ mod tests { op_rbuilder::OpRbuilderConfig, op_reth::OpRethConfig, IntegrationFramework, }; use crate::tester::{BlockGenerator, EngineApi}; + use alloy_provider::{Provider, ProviderBuilder}; + use alloy_rpc_types_eth::BlockTransactionsKind; + use op_alloy_network::Optimism; use std::path::PathBuf; use uuid::Uuid; @@ -11,7 +14,7 @@ mod tests { "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"; #[tokio::test] - async fn integration_test_chain_produces_blocks() { + async fn integration_test_chain_produces_blocks() -> eyre::Result<()> { // This is a simple test using the integration framework to test that the chain // produces blocks. let mut framework = IntegrationFramework::new().unwrap(); @@ -28,6 +31,7 @@ mod tests { .data_dir(builder_data_dir) .auth_rpc_port(1234) .network_port(1235) + .http_port(1238) .with_builder_private_key(BUILDER_PRIVATE_KEY); // create the validation reth node @@ -49,16 +53,34 @@ mod tests { let validation_api = EngineApi::new("http://localhost:1236").unwrap(); let mut generator = BlockGenerator::new(&engine_api, Some(&validation_api), false, 1); - generator.init().await.unwrap(); + generator.init().await?; + + let provider = ProviderBuilder::new() + .network::() + .on_http("http://localhost:1238".parse()?); for _ in 0..10 { - generator.generate_block().await.unwrap(); + let block_hash = generator.generate_block().await?; + + // query the block and the transactions inside the block + let block = provider + .get_block_by_hash(block_hash, BlockTransactionsKind::Hashes) + .await? + .expect("block"); + + for hash in block.transactions.hashes() { + let _ = provider + .get_transaction_receipt(hash) + .await? + .expect("receipt"); + } } // there must be a line logging the monitoring transaction op_rbuilder .find_log_line("Committed block built by builder") - .await - .unwrap(); + .await?; + + Ok(()) } } diff --git a/crates/op-rbuilder/src/integration/op_rbuilder.rs b/crates/op-rbuilder/src/integration/op_rbuilder.rs index d2a1e2ec..279889c1 100644 --- a/crates/op-rbuilder/src/integration/op_rbuilder.rs +++ b/crates/op-rbuilder/src/integration/op_rbuilder.rs @@ -51,6 +51,11 @@ impl OpRbuilderConfig { self } + pub fn http_port(mut self, port: u16) -> Self { + self.http_port = Some(port); + self + } + pub fn with_builder_private_key(mut self, private_key: &str) -> Self { self.builder_private_key = Some(private_key.to_string()); self diff --git a/crates/op-rbuilder/src/tester/mod.rs b/crates/op-rbuilder/src/tester/mod.rs index 9edd2791..592f86ef 100644 --- a/crates/op-rbuilder/src/tester/mod.rs +++ b/crates/op-rbuilder/src/tester/mod.rs @@ -6,7 +6,7 @@ use alloy_primitives::Address; use alloy_primitives::Bytes; use alloy_primitives::TxKind; use alloy_primitives::B256; -use alloy_primitives::U256; +use alloy_primitives::{hex, U256}; use alloy_rpc_types_engine::ExecutionPayloadV1; use alloy_rpc_types_engine::ExecutionPayloadV2; use alloy_rpc_types_engine::PayloadAttributes; @@ -181,6 +181,11 @@ pub async fn generate_genesis(output: Option) -> eyre::Result<()> { Ok(()) } +// L1 block info for OP mainnet block 124665056 (stored in input of tx at index 0) +// +// https://optimistic.etherscan.io/tx/0x312e290cf36df704a2217b015d6455396830b0ce678b860ebfcc30f41403d7b1 +const FJORD_DATA: &[u8] = &hex!("440a5e200000146b000f79c500000000000000040000000066d052e700000000013ad8a3000000000000000000000000000000000000000000000000000000003ef1278700000000000000000000000000000000000000000000000000000000000000012fdf87b89884a61e74b322bbcf60386f543bfae7827725efaaf0ab1de2294a590000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"); + /// A system that continuously generates blocks using the engine API pub struct BlockGenerator<'a> { engine_api: &'a EngineApi, @@ -308,6 +313,40 @@ impl<'a> BlockGenerator<'a> { .as_secs(); let timestamp = timestamp + self.block_time_secs; + // Add L1 block info as the first transaction in every L2 block + // This deposit transaction contains L1 block metadata required by the L2 chain + // Currently using hardcoded data from L1 block 124665056 + // If this info is not provided, Reth cannot decode the receipt for any transaction + // in the block since it also includes this info as part of the result. + // It does not matter if the to address (4200000000000000000000000000000000000015) is + // not deployed on the L2 chain since Reth queries the block to get the info and not the contract. + let block_info_tx: Bytes = { + let deposit_tx = TxDeposit { + source_hash: B256::default(), + from: address!("DeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001"), + to: TxKind::Call(address!("4200000000000000000000000000000000000015")), + mint: None, + value: U256::default(), + gas_limit: 210000, + is_system_transaction: true, + input: FJORD_DATA.into(), + }; + + // Create a temporary signer for the deposit + let signer = Signer::random(); + let signed_tx = signer.sign_tx(OpTypedTransaction::Deposit(deposit_tx))?; + signed_tx.encoded_2718().into() + }; + + let transactions = if let Some(transactions) = transactions { + // prepend the block info transaction + let mut all_transactions = vec![block_info_tx]; + all_transactions.extend(transactions.into_iter()); + all_transactions + } else { + vec![block_info_tx] + }; + let result = self .engine_api .update_forkchoice( @@ -321,7 +360,7 @@ impl<'a> BlockGenerator<'a> { prev_randao: B256::ZERO, suggested_fee_recipient: Default::default(), }, - transactions, + transactions: Some(transactions), no_tx_pool: Some(self.no_tx_pool), gas_limit: Some(10000000000), eip_1559_params: None,