diff --git a/Cargo.lock b/Cargo.lock index 730f75b..51b7760 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2925,7 +2925,7 @@ dependencies = [ "k256", "log", "rand 0.8.5", - "secp256k1 0.30.0", + "secp256k1", "serde", "sha3", "zeroize", @@ -6531,7 +6531,7 @@ dependencies = [ "reth-static-file-types", "reth-trie", "reth-trie-db", - "secp256k1 0.30.0", + "secp256k1", "serde", "serde_json", "tokio", @@ -6562,7 +6562,7 @@ dependencies = [ "libc", "rand 0.8.5", "reth-fs-util", - "secp256k1 0.30.0", + "secp256k1", "serde", "thiserror 2.0.12", ] @@ -6778,7 +6778,7 @@ dependencies = [ "reth-net-nat", "reth-network-peers", "schnellru", - "secp256k1 0.30.0", + "secp256k1", "serde", "thiserror 2.0.12", "tokio", @@ -6804,7 +6804,7 @@ dependencies = [ "reth-ethereum-forks", "reth-metrics", "reth-network-peers", - "secp256k1 0.30.0", + "secp256k1", "thiserror 2.0.12", "tokio", "tracing", @@ -6825,7 +6825,7 @@ dependencies = [ "reth-network-peers", "reth-tokio-util", "schnellru", - "secp256k1 0.30.0", + "secp256k1", "serde", "serde_with", "thiserror 2.0.12", @@ -6936,7 +6936,7 @@ dependencies = [ "pin-project", "rand 0.8.5", "reth-network-peers", - "secp256k1 0.30.0", + "secp256k1", "sha2 0.10.8", "sha3", "thiserror 2.0.12", @@ -7255,7 +7255,7 @@ dependencies = [ "reth-primitives-traits", "reth-zstd-compressors", "revm-context", - "secp256k1 0.30.0", + "secp256k1", "serde", "serde_with", ] @@ -7560,7 +7560,7 @@ dependencies = [ "reth-transaction-pool", "rustc-hash 2.1.1", "schnellru", - "secp256k1 0.30.0", + "secp256k1", "serde", "smallvec", "thiserror 2.0.12", @@ -7624,7 +7624,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "enr", - "secp256k1 0.30.0", + "secp256k1", "serde_with", "thiserror 2.0.12", "tokio", @@ -7743,7 +7743,7 @@ dependencies = [ "reth-tokio-util", "reth-tracing", "reth-transaction-pool", - "secp256k1 0.30.0", + "secp256k1", "tokio", "tokio-stream", "tracing", @@ -7788,7 +7788,7 @@ dependencies = [ "reth-storage-errors", "reth-tracing", "reth-transaction-pool", - "secp256k1 0.30.0", + "secp256k1", "serde", "shellexpand", "strum 0.27.1", @@ -8027,7 +8027,7 @@ dependencies = [ "revm-primitives", "revm-state", "scroll-alloy-consensus", - "secp256k1 0.30.0", + "secp256k1", "serde", "serde_with", "thiserror 2.0.12", @@ -8671,7 +8671,7 @@ dependencies = [ "revm-scroll", "scroll-alloy-consensus", "scroll-alloy-evm", - "secp256k1 0.30.0", + "secp256k1", "serde", ] @@ -8929,7 +8929,7 @@ dependencies = [ "rand 0.9.1", "reth-ethereum-primitives", "reth-primitives-traits", - "secp256k1 0.30.0", + "secp256k1", ] [[package]] @@ -9284,7 +9284,7 @@ dependencies = [ "p256", "revm-primitives", "ripemd", - "secp256k1 0.30.0", + "secp256k1", "sha2 0.10.8", ] @@ -9502,7 +9502,6 @@ dependencies = [ "scroll-migration", "scroll-network", "scroll-wire", - "secp256k1 0.29.1", "serde_json", "tokio", "tracing", @@ -9532,9 +9531,14 @@ dependencies = [ name = "rollup-node-manager" version = "0.0.1" dependencies = [ + "alloy-chains", + "alloy-consensus", "alloy-eips", + "alloy-primitives", + "alloy-provider", "alloy-rpc-types-engine", "futures", + "reth-primitives-traits", "reth-scroll-primitives", "reth-tokio-util", "rollup-node-indexer", @@ -9542,6 +9546,7 @@ dependencies = [ "rollup-node-providers", "rollup-node-sequencer", "rollup-node-watcher", + "scroll-alloy-consensus", "scroll-alloy-hardforks", "scroll-alloy-provider", "scroll-alloy-rpc-types-engine", @@ -9550,7 +9555,6 @@ dependencies = [ "scroll-engine", "scroll-network", "scroll-wire", - "secp256k1 0.29.1", "tokio", "tokio-stream", "tracing", @@ -10196,7 +10200,6 @@ dependencies = [ "reth-scroll-primitives", "reth-storage-api", "scroll-wire", - "secp256k1 0.29.1", "tokio", "tokio-stream", "tracing", @@ -10213,7 +10216,6 @@ dependencies = [ "reth-network", "reth-network-api", "reth-scroll-primitives", - "secp256k1 0.29.1", "tokio", "tokio-stream", "tracing", @@ -10399,17 +10401,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" -dependencies = [ - "rand 0.8.5", - "secp256k1-sys", - "serde", -] - [[package]] name = "secp256k1" version = "0.30.0" diff --git a/Cargo.toml b/Cargo.toml index 8fe5c48..ee22279 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,6 +143,7 @@ reth-network = { git = "https://github.com/scroll-tech/reth.git", default-featur reth-network-api = { git = "https://github.com/scroll-tech/reth.git", default-features = false } reth-network-peers = { git = "https://github.com/scroll-tech/reth.git", default-features = false } reth-primitives = { git = "https://github.com/scroll-tech/reth.git", default-features = false } +reth-primitives-traits = { git = "https://github.com/scroll-tech/reth.git", default-features = false } reth-provider = { git = "https://github.com/scroll-tech/reth.git", default-features = false } reth-tasks = { git = "https://github.com/scroll-tech/reth.git" } reth-tokio-util = { git = "https://github.com/scroll-tech/reth.git", default-features = false } @@ -180,7 +181,6 @@ futures = { version = "0.3", default-features = false } parking_lot = "0.12" rand = { version = "0.9" } reqwest = "0.12" -secp256k1 = { version = "0.29", default-features = false } serde = { version = "1.0" } sea-orm = { version = "1.1.0" } thiserror = "2.0" diff --git a/bin/rollup/Cargo.toml b/bin/rollup/Cargo.toml index c20076a..2c0ba6e 100644 --- a/bin/rollup/Cargo.toml +++ b/bin/rollup/Cargo.toml @@ -9,8 +9,8 @@ exclude.workspace = true [dependencies] # alloy alloy-chains.workspace = true -alloy-provider.workspace = true alloy-primitives.workspace = true +alloy-provider.workspace = true alloy-rpc-types-engine.workspace = true alloy-rpc-client.workspace = true alloy-transport.workspace = true @@ -58,7 +58,6 @@ rollup-node-watcher.workspace = true clap = { version = "4", features = ["derive", "env"] } eyre.workspace = true reqwest = { version = "0.12", default-features = false } -secp256k1 = { workspace = true, features = ["global-context", "recovery"] } tokio = { workspace = true, features = ["full"] } tracing.workspace = true @@ -110,7 +109,6 @@ serde = [ "scroll-engine/serde", "scroll-network/serde", "scroll-wire/serde", - "secp256k1/serde", "rollup-node-manager/serde", "alloy-chains/serde", "reth-transaction-pool/serde", diff --git a/bin/rollup/src/args.rs b/bin/rollup/src/args.rs index 591639d..067e719 100644 --- a/bin/rollup/src/args.rs +++ b/bin/rollup/src/args.rs @@ -6,6 +6,9 @@ use std::path::PathBuf; /// A struct that represents the arguments for the rollup node. #[derive(Debug, clap::Args)] pub struct ScrollRollupNodeArgs { + /// Whether the rollup node should be run in test mode. + #[arg(long)] + pub test: bool, /// A bool to represent if new blocks should be bridged from the eth wire protocol to the /// scroll wire protocol. #[arg(long, default_value_t = false)] diff --git a/bin/rollup/src/import.rs b/bin/rollup/src/import.rs index 5e4b350..c4e7486 100644 --- a/bin/rollup/src/import.rs +++ b/bin/rollup/src/import.rs @@ -1,8 +1,8 @@ +use alloy_primitives::Signature; use reth_network::import::{BlockImport as RethBlockImport, NewBlockEvent}; use reth_network_peers::PeerId; use reth_scroll_primitives::ScrollBlock; use scroll_network::NewBlockWithPeer; -use secp256k1::ecdsa::Signature; use std::{ sync::Arc, task::{Context, Poll}, @@ -10,7 +10,7 @@ use std::{ use tokio::sync::mpsc::UnboundedSender; use tracing::{trace, warn}; -const ECDSA_SIGNATURE_LEN: usize = 64; +const ECDSA_SIGNATURE_LEN: usize = 65; /// A block import implementation for the eth-wire protocol that sends block to the scroll-wire /// protocol. @@ -42,7 +42,7 @@ impl BridgeBlockImport { if let Some(signature) = extra_data .len() .checked_sub(ECDSA_SIGNATURE_LEN) - .and_then(|i| Signature::from_compact(&extra_data[i..]).ok()) + .and_then(|i| Signature::from_raw(&extra_data[i..]).ok()) { let block = block.block.clone(); trace!(target: "scroll::bridge::import", peer_id = %peer_id, block = ?block.hash_slow(), "Received new block from eth-wire protocol"); diff --git a/bin/rollup/src/network.rs b/bin/rollup/src/network.rs index 200f2f1..b3f5b4d 100644 --- a/bin/rollup/src/network.rs +++ b/bin/rollup/src/network.rs @@ -10,7 +10,7 @@ use reth_rpc_builder::config::RethRpcServerConfig; use reth_scroll_chainspec::ScrollChainSpec; use reth_scroll_primitives::ScrollPrimitives; use reth_transaction_pool::{PoolTransaction, TransactionPool}; -use rollup_node_manager::{PoAConsensus, RollupNodeManager}; +use rollup_node_manager::{Consensus, NoopConsensus, PoAConsensus, RollupNodeManager}; use rollup_node_providers::{beacon_provider, DatabaseL1MessageProvider, OnlineL1Provider}; use rollup_node_sequencer::Sequencer; use rollup_node_watcher::L1Watcher; @@ -90,10 +90,7 @@ where // Create the scroll network manager. let scroll_network_manager = ScrollNetworkManager::from_parts(handle.clone(), events); - // Spawn the scroll network manager. - let consensus = PoAConsensus::new(vec![]); - let payload_provider = NoopExecutionPayloadProvider; - + // Spawn the engine driver. let auth_port = ctx.config().rpc.auth_port; let auth_secret = ctx.config().rpc.auth_jwt_secret(ctx.config().datadir().jwt())?; @@ -101,7 +98,13 @@ where auth_secret, self.config.engine_api_url.unwrap_or(format!("http://localhost:{auth_port}").parse()?), ); - let fcs = ForkchoiceState::head_from_genesis(ctx.config().chain.genesis_hash()); + let payload_provider = NoopExecutionPayloadProvider; + + let fcs = if let Some(named) = ctx.config().chain.chain.named() { + ForkchoiceState::head_from_named_chain(named) + } else { + ForkchoiceState::head_from_genesis(ctx.config().chain.genesis_header().hash_slow()) + }; let engine = EngineDriver::new( Arc::new(engine_api), Arc::new(payload_provider), @@ -130,7 +133,7 @@ where let chain_spec = ctx.chain_spec(); // Spawn the L1Watcher - let l1_notification_rx = if let Some(l1_rpc_url) = self.config.l1_provider_args.l1_rpc_url { + let provider = if let Some(url) = self.config.l1_provider_args.l1_rpc_url { let L1ProviderArgs { max_retries, initial_backoff, compute_units_per_second, .. } = self.config.l1_provider_args; let client = RpcClient::builder() @@ -139,8 +142,30 @@ where initial_backoff, compute_units_per_second, )) - .http(l1_rpc_url); - let provider = ProviderBuilder::new().on_client(client); + .http(url); + Some(ProviderBuilder::new().on_client(client)) + } else { + None + }; + + // Create the consensus. + let consensus: Box = if self.config.test { + Box::new(NoopConsensus::default()) + } else { + let mut poa = PoAConsensus::new(vec![]); + if let Some(ref provider) = provider { + // Initialize the consensus + poa.initialize( + provider, + ctx.config().chain.chain.named().expect("expected named chain"), + ) + .await; + } + Box::new(poa) + }; + + let l1_notification_rx = if let Some(provider) = provider { + // Spawn the L1Watcher Some(L1Watcher::spawn(provider, WATCHER_START_BLOCK_NUMBER).await) } else { None diff --git a/bin/rollup/tests/e2e.rs b/bin/rollup/tests/e2e.rs index 5653c74..b499172 100644 --- a/bin/rollup/tests/e2e.rs +++ b/bin/rollup/tests/e2e.rs @@ -133,6 +133,7 @@ pub async fn build_bridge_node( // Create the node for a bridge node that will bridge messages from the eth-wire protocol // to the scroll-wire protocol. let node_args = ScrollRollupNodeArgs { + test: true, enable_eth_scroll_wire_bridge: true, enable_scroll_wire: true, database_path: Some(PathBuf::from("sqlite::memory:")), diff --git a/crates/engine/src/driver.rs b/crates/engine/src/driver.rs index a8e81d2..659aec8 100644 --- a/crates/engine/src/driver.rs +++ b/crates/engine/src/driver.rs @@ -65,7 +65,7 @@ where /// Sets the finalized block info. pub fn set_finalized_block_info(&mut self, block_info: BlockInfo) { - self.fcs.update_safe_block_info(block_info); + self.fcs.update_finalized_block_info(block_info); } /// Sets the payload building duration. diff --git a/crates/engine/src/fcs.rs b/crates/engine/src/fcs.rs index b6c8cdc..38905eb 100644 --- a/crates/engine/src/fcs.rs +++ b/crates/engine/src/fcs.rs @@ -8,7 +8,7 @@ use rollup_node_primitives::BlockInfo; /// /// The state is composed of the [`BlockInfo`] for `head`, `safe` block, and the `finalized` /// blocks. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] pub struct ForkchoiceState { head: BlockInfo, safe: BlockInfo, @@ -26,13 +26,13 @@ impl ForkchoiceState { Self { head, safe, finalized } } - /// Creates a new [`ForkchoiceState`] instance setting the `head` block info to the - /// provided `genesis` hash and the `safe` and `finalized` block info to the default values. - pub fn head_from_genesis(genesis: B256) -> Self { + /// Creates a new [`ForkchoiceState`] instance setting the `head`, `safe` and `finalized` block + /// info to the provided `genesis` hash. + pub const fn head_from_genesis(genesis: B256) -> Self { Self::new( BlockInfo { hash: genesis, number: 0 }, - BlockInfo { hash: Default::default(), number: 0 }, - BlockInfo { hash: Default::default(), number: 0 }, + BlockInfo { hash: genesis, number: 0 }, + BlockInfo { hash: genesis, number: 0 }, ) } diff --git a/crates/engine/src/future/mod.rs b/crates/engine/src/future/mod.rs index 4308e3b..a24350c 100644 --- a/crates/engine/src/future/mod.rs +++ b/crates/engine/src/future/mod.rs @@ -166,7 +166,7 @@ where Some(BlockImportOutcome::valid_block( peer_id, block, - signature.serialize_compact().to_vec().into(), + Into::>::into(signature).into(), )), )), _ => Ok((None, None)), diff --git a/crates/network/Cargo.toml b/crates/network/Cargo.toml index 7965b37..792e8fb 100644 --- a/crates/network/Cargo.toml +++ b/crates/network/Cargo.toml @@ -27,7 +27,6 @@ scroll-wire.workspace = true # misc futures.workspace = true parking_lot.workspace = true -secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] } tokio = { workspace = true, features = ["full"] } tokio-stream.workspace = true tracing.workspace = true @@ -42,5 +41,4 @@ serde = [ "reth-network-types/serde", "reth-scroll-primitives/serde", "scroll-wire/serde", - "secp256k1/serde", ] diff --git a/crates/network/src/event.rs b/crates/network/src/event.rs index d5b8341..ae81484 100644 --- a/crates/network/src/event.rs +++ b/crates/network/src/event.rs @@ -1,6 +1,6 @@ +use alloy_primitives::Signature; use reth_network_api::PeerId; use reth_scroll_primitives::ScrollBlock; -use secp256k1::ecdsa::Signature; /// A new block with the peer id that it was received from. #[derive(Debug, Clone)] diff --git a/crates/network/src/handle.rs b/crates/network/src/handle.rs index 5c159e6..bb0dbfb 100644 --- a/crates/network/src/handle.rs +++ b/crates/network/src/handle.rs @@ -1,8 +1,8 @@ +use alloy_primitives::Signature; use reth_network::{config::SecretKey, NetworkHandle as RethNetworkHandle, PeersInfo}; use reth_network_peers::PeerId; use reth_scroll_node::ScrollNetworkPrimitives; use reth_scroll_primitives::ScrollBlock; -use secp256k1::ecdsa::Signature; use std::sync::Arc; use tokio::sync::{mpsc::UnboundedSender, oneshot}; diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 67824f8..1e8386b 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -11,7 +11,11 @@ workspace = true [dependencies] # alloy +alloy-chains.workspace = true +alloy-consensus.workspace = true alloy-eips.workspace = true +alloy-primitives.workspace = true +alloy-provider.workspace = true alloy-rpc-types-engine.workspace = true # scroll-alloy @@ -22,6 +26,7 @@ scroll-alloy-provider.workspace = true reth-tokio-util.workspace = true # reth-scroll +reth-primitives-traits.workspace = true reth-scroll-primitives.workspace = true # scroll @@ -41,11 +46,14 @@ rollup-node-watcher.workspace = true # misc futures.workspace = true -secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] } tokio-stream.workspace = true tokio.workspace = true tracing.workspace = true +[dev-dependencies] +alloy-consensus.workspace = true +scroll-alloy-consensus.workspace = true + [features] serde = [ "alloy-rpc-types-engine/serde", @@ -54,7 +62,11 @@ serde = [ "scroll-engine/serde", "scroll-network/serde", "scroll-wire/serde", - "secp256k1/serde", "alloy-eips/serde", "scroll-alloy-hardforks/serde", + "alloy-chains/serde", + "alloy-consensus/serde", + "alloy-primitives/serde", + "reth-primitives-traits/serde", + "scroll-alloy-consensus/serde", ] diff --git a/crates/node/src/consensus.rs b/crates/node/src/consensus.rs index 08baeac..ef9a943 100644 --- a/crates/node/src/consensus.rs +++ b/crates/node/src/consensus.rs @@ -1,9 +1,28 @@ +use alloy_chains::NamedChain; +use alloy_consensus::Header; +use alloy_primitives::{ + address, keccak256, + private::{alloy_rlp, alloy_rlp::Encodable}, + Address, Signature, B256, U256, +}; +use alloy_provider::Provider; use reth_scroll_primitives::ScrollBlock; use scroll_network::ConsensusError; -use secp256k1::{ecdsa::Signature, PublicKey}; +use std::fmt::Debug; + +/// The address of the system contract on Sepolia. +const SEPOLIA_SYSTEM_CONTRAT_ADDRESS: Address = + address!("C706Ba9fa4fedF4507CB7A898b4766c1bbf9be57"); + +/// The address of the system contract on Mainnet. +const MAINNET_SYSTEM_CONTRAT_ADDRESS: Address = + address!("8432728A257646449245558B8b7Dbe51A16c7a4D"); + +/// The storage slot of the authorized signer. +const AUTHORIZED_SIGNER_STORAGE_SLOT: U256 = U256::from_limbs([0x67, 0x0, 0x0, 0x0]); /// A trait for consensus implementations. -pub trait Consensus { +pub trait Consensus: Send + Debug { /// Validates a new block with the given signature. fn validate_new_block( &self, @@ -12,27 +31,188 @@ pub trait Consensus { ) -> Result<(), ConsensusError>; } +/// A no-op consensus instance. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct NoopConsensus; + +impl Consensus for NoopConsensus { + fn validate_new_block( + &self, + _block: &ScrollBlock, + _signature: &Signature, + ) -> Result<(), ConsensusError> { + Ok(()) + } +} + /// A Proof of Authority consensus instance. #[derive(Debug)] pub struct PoAConsensus { - _authorized_signers: Vec, + authorized_signers: Vec
, } impl PoAConsensus { /// Creates a new [`PoAConsensus`] consensus instance with the given authorized signers. - pub const fn new(authorized_signers: Vec) -> Self { - Self { _authorized_signers: authorized_signers } + pub const fn new(authorized_signers: Vec
) -> Self { + Self { authorized_signers } + } + + /// Initialize the [`PoAConsensus`] by fetching the authorized signers from L1 system contract. + pub async fn initialize(&mut self, provider: &P, chain: NamedChain) { + let system_contract_address = match chain { + NamedChain::Scroll => MAINNET_SYSTEM_CONTRAT_ADDRESS, + NamedChain::ScrollSepolia => SEPOLIA_SYSTEM_CONTRAT_ADDRESS, + _ => panic!("unsupported chain"), + }; + let authorized_signer = provider + .get_storage_at(system_contract_address, AUTHORIZED_SIGNER_STORAGE_SLOT) + .await + .expect("failed to fetch PoAConsensus authorized signer"); + + let authorized_signer = Address::from_slice(&authorized_signer.to_be_bytes::<32>()[12..]); + self.authorized_signers.push(authorized_signer); } } impl Consensus for PoAConsensus { fn validate_new_block( &self, - _block: &ScrollBlock, - _signature: &Signature, + block: &ScrollBlock, + signature: &Signature, ) -> Result<(), ConsensusError> { - // TODO: recover the public key from the signature and check if it is in the authorized - // signers --- CURRENTLY NOOP --- + let hash = sig_encode_hash(&block.header); + let signer = reth_primitives_traits::crypto::secp256k1::recover_signer(signature, hash) + .map_err(|_| ConsensusError::Signature)?; + + if !self.authorized_signers.contains(&signer) { + return Err(ConsensusError::Signature) + } Ok(()) } } + +/// Encode and hash the header for signature. The function is similar to `Header::encode` but skips +/// the `extra_data` field. +fn sig_encode_hash(header: &Header) -> B256 { + let out = &mut Vec::new(); + let list_header = + alloy_rlp::Header { list: true, payload_length: sig_header_payload_length(header) }; + list_header.encode(out); + header.parent_hash.encode(out); + header.ommers_hash.encode(out); + header.beneficiary.encode(out); + header.state_root.encode(out); + header.transactions_root.encode(out); + header.receipts_root.encode(out); + header.logs_bloom.encode(out); + header.difficulty.encode(out); + U256::from(header.number).encode(out); + U256::from(header.gas_limit).encode(out); + U256::from(header.gas_used).encode(out); + header.timestamp.encode(out); + header.mix_hash.encode(out); + header.nonce.encode(out); + + // Encode all the fork specific fields + if let Some(ref base_fee) = header.base_fee_per_gas { + U256::from(*base_fee).encode(out); + } + keccak256(&out) +} + +/// Returns the header payload length for signature. +fn sig_header_payload_length(header: &Header) -> usize { + let mut length = 0; + length += header.parent_hash.length(); + length += header.ommers_hash.length(); + length += header.beneficiary.length(); + length += header.state_root.length(); + length += header.transactions_root.length(); + length += header.receipts_root.length(); + length += header.logs_bloom.length(); + length += header.difficulty.length(); + length += U256::from(header.number).length(); + length += U256::from(header.gas_limit).length(); + length += U256::from(header.gas_used).length(); + length += header.timestamp.length(); + length += header.mix_hash.length(); + length += header.nonce.length(); + + if let Some(base_fee) = header.base_fee_per_gas { + // Adding base fee length if it exists. + length += U256::from(base_fee).length(); + } + length +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::TxEip1559; + use alloy_primitives::{address, b256, bloom, bytes, TxKind, B64, U256}; + use reth_primitives_traits::Header; + use reth_scroll_primitives::{ScrollBlockBody, ScrollTransactionSigned}; + use scroll_alloy_consensus::ScrollTypedTransaction; + use std::{str::FromStr, sync::OnceLock}; + + #[test] + fn test_should_validate_block() { + let consensus = + PoAConsensus::new(vec![address!("d83c4892bb5aa241b63d8c4c134920111e142a20")]); + let signature = Signature::from_raw(&bytes!("6d2b8ef87f0956ea4dd10fb0725fa7196ad80c6d567a161f6b4367f95b5de6ec279142b540d3b248f08ed337bb962fa3fd83d21de622f7d6c8207272558fd15a00")).unwrap(); + + let tx_hash = OnceLock::new(); + tx_hash.get_or_init(|| { + b256!("f257edab88796a76f6d19a9fadad44b2b16c28e7aa70322cc4c6abc857128998") + }); + + let block = ScrollBlock { + header: Header { + parent_hash: b256!("3ccf36621e1f75cd1bfd2ac39ff6a00d8a5bec02e52aa7064a4860a0d02d6013"), + ommers_hash: b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), + beneficiary: address!("0000000000000000000000000000000000000000"), + state_root: b256!("bc6c2ccfdb3e0e78b53134f583e6d42760adcaebb23e7a6bab59503c4b98daeb"), + transactions_root: b256!("a11e1b74f0aada603d9b4e645a57d60259dc2545c0372b88e16e6b59cecac8b6"), + receipts_root: b256!("72de16699164034d2ed9930a021820e32e103ea7162b6f6a9a535d0a98f3fac0"), + logs_bloom: bloom!("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000008000000000010000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + difficulty: U256::ONE, + number: 14916920, + gas_limit: 10000000, + gas_used: 245760, + timestamp: 1745337938, + extra_data: bytes!("0x"), + mix_hash: b256!("0000000000000000000000000000000000000000000000000000000000000000"), + nonce: B64::from(0x0000000000000000i64), + base_fee_per_gas: Some(40306624), + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + requests_hash: None, + }, + body: ScrollBlockBody { + transactions: vec![ScrollTransactionSigned { + hash: tx_hash, + signature: + Signature::new(U256::from_str("12217337930795921874768983252881296563512928283585900928219483692173266513447").unwrap(), U256::from_str("37490897792770890087946325233571758133021734266092518377537449521790010698782").unwrap(), true) , + transaction: ScrollTypedTransaction::Eip1559(TxEip1559{ + chain_id: 534352, + nonce: 145014, + gas_limit: 600000, + max_fee_per_gas: 52355852, + max_priority_fee_per_gas: 0, + to: TxKind::Call(address!("802b65b5d9016621e66003aed0b16615093f328b")), + value: U256::ZERO, + access_list: Default::default(), + input: bytes!("a00597a00000000000000000000000000000000000000000000000000000000001826cbe0000000000000000000000000000000000005eb6831c1aa0faf2055c7d53270e00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000813df550a32d4a9d42010d057386429ad2328ed9000000000000000000000000000000000000000000000000000000006807befd"), + }) , + }], + ommers: vec![], + withdrawals: None, + }, + }; + + consensus.validate_new_block(&block, &signature).unwrap() + } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 4877317..c60b377 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -1,5 +1,6 @@ //! This library contains the main manager for the rollup node. +use alloy_primitives::Signature; use futures::StreamExt; use reth_tokio_util::{EventSender, EventStream}; use rollup_node_indexer::{Indexer, IndexerEvent}; @@ -9,7 +10,6 @@ use scroll_alloy_hardforks::ScrollHardforks; use scroll_alloy_provider::ScrollEngineApi; use scroll_engine::{EngineDriver, EngineDriverEvent}; use scroll_network::{BlockImportOutcome, NetworkManager, NetworkManagerEvent, NewBlockWithPeer}; -use secp256k1::ecdsa::Signature; use std::{ fmt, fmt::{Debug, Formatter}, @@ -28,10 +28,9 @@ use tracing::{error, trace}; pub use event::RollupEvent; mod event; -pub use consensus::PoAConsensus; +pub use consensus::{Consensus, NoopConsensus, PoAConsensus}; mod consensus; -use consensus::Consensus; use rollup_node_providers::{ExecutionPayloadProvider, L1MessageProvider, L1Provider}; use scroll_db::Database; use scroll_derivation_pipeline::DerivationPipeline; @@ -52,7 +51,7 @@ const EVENT_CHANNEL_SIZE: usize = 100; /// - `forkchoice_state`: The forkchoice state of the rollup node. /// - `pending_block_imports`: A collection of pending block imports. /// - `event_sender`: An event sender for sending events to subscribers of the rollup node manager. -pub struct RollupNodeManager { +pub struct RollupNodeManager { /// The network manager that manages the scroll p2p network. network: NetworkManager, /// The engine driver used to communicate with the engine. @@ -64,7 +63,7 @@ pub struct RollupNodeManager { /// An indexer used to index data for the rollup node. indexer: Indexer, /// The consensus algorithm used by the rollup node. - consensus: C, + consensus: Box, /// The receiver for new blocks received from the network (used to bridge from eth-wire). new_block_rx: Option>, /// An event sender for sending events to subscribers of the rollup node manager. @@ -75,8 +74,8 @@ pub struct RollupNodeManager { block_building_trigger: Option, } -impl Debug - for RollupNodeManager +impl Debug + for RollupNodeManager { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("RollupNodeManager") @@ -94,9 +93,8 @@ impl Debug } } -impl RollupNodeManager +impl RollupNodeManager where - C: Consensus + Unpin, EC: ScrollEngineApi + Unpin + Sync + Send + 'static, P: ExecutionPayloadProvider + Unpin + Send + Sync + 'static, L1P: L1Provider + Clone + Send + Sync + 'static, @@ -111,7 +109,7 @@ where l1_provider: Option, database: Arc, l1_notification_rx: Option>>, - consensus: C, + consensus: Box, chain_spec: Arc, new_block_rx: Option>, sequencer: Option>, @@ -211,7 +209,7 @@ where } EngineDriverEvent::NewPayload(payload) => { // TODO: sign blocks before sending them to the network. - let signature = Signature::from_compact(&[0; 64]).unwrap(); + let signature = Signature::from_raw(&[0; 65]).unwrap(); self.network.handle().announce_block(payload, signature); } EngineDriverEvent::L1BlockConsolidated((block_info, batch_info)) => { @@ -230,9 +228,8 @@ where } } -impl Future for RollupNodeManager +impl Future for RollupNodeManager where - C: Consensus + Unpin, EC: ScrollEngineApi + Unpin + Sync + Send + 'static, P: ExecutionPayloadProvider + Unpin + Send + Sync + 'static, L1P: L1Provider + Clone + Unpin + Send + Sync + 'static, @@ -258,7 +255,7 @@ where // Drain all L1 notifications. while let Some(Poll::Ready(Some(event))) = - this.l1_notification_rx.as_mut().map(|x| x.poll_next_unpin(cx)) + this.l1_notification_rx.as_mut().map(|rx| rx.poll_next_unpin(cx)) { this.handle_l1_notification((*event).clone()); } diff --git a/crates/scroll-wire/Cargo.toml b/crates/scroll-wire/Cargo.toml index 05cfcfb..b8a754a 100644 --- a/crates/scroll-wire/Cargo.toml +++ b/crates/scroll-wire/Cargo.toml @@ -24,7 +24,6 @@ reth-scroll-primitives = { workspace = true } # misc futures.workspace = true -secp256k1 = { workspace = true, features = ["global-context", "recovery"] } tokio = { workspace = true, features = ["full"] } tokio-stream.workspace = true tracing.workspace = true @@ -36,5 +35,4 @@ serde = [ "reth-network/serde", "reth-network-api/serde", "reth-scroll-primitives/serde", - "secp256k1/serde", ] diff --git a/crates/scroll-wire/src/connection/mod.rs b/crates/scroll-wire/src/connection/mod.rs index ba3c494..26e82c1 100644 --- a/crates/scroll-wire/src/connection/mod.rs +++ b/crates/scroll-wire/src/connection/mod.rs @@ -2,12 +2,12 @@ use crate::{ protocol::{ProtocolState, ScrollMessage, ScrollMessagePayload, ScrollWireEvent}, ScrollWireConfig, }; +use alloy_primitives::Signature; use alloy_rlp::BytesMut; use futures::{Stream, StreamExt}; use reth_eth_wire::multiplex::ProtocolConnection; use reth_network::Direction; use reth_network_api::PeerId; -use secp256k1::ecdsa::Signature; use std::{ pin::Pin, task::{ready, Context, Poll}, @@ -82,7 +82,7 @@ impl Stream for ScrollWireConnection { ScrollMessagePayload::NewBlock(new_block) => { // If the signature can be decoded then we send a new block event. trace!(target: "scroll::wire::connection", peer_id = %this.peer_id, block = ?new_block.block.hash_slow(), "Received new block from peer"); - if let Ok(signature) = Signature::from_compact(&new_block.signature[..]) { + if let Ok(signature) = Signature::from_raw(&new_block.signature[..]) { this.events .send(ScrollWireEvent::NewBlock { block: new_block.block, diff --git a/crates/scroll-wire/src/protocol/event.rs b/crates/scroll-wire/src/protocol/event.rs index e3ff477..458886d 100644 --- a/crates/scroll-wire/src/protocol/event.rs +++ b/crates/scroll-wire/src/protocol/event.rs @@ -1,7 +1,7 @@ use crate::protocol::ScrollMessage; +use alloy_primitives::Signature; use reth_network::Direction; use reth_network_api::PeerId; -use secp256k1::ecdsa::Signature; use tokio::sync::mpsc::UnboundedSender; /// The events that can be emitted by the scroll wire protocol. diff --git a/crates/scroll-wire/src/protocol/proto.rs b/crates/scroll-wire/src/protocol/proto.rs index 0c8980e..9247237 100644 --- a/crates/scroll-wire/src/protocol/proto.rs +++ b/crates/scroll-wire/src/protocol/proto.rs @@ -1,7 +1,9 @@ -use alloy_primitives::bytes::{Buf, BufMut, Bytes, BytesMut}; +use alloy_primitives::{ + bytes::{Buf, BufMut, Bytes, BytesMut}, + Signature, +}; use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; use reth_eth_wire::{protocol::Protocol, Capability}; -use secp256k1::ecdsa::Signature; /// The message IDs for messages sent over the scroll wire protocol. /// This is used to identify the type of message being sent or received @@ -30,7 +32,7 @@ pub struct NewBlock { impl NewBlock { /// Returns a [`NewBlock`] instance with the provided signature and block. pub fn new(signature: Signature, block: reth_scroll_primitives::ScrollBlock) -> Self { - Self { signature: Bytes::from(signature.serialize_compact().to_vec()), block } + Self { signature: Bytes::from(Into::>::into(signature)), block } } }