diff --git a/Cargo.lock b/Cargo.lock index 15101e18c0b2..4ae53c03f87e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8112,6 +8112,8 @@ dependencies = [ "secp256k1", "serde", "serde_json", + "taiko-reth-primitives", + "taiko-reth-provider", "tempfile", "thiserror", "tokio", @@ -8133,6 +8135,7 @@ dependencies = [ "reth-rpc-types", "serde", "serde_json", + "taiko-reth-primitives", ] [[package]] diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index c91a436a137f..deae242d54a2 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -60,7 +60,6 @@ reth-node-ethereum.workspace = true reth-node-optimism = { workspace = true, optional = true, features = [ "optimism", ] } -# reth-node-taiko = { workspace = true, optional = true } reth-node-core.workspace = true reth-db-common.workspace = true reth-node-builder.workspace = true diff --git a/crates/engine-primitives/Cargo.toml b/crates/engine-primitives/Cargo.toml index b44a4a8aa4e7..eb17a9bcf706 100644 --- a/crates/engine-primitives/Cargo.toml +++ b/crates/engine-primitives/Cargo.toml @@ -16,4 +16,7 @@ reth-chainspec.workspace = true reth-payload-primitives.workspace = true # misc -serde.workspace = true \ No newline at end of file +serde.workspace = true + +[features] +taiko = ["reth-payload-primitives/taiko"] diff --git a/crates/payload/primitives/Cargo.toml b/crates/payload/primitives/Cargo.toml index ad63d46d8c72..dc7f244bb0bb 100644 --- a/crates/payload/primitives/Cargo.toml +++ b/crates/payload/primitives/Cargo.toml @@ -24,4 +24,7 @@ tokio = { workspace = true, features = ["sync"] } # misc thiserror.workspace = true -serde.workspace = true \ No newline at end of file +serde.workspace = true + +[features] +taiko = ["reth-transaction-pool/taiko"] diff --git a/crates/rpc/rpc-api/Cargo.toml b/crates/rpc/rpc-api/Cargo.toml index 5374c46e4898..55ce4fb69c2c 100644 --- a/crates/rpc/rpc-api/Cargo.toml +++ b/crates/rpc/rpc-api/Cargo.toml @@ -18,6 +18,9 @@ reth-rpc-types.workspace = true reth-engine-primitives.workspace = true reth-network-peers.workspace = true +# taiko +taiko-reth-primitives = { workspace = true, optional = true } + # misc alloy-dyn-abi = { workspace = true, features = ["eip712"] } jsonrpsee = { workspace = true, features = ["server", "macros"] } @@ -28,3 +31,8 @@ serde_json.workspace = true [features] client = ["jsonrpsee/client", "jsonrpsee/async-client"] +taiko = [ + "reth-primitives/taiko", + "reth-engine-primitives/taiko", + "dep:taiko-reth-primitives", +] diff --git a/crates/rpc/rpc-api/src/lib.rs b/crates/rpc/rpc-api/src/lib.rs index 82af34a86d73..5e747678eaaf 100644 --- a/crates/rpc/rpc-api/src/lib.rs +++ b/crates/rpc/rpc-api/src/lib.rs @@ -58,6 +58,9 @@ pub mod servers { validation::BlockSubmissionValidationApiServer, web3::Web3ApiServer, }; + + #[cfg(feature = "taiko")] + pub use crate::taiko::TaikoApiServer; } /// re-export of all client traits @@ -86,4 +89,10 @@ pub mod clients { validation::BlockSubmissionValidationApiClient, web3::Web3ApiClient, }; + + #[cfg(feature = "taiko")] + pub use crate::taiko::TaikoApiClient; } + +#[cfg(feature = "taiko")] +mod taiko; diff --git a/crates/rpc/rpc-api/src/taiko.rs b/crates/rpc/rpc-api/src/taiko.rs new file mode 100644 index 000000000000..5310bb5e72ad --- /dev/null +++ b/crates/rpc/rpc-api/src/taiko.rs @@ -0,0 +1,37 @@ +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use reth_primitives::Address; +use reth_rpc_types::Transaction; +use taiko_reth_primitives::L1Origin; + +/// Taiko rpc interface. +#[cfg_attr(not(feature = "client"), rpc(server, namespace = "taiko"))] +#[cfg_attr(feature = "client", rpc(server, client, namespace = "taiko"))] +pub trait TaikoApi { + /// HeadL1Origin returns the latest L2 block's corresponding L1 origin. + #[method(name = "headL1Origin")] + async fn head_l1_origin(&self) -> RpcResult>; + + /// L1OriginByID returns the L2 block's corresponding L1 origin. + #[method(name = "l1OriginByID")] + async fn l1_origin_by_id(&self, block_id: u64) -> RpcResult>; + + /// GetL2ParentHeaders + #[method(name = "getL2ParentHeaders")] + async fn get_l2_parent_headers(&self, block_id: u64) + -> RpcResult>; + + /// Returns the details of all transactions currently pending for inclusion in the next + /// block(s), as well as the ones that are being scheduled for future execution only. + /// + /// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more details + #[method(name = "content")] + async fn txpool_content( + &self, + beneficiary: Address, + base_fee: u64, + block_max_gas_limit: u64, + max_bytes_per_tx_list: u64, + locals: Vec, + max_transactions_lists: u64, + ) -> RpcResult>>; +} diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index a0c1e1b5d8aa..9340cd6abf3f 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -34,6 +34,10 @@ reth-execution-types.workspace = true reth-evm-optimism = { workspace = true, optional = true } +# taiko +taiko-reth-provider = { workspace = true, optional = true } +taiko-reth-primitives = { workspace = true, optional = true } + # eth alloy-rlp.workspace = true alloy-dyn-abi = { workspace = true, features = ["eip712"] } @@ -104,5 +108,8 @@ optimism = [ taiko = [ "reth-primitives/taiko", "reth-revm/taiko", + "reth-rpc-api/taiko", "reth-rpc-engine-api/taiko", + "dep:taiko-reth-provider", + "dep:taiko-reth-primitives", ] diff --git a/crates/rpc/rpc/src/lib.rs b/crates/rpc/rpc/src/lib.rs index 17dc8fcb809f..69c4c09327ac 100644 --- a/crates/rpc/rpc/src/lib.rs +++ b/crates/rpc/rpc/src/lib.rs @@ -55,3 +55,8 @@ pub use trace::TraceApi; pub use txpool::TxPoolApi; pub use web3::Web3Api; pub mod result; + +#[cfg(feature = "taiko")] +mod taiko; +#[cfg(feature = "taiko")] +pub use taiko::TaikoApi; diff --git a/crates/rpc/rpc/src/taiko.rs b/crates/rpc/rpc/src/taiko.rs new file mode 100644 index 000000000000..cfdca52d7a58 --- /dev/null +++ b/crates/rpc/rpc/src/taiko.rs @@ -0,0 +1,163 @@ +use std::collections::BTreeMap; + +use crate::result::internal_rpc_err; +use alloy_primitives::Address; +use async_trait::async_trait; +use jsonrpsee::core::RpcResult; +use reth_provider::BlockReader; +use reth_rpc_api::TaikoApiServer; +use reth_rpc_types::{txpool::TxpoolContent, Transaction}; +use reth_transaction_pool::{AllPoolTransactions, PoolTransaction, TransactionPool}; +use taiko_reth_primitives::L1Origin; +use taiko_reth_provider::L1OriginReader; + +/// Taiko API. +#[derive(Debug)] +pub struct TaikoApi { + provider: Provider, + pool: Pool, +} + +impl TaikoApi { + /// Creates a new instance of `Taiko`. + pub const fn new(provider: Provider, pool: Pool) -> Self { + Self { provider, pool } + } +} + +impl TaikoApi +where + Provider: BlockReader + L1OriginReader + 'static, + Pool: TransactionPool + 'static, +{ + fn content(&self) -> TxpoolContent { + #[inline] + fn insert( + tx: &T, + content: &mut BTreeMap>, + ) { + content.entry(tx.sender()).or_default().insert( + tx.nonce().to_string(), + reth_rpc_types_compat::transaction::from_recovered(tx.to_recovered_transaction()), + ); + } + + let AllPoolTransactions { pending, queued } = self.pool.all_transactions(); + + let mut content = TxpoolContent::default(); + for pending in pending { + insert(&pending.transaction, &mut content.pending); + } + for queued in queued { + insert(&queued.transaction, &mut content.queued); + } + + content + } + + fn get_txs( + &self, + locals: &[String], + ) -> ( + BTreeMap>, + BTreeMap>, + ) { + self.content() + .pending + .into_iter() + .map(|(address, txs)| (address, txs, locals.contains(&address.to_string()))) + .fold( + ( + BTreeMap::>::new(), + BTreeMap::>::new(), + ), + |(mut l, mut r), (address, txs, is_local)| { + if is_local { + l.insert(address, txs); + } else { + r.insert(address, txs); + } + + (l, r) + }, + ) + } + + async fn commit_txs(&self, locals: &[String]) -> RpcResult> { + let (_local_txs, _remote_txs) = self.get_txs(&locals); + Ok(vec![]) + } +} + +#[async_trait] +impl TaikoApiServer for TaikoApi +where + Provider: BlockReader + L1OriginReader + 'static, + Pool: TransactionPool + 'static, +{ + /// HeadL1Origin returns the latest L2 block's corresponding L1 origin. + // #[cfg(feature = "taiko")] + async fn head_l1_origin(&self) -> RpcResult> { + self.provider.get_head_l1_origin().map_err(|_| { + internal_rpc_err("taiko_headL1Origin failed to read latest l2 block's L1 origin") + }) + } + + /// L1OriginByID returns the L2 block's corresponding L1 origin. + // #[cfg(feature = "taiko")] + async fn l1_origin_by_id(&self, block_id: u64) -> RpcResult> { + self.provider.get_l1_origin(block_id).map_err(|_| { + internal_rpc_err("taiko_l1OriginByID failed to read L1 origin by block id") + }) + } + + /// GetL2ParentHeaders + // #[cfg(feature = "taiko")] + async fn get_l2_parent_headers( + &self, + block_id: u64, + ) -> RpcResult> { + let start = if block_id > 256 { block_id - 255 } else { 0 }; + let mut headers = Vec::with_capacity(256); + + for id in start..=block_id { + let option = self.provider.header_by_number(id).map_err(|_| { + internal_rpc_err("taiko_getL2ParentHeaders failed to read header by number") + })?; + let Some(header) = option else { + return Err(internal_rpc_err( + "taiko_getL2ParentHeaders failed to find parent header by number", + )); + }; + headers.push(header); + } + + Ok(headers) + } + + // TODO:(petar) implement this function + /// TxPoolContent retrieves the transaction pool content with the given upper limits. + async fn txpool_content( + &self, + beneficiary: Address, + base_fee: u64, + block_max_gas_limit: u64, + max_bytes_per_tx_list: u64, + locals: Vec, + max_transactions_lists: u64, + ) -> RpcResult>> { + let mut tx_lists = Vec::with_capacity(max_transactions_lists as usize); + + for _ in 0..max_transactions_lists { + let tx_list = self.commit_txs(&locals).await?; + + if tx_list.is_empty() { + break; + } + + tx_lists.push(tx_list); + } + + Ok(tx_lists) + } +} diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 6cf456665bdf..ab276dc96e1e 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -74,5 +74,6 @@ rand.workspace = true [features] optimism = ["reth-primitives/optimism", "reth-execution-types/optimism"] +taiko = ["reth-evm/taiko"] serde = ["reth-execution-types/serde"] test-utils = ["alloy-rlp", "reth-db/test-utils", "reth-nippy-jar/test-utils"] diff --git a/crates/taiko/payload/builder/src/builder.rs b/crates/taiko/payload/builder/src/builder.rs index e1a768b439a8..4f60052ab246 100644 --- a/crates/taiko/payload/builder/src/builder.rs +++ b/crates/taiko/payload/builder/src/builder.rs @@ -33,7 +33,7 @@ use taiko_reth_evm::{ eip6110::parse_deposits_from_receipts, }; use taiko_reth_primitives::L1Origin; -use taiko_reth_provider::l1_origin::L1OriginWriter; +use taiko_reth_provider::L1OriginWriter; use tracing::{debug, trace, warn}; /// Taiko's payload builder diff --git a/crates/taiko/storage/src/lib.rs b/crates/taiko/storage/src/lib.rs index d7c08df85825..e8c80986f450 100644 --- a/crates/taiko/storage/src/lib.rs +++ b/crates/taiko/storage/src/lib.rs @@ -1,3 +1,3 @@ //! Collection of traits and trait implementations for taiko database operations. pub mod l1_origin; -// Compare this snippet from crates/taiko/storage/src/lib.rs: +pub use l1_origin::*; diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 77edd6f3e541..3cdd9bc1bd71 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -70,7 +70,12 @@ serde_json.workspace = true default = ["serde"] serde = ["dep:serde"] test-utils = ["rand", "paste", "serde"] -arbitrary = ["proptest", "reth-primitives/arbitrary", "proptest-arbitrary-interop"] +arbitrary = [ + "proptest", + "reth-primitives/arbitrary", + "proptest-arbitrary-interop", +] +taiko = ["reth-provider/taiko"] [[bench]] name = "truncate"