arb_node/
lib.rs

1//! Arbitrum node builder.
2//!
3//! Provides the node type definition and component builders
4//! needed to launch an Arbitrum reth node.
5
6pub mod addons;
7pub mod args;
8pub mod consensus;
9pub mod genesis;
10pub mod network;
11pub mod payload;
12pub mod pool;
13pub mod producer;
14pub mod validator;
15
16use std::sync::Arc;
17
18use alloy_consensus::Header;
19use arb_payload::ArbEngineTypes;
20use arb_primitives::{ArbPrimitives, ArbTransactionSigned};
21use arb_rpc::{
22    ArbApiHandler, ArbApiServer, ArbEthApiBuilder, NitroExecutionApiServer, NitroExecutionHandler,
23};
24use reth_chainspec::ChainSpec;
25use reth_node_builder::{
26    components::{ComponentsBuilder, ConsensusBuilder, ExecutorBuilder},
27    rpc::{BasicEngineApiBuilder, BasicEngineValidatorBuilder, RpcAddOns, RpcContext},
28    BuilderContext, FullNodeComponents, FullNodeTypes, Node, NodeAdapter, NodeTypes,
29};
30use reth_provider::{
31    BlockNumReader, BlockReaderIdExt, DatabaseProviderFactory, HeaderProvider, StateProviderFactory,
32};
33use reth_rpc_eth_api::EthApiTypes;
34use reth_storage_api::{BlockWriter, CanonChainTracker, DBProvider, EthStorage};
35
36use arb_evm::ArbEvmConfig;
37
38use crate::{
39    addons::ArbPayloadValidatorBuilder, args::RollupArgs, consensus::ArbConsensus,
40    network::ArbNetworkBuilder, payload::ArbPayloadServiceBuilder, pool::ArbPoolBuilder,
41    producer::ArbBlockProducer,
42};
43
44/// Arbitrum RPC add-ons type alias.
45pub type ArbAddOns<N> = RpcAddOns<
46    N,
47    ArbEthApiBuilder,
48    ArbPayloadValidatorBuilder,
49    BasicEngineApiBuilder<ArbPayloadValidatorBuilder>,
50    BasicEngineValidatorBuilder<ArbPayloadValidatorBuilder>,
51>;
52
53/// Arbitrum storage type.
54pub type ArbStorage = EthStorage<ArbTransactionSigned>;
55
56/// Arbitrum node configuration.
57#[derive(Debug, Clone, Default)]
58pub struct ArbNode {
59    /// Rollup CLI arguments.
60    pub args: RollupArgs,
61}
62
63impl ArbNode {
64    /// Create a new Arbitrum node configuration.
65    pub fn new(args: RollupArgs) -> Self {
66        Self { args }
67    }
68
69    /// Returns a [`ComponentsBuilder`] configured for Arbitrum.
70    pub fn components<N>() -> ComponentsBuilder<
71        N,
72        ArbPoolBuilder,
73        ArbPayloadServiceBuilder,
74        ArbNetworkBuilder,
75        ArbExecutorBuilder,
76        ArbConsensusBuilder,
77    >
78    where
79        N: FullNodeTypes<Types: NodeTypes<ChainSpec = ChainSpec, Primitives = ArbPrimitives>>,
80    {
81        ComponentsBuilder::default()
82            .node_types::<N>()
83            .pool(ArbPoolBuilder)
84            .executor(ArbExecutorBuilder)
85            .payload(ArbPayloadServiceBuilder)
86            .network(ArbNetworkBuilder)
87            .consensus(ArbConsensusBuilder)
88    }
89}
90
91impl NodeTypes for ArbNode {
92    type Primitives = ArbPrimitives;
93    type ChainSpec = ChainSpec;
94    type Storage = ArbStorage;
95    type Payload = ArbEngineTypes;
96}
97
98impl<N> Node<N> for ArbNode
99where
100    N: FullNodeTypes<Types = Self>,
101    N::Provider: DatabaseProviderFactory<
102            ProviderRW: BlockWriter<
103                Block = alloy_consensus::Block<ArbTransactionSigned>,
104                Receipt = arb_primitives::ArbReceipt,
105            > + reth_storage_api::StateWriter<Receipt = arb_primitives::ArbReceipt>
106                            + reth_storage_api::TrieWriter
107                            + DBProvider,
108        > + CanonChainTracker<Header = Header>,
109{
110    type ComponentsBuilder = ComponentsBuilder<
111        N,
112        ArbPoolBuilder,
113        ArbPayloadServiceBuilder,
114        ArbNetworkBuilder,
115        ArbExecutorBuilder,
116        ArbConsensusBuilder,
117    >;
118
119    type AddOns =
120        ArbAddOns<
121            NodeAdapter<
122                N,
123                <Self::ComponentsBuilder as reth_node_builder::components::NodeComponentsBuilder<
124                    N,
125                >>::Components,
126            >,
127        >;
128
129    fn components_builder(&self) -> Self::ComponentsBuilder {
130        Self::components()
131    }
132
133    fn add_ons(&self) -> Self::AddOns {
134        RpcAddOns::new(
135            ArbEthApiBuilder::default(),
136            ArbPayloadValidatorBuilder,
137            BasicEngineApiBuilder::default(),
138            BasicEngineValidatorBuilder::default(),
139            Default::default(),
140        )
141        .extend_rpc_modules(register_arb_rpc)
142    }
143}
144
145/// Builder for the Arbitrum EVM executor component.
146#[derive(Debug, Default, Clone, Copy)]
147pub struct ArbExecutorBuilder;
148
149impl<N> ExecutorBuilder<N> for ArbExecutorBuilder
150where
151    N: FullNodeTypes<Types: NodeTypes<ChainSpec = ChainSpec, Primitives = ArbPrimitives>>,
152{
153    type EVM = ArbEvmConfig;
154
155    async fn build_evm(self, ctx: &BuilderContext<N>) -> eyre::Result<Self::EVM> {
156        Ok(ArbEvmConfig::new(ctx.chain_spec()))
157    }
158}
159
160/// Registers the `arb_` and `nitroexecution_` RPC namespaces.
161fn register_arb_rpc<N, EthApi>(ctx: RpcContext<'_, N, EthApi>) -> eyre::Result<()>
162where
163    N: FullNodeComponents<
164        Types: NodeTypes<ChainSpec = ChainSpec, Primitives = ArbPrimitives>,
165        Provider: BlockNumReader
166                      + BlockReaderIdExt
167                      + HeaderProvider
168                      + StateProviderFactory
169                      + DatabaseProviderFactory<
170            ProviderRW: BlockWriter<
171                Block = alloy_consensus::Block<ArbTransactionSigned>,
172                Receipt = arb_primitives::ArbReceipt,
173            > + reth_storage_api::StateWriter<Receipt = arb_primitives::ArbReceipt>
174                            + reth_storage_api::TrieWriter
175                            + DBProvider,
176        > + CanonChainTracker<Header = Header>,
177    >,
178    EthApi: EthApiTypes,
179{
180    let arb_api = ArbApiHandler::new(ctx.provider().clone());
181    ctx.modules.merge_configured(arb_api.into_rpc())?;
182
183    // Create the block producer with a persistence closure.
184    let chain_spec: Arc<ChainSpec> = ctx.config().chain.clone();
185    let evm_config = ArbEvmConfig::new(chain_spec.clone());
186    let persist_provider = ctx.provider().clone();
187    let persist_fn =
188        move |sealed: &reth_primitives_traits::SealedBlock<
189            alloy_consensus::Block<ArbTransactionSigned>,
190        >,
191              receipts: Vec<arb_primitives::ArbReceipt>,
192              bundle_state: revm::database::BundleState,
193              hashed_state: reth_trie_common::HashedPostState,
194              trie_updates: reth_trie_common::updates::TrieUpdates| {
195            use alloy_consensus::BlockHeader;
196            use alloy_evm::block::BlockExecutionResult;
197            use reth_execution_types::BlockExecutionOutput;
198            use reth_primitives_traits::RecoveredBlock;
199            use reth_storage_api::{StateWriter, TrieWriter};
200
201            let block_number = sealed.header().number();
202            let recovered = RecoveredBlock::new_sealed(sealed.clone(), vec![]);
203
204            let provider_rw = persist_provider
205                .database_provider_rw()
206                .map_err(|e| arb_rpc::BlockProducerError::Storage(e.to_string()))?;
207
208            // Write the block (header, body, senders).
209            provider_rw
210                .insert_block(&recovered)
211                .map_err(|e| arb_rpc::BlockProducerError::Storage(e.to_string()))?;
212
213            // Write state changes and receipts to plain state tables.
214            let exec_output = BlockExecutionOutput {
215                state: bundle_state,
216                result: BlockExecutionResult {
217                    receipts,
218                    requests: Default::default(),
219                    gas_used: sealed.header().gas_used(),
220                    blob_gas_used: 0,
221                },
222            };
223
224            provider_rw
225                .write_state(
226                    reth_storage_api::WriteStateInput::Single {
227                        outcome: &exec_output,
228                        block: block_number,
229                    },
230                    revm_database::OriginalValuesKnown::No,
231                    reth_storage_api::StateWriteConfig::default(),
232                )
233                .map_err(|e| arb_rpc::BlockProducerError::Storage(e.to_string()))?;
234
235            // Write hashed state (HashedAccounts, HashedStorages tables).
236            // Required for state_by_block_hash() to see updated state.
237            provider_rw
238                .write_hashed_state(&hashed_state.into_sorted())
239                .map_err(|e| arb_rpc::BlockProducerError::Storage(e.to_string()))?;
240
241            // Write trie intermediate nodes for incremental trie updates.
242            provider_rw
243                .write_trie_updates(trie_updates)
244                .map_err(|e| arb_rpc::BlockProducerError::Storage(e.to_string()))?;
245
246            provider_rw
247                .commit()
248                .map_err(|e| arb_rpc::BlockProducerError::Storage(e.to_string()))?;
249
250            // Update the in-memory canonical head so header lookups find the new block.
251            let sealed_header =
252                reth_primitives_traits::SealedHeader::new(sealed.header().clone(), sealed.hash());
253            persist_provider.set_canonical_head(sealed_header);
254
255            Ok(())
256        };
257
258    // Genesis block number: read from chain spec genesis header.
259    // 0 for Arbitrum Sepolia, 22207817 for Arbitrum One.
260    let genesis_block_num = chain_spec.genesis_header().number;
261
262    let block_producer = Arc::new(ArbBlockProducer::new(
263        ctx.provider().clone(),
264        chain_spec,
265        evm_config,
266        persist_fn,
267    ));
268
269    // Register the nitroexecution namespace on both the regular RPC and auth endpoints.
270    // The consensus layer connects to the auth RPC port with JWT authentication.
271    let nitro_exec =
272        NitroExecutionHandler::new(ctx.provider().clone(), block_producer, genesis_block_num);
273    let nitro_rpc = nitro_exec.into_rpc();
274    ctx.modules.merge_configured(nitro_rpc.clone())?;
275    ctx.auth_module.merge_auth_methods(nitro_rpc)?;
276
277    Ok(())
278}
279
280/// Builder for the Arbitrum consensus component.
281#[derive(Debug, Default, Clone, Copy)]
282pub struct ArbConsensusBuilder;
283
284impl<N> ConsensusBuilder<N> for ArbConsensusBuilder
285where
286    N: FullNodeTypes<Types: NodeTypes<ChainSpec = ChainSpec, Primitives = ArbPrimitives>>,
287{
288    type Consensus = Arc<ArbConsensus<ChainSpec>>;
289
290    async fn build_consensus(self, ctx: &BuilderContext<N>) -> eyre::Result<Self::Consensus> {
291        Ok(Arc::new(ArbConsensus::new(ctx.chain_spec())))
292    }
293}