From 40d06252a4a5440d64ac626aaff514b5a2e7d9a8 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 17:30:18 +0200 Subject: [PATCH 01/34] Separate rpc Ethereum context into its file --- crates/ethereum-rpc/src/ethereum.rs | 107 +++++++++++++++++++++ crates/ethereum-rpc/src/lib.rs | 139 +--------------------------- 2 files changed, 111 insertions(+), 135 deletions(-) create mode 100644 crates/ethereum-rpc/src/ethereum.rs diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs new file mode 100644 index 000000000..6ee58ea00 --- /dev/null +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -0,0 +1,107 @@ +use std::sync::Mutex; + +#[cfg(feature = "local")] +use citrea_evm::DevSigner; +use citrea_evm::Evm; +use reth_primitives::U256; +use reth_rpc_types::trace::geth::GethTrace; +use rustc_version_runtime::version; +use schnellru::{ByLength, LruMap}; +use sequencer_client::SequencerClient; +use sov_modules_api::WorkingSet; +use sov_rollup_interface::services::da::DaService; +use sov_rollup_interface::CITREA_VERSION; +use tracing::instrument; + +use crate::gas_price::fee_history::FeeHistoryCacheConfig; +use crate::gas_price::gas_oracle::{GasPriceOracle, GasPriceOracleConfig}; + +const MAX_TRACE_BLOCK: u32 = 1000; + +#[derive(Clone)] +pub struct EthRpcConfig { + pub gas_price_oracle_config: GasPriceOracleConfig, + pub fee_history_cache_config: FeeHistoryCacheConfig, + #[cfg(feature = "local")] + pub eth_signer: DevSigner, +} + +pub struct Ethereum { + #[allow(dead_code)] + pub(crate) da_service: Da, + pub(crate) gas_price_oracle: GasPriceOracle, + #[cfg(feature = "local")] + pub(crate) eth_signer: DevSigner, + pub(crate) storage: C::Storage, + pub(crate) sequencer_client: Option, + pub(crate) web3_client_version: String, + pub(crate) trace_cache: Mutex, ByLength>>, +} + +impl Ethereum { + pub(crate) fn new( + da_service: Da, + gas_price_oracle_config: GasPriceOracleConfig, + fee_history_cache_config: FeeHistoryCacheConfig, + #[cfg(feature = "local")] eth_signer: DevSigner, + storage: C::Storage, + sequencer_client: Option, + ) -> Self { + let evm = Evm::::default(); + let gas_price_oracle = + GasPriceOracle::new(evm, gas_price_oracle_config, fee_history_cache_config); + + let rollup = "citrea"; + let arch = std::env::consts::ARCH; + let rustc_v = version(); + + let current_version = format!("{}/{}/{}/rust-{}", rollup, CITREA_VERSION, arch, rustc_v); + + let trace_cache = Mutex::new(LruMap::new(ByLength::new(MAX_TRACE_BLOCK))); + + Self { + da_service, + gas_price_oracle, + #[cfg(feature = "local")] + eth_signer, + storage, + sequencer_client, + web3_client_version: current_version, + trace_cache, + } + } + + #[instrument(level = "trace", skip_all)] + pub(crate) async fn max_fee_per_gas(&self, working_set: &mut WorkingSet) -> (U256, U256) { + let suggested_tip = self + .gas_price_oracle + .suggest_tip_cap(working_set) + .await + .unwrap(); + + let evm = Evm::::default(); + let base_fee = evm + .get_block_by_number(None, None, working_set) + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap_or_default(); + + (U256::from(base_fee), U256::from(suggested_tip)) + } + + // fn make_raw_tx( + // &self, + // raw_tx: RlpEvmTransaction, + // ) -> Result<(B256, Vec), jsonrpsee::core::RegisterMethodError> { + // let signed_transaction: RethTransactionSignedNoHash = raw_tx.clone().try_into()?; + + // let tx_hash = signed_transaction.hash(); + + // let tx = CallMessage { txs: vec![raw_tx] }; + // let message = as EncodeCall>>::encode_call(tx); + + // Ok((B256::from(tx_hash), message)) + // } +} diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 433f12305..bca009f25 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -1,14 +1,14 @@ +mod ethereum; mod gas_price; use std::collections::BTreeMap; -use std::process::Command; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; #[cfg(feature = "local")] pub use citrea_evm::DevSigner; use citrea_evm::Evm; +pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; -use gas_price::gas_oracle::GasPriceOracle; pub use gas_price::gas_oracle::GasPriceOracleConfig; use jsonrpsee::types::{ErrorObjectOwned, ParamsSequence}; use jsonrpsee::{PendingSubscriptionSink, RpcModule, SubscriptionMessage}; @@ -19,25 +19,12 @@ use reth_rpc_types::trace::geth::{ GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame, }; use reth_rpc_types::{FeeHistory, Index}; -use rustc_version_runtime::version; -use schnellru::{ByLength, LruMap}; use sequencer_client::SequencerClient; use serde_json::json; use sov_modules_api::utils::to_jsonrpsee_error_object; use sov_modules_api::{Context, WorkingSet}; use sov_rollup_interface::services::da::DaService; -use sov_rollup_interface::CITREA_VERSION; -use tracing::{error, info, instrument}; - -const MAX_TRACE_BLOCK: u32 = 1000; - -#[derive(Clone)] -pub struct EthRpcConfig { - pub gas_price_oracle_config: GasPriceOracleConfig, - pub fee_history_cache_config: FeeHistoryCacheConfig, - #[cfg(feature = "local")] - pub eth_signer: DevSigner, -} +use tracing::{error, info}; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] @@ -85,90 +72,6 @@ pub fn get_ethereum_rpc( rpc } -pub struct Ethereum { - #[allow(dead_code)] - da_service: Da, - gas_price_oracle: GasPriceOracle, - #[cfg(feature = "local")] - eth_signer: DevSigner, - storage: C::Storage, - sequencer_client: Option, - web3_client_version: String, - trace_cache: Mutex, ByLength>>, -} - -impl Ethereum { - fn new( - da_service: Da, - gas_price_oracle_config: GasPriceOracleConfig, - fee_history_cache_config: FeeHistoryCacheConfig, - #[cfg(feature = "local")] eth_signer: DevSigner, - storage: C::Storage, - sequencer_client: Option, - ) -> Self { - let evm = Evm::::default(); - let gas_price_oracle = - GasPriceOracle::new(evm, gas_price_oracle_config, fee_history_cache_config); - - let rollup = "citrea"; - let arch = std::env::consts::ARCH; - let rustc_v = version(); - - let current_version = format!("{}/{}/{}/rust-{}", rollup, CITREA_VERSION, arch, rustc_v); - - let trace_cache = Mutex::new(LruMap::new(ByLength::new(MAX_TRACE_BLOCK))); - - Self { - da_service, - gas_price_oracle, - #[cfg(feature = "local")] - eth_signer, - storage, - sequencer_client, - web3_client_version: current_version, - trace_cache, - } - } -} - -impl Ethereum { - #[instrument(level = "trace", skip_all)] - async fn max_fee_per_gas(&self, working_set: &mut WorkingSet) -> (U256, U256) { - let suggested_tip = self - .gas_price_oracle - .suggest_tip_cap(working_set) - .await - .unwrap(); - - let evm = Evm::::default(); - let base_fee = evm - .get_block_by_number(None, None, working_set) - .unwrap() - .unwrap() - .header - .base_fee_per_gas - .unwrap_or_default(); - - (U256::from(base_fee), U256::from(suggested_tip)) - } -} - -// impl Ethereum { -// fn make_raw_tx( -// &self, -// raw_tx: RlpEvmTransaction, -// ) -> Result<(B256, Vec), jsonrpsee::core::RegisterMethodError> { -// let signed_transaction: RethTransactionSignedNoHash = raw_tx.clone().try_into()?; - -// let tx_hash = signed_transaction.hash(); - -// let tx = CallMessage { txs: vec![raw_tx] }; -// let message = as EncodeCall>>::encode_call(tx); - -// Ok((B256::from(tx_hash), message)) -// } -// } - fn register_rpc_methods( rpc: &mut RpcModule>, // Checks wether the running node is a sequencer or not, if it is not a sequencer it should also have methods like eth_sendRawTransaction here. @@ -804,40 +707,6 @@ fn register_rpc_methods( // (call_request, gas_price, max_fee_per_gas) // } -pub fn get_latest_git_tag() -> Result { - let latest_tag_commit = Command::new("git") - .args(["rev-list", "--tags", "--max-count=1"]) - .output() - .map_err(|e| to_jsonrpsee_error_object("FULL_NODE_ERROR", e))?; - - if !latest_tag_commit.status.success() { - return Err(to_jsonrpsee_error_object( - "Failure", - "Failed to get version", - )); - } - - let latest_tag_commit = String::from_utf8_lossy(&latest_tag_commit.stdout) - .trim() - .to_string(); - - let latest_tag = Command::new("git") - .args(["describe", "--tags", &latest_tag_commit]) - .output() - .map_err(|e| to_jsonrpsee_error_object("FULL_NODE_ERROR", e))?; - - if !latest_tag.status.success() { - return Err(to_jsonrpsee_error_object( - "Failure", - "Failed to get version", - )); - } - - Ok(String::from_utf8_lossy(&latest_tag.stdout) - .trim() - .to_string()) -} - fn apply_call_config(call_frame: CallFrame, call_config: CallConfig) -> CallFrame { // let only_top_call = call_config.only_top_call.unwrap_or(); let mut new_call_frame = call_frame.clone(); From 24c27bd1dd56efcd3de2d37780de8cbafbc34017 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 17:43:30 +0200 Subject: [PATCH 02/34] Separate debug trace --- crates/ethereum-rpc/src/debug_trace.rs | 309 +++++++++++++++++++++++++ crates/ethereum-rpc/src/lib.rs | 309 +------------------------ 2 files changed, 316 insertions(+), 302 deletions(-) create mode 100644 crates/ethereum-rpc/src/debug_trace.rs diff --git a/crates/ethereum-rpc/src/debug_trace.rs b/crates/ethereum-rpc/src/debug_trace.rs new file mode 100644 index 000000000..098e23294 --- /dev/null +++ b/crates/ethereum-rpc/src/debug_trace.rs @@ -0,0 +1,309 @@ +use std::collections::BTreeMap; +use std::sync::Arc; + +#[cfg(feature = "local")] +use citrea_evm::Evm; +use jsonrpsee::types::{ErrorObjectOwned, ParamsSequence}; +use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage}; +use reth_primitives::BlockNumberOrTag; +use reth_rpc::eth::error::EthApiError; +use reth_rpc_types::trace::geth::{ + CallConfig, CallFrame, FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig, + GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame, +}; +use sov_modules_api::WorkingSet; +use sov_rollup_interface::services::da::DaService; +use tracing::error; + +use crate::ethereum::Ethereum; + +pub async fn handle_debug_trace_chain( + mut params: ParamsSequence<'_>, + pending: PendingSubscriptionSink, + ethereum: Arc>, +) { + let start_block: BlockNumberOrTag = match params.next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return; + } + }; + let end_block: BlockNumberOrTag = match params.next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return; + } + }; + + // start block is exclusive, hence latest is not supported + let BlockNumberOrTag::Number(start_block) = start_block else { + pending.reject(EthApiError::Unsupported( + "Latest, earliest, pending, safe and finalized are not supported for traceChain start block", + )).await; + return; + }; + + let mut working_set = WorkingSet::::new(ethereum.storage.clone()); + let evm = Evm::::default(); + let latest_block_number: u64 = evm + .block_number(&mut working_set) + .expect("Expected at least one block") + .saturating_to(); + let end_block = match end_block { + BlockNumberOrTag::Number(end_block) => { + if end_block > latest_block_number { + pending.reject(EthApiError::UnknownBlockNumber).await; + return; + } + end_block + } + BlockNumberOrTag::Latest => latest_block_number, + _ => { + pending.reject(EthApiError::Unsupported( + "Earliest, pending, safe and finalized are not supported for traceChain end block", + )).await; + return; + } + }; + + if start_block >= end_block { + pending.reject(EthApiError::InvalidBlockRange).await; + return; + } + + let opts: Option = match params.optional_next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return; + } + }; + + let subscription = pending.accept().await.unwrap(); + tokio::spawn(async move { + for block_number in start_block + 1..=end_block { + let mut working_set = WorkingSet::::new(ethereum.storage.clone()); + let traces = debug_trace_by_block_number( + block_number, + None, + ðereum, + &evm, + &mut working_set, + opts.clone(), + ); + match traces { + Ok(traces) => { + let msg = SubscriptionMessage::new( + subscription.method_name(), + subscription.subscription_id(), + &traces, + ) + .unwrap(); + let Ok(_) = subscription.send(msg).await else { + return; + }; + } + Err(err) => { + error!( + "Failed to get traces of block {} in traceChain: {}", + block_number, err + ); + + let msg = SubscriptionMessage::new( + subscription.method_name(), + subscription.subscription_id(), + &"Internal error", + ) + .unwrap(); + let _ = subscription.send(msg).await; + return; + } + }; + } + }); +} + +pub fn debug_trace_by_block_number( + block_number: u64, + trace_idx: Option, + ethereum: &Ethereum, + evm: &Evm, + working_set: &mut WorkingSet, + opts: Option, +) -> Result, ErrorObjectOwned> { + // If opts is None or if opts.tracer is None, then do not check cache or insert cache, just perform the operation + if opts.as_ref().map_or(true, |o| o.tracer.is_none()) { + let traces = + evm.trace_block_transactions_by_number(block_number, opts, trace_idx, working_set)?; + return match trace_idx { + Some(idx) => Ok(vec![traces[idx].clone()]), + None => Ok(traces), + }; + } + + let requested_opts = opts.unwrap(); + let tracer_type = requested_opts.tracer.unwrap(); + let tracer_config = requested_opts.tracer_config; + + if let Some(traces) = ethereum.trace_cache.lock().unwrap().get(&block_number) { + // If traces are found in cache convert them to specified opts and then return + let traces = match trace_idx { + Some(idx) => vec![traces[idx].clone()], + None => traces.to_vec(), + }; + let traces = + get_traces_with_requested_tracer_and_config(traces, tracer_type, tracer_config)?; + return Ok(traces); + } + + let cache_options = create_trace_cache_opts(); + let traces = evm.trace_block_transactions_by_number( + block_number, + Some(cache_options), + None, + working_set, + )?; + ethereum + .trace_cache + .lock() + .unwrap() + .insert(block_number, traces.clone()); + + // Convert the traces to the requested tracer and config + let traces = match trace_idx { + Some(idx) => vec![traces[idx].clone()], + None => traces, + }; + let traces = get_traces_with_requested_tracer_and_config(traces, tracer_type, tracer_config)?; + + Ok(traces) +} + +fn apply_call_config(call_frame: CallFrame, call_config: CallConfig) -> CallFrame { + // let only_top_call = call_config.only_top_call.unwrap_or(); + let mut new_call_frame = call_frame.clone(); + if let Some(true) = call_config.only_top_call { + new_call_frame.calls = vec![]; + } + if !call_config.with_log.unwrap_or(false) { + remove_logs_from_call_frame(&mut vec![new_call_frame.clone()]); + } + new_call_frame +} + +fn remove_logs_from_call_frame(call_frame: &mut Vec) { + for frame in call_frame { + frame.logs = vec![]; + remove_logs_from_call_frame(&mut frame.calls); + } +} + +fn get_traces_with_requested_tracer_and_config( + traces: Vec, + tracer: GethDebugTracerType, + tracer_config: GethDebugTracerConfig, +) -> Result, EthApiError> { + // This can be only CallConfig or PreStateConfig if it is not CallConfig return Error for now + + let mut new_traces = vec![]; + match tracer { + GethDebugTracerType::BuiltInTracer(builtin_tracer) => { + match builtin_tracer { + GethDebugBuiltInTracerType::CallTracer => { + // Apply the call config to the traces + let call_config = + GethDebugTracerConfig::into_call_config(tracer_config).unwrap_or_default(); + // if call config is the same in the cache then do not process again and return early + match call_config { + CallConfig { + only_top_call: None, + with_log: Some(true), + } + | CallConfig { + only_top_call: Some(false), + with_log: Some(true), + } => { + return Ok(traces); + } + _ => { + traces.into_iter().for_each(|trace| { + if let GethTrace::CallTracer(call_frame) = trace { + let new_call_frame = + apply_call_config(call_frame.clone(), call_config); + new_traces.push(GethTrace::CallTracer(new_call_frame)); + } + }); + } + } + Ok(new_traces) + } + GethDebugBuiltInTracerType::FourByteTracer => { + traces.into_iter().for_each(|trace| { + if let GethTrace::CallTracer(call_frame) = trace { + let four_byte_frame = + convert_call_trace_into_4byte_frame(vec![call_frame]); + new_traces.push(GethTrace::FourByteTracer(four_byte_frame)); + } + }); + Ok(new_traces) + } + GethDebugBuiltInTracerType::NoopTracer => { + Ok(vec![GethTrace::NoopTracer(NoopFrame::default())]) + } + _ => Err(EthApiError::Unsupported("This tracer is not supported")), + } + } + GethDebugTracerType::JsTracer(_code) => { + // This also requires DatabaseRef trait + // Implement after readonly state is implemented + Err(EthApiError::Unsupported("JsTracer")) + } + } +} + +fn convert_call_trace_into_4byte_frame(call_frames: Vec) -> FourByteFrame { + FourByteFrame(convert_call_trace_into_4byte_map( + call_frames, + BTreeMap::new(), + )) +} + +fn convert_call_trace_into_4byte_map( + call_frames: Vec, + mut four_byte_map: BTreeMap, +) -> BTreeMap { + // For each input in each call + // get the first 4 bytes, get the size of the input + // the key is : "-" + // value is the occurence of the key + for call_frame in call_frames { + let input = call_frame.input; + // If this is a function call (function selector is 4 bytes long) + if input.len() >= 4 { + let input_size = input.0.len() - 4; + let four_byte = &input.to_string()[2..10]; // Ignore the 0x + let key = format!("{}-{}", four_byte, input_size); + let count = four_byte_map.entry(key).or_insert(0); + *count += 1; + } + four_byte_map = convert_call_trace_into_4byte_map(call_frame.calls, four_byte_map); + } + four_byte_map +} + +fn create_trace_cache_opts() -> GethDebugTracingOptions { + // Get the traces with call tracer onlytopcall false and withlog true and always cache this way + let mut call_config_map = serde_json::Map::new(); + call_config_map.insert("only_top_call".to_string(), serde_json::Value::Bool(false)); + call_config_map.insert("with_log".to_string(), serde_json::Value::Bool(true)); + let call_config = serde_json::Value::Object(call_config_map); + GethDebugTracingOptions { + tracer: Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::CallTracer, + )), + tracer_config: GethDebugTracerConfig(call_config), + ..Default::default() + } +} diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index bca009f25..1df72aa2b 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -1,30 +1,26 @@ +mod debug_trace; mod ethereum; mod gas_price; -use std::collections::BTreeMap; -use std::sync::Arc; - #[cfg(feature = "local")] pub use citrea_evm::DevSigner; use citrea_evm::Evm; +use debug_trace::{debug_trace_by_block_number, handle_debug_trace_chain}; pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; pub use gas_price::gas_oracle::GasPriceOracleConfig; -use jsonrpsee::types::{ErrorObjectOwned, ParamsSequence}; -use jsonrpsee::{PendingSubscriptionSink, RpcModule, SubscriptionMessage}; +use jsonrpsee::types::ErrorObjectOwned; +use jsonrpsee::RpcModule; use reth_primitives::{keccak256, BlockNumberOrTag, Bytes, B256, U256}; use reth_rpc::eth::error::EthApiError; -use reth_rpc_types::trace::geth::{ - CallConfig, CallFrame, FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig, - GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame, -}; +use reth_rpc_types::trace::geth::{GethDebugTracingOptions, GethTrace}; use reth_rpc_types::{FeeHistory, Index}; use sequencer_client::SequencerClient; use serde_json::json; use sov_modules_api::utils::to_jsonrpsee_error_object; -use sov_modules_api::{Context, WorkingSet}; +use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; -use tracing::{error, info}; +use tracing::info; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] @@ -706,294 +702,3 @@ fn register_rpc_methods( // (call_request, gas_price, max_fee_per_gas) // } - -fn apply_call_config(call_frame: CallFrame, call_config: CallConfig) -> CallFrame { - // let only_top_call = call_config.only_top_call.unwrap_or(); - let mut new_call_frame = call_frame.clone(); - if let Some(true) = call_config.only_top_call { - new_call_frame.calls = vec![]; - } - if !call_config.with_log.unwrap_or(false) { - remove_logs_from_call_frame(&mut vec![new_call_frame.clone()]); - } - new_call_frame -} - -fn remove_logs_from_call_frame(call_frame: &mut Vec) { - for frame in call_frame { - frame.logs = vec![]; - remove_logs_from_call_frame(&mut frame.calls); - } -} - -async fn handle_debug_trace_chain( - mut params: ParamsSequence<'_>, - pending: PendingSubscriptionSink, - ethereum: Arc>, -) { - let start_block: BlockNumberOrTag = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return; - } - }; - let end_block: BlockNumberOrTag = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return; - } - }; - - // start block is exclusive, hence latest is not supported - let BlockNumberOrTag::Number(start_block) = start_block else { - pending.reject(EthApiError::Unsupported( - "Latest, earliest, pending, safe and finalized are not supported for traceChain start block", - )).await; - return; - }; - - let mut working_set = WorkingSet::::new(ethereum.storage.clone()); - let evm = Evm::::default(); - let latest_block_number: u64 = evm - .block_number(&mut working_set) - .expect("Expected at least one block") - .saturating_to(); - let end_block = match end_block { - BlockNumberOrTag::Number(end_block) => { - if end_block > latest_block_number { - pending.reject(EthApiError::UnknownBlockNumber).await; - return; - } - end_block - } - BlockNumberOrTag::Latest => latest_block_number, - _ => { - pending.reject(EthApiError::Unsupported( - "Earliest, pending, safe and finalized are not supported for traceChain end block", - )).await; - return; - } - }; - - if start_block >= end_block { - pending.reject(EthApiError::InvalidBlockRange).await; - return; - } - - let opts: Option = match params.optional_next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return; - } - }; - - let subscription = pending.accept().await.unwrap(); - tokio::spawn(async move { - for block_number in start_block + 1..=end_block { - let mut working_set = WorkingSet::::new(ethereum.storage.clone()); - let traces = debug_trace_by_block_number( - block_number, - None, - ðereum, - &evm, - &mut working_set, - opts.clone(), - ); - match traces { - Ok(traces) => { - let msg = SubscriptionMessage::new( - subscription.method_name(), - subscription.subscription_id(), - &traces, - ) - .unwrap(); - let Ok(_) = subscription.send(msg).await else { - return; - }; - } - Err(err) => { - error!( - "Failed to get traces of block {} in traceChain: {}", - block_number, err - ); - - let msg = SubscriptionMessage::new( - subscription.method_name(), - subscription.subscription_id(), - &"Internal error", - ) - .unwrap(); - let _ = subscription.send(msg).await; - return; - } - }; - } - }); -} - -fn debug_trace_by_block_number( - block_number: u64, - trace_idx: Option, - ethereum: &Ethereum, - evm: &Evm, - working_set: &mut WorkingSet, - opts: Option, -) -> Result, ErrorObjectOwned> { - // If opts is None or if opts.tracer is None, then do not check cache or insert cache, just perform the operation - if opts.as_ref().map_or(true, |o| o.tracer.is_none()) { - let traces = - evm.trace_block_transactions_by_number(block_number, opts, trace_idx, working_set)?; - return match trace_idx { - Some(idx) => Ok(vec![traces[idx].clone()]), - None => Ok(traces), - }; - } - - let requested_opts = opts.unwrap(); - let tracer_type = requested_opts.tracer.unwrap(); - let tracer_config = requested_opts.tracer_config; - - if let Some(traces) = ethereum.trace_cache.lock().unwrap().get(&block_number) { - // If traces are found in cache convert them to specified opts and then return - let traces = match trace_idx { - Some(idx) => vec![traces[idx].clone()], - None => traces.to_vec(), - }; - let traces = - get_traces_with_requested_tracer_and_config(traces, tracer_type, tracer_config)?; - return Ok(traces); - } - - let cache_options = create_trace_cache_opts(); - let traces = evm.trace_block_transactions_by_number( - block_number, - Some(cache_options), - None, - working_set, - )?; - ethereum - .trace_cache - .lock() - .unwrap() - .insert(block_number, traces.clone()); - - // Convert the traces to the requested tracer and config - let traces = match trace_idx { - Some(idx) => vec![traces[idx].clone()], - None => traces, - }; - let traces = get_traces_with_requested_tracer_and_config(traces, tracer_type, tracer_config)?; - - Ok(traces) -} - -fn get_traces_with_requested_tracer_and_config( - traces: Vec, - tracer: GethDebugTracerType, - tracer_config: GethDebugTracerConfig, -) -> Result, EthApiError> { - // This can be only CallConfig or PreStateConfig if it is not CallConfig return Error for now - - let mut new_traces = vec![]; - match tracer { - GethDebugTracerType::BuiltInTracer(builtin_tracer) => { - match builtin_tracer { - GethDebugBuiltInTracerType::CallTracer => { - // Apply the call config to the traces - let call_config = - GethDebugTracerConfig::into_call_config(tracer_config).unwrap_or_default(); - // if call config is the same in the cache then do not process again and return early - match call_config { - CallConfig { - only_top_call: None, - with_log: Some(true), - } - | CallConfig { - only_top_call: Some(false), - with_log: Some(true), - } => { - return Ok(traces); - } - _ => { - traces.into_iter().for_each(|trace| { - if let GethTrace::CallTracer(call_frame) = trace { - let new_call_frame = - apply_call_config(call_frame.clone(), call_config); - new_traces.push(GethTrace::CallTracer(new_call_frame)); - } - }); - } - } - Ok(new_traces) - } - GethDebugBuiltInTracerType::FourByteTracer => { - traces.into_iter().for_each(|trace| { - if let GethTrace::CallTracer(call_frame) = trace { - let four_byte_frame = - convert_call_trace_into_4byte_frame(vec![call_frame]); - new_traces.push(GethTrace::FourByteTracer(four_byte_frame)); - } - }); - Ok(new_traces) - } - GethDebugBuiltInTracerType::NoopTracer => { - Ok(vec![GethTrace::NoopTracer(NoopFrame::default())]) - } - _ => Err(EthApiError::Unsupported("This tracer is not supported")), - } - } - GethDebugTracerType::JsTracer(_code) => { - // This also requires DatabaseRef trait - // Implement after readonly state is implemented - Err(EthApiError::Unsupported("JsTracer")) - } - } -} - -pub fn convert_call_trace_into_4byte_frame(call_frames: Vec) -> FourByteFrame { - FourByteFrame(convert_call_trace_into_4byte_map( - call_frames, - BTreeMap::new(), - )) -} - -fn convert_call_trace_into_4byte_map( - call_frames: Vec, - mut four_byte_map: BTreeMap, -) -> BTreeMap { - // For each input in each call - // get the first 4 bytes, get the size of the input - // the key is : "-" - // value is the occurence of the key - for call_frame in call_frames { - let input = call_frame.input; - // If this is a function call (function selector is 4 bytes long) - if input.len() >= 4 { - let input_size = input.0.len() - 4; - let four_byte = &input.to_string()[2..10]; // Ignore the 0x - let key = format!("{}-{}", four_byte, input_size); - let count = four_byte_map.entry(key).or_insert(0); - *count += 1; - } - four_byte_map = convert_call_trace_into_4byte_map(call_frame.calls, four_byte_map); - } - four_byte_map -} - -fn create_trace_cache_opts() -> GethDebugTracingOptions { - // Get the traces with call tracer onlytopcall false and withlog true and always cache this way - let mut call_config_map = serde_json::Map::new(); - call_config_map.insert("only_top_call".to_string(), serde_json::Value::Bool(false)); - call_config_map.insert("with_log".to_string(), serde_json::Value::Bool(true)); - let call_config = serde_json::Value::Object(call_config_map); - GethDebugTracingOptions { - tracer: Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::CallTracer, - )), - tracer_config: GethDebugTracerConfig(call_config), - ..Default::default() - } -} From c5517c84097667a9ed0bd1dcda0de4d2a87faa47 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 17:46:00 +0200 Subject: [PATCH 03/34] Carry subscriptions at the end --- crates/ethereum-rpc/src/lib.rs | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 1df72aa2b..bd55abd3d 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -476,34 +476,6 @@ fn register_rpc_methods( }, )?; - rpc.register_subscription( - "debug_subscribe", - "debug_subscription", - "debug_unsubscribe", - |parameters, pending, ethereum| async move { - let mut params = parameters.sequence(); - - let topic: String = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return Ok(()); - } - }; - match topic.as_str() { - "traceChain" => handle_debug_trace_chain(params, pending, ethereum).await, - _ => { - pending - .reject(EthApiError::Unsupported("Unsupported subscription topic")) - .await; - return Ok(()); - } - }; - - Ok(()) - }, - )?; - rpc.register_async_method("txpool_content", |_, _| async move { info!("eth module: txpool_content"); @@ -666,6 +638,34 @@ fn register_rpc_methods( )?; } + rpc.register_subscription( + "debug_subscribe", + "debug_subscription", + "debug_unsubscribe", + |parameters, pending, ethereum| async move { + let mut params = parameters.sequence(); + + let topic: String = match params.next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return Ok(()); + } + }; + match topic.as_str() { + "traceChain" => handle_debug_trace_chain(params, pending, ethereum).await, + _ => { + pending + .reject(EthApiError::Unsupported("Unsupported subscription topic")) + .await; + return Ok(()); + } + }; + + Ok(()) + }, + )?; + Ok(()) } From 96bdab04f00d6cb0f71d6ab54e87e93f5f19ae59 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 17:56:11 +0200 Subject: [PATCH 04/34] Create eth subscription handler signature --- crates/ethereum-rpc/src/eth_subscription.rs | 15 ++++++++ crates/ethereum-rpc/src/lib.rs | 34 +++++++++++++++++-- .../src/{debug_trace.rs => trace.rs} | 0 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 crates/ethereum-rpc/src/eth_subscription.rs rename crates/ethereum-rpc/src/{debug_trace.rs => trace.rs} (100%) diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs new file mode 100644 index 000000000..8012c00e0 --- /dev/null +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use jsonrpsee::types::ParamsSequence; +use jsonrpsee::PendingSubscriptionSink; +use sov_rollup_interface::services::da::DaService; + +use crate::ethereum::Ethereum; + +pub async fn handle_new_heads_subscription( + _params: ParamsSequence<'_>, + _pending: PendingSubscriptionSink, + _ethereum: Arc>, +) { + unimplemented!() +} diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index bd55abd3d..49f656865 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -1,11 +1,12 @@ -mod debug_trace; +mod eth_subscription; mod ethereum; mod gas_price; +mod trace; #[cfg(feature = "local")] pub use citrea_evm::DevSigner; use citrea_evm::Evm; -use debug_trace::{debug_trace_by_block_number, handle_debug_trace_chain}; +use eth_subscription::handle_new_heads_subscription; pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; pub use gas_price::gas_oracle::GasPriceOracleConfig; @@ -20,6 +21,7 @@ use serde_json::json; use sov_modules_api::utils::to_jsonrpsee_error_object; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; +use trace::{debug_trace_by_block_number, handle_debug_trace_chain}; use tracing::info; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] @@ -666,6 +668,34 @@ fn register_rpc_methods( }, )?; + rpc.register_subscription( + "eth_subscribe", + "eth_subscription", + "eth_unsubscribe", + |parameters, pending, ethereum| async move { + let mut params = parameters.sequence(); + + let topic: String = match params.next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return Ok(()); + } + }; + match topic.as_str() { + "newHeads" => handle_new_heads_subscription(params, pending, ethereum).await, + _ => { + pending + .reject(EthApiError::Unsupported("Unsupported subscription topic")) + .await; + return Ok(()); + } + }; + + Ok(()) + }, + )?; + Ok(()) } diff --git a/crates/ethereum-rpc/src/debug_trace.rs b/crates/ethereum-rpc/src/trace.rs similarity index 100% rename from crates/ethereum-rpc/src/debug_trace.rs rename to crates/ethereum-rpc/src/trace.rs From f022b1158830e3baa12da02e0a83c697b1a0efe0 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 19:15:33 +0200 Subject: [PATCH 05/34] Pass tx to ledger db --- bin/citrea/src/rollup/mod.rs | 10 +++++++--- bin/citrea/src/test_rpc.rs | 3 ++- crates/fullnode/tests/runner_initialization_tests.rs | 4 +++- crates/fullnode/tests/runner_reorg_tests.rs | 6 ++++-- .../full-node/db/sov-db/src/ledger_db/mod.rs | 12 +++++++++--- .../full-node/db/sov-db/src/ledger_db/rpc.rs | 3 ++- .../full-node/sov-ledger-rpc/src/server.rs | 3 ++- .../full-node/sov-ledger-rpc/tests/empty_ledger.rs | 3 ++- .../sov-modules-rollup-blueprint/src/lib.rs | 10 ++++++++-- 9 files changed, 39 insertions(+), 15 deletions(-) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index 83238e722..1f0e36557 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -10,6 +10,7 @@ use sov_modules_rollup_blueprint::RollupBlueprint; use sov_modules_stf_blueprint::{Runtime as RuntimeTrait, StfBlueprint}; use sov_state::storage::NativeStorage; use sov_stf_runner::{FullNodeConfig, InitVariant, ProverConfig}; +use tokio::sync::broadcast; use tracing::instrument; mod bitcoin; mod mock; @@ -37,7 +38,8 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { // Maybe whole "prev_root" can be initialized inside runner // Getting block here, so prover_service doesn't have to be `Send` - let ledger_db = self.create_ledger_db(&rollup_config); + let (soft_confirmation_tx, _) = broadcast::channel(10); + let ledger_db = self.create_ledger_db(&rollup_config, soft_confirmation_tx.clone()); let genesis_config = self.create_genesis_config(runtime_genesis_paths, &rollup_config)?; let mut storage_manager = self.create_storage_manager(&rollup_config)?; @@ -103,7 +105,8 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { // Maybe whole "prev_root" can be initialized inside runner // Getting block here, so prover_service doesn't have to be `Send` - let ledger_db = self.create_ledger_db(&rollup_config); + let (soft_confirmation_tx, _) = broadcast::channel(10); + let ledger_db = self.create_ledger_db(&rollup_config, soft_confirmation_tx.clone()); let genesis_config = self.create_genesis_config(runtime_genesis_paths, &rollup_config)?; let mut storage_manager = self.create_storage_manager(&rollup_config)?; @@ -181,7 +184,8 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { // Maybe whole "prev_root" can be initialized inside runner // Getting block here, so prover_service doesn't have to be `Send` - let ledger_db = self.create_ledger_db(&rollup_config); + let (soft_confirmation_tx, _) = broadcast::channel(10); + let ledger_db = self.create_ledger_db(&rollup_config, soft_confirmation_tx.clone()); let genesis_config = self.create_genesis_config(runtime_genesis_paths, &rollup_config)?; let mut storage_manager = self.create_storage_manager(&rollup_config)?; diff --git a/bin/citrea/src/test_rpc.rs b/bin/citrea/src/test_rpc.rs index a68cf781b..1686466cc 100644 --- a/bin/citrea/src/test_rpc.rs +++ b/bin/citrea/src/test_rpc.rs @@ -20,6 +20,7 @@ use sov_rollup_interface::stf::fuzzing::BatchReceiptStrategyArgs; use sov_rollup_interface::stf::{BatchReceipt, Event, SoftBatchReceipt, TransactionReceipt}; #[cfg(test)] use sov_stf_runner::RpcConfig; +use tokio::sync::broadcast; struct TestExpect { payload: serde_json::Value, @@ -81,7 +82,7 @@ fn test_helper( rt.block_on(async { // Initialize the ledger database, which stores blocks, transactions, events, etc. let tmpdir = tempfile::tempdir().unwrap(); - let mut ledger_db = LedgerDB::with_path(tmpdir.path()).unwrap(); + let mut ledger_db = LedgerDB::with_path(tmpdir.path(), broadcast::channel(1).0).unwrap(); populate_ledger(&mut ledger_db, slots, soft_batch_receipts); let server = jsonrpsee::server::ServerBuilder::default() .build("127.0.0.1:0") diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index 46181c348..9c64da9fd 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -11,6 +11,7 @@ use sov_stf_runner::{ mod hash_stf; use hash_stf::HashStf; +use tokio::sync::broadcast; type MockInitVariant = InitVariant, MockZkvm, MockDaSpec>; @@ -91,7 +92,8 @@ fn initialize_runner( let da_service = MockDaService::new(address, &da_storage_path); - let ledger_db = LedgerDB::with_path(rollup_storage_path.clone()).unwrap(); + let ledger_db = + LedgerDB::with_path(rollup_storage_path.clone(), broadcast::channel(1).0).unwrap(); let stf = HashStf::::new(); diff --git a/crates/fullnode/tests/runner_reorg_tests.rs b/crates/fullnode/tests/runner_reorg_tests.rs index d89afb303..4e43a5d31 100644 --- a/crates/fullnode/tests/runner_reorg_tests.rs +++ b/crates/fullnode/tests/runner_reorg_tests.rs @@ -19,6 +19,7 @@ use sov_rollup_interface::services::da::DaService; use sov_rollup_interface::storage::HierarchicalStorageManager; use sov_state::storage::NativeStorage; use sov_state::{ProverStorage, Storage}; +use tokio::sync::broadcast; type MockInitVariant = InitVariant, MockZkvm, MockDaSpec>; @@ -147,7 +148,8 @@ async fn runner_execution( sync_blocks_count: 10, }; - let ledger_db = LedgerDB::with_path(rollup_storage_path.clone()).unwrap(); + let ledger_db = + LedgerDB::with_path(rollup_storage_path.clone(), broadcast::channel(1).0).unwrap(); let stf = HashStf::::new(); @@ -187,7 +189,7 @@ fn get_saved_root_hash( let mut storage_manager = ProverStorageManager::::new(storage_config).unwrap(); let finalized_storage = storage_manager.create_finalized_storage()?; - let ledger_db = LedgerDB::with_path(path).unwrap(); + let ledger_db = LedgerDB::with_path(path, broadcast::channel(1).0).unwrap(); ledger_db .get_head_slot()? diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs index 85f45324d..165f0279f 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs @@ -8,6 +8,7 @@ use sov_rollup_interface::services::da::SlotData; use sov_rollup_interface::stf::{BatchReceipt, Event, SoftBatchReceipt, StateDiff}; use sov_rollup_interface::zk::Proof; use sov_schema_db::{Schema, SchemaBatch, SeekKeyEncoder, DB}; +use tokio::sync::broadcast; use tracing::instrument; use crate::rocks_db_config::gen_rocksdb_options; @@ -37,7 +38,8 @@ pub struct LedgerDB { /// requires transactions to be executed before being committed. db: Arc, next_item_numbers: Arc>, - slot_subscriptions: tokio::sync::broadcast::Sender, + slot_subscriptions: broadcast::Sender, + soft_confirmation_tx: broadcast::Sender, } /// A SlotNumber, BatchNumber, TxNumber, and EventNumber which are grouped together, typically representing @@ -99,7 +101,10 @@ impl LedgerDB { /// Open a [`LedgerDB`] (backed by RocksDB) at the specified path. /// The returned instance will be at the path `{path}/ledger-db`. #[instrument(level = "trace", skip_all, err)] - pub fn with_path(path: impl AsRef) -> Result { + pub fn with_path( + path: impl AsRef, + soft_confirmation_tx: broadcast::Sender, + ) -> Result { let path = path.as_ref().join(LEDGER_DB_PATH_SUFFIX); let inner = DB::open( path, @@ -123,7 +128,8 @@ impl LedgerDB { Ok(Self { db: Arc::new(inner), next_item_numbers: Arc::new(Mutex::new(next_item_numbers)), - slot_subscriptions: tokio::sync::broadcast::channel(10).0, + slot_subscriptions: broadcast::channel(10).0, + soft_confirmation_tx, }) } diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs index d0a2cb951..49c687ee3 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs @@ -624,13 +624,14 @@ impl LedgerDB { mod tests { use sov_mock_da::{MockBlob, MockBlock}; use sov_rollup_interface::rpc::LedgerRpcProvider; + use tokio::sync::broadcast; use crate::ledger_db::{LedgerDB, SlotCommit}; #[test] fn test_slot_subscription() { let temp_dir = tempfile::tempdir().unwrap(); let path = temp_dir.path(); - let db = LedgerDB::with_path(path).unwrap(); + let db = LedgerDB::with_path(path, broadcast::channel(1).0).unwrap(); let mut rx = db.subscribe_slots().unwrap(); db.commit_slot(SlotCommit::<_, MockBlob, Vec>::new(MockBlock::default())) diff --git a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs index f9c2a8b92..ebae9dcbd 100644 --- a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs +++ b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs @@ -21,11 +21,12 @@ const LEDGER_RPC_ERROR: &str = "LEDGER_RPC_ERROR"; /// use sov_ledger_rpc::server::rpc_module; /// use tempfile::tempdir; /// use sov_db::ledger_db::LedgerDB; +/// use tokio::sync::broadcast; /// /// /// Creates a new [`LedgerDB`] and starts serving JSON-RPC requests. /// async fn rpc_server() -> jsonrpsee::server::ServerHandle { /// let dir = tempdir().unwrap(); -/// let db = LedgerDB::with_path(dir).unwrap(); +/// let db = LedgerDB::with_path(dir, broadcast::channel(10).0).unwrap(); /// let rpc_module = rpc_module::(db).unwrap(); /// /// let server = jsonrpsee::server::ServerBuilder::default() diff --git a/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs b/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs index b7097642e..58f3fa645 100644 --- a/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs +++ b/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs @@ -13,10 +13,11 @@ use sov_rollup_interface::rpc::{ TxResponse, }; use tempfile::tempdir; +use tokio::sync::broadcast; async fn rpc_server() -> (jsonrpsee::server::ServerHandle, SocketAddr) { let dir = tempdir().unwrap(); - let db = LedgerDB::with_path(dir).unwrap(); + let db = LedgerDB::with_path(dir, broadcast::channel(1).0).unwrap(); let rpc_module = rpc_module::(db).unwrap(); let server = jsonrpsee::server::ServerBuilder::default() diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index c65781edf..a5061a97c 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -14,6 +14,7 @@ use sov_rollup_interface::storage::HierarchicalStorageManager; use sov_rollup_interface::zk::{Zkvm, ZkvmHost}; use sov_state::Storage; use sov_stf_runner::{FullNodeConfig, ProverConfig, ProverService}; +use tokio::sync::broadcast; pub use wallet::*; /// This trait defines how to crate all the necessary dependencies required by a rollup. @@ -115,7 +116,12 @@ pub trait RollupBlueprint: Sized + Send + Sync { ) -> Result; /// Creates instance of a LedgerDB. - fn create_ledger_db(&self, rollup_config: &FullNodeConfig) -> LedgerDB { - LedgerDB::with_path(&rollup_config.storage.path).expect("Ledger DB failed to open") + fn create_ledger_db( + &self, + rollup_config: &FullNodeConfig, + soft_confirmation_tx: broadcast::Sender, + ) -> LedgerDB { + LedgerDB::with_path(&rollup_config.storage.path, soft_confirmation_tx) + .expect("Ledger DB failed to open") } } From 63ac5c3c360fde1eb6e1833e608d8589bd8fd4fa Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 19:29:00 +0200 Subject: [PATCH 06/34] Pass down the tx to rpc context --- bin/citrea/src/eth.rs | 3 +++ bin/citrea/src/rollup/bitcoin.rs | 3 +++ bin/citrea/src/rollup/mock.rs | 3 +++ bin/citrea/src/rollup/mod.rs | 11 +++++++++-- crates/ethereum-rpc/src/ethereum.rs | 4 ++++ crates/ethereum-rpc/src/lib.rs | 3 +++ .../sov-modules-rollup-blueprint/src/lib.rs | 1 + 7 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bin/citrea/src/eth.rs b/bin/citrea/src/eth.rs index c09f568f7..1b0d7a987 100644 --- a/bin/citrea/src/eth.rs +++ b/bin/citrea/src/eth.rs @@ -6,6 +6,7 @@ use sov_modules_api::default_context::DefaultContext; use sov_prover_storage_manager::SnapshotManager; use sov_rollup_interface::services::da::DaService; use sov_state::ProverStorage; +use tokio::sync::broadcast; // register ethereum methods. pub(crate) fn register_ethereum( @@ -13,6 +14,7 @@ pub(crate) fn register_ethereum( storage: ProverStorage, methods: &mut jsonrpsee::RpcModule<()>, sequencer_client_url: Option, + soft_commitment_tx: broadcast::Sender, ) -> Result<(), anyhow::Error> { let eth_rpc_config = { let eth_signer = eth_dev_signer(); @@ -28,6 +30,7 @@ pub(crate) fn register_ethereum( eth_rpc_config, storage, sequencer_client_url, + soft_commitment_tx, ); methods .merge(ethereum_rpc) diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index 3326add5d..1e6b8b174 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -18,6 +18,7 @@ use sov_rollup_interface::da::DaVerifier; use sov_rollup_interface::zk::{Zkvm, ZkvmHost}; use sov_state::{DefaultStorageSpec, Storage, ZkStorage}; use sov_stf_runner::{FullNodeConfig, ProverConfig}; +use tokio::sync::broadcast; use tracing::instrument; use crate::CitreaRollupBlueprint; @@ -61,6 +62,7 @@ impl RollupBlueprint for BitcoinRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, + soft_commitment_tx: broadcast::Sender, ) -> Result, anyhow::Error> { // unused inside register RPC let sov_sequencer = Address::new([0; 32]); @@ -77,6 +79,7 @@ impl RollupBlueprint for BitcoinRollup { storage.clone(), &mut rpc_methods, sequencer_client_url, + soft_commitment_tx, )?; Ok(rpc_methods) diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index b0991f0ea..868cfbaef 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -14,6 +14,7 @@ use sov_prover_storage_manager::ProverStorageManager; use sov_rollup_interface::zk::{Zkvm, ZkvmHost}; use sov_state::{DefaultStorageSpec, Storage, ZkStorage}; use sov_stf_runner::{FullNodeConfig, ProverConfig}; +use tokio::sync::broadcast; use crate::CitreaRollupBlueprint; @@ -55,6 +56,7 @@ impl RollupBlueprint for MockDemoRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, + soft_commitment_tx: broadcast::Sender, ) -> Result, anyhow::Error> { // TODO set the sequencer address let sequencer = Address::new([0; 32]); @@ -71,6 +73,7 @@ impl RollupBlueprint for MockDemoRollup { storage.clone(), &mut rpc_methods, sequencer_client_url, + soft_commitment_tx, )?; Ok(rpc_methods) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index 1f0e36557..0f3a37cd5 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -46,8 +46,13 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let prover_storage = storage_manager.create_finalized_storage()?; // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) - let rpc_methods = - self.create_rpc_methods(&prover_storage, &ledger_db, &da_service, None)?; + let rpc_methods = self.create_rpc_methods( + &prover_storage, + &ledger_db, + &da_service, + None, + soft_confirmation_tx, + )?; let native_stf = StfBlueprint::new(); @@ -119,6 +124,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), + soft_confirmation_tx, )?; let native_stf = StfBlueprint::new(); @@ -198,6 +204,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), + soft_confirmation_tx, )?; let native_stf = StfBlueprint::new(); diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs index 6ee58ea00..020116353 100644 --- a/crates/ethereum-rpc/src/ethereum.rs +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -11,6 +11,7 @@ use sequencer_client::SequencerClient; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; use sov_rollup_interface::CITREA_VERSION; +use tokio::sync::broadcast; use tracing::instrument; use crate::gas_price::fee_history::FeeHistoryCacheConfig; @@ -36,6 +37,7 @@ pub struct Ethereum { pub(crate) sequencer_client: Option, pub(crate) web3_client_version: String, pub(crate) trace_cache: Mutex, ByLength>>, + pub(crate) soft_commitment_tx: broadcast::Sender, } impl Ethereum { @@ -46,6 +48,7 @@ impl Ethereum { #[cfg(feature = "local")] eth_signer: DevSigner, storage: C::Storage, sequencer_client: Option, + soft_commitment_tx: broadcast::Sender, ) -> Self { let evm = Evm::::default(); let gas_price_oracle = @@ -68,6 +71,7 @@ impl Ethereum { sequencer_client, web3_client_version: current_version, trace_cache, + soft_commitment_tx, } } diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 49f656865..ae58d7b32 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -21,6 +21,7 @@ use serde_json::json; use sov_modules_api::utils::to_jsonrpsee_error_object; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; +use tokio::sync::broadcast; use trace::{debug_trace_by_block_number, handle_debug_trace_chain}; use tracing::info; @@ -43,6 +44,7 @@ pub fn get_ethereum_rpc( eth_rpc_config: EthRpcConfig, storage: C::Storage, sequencer_client_url: Option, + soft_commitment_tx: broadcast::Sender, ) -> RpcModule> { // Unpack config let EthRpcConfig { @@ -64,6 +66,7 @@ pub fn get_ethereum_rpc( eth_signer, storage, sequencer_client_url.map(SequencerClient::new), + soft_commitment_tx, )); register_rpc_methods(&mut rpc, is_sequencer).expect("Failed to register ethereum RPC methods"); diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index a5061a97c..138565eee 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -68,6 +68,7 @@ pub trait RollupBlueprint: Sized + Send + Sync { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, + soft_confirmation_tx: broadcast::Sender, ) -> Result, anyhow::Error>; /// Creates GenesisConfig from genesis files. From 01e94bc725e88b318ee05c430e99ff207dfcfb80 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 19:32:53 +0200 Subject: [PATCH 07/34] Send height after commit --- crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs index 165f0279f..a9d9c32f1 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs @@ -359,6 +359,9 @@ impl LedgerDB { self.db.write_schemas(schema_batch)?; + // Error can be ignored becaue it only errors when there are no receivers + let _ = self.soft_confirmation_tx.send(batch_to_store.l2_height); + Ok(()) } From 5a23d739f66551538822baef9bd86fca5c3aef9c Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 19:36:53 +0200 Subject: [PATCH 08/34] Update new head handler fn signature --- crates/ethereum-rpc/src/eth_subscription.rs | 1 - crates/ethereum-rpc/src/lib.rs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs index 8012c00e0..aa9779f9d 100644 --- a/crates/ethereum-rpc/src/eth_subscription.rs +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -7,7 +7,6 @@ use sov_rollup_interface::services::da::DaService; use crate::ethereum::Ethereum; pub async fn handle_new_heads_subscription( - _params: ParamsSequence<'_>, _pending: PendingSubscriptionSink, _ethereum: Arc>, ) { diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index ae58d7b32..fbd37d113 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -676,9 +676,7 @@ fn register_rpc_methods( "eth_subscription", "eth_unsubscribe", |parameters, pending, ethereum| async move { - let mut params = parameters.sequence(); - - let topic: String = match params.next() { + let topic: String = match parameters.one() { Ok(v) => v, Err(err) => { pending.reject(err).await; @@ -686,7 +684,7 @@ fn register_rpc_methods( } }; match topic.as_str() { - "newHeads" => handle_new_heads_subscription(params, pending, ethereum).await, + "newHeads" => handle_new_heads_subscription(pending, ethereum).await, _ => { pending .reject(EthApiError::Unsupported("Unsupported subscription topic")) From 1ae585ac7601713d600020cd6cb11180c8348ba7 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 19:55:27 +0200 Subject: [PATCH 09/34] Finalize handling --- crates/ethereum-rpc/src/eth_subscription.rs | 36 ++++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs index aa9779f9d..993a588c0 100644 --- a/crates/ethereum-rpc/src/eth_subscription.rs +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -1,14 +1,40 @@ use std::sync::Arc; -use jsonrpsee::types::ParamsSequence; -use jsonrpsee::PendingSubscriptionSink; +use citrea_evm::Evm; +use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage}; +use reth_rpc_types::BlockNumberOrTag; +use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; use crate::ethereum::Ethereum; pub async fn handle_new_heads_subscription( - _pending: PendingSubscriptionSink, - _ethereum: Arc>, + pending: PendingSubscriptionSink, + ethereum: Arc>, ) { - unimplemented!() + let mut rx = ethereum.soft_commitment_tx.subscribe(); + let evm = Evm::::default(); + let subscription = pending.accept().await.unwrap(); + tokio::spawn(async move { + loop { + let block_number = rx.recv().await.unwrap(); + let mut working_set = WorkingSet::::new(ethereum.storage.clone()); + let block = evm + .get_block_by_number( + Some(BlockNumberOrTag::Number(block_number)), + None, + &mut working_set, + ) + .unwrap() + .unwrap(); + + let msg = SubscriptionMessage::new( + subscription.method_name(), + subscription.subscription_id(), + &block, + ) + .unwrap(); + let _ = subscription.send(msg).await; + } + }); } From c7adbcc678486fc71d39831ffa8f72f0f90e0b66 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 22:08:42 +0200 Subject: [PATCH 10/34] Add test --- bin/citrea/tests/evm/mod.rs | 1 + bin/citrea/tests/evm/subscription.rs | 90 ++++++++++++++++++++++++++++ bin/citrea/tests/test_client/mod.rs | 22 +++++++ 3 files changed, 113 insertions(+) create mode 100644 bin/citrea/tests/evm/subscription.rs diff --git a/bin/citrea/tests/evm/mod.rs b/bin/citrea/tests/evm/mod.rs index 1e368fb4c..322dd82bb 100644 --- a/bin/citrea/tests/evm/mod.rs +++ b/bin/citrea/tests/evm/mod.rs @@ -20,6 +20,7 @@ use crate::{ mod archival_state; mod gas_price; +mod subscription; mod tracing; #[tokio::test(flavor = "multi_thread")] diff --git a/bin/citrea/tests/evm/subscription.rs b/bin/citrea/tests/evm/subscription.rs new file mode 100644 index 000000000..1646a3986 --- /dev/null +++ b/bin/citrea/tests/evm/subscription.rs @@ -0,0 +1,90 @@ +use std::sync::{Arc, Mutex}; + +// use citrea::initialize_logging; +use citrea_stf::genesis_config::GenesisPaths; +use reth_primitives::Address; + +use crate::evm::make_test_client; +use crate::test_helpers::{start_rollup, tempdir_with_children, wait_for_l2_block, NodeMode}; +use crate::{ + DEFAULT_DEPOSIT_MEMPOOL_FETCH_LIMIT, DEFAULT_MIN_SOFT_CONFIRMATIONS_PER_COMMITMENT, + TEST_DATA_GENESIS_PATH, +}; + +#[tokio::test(flavor = "multi_thread")] +async fn test_eth_subscriptions() -> Result<(), Box> { + let storage_dir = tempdir_with_children(&["DA", "sequencer", "full-node"]); + let da_db_dir = storage_dir.path().join("DA").to_path_buf(); + let sequencer_db_dir = storage_dir.path().join("sequencer").to_path_buf(); + + let (port_tx, port_rx) = tokio::sync::oneshot::channel(); + let da_db_dir_cloned = da_db_dir.clone(); + let seq_task = tokio::spawn(async { + // Don't provide a prover since the EVM is not currently provable + start_rollup( + port_tx, + GenesisPaths::from_dir(TEST_DATA_GENESIS_PATH), + None, + NodeMode::SequencerNode, + sequencer_db_dir, + da_db_dir_cloned, + DEFAULT_MIN_SOFT_CONFIRMATIONS_PER_COMMITMENT, + true, + None, + None, + Some(true), + DEFAULT_DEPOSIT_MEMPOOL_FETCH_LIMIT, + ) + .await; + }); + + // Wait for rollup task to start: + let port = port_rx.await.unwrap(); + + let test_client = make_test_client(port).await; + + test_client.send_publish_batch_request().await; + wait_for_l2_block(&test_client, 1, None).await; + + let new_block_rx = test_client.subscribe_new_heads().await; + let last_received_block = Arc::new(Mutex::new(None)); + let last_received_block_clone = last_received_block.clone(); + tokio::spawn(async move { + loop { + let Ok(block) = new_block_rx.recv() else { + return; + }; + *(last_received_block_clone.lock().unwrap()) = Some(block); + } + }); + + { + test_client.send_publish_batch_request().await; + wait_for_l2_block(&test_client, 2, None).await; + + let block = last_received_block.lock().unwrap(); + let block = block.as_ref().unwrap(); + assert_eq!(block.header.number, Some(2)); + assert!(block.transactions.is_empty()); + } + + { + let pending_tx = test_client + .send_eth(Address::random(), None, None, None, 10000) + .await + .unwrap(); + let tx_hash = *pending_tx.tx_hash(); + + test_client.send_publish_batch_request().await; + wait_for_l2_block(&test_client, 3, None).await; + + let block = last_received_block.lock().unwrap(); + let block = block.as_ref().unwrap(); + assert_eq!(block.header.number, Some(3)); + assert_eq!(block.transactions.len(), 1); + assert_eq!(block.transactions.hashes().last().unwrap().clone(), tx_hash); + } + + seq_task.abort(); + Ok(()) +} diff --git a/bin/citrea/tests/test_client/mod.rs b/bin/citrea/tests/test_client/mod.rs index 98f0cd2e8..790c58781 100644 --- a/bin/citrea/tests/test_client/mod.rs +++ b/bin/citrea/tests/test_client/mod.rs @@ -1,5 +1,6 @@ use std::str::FromStr; use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::mpsc; use std::time::Duration; use alloy::providers::network::{Ethereum, EthereumSigner}; @@ -16,6 +17,7 @@ use jsonrpsee::ws_client::{PingConfig, WsClient, WsClientBuilder}; use reth_primitives::{Address, BlockId, BlockNumberOrTag, Bytes, TxHash, TxKind, B256, U256, U64}; // use reth_rpc_types::TransactionReceipt; use reth_rpc_types::trace::geth::{GethDebugTracingOptions, GethTrace}; +use reth_rpc_types::RichBlock; use sequencer_client::GetSoftBatchResponse; use sov_rollup_interface::rpc::{ LastVerifiedProofResponse, ProofResponse, SequencerCommitmentResponse, SoftBatchResponse, @@ -691,6 +693,26 @@ impl TestClient { traces.into_iter().flatten().collect() } + pub(crate) async fn subscribe_new_heads(&self) -> mpsc::Receiver { + let (tx, rx) = mpsc::channel(); + let mut subscription = self + .ws_client + .subscribe("eth_subscribe", rpc_params!["newHeads"], "eth_unsubscribe") + .await + .unwrap(); + + tokio::spawn(async move { + loop { + let Some(Ok(block)) = subscription.next().await else { + return; + }; + tx.send(block).unwrap(); + } + }); + + rx + } + pub(crate) async fn eth_block_number(&self) -> u64 { let block_number: U256 = self .http_client From d0174f0f4fa8792e223ac315e846bfb143312e2d Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 22 Jul 2024 22:13:52 +0200 Subject: [PATCH 11/34] unwrap to expect --- crates/ethereum-rpc/src/eth_subscription.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs index 993a588c0..6e0114721 100644 --- a/crates/ethereum-rpc/src/eth_subscription.rs +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -17,7 +17,10 @@ pub async fn handle_new_heads_subscription::new(ethereum.storage.clone()); let block = evm .get_block_by_number( @@ -25,8 +28,8 @@ pub async fn handle_new_heads_subscription Date: Tue, 23 Jul 2024 19:57:35 +0200 Subject: [PATCH 12/34] Remove notifying from ledger db --- bin/citrea/src/rollup/mod.rs | 18 +++++++++--------- bin/citrea/src/test_rpc.rs | 3 +-- .../tests/runner_initialization_tests.rs | 4 +--- crates/fullnode/tests/runner_reorg_tests.rs | 6 ++---- .../full-node/db/sov-db/src/ledger_db/mod.rs | 10 +--------- .../full-node/db/sov-db/src/ledger_db/rpc.rs | 3 +-- .../full-node/sov-ledger-rpc/src/server.rs | 3 +-- .../sov-ledger-rpc/tests/empty_ledger.rs | 3 +-- .../sov-modules-rollup-blueprint/src/lib.rs | 9 ++------- 9 files changed, 19 insertions(+), 40 deletions(-) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index 0f3a37cd5..1d655b965 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -38,20 +38,20 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { // Maybe whole "prev_root" can be initialized inside runner // Getting block here, so prover_service doesn't have to be `Send` - let (soft_confirmation_tx, _) = broadcast::channel(10); - let ledger_db = self.create_ledger_db(&rollup_config, soft_confirmation_tx.clone()); + let ledger_db = self.create_ledger_db(&rollup_config); let genesis_config = self.create_genesis_config(runtime_genesis_paths, &rollup_config)?; let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; + let (soft_confirmation_tx, _) = broadcast::channel(10); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( &prover_storage, &ledger_db, &da_service, None, - soft_confirmation_tx, + soft_confirmation_tx.clone(), )?; let native_stf = StfBlueprint::new(); @@ -110,21 +110,21 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { // Maybe whole "prev_root" can be initialized inside runner // Getting block here, so prover_service doesn't have to be `Send` - let (soft_confirmation_tx, _) = broadcast::channel(10); - let ledger_db = self.create_ledger_db(&rollup_config, soft_confirmation_tx.clone()); + let ledger_db = self.create_ledger_db(&rollup_config); let genesis_config = self.create_genesis_config(runtime_genesis_paths, &rollup_config)?; let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; let runner_config = rollup_config.runner.expect("Runner config is missing"); + let (soft_confirmation_tx, _) = broadcast::channel(10); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( &prover_storage, &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - soft_confirmation_tx, + soft_confirmation_tx.clone(), )?; let native_stf = StfBlueprint::new(); @@ -190,13 +190,13 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { // Maybe whole "prev_root" can be initialized inside runner // Getting block here, so prover_service doesn't have to be `Send` - let (soft_confirmation_tx, _) = broadcast::channel(10); - let ledger_db = self.create_ledger_db(&rollup_config, soft_confirmation_tx.clone()); + let ledger_db = self.create_ledger_db(&rollup_config); let genesis_config = self.create_genesis_config(runtime_genesis_paths, &rollup_config)?; let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; + let (soft_confirmation_tx, _) = broadcast::channel(10); let runner_config = rollup_config.runner.expect("Runner config is missing"); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( @@ -204,7 +204,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - soft_confirmation_tx, + soft_confirmation_tx.clone(), )?; let native_stf = StfBlueprint::new(); diff --git a/bin/citrea/src/test_rpc.rs b/bin/citrea/src/test_rpc.rs index 1686466cc..a68cf781b 100644 --- a/bin/citrea/src/test_rpc.rs +++ b/bin/citrea/src/test_rpc.rs @@ -20,7 +20,6 @@ use sov_rollup_interface::stf::fuzzing::BatchReceiptStrategyArgs; use sov_rollup_interface::stf::{BatchReceipt, Event, SoftBatchReceipt, TransactionReceipt}; #[cfg(test)] use sov_stf_runner::RpcConfig; -use tokio::sync::broadcast; struct TestExpect { payload: serde_json::Value, @@ -82,7 +81,7 @@ fn test_helper( rt.block_on(async { // Initialize the ledger database, which stores blocks, transactions, events, etc. let tmpdir = tempfile::tempdir().unwrap(); - let mut ledger_db = LedgerDB::with_path(tmpdir.path(), broadcast::channel(1).0).unwrap(); + let mut ledger_db = LedgerDB::with_path(tmpdir.path()).unwrap(); populate_ledger(&mut ledger_db, slots, soft_batch_receipts); let server = jsonrpsee::server::ServerBuilder::default() .build("127.0.0.1:0") diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index 9c64da9fd..46181c348 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -11,7 +11,6 @@ use sov_stf_runner::{ mod hash_stf; use hash_stf::HashStf; -use tokio::sync::broadcast; type MockInitVariant = InitVariant, MockZkvm, MockDaSpec>; @@ -92,8 +91,7 @@ fn initialize_runner( let da_service = MockDaService::new(address, &da_storage_path); - let ledger_db = - LedgerDB::with_path(rollup_storage_path.clone(), broadcast::channel(1).0).unwrap(); + let ledger_db = LedgerDB::with_path(rollup_storage_path.clone()).unwrap(); let stf = HashStf::::new(); diff --git a/crates/fullnode/tests/runner_reorg_tests.rs b/crates/fullnode/tests/runner_reorg_tests.rs index 4e43a5d31..d89afb303 100644 --- a/crates/fullnode/tests/runner_reorg_tests.rs +++ b/crates/fullnode/tests/runner_reorg_tests.rs @@ -19,7 +19,6 @@ use sov_rollup_interface::services::da::DaService; use sov_rollup_interface::storage::HierarchicalStorageManager; use sov_state::storage::NativeStorage; use sov_state::{ProverStorage, Storage}; -use tokio::sync::broadcast; type MockInitVariant = InitVariant, MockZkvm, MockDaSpec>; @@ -148,8 +147,7 @@ async fn runner_execution( sync_blocks_count: 10, }; - let ledger_db = - LedgerDB::with_path(rollup_storage_path.clone(), broadcast::channel(1).0).unwrap(); + let ledger_db = LedgerDB::with_path(rollup_storage_path.clone()).unwrap(); let stf = HashStf::::new(); @@ -189,7 +187,7 @@ fn get_saved_root_hash( let mut storage_manager = ProverStorageManager::::new(storage_config).unwrap(); let finalized_storage = storage_manager.create_finalized_storage()?; - let ledger_db = LedgerDB::with_path(path, broadcast::channel(1).0).unwrap(); + let ledger_db = LedgerDB::with_path(path).unwrap(); ledger_db .get_head_slot()? diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs index a9d9c32f1..40833dbef 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs @@ -39,7 +39,6 @@ pub struct LedgerDB { db: Arc, next_item_numbers: Arc>, slot_subscriptions: broadcast::Sender, - soft_confirmation_tx: broadcast::Sender, } /// A SlotNumber, BatchNumber, TxNumber, and EventNumber which are grouped together, typically representing @@ -101,10 +100,7 @@ impl LedgerDB { /// Open a [`LedgerDB`] (backed by RocksDB) at the specified path. /// The returned instance will be at the path `{path}/ledger-db`. #[instrument(level = "trace", skip_all, err)] - pub fn with_path( - path: impl AsRef, - soft_confirmation_tx: broadcast::Sender, - ) -> Result { + pub fn with_path(path: impl AsRef) -> Result { let path = path.as_ref().join(LEDGER_DB_PATH_SUFFIX); let inner = DB::open( path, @@ -129,7 +125,6 @@ impl LedgerDB { db: Arc::new(inner), next_item_numbers: Arc::new(Mutex::new(next_item_numbers)), slot_subscriptions: broadcast::channel(10).0, - soft_confirmation_tx, }) } @@ -359,9 +354,6 @@ impl LedgerDB { self.db.write_schemas(schema_batch)?; - // Error can be ignored becaue it only errors when there are no receivers - let _ = self.soft_confirmation_tx.send(batch_to_store.l2_height); - Ok(()) } diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs index 49c687ee3..d0a2cb951 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/rpc.rs @@ -624,14 +624,13 @@ impl LedgerDB { mod tests { use sov_mock_da::{MockBlob, MockBlock}; use sov_rollup_interface::rpc::LedgerRpcProvider; - use tokio::sync::broadcast; use crate::ledger_db::{LedgerDB, SlotCommit}; #[test] fn test_slot_subscription() { let temp_dir = tempfile::tempdir().unwrap(); let path = temp_dir.path(); - let db = LedgerDB::with_path(path, broadcast::channel(1).0).unwrap(); + let db = LedgerDB::with_path(path).unwrap(); let mut rx = db.subscribe_slots().unwrap(); db.commit_slot(SlotCommit::<_, MockBlob, Vec>::new(MockBlock::default())) diff --git a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs index ebae9dcbd..3ce23311f 100644 --- a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs +++ b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs @@ -21,12 +21,11 @@ const LEDGER_RPC_ERROR: &str = "LEDGER_RPC_ERROR"; /// use sov_ledger_rpc::server::rpc_module; /// use tempfile::tempdir; /// use sov_db::ledger_db::LedgerDB; -/// use tokio::sync::broadcast; /// /// /// Creates a new [`LedgerDB`] and starts serving JSON-RPC requests. /// async fn rpc_server() -> jsonrpsee::server::ServerHandle { /// let dir = tempdir().unwrap(); -/// let db = LedgerDB::with_path(dir, broadcast::channel(10).0).unwrap(); +/// let db = LedgerDB::with_path(dir)).unwrap(); /// let rpc_module = rpc_module::(db).unwrap(); /// /// let server = jsonrpsee::server::ServerBuilder::default() diff --git a/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs b/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs index 58f3fa645..b7097642e 100644 --- a/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs +++ b/crates/sovereign-sdk/full-node/sov-ledger-rpc/tests/empty_ledger.rs @@ -13,11 +13,10 @@ use sov_rollup_interface::rpc::{ TxResponse, }; use tempfile::tempdir; -use tokio::sync::broadcast; async fn rpc_server() -> (jsonrpsee::server::ServerHandle, SocketAddr) { let dir = tempdir().unwrap(); - let db = LedgerDB::with_path(dir, broadcast::channel(1).0).unwrap(); + let db = LedgerDB::with_path(dir).unwrap(); let rpc_module = rpc_module::(db).unwrap(); let server = jsonrpsee::server::ServerBuilder::default() diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index 138565eee..b7530bc71 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -117,12 +117,7 @@ pub trait RollupBlueprint: Sized + Send + Sync { ) -> Result; /// Creates instance of a LedgerDB. - fn create_ledger_db( - &self, - rollup_config: &FullNodeConfig, - soft_confirmation_tx: broadcast::Sender, - ) -> LedgerDB { - LedgerDB::with_path(&rollup_config.storage.path, soft_confirmation_tx) - .expect("Ledger DB failed to open") + fn create_ledger_db(&self, rollup_config: &FullNodeConfig) -> LedgerDB { + LedgerDB::with_path(&rollup_config.storage.path).expect("Ledger DB failed to open") } } From 5b5ac8eaa71024b5951c008b4da7ab8a22a15aa7 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 20:16:47 +0200 Subject: [PATCH 13/34] Send new l2 block notif in sequencer and fullnode --- bin/citrea/src/rollup/mod.rs | 2 ++ crates/fullnode/src/runner.rs | 8 +++++++- crates/fullnode/tests/runner_initialization_tests.rs | 2 ++ crates/fullnode/tests/runner_reorg_tests.rs | 2 ++ crates/sequencer/src/sequencer.rs | 8 +++++++- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index 1d655b965..f92c62b9b 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -82,6 +82,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { rollup_config.public_keys, ledger_db, rollup_config.rpc, + soft_confirmation_tx, ) .unwrap(); @@ -158,6 +159,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { init_variant, code_commitment, rollup_config.sync_blocks_count, + soft_confirmation_tx, )?; Ok(FullNode { diff --git a/crates/fullnode/src/runner.rs b/crates/fullnode/src/runner.rs index 38c9d9d22..c711088ae 100644 --- a/crates/fullnode/src/runner.rs +++ b/crates/fullnode/src/runner.rs @@ -29,7 +29,7 @@ use sov_rollup_interface::storage::HierarchicalStorageManager; use sov_rollup_interface::zk::{Proof, Zkvm, ZkvmHost}; use sov_stf_runner::{InitVariant, RollupPublicKeys, RpcConfig, RunnerConfig}; use tokio::select; -use tokio::sync::{mpsc, oneshot, Mutex}; +use tokio::sync::{broadcast, mpsc, oneshot, Mutex}; use tokio::time::{sleep, Duration}; use tracing::{debug, error, info, instrument, warn}; @@ -65,6 +65,7 @@ where accept_public_input_as_proven: bool, l1_block_cache: Arc>>, sync_blocks_count: u64, + soft_confirmation_tx: broadcast::Sender, } impl CitreaFullnode @@ -98,6 +99,7 @@ where init_variant: InitVariant, code_commitment: Vm::CodeCommitment, sync_blocks_count: u64, + soft_confirmation_tx: broadcast::Sender, ) -> Result { let (prev_state_root, prev_batch_hash) = match init_variant { InitVariant::Initialized((state_root, batch_hash)) => { @@ -148,6 +150,7 @@ where .unwrap_or(false), sync_blocks_count, l1_block_cache: Arc::new(Mutex::new(L1BlockCache::new())), + soft_confirmation_tx, }) } @@ -466,6 +469,9 @@ where BatchNumber(l2_height), )?; + // Only errors when there are no receivers + let _ = self.soft_confirmation_tx.send(l2_height); + self.state_root = next_state_root; self.batch_hash = soft_batch.hash; diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index 46181c348..420c44d26 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -11,6 +11,7 @@ use sov_stf_runner::{ mod hash_stf; use hash_stf::HashStf; +use tokio::sync::broadcast; type MockInitVariant = InitVariant, MockZkvm, MockDaSpec>; @@ -114,6 +115,7 @@ fn initialize_runner( init_variant, MockCodeCommitment([1u8; 32]), 10, + broadcast::channel(1).0, ) .unwrap() } diff --git a/crates/fullnode/tests/runner_reorg_tests.rs b/crates/fullnode/tests/runner_reorg_tests.rs index d89afb303..8b440a2f2 100644 --- a/crates/fullnode/tests/runner_reorg_tests.rs +++ b/crates/fullnode/tests/runner_reorg_tests.rs @@ -19,6 +19,7 @@ use sov_rollup_interface::services::da::DaService; use sov_rollup_interface::storage::HierarchicalStorageManager; use sov_state::storage::NativeStorage; use sov_state::{ProverStorage, Storage}; +use tokio::sync::broadcast; type MockInitVariant = InitVariant, MockZkvm, MockDaSpec>; @@ -167,6 +168,7 @@ async fn runner_execution( init_variant, MockCodeCommitment([1u8; 32]), 10, + broadcast::channel(1).0, ) .unwrap(); diff --git a/crates/sequencer/src/sequencer.rs b/crates/sequencer/src/sequencer.rs index 79bdff3bf..6a906702e 100644 --- a/crates/sequencer/src/sequencer.rs +++ b/crates/sequencer/src/sequencer.rs @@ -43,7 +43,7 @@ use sov_rollup_interface::storage::HierarchicalStorageManager; use sov_rollup_interface::zk::ZkvmHost; use sov_stf_runner::{InitVariant, RollupPublicKeys, RpcConfig}; use tokio::sync::oneshot::channel as oneshot_channel; -use tokio::sync::{mpsc, Mutex}; +use tokio::sync::{broadcast, mpsc, Mutex}; use tokio::time::{sleep, Instant}; use tower_http::cors::{Any, CorsLayer}; use tracing::{debug, error, info, instrument, trace, warn}; @@ -91,6 +91,7 @@ where rpc_config: RpcConfig, soft_confirmation_rule_enforcer: SoftConfirmationRuleEnforcer, last_state_diff: StateDiff, + soft_confirmation_tx: broadcast::Sender, } enum L2BlockMode { @@ -123,6 +124,7 @@ where public_keys: RollupPublicKeys, ledger_db: LedgerDB, rpc_config: RpcConfig, + soft_confirmation_tx: broadcast::Sender, ) -> anyhow::Result { let (l2_force_block_tx, l2_force_block_rx) = unbounded(); @@ -180,6 +182,7 @@ where rpc_config, soft_confirmation_rule_enforcer, last_state_diff, + soft_confirmation_tx, }) } @@ -527,6 +530,9 @@ where BatchNumber(l2_height), )?; + // Only errors when there are no receivers + let _ = self.soft_confirmation_tx.send(l2_height); + let l1_height = da_block.header().height(); info!( "New block #{}, DA #{}, Tx count: #{}", From e9e26a9b3fcc0a21229eb86c2b9cfd6c23266400 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 20:32:06 +0200 Subject: [PATCH 14/34] Add to prover as well --- bin/citrea/src/rollup/mod.rs | 1 + crates/prover/src/runner.rs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index f92c62b9b..f43b974eb 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -242,6 +242,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { Some(prover_config), code_commitment, rollup_config.sync_blocks_count, + soft_confirmation_tx, )?; Ok(Prover { diff --git a/crates/prover/src/runner.rs b/crates/prover/src/runner.rs index d9b516eae..235167bc8 100644 --- a/crates/prover/src/runner.rs +++ b/crates/prover/src/runner.rs @@ -30,7 +30,7 @@ use sov_stf_runner::{ InitVariant, ProverConfig, ProverService, RollupPublicKeys, RpcConfig, RunnerConfig, }; use tokio::select; -use tokio::sync::{mpsc, oneshot, Mutex}; +use tokio::sync::{broadcast, mpsc, oneshot, Mutex}; use tokio::time::sleep; use tracing::{debug, error, info, instrument, warn}; @@ -72,6 +72,7 @@ where code_commitment: Vm::CodeCommitment, l1_block_cache: Arc>>, sync_blocks_count: u64, + soft_confirmation_tx: broadcast::Sender, } impl CitreaProver @@ -108,6 +109,7 @@ where prover_config: Option, code_commitment: Vm::CodeCommitment, sync_blocks_count: u64, + soft_confirmation_tx: broadcast::Sender, ) -> Result { let (prev_state_root, prev_batch_hash) = match init_variant { InitVariant::Initialized((state_root, batch_hash)) => { @@ -153,6 +155,7 @@ where code_commitment, l1_block_cache: Arc::new(Mutex::new(L1BlockCache::new())), sync_blocks_count, + soft_confirmation_tx, }) } @@ -373,6 +376,9 @@ where BatchNumber(l2_height), )?; + // Only errors when there are no receivers + let _ = self.soft_confirmation_tx.send(l2_height); + self.state_root = next_state_root; self.batch_hash = soft_batch.hash; From 75550b2d3cb07413f5c251925e0910d6c9ae49c0 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 20:40:30 +0200 Subject: [PATCH 15/34] Make tx optional in rpc --- bin/citrea/src/eth.rs | 2 +- bin/citrea/src/rollup/bitcoin.rs | 2 +- bin/citrea/src/rollup/mock.rs | 2 +- bin/citrea/src/rollup/mod.rs | 6 +- crates/ethereum-rpc/src/eth_subscription.rs | 2 +- crates/ethereum-rpc/src/ethereum.rs | 4 +- crates/ethereum-rpc/src/lib.rs | 57 ++++++++++--------- .../sov-modules-rollup-blueprint/src/lib.rs | 2 +- 8 files changed, 41 insertions(+), 36 deletions(-) diff --git a/bin/citrea/src/eth.rs b/bin/citrea/src/eth.rs index 1b0d7a987..d3187d811 100644 --- a/bin/citrea/src/eth.rs +++ b/bin/citrea/src/eth.rs @@ -14,7 +14,7 @@ pub(crate) fn register_ethereum( storage: ProverStorage, methods: &mut jsonrpsee::RpcModule<()>, sequencer_client_url: Option, - soft_commitment_tx: broadcast::Sender, + soft_commitment_tx: Option>, ) -> Result<(), anyhow::Error> { let eth_rpc_config = { let eth_signer = eth_dev_signer(); diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index 1e6b8b174..689f08349 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -62,7 +62,7 @@ impl RollupBlueprint for BitcoinRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_commitment_tx: broadcast::Sender, + soft_commitment_tx: Option>, ) -> Result, anyhow::Error> { // unused inside register RPC let sov_sequencer = Address::new([0; 32]); diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index 868cfbaef..8dcb43692 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -56,7 +56,7 @@ impl RollupBlueprint for MockDemoRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_commitment_tx: broadcast::Sender, + soft_commitment_tx: Option>, ) -> Result, anyhow::Error> { // TODO set the sequencer address let sequencer = Address::new([0; 32]); diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index f43b974eb..bbe956db6 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -51,7 +51,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, None, - soft_confirmation_tx.clone(), + Some(soft_confirmation_tx.clone()), )?; let native_stf = StfBlueprint::new(); @@ -125,7 +125,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - soft_confirmation_tx.clone(), + Some(soft_confirmation_tx.clone()), )?; let native_stf = StfBlueprint::new(); @@ -206,7 +206,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - soft_confirmation_tx.clone(), + Some(soft_confirmation_tx.clone()), )?; let native_stf = StfBlueprint::new(); diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs index 6e0114721..e6c1380e5 100644 --- a/crates/ethereum-rpc/src/eth_subscription.rs +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -12,7 +12,7 @@ pub async fn handle_new_heads_subscription>, ) { - let mut rx = ethereum.soft_commitment_tx.subscribe(); + let mut rx = ethereum.soft_commitment_tx.as_ref().unwrap().subscribe(); let evm = Evm::::default(); let subscription = pending.accept().await.unwrap(); tokio::spawn(async move { diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs index 020116353..eaa26117e 100644 --- a/crates/ethereum-rpc/src/ethereum.rs +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -37,7 +37,7 @@ pub struct Ethereum { pub(crate) sequencer_client: Option, pub(crate) web3_client_version: String, pub(crate) trace_cache: Mutex, ByLength>>, - pub(crate) soft_commitment_tx: broadcast::Sender, + pub(crate) soft_commitment_tx: Option>, } impl Ethereum { @@ -48,7 +48,7 @@ impl Ethereum { #[cfg(feature = "local")] eth_signer: DevSigner, storage: C::Storage, sequencer_client: Option, - soft_commitment_tx: broadcast::Sender, + soft_commitment_tx: Option>, ) -> Self { let evm = Evm::::default(); let gas_price_oracle = diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index fbd37d113..d8ae61045 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -44,7 +44,7 @@ pub fn get_ethereum_rpc( eth_rpc_config: EthRpcConfig, storage: C::Storage, sequencer_client_url: Option, - soft_commitment_tx: broadcast::Sender, + soft_commitment_tx: Option>, ) -> RpcModule> { // Unpack config let EthRpcConfig { @@ -56,6 +56,7 @@ pub fn get_ethereum_rpc( // If the node does not have a sequencer client, then it is the sequencer. let is_sequencer = sequencer_client_url.is_none(); + let enable_subscriptions = soft_commitment_tx.is_some(); // If the running node is a full node rpc context should also have sequencer client so that it can send txs to sequencer let mut rpc = RpcModule::new(Ethereum::new( @@ -69,7 +70,8 @@ pub fn get_ethereum_rpc( soft_commitment_tx, )); - register_rpc_methods(&mut rpc, is_sequencer).expect("Failed to register ethereum RPC methods"); + register_rpc_methods(&mut rpc, is_sequencer, enable_subscriptions) + .expect("Failed to register ethereum RPC methods"); rpc } @@ -77,6 +79,7 @@ fn register_rpc_methods( rpc: &mut RpcModule>, // Checks wether the running node is a sequencer or not, if it is not a sequencer it should also have methods like eth_sendRawTransaction here. is_sequencer: bool, + enable_subscriptions: bool, ) -> Result<(), jsonrpsee::core::RegisterMethodError> { rpc.register_async_method("web3_clientVersion", |_, ethereum| async move { info!("eth module: web3_clientVersion"); @@ -671,31 +674,33 @@ fn register_rpc_methods( }, )?; - rpc.register_subscription( - "eth_subscribe", - "eth_subscription", - "eth_unsubscribe", - |parameters, pending, ethereum| async move { - let topic: String = match parameters.one() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return Ok(()); - } - }; - match topic.as_str() { - "newHeads" => handle_new_heads_subscription(pending, ethereum).await, - _ => { - pending - .reject(EthApiError::Unsupported("Unsupported subscription topic")) - .await; - return Ok(()); - } - }; + if enable_subscriptions { + rpc.register_subscription( + "eth_subscribe", + "eth_subscription", + "eth_unsubscribe", + |parameters, pending, ethereum| async move { + let topic: String = match parameters.one() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return Ok(()); + } + }; + match topic.as_str() { + "newHeads" => handle_new_heads_subscription(pending, ethereum).await, + _ => { + pending + .reject(EthApiError::Unsupported("Unsupported subscription topic")) + .await; + return Ok(()); + } + }; - Ok(()) - }, - )?; + Ok(()) + }, + )?; + } Ok(()) } diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index b7530bc71..ea78963cf 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -68,7 +68,7 @@ pub trait RollupBlueprint: Sized + Send + Sync { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_confirmation_tx: broadcast::Sender, + soft_confirmation_tx: Option>, ) -> Result, anyhow::Error>; /// Creates GenesisConfig from genesis files. From a349c1a603075657743222ccc58025147ed83a38 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 20:42:08 +0200 Subject: [PATCH 16/34] Use params sequence --- crates/ethereum-rpc/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index d8ae61045..e132be8fe 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -680,7 +680,9 @@ fn register_rpc_methods( "eth_subscription", "eth_unsubscribe", |parameters, pending, ethereum| async move { - let topic: String = match parameters.one() { + let mut params = parameters.sequence(); + + let topic: String = match params.next() { Ok(v) => v, Err(err) => { pending.reject(err).await; From 1867614a6b3d55e3feb8ba91b4b01bd6eec87c25 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 21:24:00 +0200 Subject: [PATCH 17/34] Rename --- bin/citrea/src/eth.rs | 4 ++-- bin/citrea/src/rollup/bitcoin.rs | 4 ++-- bin/citrea/src/rollup/mock.rs | 4 ++-- crates/ethereum-rpc/src/eth_subscription.rs | 2 +- crates/ethereum-rpc/src/ethereum.rs | 6 +++--- crates/ethereum-rpc/src/lib.rs | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bin/citrea/src/eth.rs b/bin/citrea/src/eth.rs index d3187d811..2b49d1896 100644 --- a/bin/citrea/src/eth.rs +++ b/bin/citrea/src/eth.rs @@ -14,7 +14,7 @@ pub(crate) fn register_ethereum( storage: ProverStorage, methods: &mut jsonrpsee::RpcModule<()>, sequencer_client_url: Option, - soft_commitment_tx: Option>, + soft_confirmation_tx: Option>, ) -> Result<(), anyhow::Error> { let eth_rpc_config = { let eth_signer = eth_dev_signer(); @@ -30,7 +30,7 @@ pub(crate) fn register_ethereum( eth_rpc_config, storage, sequencer_client_url, - soft_commitment_tx, + soft_confirmation_tx, ); methods .merge(ethereum_rpc) diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index 689f08349..c748ea2d1 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -62,7 +62,7 @@ impl RollupBlueprint for BitcoinRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_commitment_tx: Option>, + soft_confirmation_tx: Option>, ) -> Result, anyhow::Error> { // unused inside register RPC let sov_sequencer = Address::new([0; 32]); @@ -79,7 +79,7 @@ impl RollupBlueprint for BitcoinRollup { storage.clone(), &mut rpc_methods, sequencer_client_url, - soft_commitment_tx, + soft_confirmation_tx, )?; Ok(rpc_methods) diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index 8dcb43692..5ea3c217f 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -56,7 +56,7 @@ impl RollupBlueprint for MockDemoRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_commitment_tx: Option>, + soft_confirmation_tx: Option>, ) -> Result, anyhow::Error> { // TODO set the sequencer address let sequencer = Address::new([0; 32]); @@ -73,7 +73,7 @@ impl RollupBlueprint for MockDemoRollup { storage.clone(), &mut rpc_methods, sequencer_client_url, - soft_commitment_tx, + soft_confirmation_tx, )?; Ok(rpc_methods) diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs index e6c1380e5..de270bcce 100644 --- a/crates/ethereum-rpc/src/eth_subscription.rs +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -12,7 +12,7 @@ pub async fn handle_new_heads_subscription>, ) { - let mut rx = ethereum.soft_commitment_tx.as_ref().unwrap().subscribe(); + let mut rx = ethereum.soft_confirmation_tx.as_ref().unwrap().subscribe(); let evm = Evm::::default(); let subscription = pending.accept().await.unwrap(); tokio::spawn(async move { diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs index eaa26117e..01f066217 100644 --- a/crates/ethereum-rpc/src/ethereum.rs +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -37,7 +37,7 @@ pub struct Ethereum { pub(crate) sequencer_client: Option, pub(crate) web3_client_version: String, pub(crate) trace_cache: Mutex, ByLength>>, - pub(crate) soft_commitment_tx: Option>, + pub(crate) soft_confirmation_tx: Option>, } impl Ethereum { @@ -48,7 +48,7 @@ impl Ethereum { #[cfg(feature = "local")] eth_signer: DevSigner, storage: C::Storage, sequencer_client: Option, - soft_commitment_tx: Option>, + soft_confirmation_tx: Option>, ) -> Self { let evm = Evm::::default(); let gas_price_oracle = @@ -71,7 +71,7 @@ impl Ethereum { sequencer_client, web3_client_version: current_version, trace_cache, - soft_commitment_tx, + soft_confirmation_tx, } } diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index e132be8fe..5cf1ca523 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -44,7 +44,7 @@ pub fn get_ethereum_rpc( eth_rpc_config: EthRpcConfig, storage: C::Storage, sequencer_client_url: Option, - soft_commitment_tx: Option>, + soft_confirmation_tx: Option>, ) -> RpcModule> { // Unpack config let EthRpcConfig { @@ -56,7 +56,7 @@ pub fn get_ethereum_rpc( // If the node does not have a sequencer client, then it is the sequencer. let is_sequencer = sequencer_client_url.is_none(); - let enable_subscriptions = soft_commitment_tx.is_some(); + let enable_subscriptions = soft_confirmation_tx.is_some(); // If the running node is a full node rpc context should also have sequencer client so that it can send txs to sequencer let mut rpc = RpcModule::new(Ethereum::new( @@ -67,7 +67,7 @@ pub fn get_ethereum_rpc( eth_signer, storage, sequencer_client_url.map(SequencerClient::new), - soft_commitment_tx, + soft_confirmation_tx, )); register_rpc_methods(&mut rpc, is_sequencer, enable_subscriptions) From 9b72af0b68a088d10a36ea4106c5b56c54aa2c3b Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 21:25:49 +0200 Subject: [PATCH 18/34] Fix doc --- crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs index 3ce23311f..f9c2a8b92 100644 --- a/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs +++ b/crates/sovereign-sdk/full-node/sov-ledger-rpc/src/server.rs @@ -25,7 +25,7 @@ const LEDGER_RPC_ERROR: &str = "LEDGER_RPC_ERROR"; /// /// Creates a new [`LedgerDB`] and starts serving JSON-RPC requests. /// async fn rpc_server() -> jsonrpsee::server::ServerHandle { /// let dir = tempdir().unwrap(); -/// let db = LedgerDB::with_path(dir)).unwrap(); +/// let db = LedgerDB::with_path(dir).unwrap(); /// let rpc_module = rpc_module::(db).unwrap(); /// /// let server = jsonrpsee::server::ServerBuilder::default() From 0b4e5478639a1302298298a0f265ce8a56a2fc92 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 21:49:24 +0200 Subject: [PATCH 19/34] Replace tx with rx in rpc --- bin/citrea/src/eth.rs | 4 ++-- bin/citrea/src/rollup/bitcoin.rs | 4 ++-- bin/citrea/src/rollup/mock.rs | 4 ++-- bin/citrea/src/rollup/mod.rs | 12 ++++++------ bin/citrea/tests/evm/subscription.rs | 2 +- crates/ethereum-rpc/src/eth_subscription.rs | 6 +++++- crates/ethereum-rpc/src/ethereum.rs | 6 +++--- crates/ethereum-rpc/src/lib.rs | 6 +++--- .../sov-modules-rollup-blueprint/src/lib.rs | 2 +- 9 files changed, 25 insertions(+), 21 deletions(-) diff --git a/bin/citrea/src/eth.rs b/bin/citrea/src/eth.rs index 2b49d1896..289154c2b 100644 --- a/bin/citrea/src/eth.rs +++ b/bin/citrea/src/eth.rs @@ -14,7 +14,7 @@ pub(crate) fn register_ethereum( storage: ProverStorage, methods: &mut jsonrpsee::RpcModule<()>, sequencer_client_url: Option, - soft_confirmation_tx: Option>, + soft_confirmation_rx: Option>, ) -> Result<(), anyhow::Error> { let eth_rpc_config = { let eth_signer = eth_dev_signer(); @@ -30,7 +30,7 @@ pub(crate) fn register_ethereum( eth_rpc_config, storage, sequencer_client_url, - soft_confirmation_tx, + soft_confirmation_rx, ); methods .merge(ethereum_rpc) diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index c748ea2d1..0f35a67c2 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -62,7 +62,7 @@ impl RollupBlueprint for BitcoinRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_confirmation_tx: Option>, + soft_confirmation_rx: Option>, ) -> Result, anyhow::Error> { // unused inside register RPC let sov_sequencer = Address::new([0; 32]); @@ -79,7 +79,7 @@ impl RollupBlueprint for BitcoinRollup { storage.clone(), &mut rpc_methods, sequencer_client_url, - soft_confirmation_tx, + soft_confirmation_rx, )?; Ok(rpc_methods) diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index 5ea3c217f..463f81805 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -56,7 +56,7 @@ impl RollupBlueprint for MockDemoRollup { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_confirmation_tx: Option>, + soft_confirmation_rx: Option>, ) -> Result, anyhow::Error> { // TODO set the sequencer address let sequencer = Address::new([0; 32]); @@ -73,7 +73,7 @@ impl RollupBlueprint for MockDemoRollup { storage.clone(), &mut rpc_methods, sequencer_client_url, - soft_confirmation_tx, + soft_confirmation_rx, )?; Ok(rpc_methods) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index bbe956db6..bf86cb724 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -44,14 +44,14 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; - let (soft_confirmation_tx, _) = broadcast::channel(10); + let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( &prover_storage, &ledger_db, &da_service, None, - Some(soft_confirmation_tx.clone()), + Some(soft_confirmation_rx), )?; let native_stf = StfBlueprint::new(); @@ -118,14 +118,14 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let prover_storage = storage_manager.create_finalized_storage()?; let runner_config = rollup_config.runner.expect("Runner config is missing"); - let (soft_confirmation_tx, _) = broadcast::channel(10); + let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( &prover_storage, &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - Some(soft_confirmation_tx.clone()), + Some(soft_confirmation_rx), )?; let native_stf = StfBlueprint::new(); @@ -198,7 +198,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let mut storage_manager = self.create_storage_manager(&rollup_config)?; let prover_storage = storage_manager.create_finalized_storage()?; - let (soft_confirmation_tx, _) = broadcast::channel(10); + let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); let runner_config = rollup_config.runner.expect("Runner config is missing"); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( @@ -206,7 +206,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - Some(soft_confirmation_tx.clone()), + Some(soft_confirmation_rx), )?; let native_stf = StfBlueprint::new(); diff --git a/bin/citrea/tests/evm/subscription.rs b/bin/citrea/tests/evm/subscription.rs index 1646a3986..9c46cbb35 100644 --- a/bin/citrea/tests/evm/subscription.rs +++ b/bin/citrea/tests/evm/subscription.rs @@ -13,7 +13,7 @@ use crate::{ #[tokio::test(flavor = "multi_thread")] async fn test_eth_subscriptions() -> Result<(), Box> { - let storage_dir = tempdir_with_children(&["DA", "sequencer", "full-node"]); + let storage_dir = tempdir_with_children(&["DA", "sequencer"]); let da_db_dir = storage_dir.path().join("DA").to_path_buf(); let sequencer_db_dir = storage_dir.path().join("sequencer").to_path_buf(); diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/eth_subscription.rs index de270bcce..f556a1cad 100644 --- a/crates/ethereum-rpc/src/eth_subscription.rs +++ b/crates/ethereum-rpc/src/eth_subscription.rs @@ -12,7 +12,11 @@ pub async fn handle_new_heads_subscription>, ) { - let mut rx = ethereum.soft_confirmation_tx.as_ref().unwrap().subscribe(); + let mut rx = ethereum + .soft_confirmation_rx + .as_ref() + .unwrap() + .resubscribe(); let evm = Evm::::default(); let subscription = pending.accept().await.unwrap(); tokio::spawn(async move { diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs index 01f066217..4db636be3 100644 --- a/crates/ethereum-rpc/src/ethereum.rs +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -37,7 +37,7 @@ pub struct Ethereum { pub(crate) sequencer_client: Option, pub(crate) web3_client_version: String, pub(crate) trace_cache: Mutex, ByLength>>, - pub(crate) soft_confirmation_tx: Option>, + pub(crate) soft_confirmation_rx: Option>, } impl Ethereum { @@ -48,7 +48,7 @@ impl Ethereum { #[cfg(feature = "local")] eth_signer: DevSigner, storage: C::Storage, sequencer_client: Option, - soft_confirmation_tx: Option>, + soft_confirmation_rx: Option>, ) -> Self { let evm = Evm::::default(); let gas_price_oracle = @@ -71,7 +71,7 @@ impl Ethereum { sequencer_client, web3_client_version: current_version, trace_cache, - soft_confirmation_tx, + soft_confirmation_rx, } } diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 5cf1ca523..40d63f667 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -44,7 +44,7 @@ pub fn get_ethereum_rpc( eth_rpc_config: EthRpcConfig, storage: C::Storage, sequencer_client_url: Option, - soft_confirmation_tx: Option>, + soft_confirmation_rx: Option>, ) -> RpcModule> { // Unpack config let EthRpcConfig { @@ -56,7 +56,7 @@ pub fn get_ethereum_rpc( // If the node does not have a sequencer client, then it is the sequencer. let is_sequencer = sequencer_client_url.is_none(); - let enable_subscriptions = soft_confirmation_tx.is_some(); + let enable_subscriptions = soft_confirmation_rx.is_some(); // If the running node is a full node rpc context should also have sequencer client so that it can send txs to sequencer let mut rpc = RpcModule::new(Ethereum::new( @@ -67,7 +67,7 @@ pub fn get_ethereum_rpc( eth_signer, storage, sequencer_client_url.map(SequencerClient::new), - soft_confirmation_tx, + soft_confirmation_rx, )); register_rpc_methods(&mut rpc, is_sequencer, enable_subscriptions) diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index ea78963cf..ffe86eff8 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -68,7 +68,7 @@ pub trait RollupBlueprint: Sized + Send + Sync { ledger_db: &LedgerDB, da_service: &Self::DaService, sequencer_client_url: Option, - soft_confirmation_tx: Option>, + soft_confirmation_rx: Option>, ) -> Result, anyhow::Error>; /// Creates GenesisConfig from genesis files. From 60f3ecc6d73482d8d66e2436e3e7c028a97f7443 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 21:59:34 +0200 Subject: [PATCH 20/34] Rename file --- crates/ethereum-rpc/src/lib.rs | 4 ++-- .../ethereum-rpc/src/{eth_subscription.rs => subscription.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename crates/ethereum-rpc/src/{eth_subscription.rs => subscription.rs} (100%) diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 40d63f667..f57d547a9 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -1,12 +1,12 @@ -mod eth_subscription; mod ethereum; mod gas_price; +mod subscription; mod trace; #[cfg(feature = "local")] pub use citrea_evm::DevSigner; use citrea_evm::Evm; -use eth_subscription::handle_new_heads_subscription; +use subscription::handle_new_heads_subscription; pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; pub use gas_price::gas_oracle::GasPriceOracleConfig; diff --git a/crates/ethereum-rpc/src/eth_subscription.rs b/crates/ethereum-rpc/src/subscription.rs similarity index 100% rename from crates/ethereum-rpc/src/eth_subscription.rs rename to crates/ethereum-rpc/src/subscription.rs From c1ad4475344b23dcfcd06d52447d9ec1526e1db1 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 22:27:20 +0200 Subject: [PATCH 21/34] IMplement SubscriptionManager --- crates/ethereum-rpc/src/subscription.rs | 53 ++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index f556a1cad..3d028e93c 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -2,12 +2,63 @@ use std::sync::Arc; use citrea_evm::Evm; use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage}; -use reth_rpc_types::BlockNumberOrTag; +use reth_rpc_types::{BlockNumberOrTag, RichBlock}; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; +use tokio::sync::broadcast; use crate::ethereum::Ethereum; +pub(crate) struct SubscriptionManager { + storage: C::Storage, + new_heads_tx: broadcast::Sender, +} + +impl SubscriptionManager { + pub(crate) fn new(storage: C::Storage, soft_confirmation_rx: broadcast::Receiver) -> Self { + let mut soft_confirmation_rx = soft_confirmation_rx; + let storage_c = storage.clone(); + let new_heads_tx = broadcast::channel(16).0; + let new_heads_tx_c = new_heads_tx.clone(); + // let new_heads_tx_c = new_heads_tx.clone(); + // Spawn the task that will listen for new soft confirmation heights + // and send the corresponding ethereum block to subscribers + tokio::spawn(async move { + let evm = Evm::::default(); + loop { + let Ok(height) = soft_confirmation_rx.recv().await else { + return; + }; + + if new_heads_tx_c.receiver_count() == 0 { + continue; + } + + let mut working_set = WorkingSet::::new(storage_c.clone()); + let block = evm + .get_block_by_number( + Some(BlockNumberOrTag::Number(height)), + None, + &mut working_set, + ) + .expect("Error querying block from evm") + .expect("Received signal but evm block is not found"); + + // Only error is no receiver + let _ = new_heads_tx_c.send(block); + } + }); + Self { + storage, + new_heads_tx, + } + } + + pub(crate) fn subscribe_new_heads(&self) -> broadcast::Receiver { + self.new_heads_tx.subscribe() + } +} + pub async fn handle_new_heads_subscription( pending: PendingSubscriptionSink, ethereum: Arc>, From d577518502aab5ad93b14b5cb9396dff9a86acbb Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 23 Jul 2024 22:50:42 +0200 Subject: [PATCH 22/34] Use SubscriptionManager --- crates/ethereum-rpc/src/ethereum.rs | 8 ++++-- crates/ethereum-rpc/src/lib.rs | 2 +- crates/ethereum-rpc/src/subscription.rs | 35 ++++++++----------------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/crates/ethereum-rpc/src/ethereum.rs b/crates/ethereum-rpc/src/ethereum.rs index 4db636be3..f9e129cc4 100644 --- a/crates/ethereum-rpc/src/ethereum.rs +++ b/crates/ethereum-rpc/src/ethereum.rs @@ -16,6 +16,7 @@ use tracing::instrument; use crate::gas_price::fee_history::FeeHistoryCacheConfig; use crate::gas_price::gas_oracle::{GasPriceOracle, GasPriceOracleConfig}; +use crate::subscription::SubscriptionManager; const MAX_TRACE_BLOCK: u32 = 1000; @@ -37,7 +38,7 @@ pub struct Ethereum { pub(crate) sequencer_client: Option, pub(crate) web3_client_version: String, pub(crate) trace_cache: Mutex, ByLength>>, - pub(crate) soft_confirmation_rx: Option>, + pub(crate) subscription_manager: Option, } impl Ethereum { @@ -62,6 +63,9 @@ impl Ethereum { let trace_cache = Mutex::new(LruMap::new(ByLength::new(MAX_TRACE_BLOCK))); + let subscription_manager = + soft_confirmation_rx.map(|rx| SubscriptionManager::new::(storage.clone(), rx)); + Self { da_service, gas_price_oracle, @@ -71,7 +75,7 @@ impl Ethereum { sequencer_client, web3_client_version: current_version, trace_cache, - soft_confirmation_rx, + subscription_manager, } } diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index f57d547a9..2e2b1e15c 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -6,7 +6,6 @@ mod trace; #[cfg(feature = "local")] pub use citrea_evm::DevSigner; use citrea_evm::Evm; -use subscription::handle_new_heads_subscription; pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; pub use gas_price::gas_oracle::GasPriceOracleConfig; @@ -21,6 +20,7 @@ use serde_json::json; use sov_modules_api::utils::to_jsonrpsee_error_object; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; +use subscription::handle_new_heads_subscription; use tokio::sync::broadcast; use trace::{debug_trace_by_block_number, handle_debug_trace_chain}; use tracing::info; diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index 3d028e93c..812d34643 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -9,18 +9,18 @@ use tokio::sync::broadcast; use crate::ethereum::Ethereum; -pub(crate) struct SubscriptionManager { - storage: C::Storage, +pub(crate) struct SubscriptionManager { new_heads_tx: broadcast::Sender, } -impl SubscriptionManager { - pub(crate) fn new(storage: C::Storage, soft_confirmation_rx: broadcast::Receiver) -> Self { +impl SubscriptionManager { + pub(crate) fn new( + storage: C::Storage, + soft_confirmation_rx: broadcast::Receiver, + ) -> Self { let mut soft_confirmation_rx = soft_confirmation_rx; - let storage_c = storage.clone(); let new_heads_tx = broadcast::channel(16).0; let new_heads_tx_c = new_heads_tx.clone(); - // let new_heads_tx_c = new_heads_tx.clone(); // Spawn the task that will listen for new soft confirmation heights // and send the corresponding ethereum block to subscribers tokio::spawn(async move { @@ -34,7 +34,7 @@ impl SubscriptionManager { continue; } - let mut working_set = WorkingSet::::new(storage_c.clone()); + let mut working_set = WorkingSet::::new(storage.clone()); let block = evm .get_block_by_number( Some(BlockNumberOrTag::Number(height)), @@ -48,10 +48,7 @@ impl SubscriptionManager { let _ = new_heads_tx_c.send(block); } }); - Self { - storage, - new_heads_tx, - } + Self { new_heads_tx } } pub(crate) fn subscribe_new_heads(&self) -> broadcast::Receiver { @@ -64,27 +61,17 @@ pub async fn handle_new_heads_subscription>, ) { let mut rx = ethereum - .soft_confirmation_rx + .subscription_manager .as_ref() .unwrap() - .resubscribe(); - let evm = Evm::::default(); + .subscribe_new_heads(); let subscription = pending.accept().await.unwrap(); tokio::spawn(async move { loop { - let Ok(block_number) = rx.recv().await else { + let Ok(block) = rx.recv().await else { // Connection closed return; }; - let mut working_set = WorkingSet::::new(ethereum.storage.clone()); - let block = evm - .get_block_by_number( - Some(BlockNumberOrTag::Number(block_number)), - None, - &mut working_set, - ) - .expect("Error querying block from evm") - .expect("Received signal but evm block is not found"); let msg = SubscriptionMessage::new( subscription.method_name(), From 6ec1b4629d679d72afc6bb4d6e2a13b737d2d93a Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Wed, 24 Jul 2024 00:15:27 +0200 Subject: [PATCH 23/34] Minor changes --- crates/ethereum-rpc/src/subscription.rs | 24 ++++++++++++++++-------- crates/evm/src/query.rs | 9 ++++----- crates/evm/src/rpc_helpers/filter.rs | 7 +++++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index 812d34643..b546e0856 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -1,16 +1,18 @@ +use std::collections::HashMap; use std::sync::Arc; -use citrea_evm::Evm; +use citrea_evm::{Evm, Filter, LogResponse}; use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage}; use reth_rpc_types::{BlockNumberOrTag, RichBlock}; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; -use tokio::sync::broadcast; +use tokio::sync::{broadcast, Mutex}; use crate::ethereum::Ethereum; pub(crate) struct SubscriptionManager { new_heads_tx: broadcast::Sender, + logs_tx_by_filter: Arc>>>, } impl SubscriptionManager { @@ -18,9 +20,14 @@ impl SubscriptionManager { storage: C::Storage, soft_confirmation_rx: broadcast::Receiver, ) -> Self { - let mut soft_confirmation_rx = soft_confirmation_rx; let new_heads_tx = broadcast::channel(16).0; - let new_heads_tx_c = new_heads_tx.clone(); + let logs_tx_by_filter = Arc::new(Mutex::new(HashMap::new())); + let manager = Self { + new_heads_tx: new_heads_tx.clone(), + logs_tx_by_filter: logs_tx_by_filter.clone(), + }; + + let mut soft_confirmation_rx = soft_confirmation_rx; // Spawn the task that will listen for new soft confirmation heights // and send the corresponding ethereum block to subscribers tokio::spawn(async move { @@ -30,7 +37,7 @@ impl SubscriptionManager { return; }; - if new_heads_tx_c.receiver_count() == 0 { + if new_heads_tx.receiver_count() == 0 { continue; } @@ -44,11 +51,12 @@ impl SubscriptionManager { .expect("Error querying block from evm") .expect("Received signal but evm block is not found"); - // Only error is no receiver - let _ = new_heads_tx_c.send(block); + // Only possible error is no receiver + let _ = new_heads_tx.send(block); } }); - Self { new_heads_tx } + + manager } pub(crate) fn subscribe_new_heads(&self) -> broadcast::Receiver { diff --git a/crates/evm/src/query.rs b/crates/evm/src/query.rs index fba72086c..2ee96bca5 100644 --- a/crates/evm/src/query.rs +++ b/crates/evm/src/query.rs @@ -1434,11 +1434,10 @@ impl Evm { BlockRangeInclusiveIter::new(from_block_number..=to_block_number, max_headers_range) { for idx in from..=to { - let block = match self.blocks.get( - // Index from +1 or just from? - (idx) as usize, - &mut working_set.accessory_state(), - ) { + let block = match self + .blocks + .get((idx) as usize, &mut working_set.accessory_state()) + { Some(block) => block, None => { return Err(FilterError::EthAPIError( diff --git a/crates/evm/src/rpc_helpers/filter.rs b/crates/evm/src/rpc_helpers/filter.rs index 4979f7a53..ff99ac760 100644 --- a/crates/evm/src/rpc_helpers/filter.rs +++ b/crates/evm/src/rpc_helpers/filter.rs @@ -555,6 +555,13 @@ impl<'de> Deserialize<'de> for Filter { } } +impl std::hash::Hash for Filter { + fn hash(&self, state: &mut H) { + // TODO: find a better way + state.write(&serde_json::to_vec(&self).unwrap()); + } +} + /// Union type for representing a single value or a vector of values inside a filter #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum ValueOrArray { From cfbcc80d5cd918bd9f6ae8e55e755608ce4800dd Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Wed, 24 Jul 2024 01:56:18 +0200 Subject: [PATCH 24/34] Implemented logs subscription --- crates/ethereum-rpc/src/lib.rs | 31 ++++++++- crates/ethereum-rpc/src/subscription.rs | 85 ++++++++++++++++++------- 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 2e2b1e15c..6dafeda70 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -5,7 +5,7 @@ mod trace; #[cfg(feature = "local")] pub use citrea_evm::DevSigner; -use citrea_evm::Evm; +use citrea_evm::{Evm, Filter}; pub use ethereum::{EthRpcConfig, Ethereum}; pub use gas_price::fee_history::FeeHistoryCacheConfig; pub use gas_price::gas_oracle::GasPriceOracleConfig; @@ -20,7 +20,7 @@ use serde_json::json; use sov_modules_api::utils::to_jsonrpsee_error_object; use sov_modules_api::WorkingSet; use sov_rollup_interface::services::da::DaService; -use subscription::handle_new_heads_subscription; +use subscription::{handle_logs_subscription, handle_new_heads_subscription}; use tokio::sync::broadcast; use trace::{debug_trace_by_block_number, handle_debug_trace_chain}; use tracing::info; @@ -690,7 +690,32 @@ fn register_rpc_methods( } }; match topic.as_str() { - "newHeads" => handle_new_heads_subscription(pending, ethereum).await, + "newHeads" => { + let subscription = pending.accept().await.unwrap(); + let rx = ethereum + .subscription_manager + .as_ref() + .unwrap() + .subscribe_new_heads(); + handle_new_heads_subscription(subscription, rx).await + } + "logs" => { + let filter: Filter = match params.next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return Ok(()); + } + }; + let subscription = pending.accept().await.unwrap(); + let rx = ethereum + .subscription_manager + .as_ref() + .unwrap() + .subscribe_logs(filter) + .await; + handle_logs_subscription(subscription, rx).await + } _ => { pending .reject(EthApiError::Unsupported("Unsupported subscription topic")) diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index b546e0856..d5b8765f1 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -2,17 +2,14 @@ use std::collections::HashMap; use std::sync::Arc; use citrea_evm::{Evm, Filter, LogResponse}; -use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage}; +use jsonrpsee::{SubscriptionMessage, SubscriptionSink}; use reth_rpc_types::{BlockNumberOrTag, RichBlock}; use sov_modules_api::WorkingSet; -use sov_rollup_interface::services::da::DaService; use tokio::sync::{broadcast, Mutex}; -use crate::ethereum::Ethereum; - pub(crate) struct SubscriptionManager { new_heads_tx: broadcast::Sender, - logs_tx_by_filter: Arc>>>, + logs_tx_by_filter: Arc>>>>, } impl SubscriptionManager { @@ -37,22 +34,34 @@ impl SubscriptionManager { return; }; - if new_heads_tx.receiver_count() == 0 { - continue; + { + let logs_tx_by_filter = logs_tx_by_filter.lock().await; + if new_heads_tx.receiver_count() == 0 && logs_tx_by_filter.is_empty() { + continue; + } } + let block_number = BlockNumberOrTag::Number(height); + let mut working_set = WorkingSet::::new(storage.clone()); let block = evm - .get_block_by_number( - Some(BlockNumberOrTag::Number(height)), - None, - &mut working_set, - ) + .get_block_by_number(Some(block_number), None, &mut working_set) .expect("Error querying block from evm") .expect("Received signal but evm block is not found"); // Only possible error is no receiver - let _ = new_heads_tx.send(block); + let _ = new_heads_tx.send(block.clone()); + + // Prune filters that have no subscriptions + let mut logs_tx_by_filter = logs_tx_by_filter.lock().await; + logs_tx_by_filter.retain(|_, tx| tx.receiver_count() != 0); + let logs_tx_by_filter = logs_tx_by_filter.clone(); + + for (filter, logs_tx) in logs_tx_by_filter.iter() { + let logs = evm.eth_get_logs(filter.clone(), &mut working_set).unwrap(); + // Only possible error is no receiver + let _ = logs_tx.send(logs); + } } }); @@ -62,18 +71,23 @@ impl SubscriptionManager { pub(crate) fn subscribe_new_heads(&self) -> broadcast::Receiver { self.new_heads_tx.subscribe() } + + pub(crate) async fn subscribe_logs( + &self, + filter: Filter, + ) -> broadcast::Receiver> { + let mut logs_tx_by_filter = self.logs_tx_by_filter.lock().await; + let tx = logs_tx_by_filter + .entry(filter) + .or_insert_with(|| broadcast::channel(8).0); + tx.subscribe() + } } -pub async fn handle_new_heads_subscription( - pending: PendingSubscriptionSink, - ethereum: Arc>, +pub async fn handle_new_heads_subscription( + subscription: SubscriptionSink, + mut rx: broadcast::Receiver, ) { - let mut rx = ethereum - .subscription_manager - .as_ref() - .unwrap() - .subscribe_new_heads(); - let subscription = pending.accept().await.unwrap(); tokio::spawn(async move { loop { let Ok(block) = rx.recv().await else { @@ -94,3 +108,30 @@ pub async fn handle_new_heads_subscription>, +) { + tokio::spawn(async move { + loop { + let Ok(logs) = rx.recv().await else { + // Connection closed + return; + }; + + for log in logs { + let msg = SubscriptionMessage::new( + subscription.method_name(), + subscription.subscription_id(), + &log, + ) + .unwrap(); + let Ok(_) = subscription.send(msg).await else { + // Connection closed + return; + }; + } + } + }); +} From 33f724bd12892edd2b642bbb7c7c3ba08f2c3591 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Wed, 24 Jul 2024 19:10:39 +0200 Subject: [PATCH 25/34] Add log subscription test --- bin/citrea/tests/evm/subscription.rs | 197 +++++++++++++++++- bin/citrea/tests/test_client/mod.rs | 26 ++- crates/evm/src/rpc_helpers/responses.rs | 10 +- .../evm/src/smart_contracts/logs_contract.rs | 19 +- crates/evm/src/smart_contracts/mod.rs | 2 +- 5 files changed, 248 insertions(+), 6 deletions(-) diff --git a/bin/citrea/tests/evm/subscription.rs b/bin/citrea/tests/evm/subscription.rs index 9c46cbb35..823ff8624 100644 --- a/bin/citrea/tests/evm/subscription.rs +++ b/bin/citrea/tests/evm/subscription.rs @@ -1,10 +1,18 @@ +use std::collections::HashMap; use std::sync::{Arc, Mutex}; +use std::time::Duration; +use alloy_primitives::FixedBytes; +use alloy_sol_types::SolEvent; +use citrea_evm::smart_contracts::{AnotherLogEvent, LogEvent, LogsContract, TestContract}; +use citrea_evm::{Filter, LogResponse}; // use citrea::initialize_logging; use citrea_stf::genesis_config::GenesisPaths; -use reth_primitives::Address; +use reth_primitives::{keccak256, Address}; +use tokio::time::sleep; use crate::evm::make_test_client; +use crate::test_client::TestClient; use crate::test_helpers::{start_rollup, tempdir_with_children, wait_for_l2_block, NodeMode}; use crate::{ DEFAULT_DEPOSIT_MEMPOOL_FETCH_LIMIT, DEFAULT_MIN_SOFT_CONFIRMATIONS_PER_COMMITMENT, @@ -46,6 +54,7 @@ async fn test_eth_subscriptions() -> Result<(), Box> { test_client.send_publish_batch_request().await; wait_for_l2_block(&test_client, 1, None).await; + // Spawn newHeads subscriber let new_block_rx = test_client.subscribe_new_heads().await; let last_received_block = Arc::new(Mutex::new(None)); let last_received_block_clone = last_received_block.clone(); @@ -58,9 +67,12 @@ async fn test_eth_subscriptions() -> Result<(), Box> { } }); + // Produce an empty block and receive it from subscription { test_client.send_publish_batch_request().await; wait_for_l2_block(&test_client, 2, None).await; + // Sleep in case of subscription delay + sleep(Duration::from_millis(100)).await; let block = last_received_block.lock().unwrap(); let block = block.as_ref().unwrap(); @@ -68,6 +80,7 @@ async fn test_eth_subscriptions() -> Result<(), Box> { assert!(block.transactions.is_empty()); } + // Produce a block with 1 send transaction and receive it from subscription { let pending_tx = test_client .send_eth(Address::random(), None, None, None, 10000) @@ -77,6 +90,8 @@ async fn test_eth_subscriptions() -> Result<(), Box> { test_client.send_publish_batch_request().await; wait_for_l2_block(&test_client, 3, None).await; + // Sleep in case of subscription delay + sleep(Duration::from_millis(100)).await; let block = last_received_block.lock().unwrap(); let block = block.as_ref().unwrap(); @@ -85,6 +100,186 @@ async fn test_eth_subscriptions() -> Result<(), Box> { assert_eq!(block.transactions.hashes().last().unwrap().clone(), tx_hash); } + // Deploy 2 LogsContract + let (logs_contract1, logs_contract_address1, logs_contract2, logs_contract_address2) = { + let logs_contract1 = LogsContract::default(); + let deploy_logs_contract_req1 = test_client + .deploy_contract(logs_contract1.byte_code(), None) + .await?; + let logs_contract2 = LogsContract::default(); + let deploy_logs_contract_req2 = test_client + .deploy_contract(logs_contract2.byte_code(), None) + .await?; + + test_client.send_publish_batch_request().await; + + let logs_contract_address1 = deploy_logs_contract_req1 + .get_receipt() + .await? + .contract_address + .unwrap(); + let logs_contract_address2 = deploy_logs_contract_req2 + .get_receipt() + .await? + .contract_address + .unwrap(); + + ( + logs_contract1, + logs_contract_address1, + logs_contract2, + logs_contract_address2, + ) + }; + + // Spawn logs subscriber with no filter + let logs_by_tx_no_filter = spawn_logs_subscriber(&test_client, Filter::default()).await; + // Spawn logs subscriber with logs_contract_address1 filter + let mut filter = Filter::default(); + filter.address.0.insert(logs_contract_address1); + let logs_by_tx_address1_filter = spawn_logs_subscriber(&test_client, filter).await; + // Spawn logs subscriber with logs_contract_address2 filter and a topic + let mut filter = Filter::default(); + filter.address.0.insert(logs_contract_address2); + filter.topics[0].0.insert(AnotherLogEvent::SIGNATURE_HASH); + let logs_by_tx_address2_filter = spawn_logs_subscriber(&test_client, filter).await; + + // Call logs_contract1 and logs_contract2 contracts once and observe that + // each log subscription receives the respective events + { + // Send transaction to 1st contract + let test_log_msg: String = "DRAGONBALLZ".into(); + let pending_tx1 = test_client + .contract_transaction( + logs_contract_address1, + logs_contract1.publish_event(test_log_msg.clone()), + None, + ) + .await; + let tx_hash1 = *pending_tx1.tx_hash(); + // Send transaction to 2nd contract + let pending_tx2 = test_client + .contract_transaction( + logs_contract_address2, + logs_contract2.publish_event(test_log_msg.clone()), + None, + ) + .await; + let tx_hash2 = *pending_tx2.tx_hash(); + + // Wait for them to be mined + test_client.send_publish_batch_request().await; + wait_for_l2_block(&test_client, 5, None).await; + // Sleep in case of subscription delay + sleep(Duration::from_millis(100)).await; + + // Observe that we received a block and it contains 2 transactions + let block = last_received_block.lock().unwrap(); + let block = block.as_ref().unwrap(); + let mut tx_hashes = block.transactions.hashes(); + assert_eq!(block.header.number, Some(5)); + assert_eq!(block.transactions.len(), 2); + assert_eq!(tx_hashes.next().unwrap().clone(), tx_hash1); + assert_eq!(tx_hashes.next().unwrap().clone(), tx_hash2); + + { + // Observe that no filter logs subscription received all 4 events + let logs_by_tx_no_filter = logs_by_tx_no_filter.lock().unwrap(); + let (log_payload1, another_log_payload1) = + parse_log_contract_logs(logs_by_tx_no_filter.get(&tx_hash1).unwrap()); + let (log_payload2, another_log_payload2) = + parse_log_contract_logs(logs_by_tx_no_filter.get(&tx_hash2).unwrap()); + + // Verify tx1 events payload + assert_eq!(log_payload1.address, logs_contract_address1); + assert_eq!(log_payload1.sender, test_client.from_addr); + assert_eq!(log_payload1.contractAddress, logs_contract_address1); + assert_eq!(log_payload1.senderMessage, keccak256(test_log_msg.clone())); + assert_eq!(log_payload1.message, "Hello World!"); + assert_eq!(another_log_payload1.contractAddress, logs_contract_address1); + + // Verify tx2 events payload + assert_eq!(log_payload2.address, logs_contract_address2); + assert_eq!(log_payload2.sender, test_client.from_addr); + assert_eq!(log_payload2.contractAddress, logs_contract_address2); + assert_eq!(log_payload2.senderMessage, keccak256(test_log_msg.clone())); + assert_eq!(log_payload2.message, "Hello World!"); + assert_eq!(another_log_payload2.contractAddress, logs_contract_address2); + } + + { + // Observe that address1 filtered subscription received only 2 events from contract1 + let logs_by_tx_address1_filter = logs_by_tx_address1_filter.lock().unwrap(); + assert!(logs_by_tx_address1_filter.get(&tx_hash2).is_none()); + + let (log_payload1, another_log_payload1) = + parse_log_contract_logs(logs_by_tx_address1_filter.get(&tx_hash1).unwrap()); + + // Verify tx1 events payload + assert_eq!(log_payload1.address, logs_contract_address1); + assert_eq!(log_payload1.sender, test_client.from_addr); + assert_eq!(log_payload1.contractAddress, logs_contract_address1); + assert_eq!(log_payload1.senderMessage, keccak256(test_log_msg.clone())); + assert_eq!(log_payload1.message, "Hello World!"); + assert_eq!(another_log_payload1.contractAddress, logs_contract_address1); + } + + { + // Observe that address1 filtered subscription received only 2 events from contract1 + let logs_by_tx_address2_filter = logs_by_tx_address2_filter.lock().unwrap(); + assert!(logs_by_tx_address2_filter.get(&tx_hash1).is_none()); + + let logs = logs_by_tx_address2_filter.get(&tx_hash2).unwrap(); + assert_eq!(logs.len(), 1); + + let log: alloy_primitives::Log = logs[0].clone().try_into().unwrap(); + let another_log_payload = LogsContract::decode_another_log_event(&log).unwrap(); + + // Verify tx1 events payload + assert_eq!(another_log_payload.contractAddress, logs_contract_address2); + } + } + seq_task.abort(); Ok(()) } + +async fn spawn_logs_subscriber( + client: &TestClient, + filter: Filter, +) -> Arc, Vec>>> { + let logs_rx = client.subscribe_logs(filter).await; + let logs_by_tx = Arc::new(Mutex::new(HashMap::new())); + let logs_by_tx_c = logs_by_tx.clone(); + tokio::spawn(async move { + loop { + let Ok(log) = logs_rx.recv() else { + return; + }; + let mut logs_by_tx_c = logs_by_tx_c.lock().unwrap(); + let logs = logs_by_tx_c + .entry(log.transaction_hash.unwrap()) + .or_insert(vec![]); + logs.push(log); + } + }); + + logs_by_tx +} + +fn parse_log_contract_logs( + logs: &[LogResponse], +) -> ( + alloy_primitives::Log, + alloy_primitives::Log, +) { + assert_eq!(logs.len(), 2); + + let log1: alloy_primitives::Log = logs[0].clone().try_into().unwrap(); + let log2: alloy_primitives::Log = logs[1].clone().try_into().unwrap(); + + let log_payload = LogsContract::decode_log_event(&log1).unwrap(); + let another_log_payload = LogsContract::decode_another_log_event(&log2).unwrap(); + + (log_payload, another_log_payload) +} diff --git a/bin/citrea/tests/test_client/mod.rs b/bin/citrea/tests/test_client/mod.rs index 790c58781..11c173e25 100644 --- a/bin/citrea/tests/test_client/mod.rs +++ b/bin/citrea/tests/test_client/mod.rs @@ -8,7 +8,7 @@ use alloy::providers::{PendingTransactionBuilder, Provider as AlloyProvider, Pro use alloy::rpc::types::eth::{Block, Transaction, TransactionReceipt, TransactionRequest}; use alloy::signers::wallet::LocalWallet; use alloy::transports::http::{Http, HyperClient}; -use citrea_evm::LogResponse; +use citrea_evm::{Filter, LogResponse}; use ethereum_rpc::CitreaStatus; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; @@ -713,6 +713,30 @@ impl TestClient { rx } + pub(crate) async fn subscribe_logs(&self, filter: Filter) -> mpsc::Receiver { + let (tx, rx) = mpsc::channel(); + let mut subscription = self + .ws_client + .subscribe( + "eth_subscribe", + rpc_params!["logs", filter], + "eth_unsubscribe", + ) + .await + .unwrap(); + + tokio::spawn(async move { + loop { + let Some(Ok(log)) = subscription.next().await else { + return; + }; + tx.send(log).unwrap(); + } + }); + + rx + } + pub(crate) async fn eth_block_number(&self) -> u64 { let block_number: U256 = self .http_client diff --git a/crates/evm/src/rpc_helpers/responses.rs b/crates/evm/src/rpc_helpers/responses.rs index 6720ffa48..076c9b75c 100644 --- a/crates/evm/src/rpc_helpers/responses.rs +++ b/crates/evm/src/rpc_helpers/responses.rs @@ -1,7 +1,7 @@ use std::hash::Hash; use alloy_primitives::Bytes; -use reth_primitives::{Address, U256}; +use reth_primitives::{Address, Log, U256}; use revm::primitives::B256; /// Ethereum Log emitted by a transaction @@ -28,3 +28,11 @@ pub struct LogResponse { #[serde(default)] pub removed: bool, } + +impl TryInto for LogResponse { + type Error = &'static str; + + fn try_into(self) -> Result { + Log::new(self.address, self.topics, self.data).ok_or("Invalid LogResponse") + } +} diff --git a/crates/evm/src/smart_contracts/logs_contract.rs b/crates/evm/src/smart_contracts/logs_contract.rs index e946a5072..e1be3ede4 100644 --- a/crates/evm/src/smart_contracts/logs_contract.rs +++ b/crates/evm/src/smart_contracts/logs_contract.rs @@ -1,14 +1,15 @@ -use alloy_sol_types::{sol, SolCall}; +use alloy_sol_types::{sol, SolCall, SolEvent}; use super::TestContract; -// Logs wrapper. sol! { #[sol(abi)] Logs, "./src/evm/test_data/Logs.abi" } +pub use Logs::{AnotherLog as AnotherLogEvent, Log as LogEvent}; + /// Logs wrapper. pub struct LogsContract { bytecode: Vec, @@ -39,4 +40,18 @@ impl LogsContract { } .abi_encode() } + + /// Decode Log event of the Logs smart contract. + pub fn decode_log_event( + log: &alloy_primitives::Log, + ) -> anyhow::Result> { + Ok(Logs::Log::decode_log(log, true)?) + } + + /// Decode AnotherLog event of the Logs smart contract. + pub fn decode_another_log_event( + log: &alloy_primitives::Log, + ) -> anyhow::Result> { + Ok(Logs::AnotherLog::decode_log(log, true)?) + } } diff --git a/crates/evm/src/smart_contracts/mod.rs b/crates/evm/src/smart_contracts/mod.rs index 122e693bd..145c70839 100644 --- a/crates/evm/src/smart_contracts/mod.rs +++ b/crates/evm/src/smart_contracts/mod.rs @@ -15,7 +15,7 @@ pub use caller_contract::CallerContract; pub use coinbase_contract::CoinbaseContract; pub use hive_contract::HiveContract; pub use infinite_loop_contract::InfiniteLoopContract; -pub use logs_contract::LogsContract; +pub use logs_contract::{AnotherLogEvent, LogEvent, LogsContract}; pub use payable_contract::SimplePayableContract; pub use self_destructor_contract::SelfDestructorContract; pub use simple_storage_contract::SimpleStorageContract; From 661b339ef22b6df3bbac26118f62ba40ee65cf6d Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Wed, 24 Jul 2024 21:49:11 +0200 Subject: [PATCH 26/34] Simplify logs subscription flow --- bin/citrea/tests/evm/subscription.rs | 2 +- crates/ethereum-rpc/src/lib.rs | 4 +- crates/ethereum-rpc/src/subscription.rs | 99 +++++++++++++------------ crates/evm/src/query.rs | 2 +- crates/evm/src/rpc_helpers/filter.rs | 9 +-- 5 files changed, 56 insertions(+), 60 deletions(-) diff --git a/bin/citrea/tests/evm/subscription.rs b/bin/citrea/tests/evm/subscription.rs index 823ff8624..821cff622 100644 --- a/bin/citrea/tests/evm/subscription.rs +++ b/bin/citrea/tests/evm/subscription.rs @@ -225,7 +225,7 @@ async fn test_eth_subscriptions() -> Result<(), Box> { } { - // Observe that address1 filtered subscription received only 2 events from contract1 + // Observe that address1 and topic filtered subscription received only 1 event from contract1 let logs_by_tx_address2_filter = logs_by_tx_address2_filter.lock().unwrap(); assert!(logs_by_tx_address2_filter.get(&tx_hash1).is_none()); diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index 6dafeda70..f5b85fb9a 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -712,9 +712,9 @@ fn register_rpc_methods( .subscription_manager .as_ref() .unwrap() - .subscribe_logs(filter) + .subscribe_logs() .await; - handle_logs_subscription(subscription, rx).await + handle_logs_subscription(subscription, rx, filter).await } _ => { pending diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index d5b8765f1..aba2d4777 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -1,15 +1,12 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use citrea_evm::{Evm, Filter, LogResponse}; +use citrea_evm::{log_matches_filter, Evm, Filter, LogResponse}; use jsonrpsee::{SubscriptionMessage, SubscriptionSink}; use reth_rpc_types::{BlockNumberOrTag, RichBlock}; use sov_modules_api::WorkingSet; -use tokio::sync::{broadcast, Mutex}; +use tokio::sync::broadcast; pub(crate) struct SubscriptionManager { new_heads_tx: broadcast::Sender, - logs_tx_by_filter: Arc>>>>, + logs_tx: broadcast::Sender>, } impl SubscriptionManager { @@ -18,10 +15,10 @@ impl SubscriptionManager { soft_confirmation_rx: broadcast::Receiver, ) -> Self { let new_heads_tx = broadcast::channel(16).0; - let logs_tx_by_filter = Arc::new(Mutex::new(HashMap::new())); + let logs_tx = broadcast::channel(16).0; let manager = Self { new_heads_tx: new_heads_tx.clone(), - logs_tx_by_filter: logs_tx_by_filter.clone(), + logs_tx: logs_tx.clone(), }; let mut soft_confirmation_rx = soft_confirmation_rx; @@ -34,31 +31,35 @@ impl SubscriptionManager { return; }; - { - let logs_tx_by_filter = logs_tx_by_filter.lock().await; - if new_heads_tx.receiver_count() == 0 && logs_tx_by_filter.is_empty() { - continue; - } - } - - let block_number = BlockNumberOrTag::Number(height); + let mut working_set = None; - let mut working_set = WorkingSet::::new(storage.clone()); - let block = evm - .get_block_by_number(Some(block_number), None, &mut working_set) - .expect("Error querying block from evm") - .expect("Received signal but evm block is not found"); + if new_heads_tx.receiver_count() != 0 { + working_set = Some(WorkingSet::::new(storage.clone())); + let block = evm + .get_block_by_number( + Some(BlockNumberOrTag::Number(height)), + None, + working_set.as_mut().unwrap(), + ) + .expect("Error querying block from evm") + .expect("Received signal but evm block is not found"); - // Only possible error is no receiver - let _ = new_heads_tx.send(block.clone()); + // Only possible error is no receiver + let _ = new_heads_tx.send(block.clone()); + } - // Prune filters that have no subscriptions - let mut logs_tx_by_filter = logs_tx_by_filter.lock().await; - logs_tx_by_filter.retain(|_, tx| tx.receiver_count() != 0); - let logs_tx_by_filter = logs_tx_by_filter.clone(); + if logs_tx.receiver_count() != 0 { + let mut working_set = + working_set.unwrap_or_else(|| WorkingSet::::new(storage.clone())); + let logs = evm + .get_logs_in_block_range( + &mut working_set, + &Filter::default(), + height, + height, + ) + .expect("Error getting logs in block range"); - for (filter, logs_tx) in logs_tx_by_filter.iter() { - let logs = evm.eth_get_logs(filter.clone(), &mut working_set).unwrap(); // Only possible error is no receiver let _ = logs_tx.send(logs); } @@ -72,15 +73,8 @@ impl SubscriptionManager { self.new_heads_tx.subscribe() } - pub(crate) async fn subscribe_logs( - &self, - filter: Filter, - ) -> broadcast::Receiver> { - let mut logs_tx_by_filter = self.logs_tx_by_filter.lock().await; - let tx = logs_tx_by_filter - .entry(filter) - .or_insert_with(|| broadcast::channel(8).0); - tx.subscribe() + pub(crate) async fn subscribe_logs(&self) -> broadcast::Receiver> { + self.logs_tx.subscribe() } } @@ -112,6 +106,7 @@ pub async fn handle_new_heads_subscription( pub async fn handle_logs_subscription( subscription: SubscriptionSink, mut rx: broadcast::Receiver>, + filter: Filter, ) { tokio::spawn(async move { loop { @@ -121,16 +116,24 @@ pub async fn handle_logs_subscription( }; for log in logs { - let msg = SubscriptionMessage::new( - subscription.method_name(), - subscription.subscription_id(), - &log, - ) - .unwrap(); - let Ok(_) = subscription.send(msg).await else { - // Connection closed - return; - }; + if log_matches_filter( + &log.clone().try_into().unwrap(), + &filter, + &filter.topics, + log.block_hash.as_ref().unwrap(), + &log.block_number.as_ref().unwrap().to::(), + ) { + let msg = SubscriptionMessage::new( + subscription.method_name(), + subscription.subscription_id(), + &log, + ) + .unwrap(); + let Ok(_) = subscription.send(msg).await else { + // Connection closed + return; + }; + } } } }); diff --git a/crates/evm/src/query.rs b/crates/evm/src/query.rs index 2ee96bca5..c31dfbd83 100644 --- a/crates/evm/src/query.rs +++ b/crates/evm/src/query.rs @@ -1408,7 +1408,7 @@ impl Evm { /// Returns an error if: /// - underlying database error /// - amount of matches exceeds configured limit - fn get_logs_in_block_range( + pub fn get_logs_in_block_range( &self, working_set: &mut WorkingSet, filter: &Filter, diff --git a/crates/evm/src/rpc_helpers/filter.rs b/crates/evm/src/rpc_helpers/filter.rs index ff99ac760..6f708f1cc 100644 --- a/crates/evm/src/rpc_helpers/filter.rs +++ b/crates/evm/src/rpc_helpers/filter.rs @@ -555,13 +555,6 @@ impl<'de> Deserialize<'de> for Filter { } } -impl std::hash::Hash for Filter { - fn hash(&self, state: &mut H) { - // TODO: find a better way - state.write(&serde_json::to_vec(&self).unwrap()); - } -} - /// Union type for representing a single value or a vector of values inside a filter #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum ValueOrArray { @@ -636,7 +629,7 @@ where // https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc/src/eth/logs_utils.rs#L56 /// Returns true if the log matches the filter and should be included -pub(crate) fn log_matches_filter( +pub fn log_matches_filter( log: &reth_primitives::Log, filter: &Filter, topics: &[FilterSet; 4], From af215de0c95feaf1ed6cfc82251dc20b46842322 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 12:42:31 +0200 Subject: [PATCH 27/34] Disable debug_subscribe if subscriptions disabled --- crates/ethereum-rpc/src/lib.rs | 52 +++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/crates/ethereum-rpc/src/lib.rs b/crates/ethereum-rpc/src/lib.rs index f5b85fb9a..5ad63eade 100644 --- a/crates/ethereum-rpc/src/lib.rs +++ b/crates/ethereum-rpc/src/lib.rs @@ -646,35 +646,35 @@ fn register_rpc_methods( )?; } - rpc.register_subscription( - "debug_subscribe", - "debug_subscription", - "debug_unsubscribe", - |parameters, pending, ethereum| async move { - let mut params = parameters.sequence(); + if enable_subscriptions { + rpc.register_subscription( + "debug_subscribe", + "debug_subscription", + "debug_unsubscribe", + |parameters, pending, ethereum| async move { + let mut params = parameters.sequence(); - let topic: String = match params.next() { - Ok(v) => v, - Err(err) => { - pending.reject(err).await; - return Ok(()); - } - }; - match topic.as_str() { - "traceChain" => handle_debug_trace_chain(params, pending, ethereum).await, - _ => { - pending - .reject(EthApiError::Unsupported("Unsupported subscription topic")) - .await; - return Ok(()); - } - }; + let topic: String = match params.next() { + Ok(v) => v, + Err(err) => { + pending.reject(err).await; + return Ok(()); + } + }; + match topic.as_str() { + "traceChain" => handle_debug_trace_chain(params, pending, ethereum).await, + _ => { + pending + .reject(EthApiError::Unsupported("Unsupported subscription topic")) + .await; + return Ok(()); + } + }; - Ok(()) - }, - )?; + Ok(()) + }, + )?; - if enable_subscriptions { rpc.register_subscription( "eth_subscribe", "eth_subscription", From e7b7531ee66d0e820c6bcf4985013459799193cd Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 12:47:31 +0200 Subject: [PATCH 28/34] Update configs --- resources/configs/bitcoin-regtest/prover_rollup_config.toml | 1 + resources/configs/bitcoin-regtest/rollup_config.toml | 2 ++ resources/configs/bitcoin-regtest/sequencer_rollup_config.toml | 2 ++ resources/configs/devnet/rollup_config.toml | 2 ++ resources/configs/mock-dockerized/rollup_config.toml | 2 ++ resources/configs/mock/rollup_config.toml | 2 ++ resources/configs/mock/sequencer_rollup_config.toml | 2 ++ 7 files changed, 13 insertions(+) diff --git a/resources/configs/bitcoin-regtest/prover_rollup_config.toml b/resources/configs/bitcoin-regtest/prover_rollup_config.toml index 98a326f7f..db4ce681a 100644 --- a/resources/configs/bitcoin-regtest/prover_rollup_config.toml +++ b/resources/configs/bitcoin-regtest/prover_rollup_config.toml @@ -20,6 +20,7 @@ path = "resources/dbs/prover-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12346 +disable_subscriptions = true [runner] sequencer_client_url = "http://0.0.0.0:12345" diff --git a/resources/configs/bitcoin-regtest/rollup_config.toml b/resources/configs/bitcoin-regtest/rollup_config.toml index 5ef41ca40..395e72d43 100644 --- a/resources/configs/bitcoin-regtest/rollup_config.toml +++ b/resources/configs/bitcoin-regtest/rollup_config.toml @@ -20,6 +20,8 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12346 +disable_subscriptions = false +max_subscription_connection = 2000 [runner] sequencer_client_url = "http://0.0.0.0:12345" diff --git a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml index 065e6b1d9..901111392 100644 --- a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml +++ b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml @@ -20,3 +20,5 @@ path = "resources/dbs/sequencer-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12345 +disable_subscriptions = false +max_subscription_connection = 2000 diff --git a/resources/configs/devnet/rollup_config.toml b/resources/configs/devnet/rollup_config.toml index 430b69bd4..668d6ecdf 100644 --- a/resources/configs/devnet/rollup_config.toml +++ b/resources/configs/devnet/rollup_config.toml @@ -20,6 +20,8 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "0.0.0.0" bind_port = 12346 +disable_subscriptions = false +max_subscription_connection = 2000 [runner] sequencer_client_url = "https://rpc.devnet.citrea.xyz" diff --git a/resources/configs/mock-dockerized/rollup_config.toml b/resources/configs/mock-dockerized/rollup_config.toml index 93e55cdc0..9a73f48e1 100644 --- a/resources/configs/mock-dockerized/rollup_config.toml +++ b/resources/configs/mock-dockerized/rollup_config.toml @@ -15,6 +15,8 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "0.0.0.0" bind_port = 8545 +disable_subscriptions = false +max_subscription_connection = 2000 [runner] sequencer_client_url = "http://0.0.0.0:8545" diff --git a/resources/configs/mock/rollup_config.toml b/resources/configs/mock/rollup_config.toml index d2b6f7dd6..b772e25d9 100644 --- a/resources/configs/mock/rollup_config.toml +++ b/resources/configs/mock/rollup_config.toml @@ -15,6 +15,8 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12346 +disable_subscriptions = false +max_subscription_connection = 2000 [runner] include_tx_body = false diff --git a/resources/configs/mock/sequencer_rollup_config.toml b/resources/configs/mock/sequencer_rollup_config.toml index 83700ba16..7d1f50047 100644 --- a/resources/configs/mock/sequencer_rollup_config.toml +++ b/resources/configs/mock/sequencer_rollup_config.toml @@ -16,3 +16,5 @@ path = "resources/dbs/sequencer-db" bind_host = "127.0.0.1" bind_port = 12345 max_connections = 10000 +disable_subscriptions = false +max_subscription_connection = 2000 From f64a5939178ba022d1010243ae3763572c282d79 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 12:51:48 +0200 Subject: [PATCH 29/34] Rename --- resources/configs/bitcoin-regtest/rollup_config.toml | 2 +- resources/configs/bitcoin-regtest/sequencer_rollup_config.toml | 2 +- resources/configs/devnet/rollup_config.toml | 2 +- resources/configs/mock-dockerized/rollup_config.toml | 2 +- resources/configs/mock/rollup_config.toml | 2 +- resources/configs/mock/sequencer_rollup_config.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/configs/bitcoin-regtest/rollup_config.toml b/resources/configs/bitcoin-regtest/rollup_config.toml index 395e72d43..de24377a6 100644 --- a/resources/configs/bitcoin-regtest/rollup_config.toml +++ b/resources/configs/bitcoin-regtest/rollup_config.toml @@ -21,7 +21,7 @@ path = "resources/dbs/full-node-db" bind_host = "127.0.0.1" bind_port = 12346 disable_subscriptions = false -max_subscription_connection = 2000 +max_subscription_connections = 2000 [runner] sequencer_client_url = "http://0.0.0.0:12345" diff --git a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml index 901111392..434fd3b34 100644 --- a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml +++ b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml @@ -21,4 +21,4 @@ path = "resources/dbs/sequencer-db" bind_host = "127.0.0.1" bind_port = 12345 disable_subscriptions = false -max_subscription_connection = 2000 +max_subscription_connections = 2000 diff --git a/resources/configs/devnet/rollup_config.toml b/resources/configs/devnet/rollup_config.toml index 668d6ecdf..534bbe364 100644 --- a/resources/configs/devnet/rollup_config.toml +++ b/resources/configs/devnet/rollup_config.toml @@ -21,7 +21,7 @@ path = "resources/dbs/full-node-db" bind_host = "0.0.0.0" bind_port = 12346 disable_subscriptions = false -max_subscription_connection = 2000 +max_subscription_connections = 2000 [runner] sequencer_client_url = "https://rpc.devnet.citrea.xyz" diff --git a/resources/configs/mock-dockerized/rollup_config.toml b/resources/configs/mock-dockerized/rollup_config.toml index 9a73f48e1..3bca71703 100644 --- a/resources/configs/mock-dockerized/rollup_config.toml +++ b/resources/configs/mock-dockerized/rollup_config.toml @@ -16,7 +16,7 @@ path = "resources/dbs/full-node-db" bind_host = "0.0.0.0" bind_port = 8545 disable_subscriptions = false -max_subscription_connection = 2000 +max_subscription_connections = 2000 [runner] sequencer_client_url = "http://0.0.0.0:8545" diff --git a/resources/configs/mock/rollup_config.toml b/resources/configs/mock/rollup_config.toml index b772e25d9..aa41fe7a6 100644 --- a/resources/configs/mock/rollup_config.toml +++ b/resources/configs/mock/rollup_config.toml @@ -16,7 +16,7 @@ path = "resources/dbs/full-node-db" bind_host = "127.0.0.1" bind_port = 12346 disable_subscriptions = false -max_subscription_connection = 2000 +max_subscription_connections = 2000 [runner] include_tx_body = false diff --git a/resources/configs/mock/sequencer_rollup_config.toml b/resources/configs/mock/sequencer_rollup_config.toml index 7d1f50047..19aff442c 100644 --- a/resources/configs/mock/sequencer_rollup_config.toml +++ b/resources/configs/mock/sequencer_rollup_config.toml @@ -17,4 +17,4 @@ bind_host = "127.0.0.1" bind_port = 12345 max_connections = 10000 disable_subscriptions = false -max_subscription_connection = 2000 +max_subscription_connections = 2000 From 09039032744c80b97a78d095e109e2d62d3f8ad1 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 14:06:31 +0200 Subject: [PATCH 30/34] Add new conf params --- bin/citrea/src/rollup/mod.rs | 24 ++++++++++++++++--- bin/citrea/src/test_rpc.rs | 2 ++ bin/citrea/tests/test_helpers/mod.rs | 2 ++ .../tests/runner_initialization_tests.rs | 2 ++ crates/fullnode/tests/runner_reorg_tests.rs | 2 ++ .../full-node/sov-stf-runner/src/config.rs | 20 ++++++++++++++++ 6 files changed, 49 insertions(+), 3 deletions(-) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index bf86cb724..31099d970 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -45,13 +45,19 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let prover_storage = storage_manager.create_finalized_storage()?; let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); + // If subscriptions disabled, pass None + let soft_confirmation_rx = if !rollup_config.rpc.disable_subscriptions { + Some(soft_confirmation_rx) + } else { + None + }; // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( &prover_storage, &ledger_db, &da_service, None, - Some(soft_confirmation_rx), + soft_confirmation_rx, )?; let native_stf = StfBlueprint::new(); @@ -119,13 +125,19 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let runner_config = rollup_config.runner.expect("Runner config is missing"); let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); + // If subscriptions disabled, pass None + let soft_confirmation_rx = if !rollup_config.rpc.disable_subscriptions { + Some(soft_confirmation_rx) + } else { + None + }; // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( &prover_storage, &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - Some(soft_confirmation_rx), + soft_confirmation_rx, )?; let native_stf = StfBlueprint::new(); @@ -199,6 +211,12 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let prover_storage = storage_manager.create_finalized_storage()?; let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); + // If subscriptions disabled, pass None + let soft_confirmation_rx = if !rollup_config.rpc.disable_subscriptions { + Some(soft_confirmation_rx) + } else { + None + }; let runner_config = rollup_config.runner.expect("Runner config is missing"); // TODO(https://github.com/Sovereign-Labs/sovereign-sdk/issues/1218) let rpc_methods = self.create_rpc_methods( @@ -206,7 +224,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &ledger_db, &da_service, Some(runner_config.sequencer_client_url.clone()), - Some(soft_confirmation_rx), + soft_confirmation_rx, )?; let native_stf = StfBlueprint::new(); diff --git a/bin/citrea/src/test_rpc.rs b/bin/citrea/src/test_rpc.rs index a68cf781b..a96d5dab1 100644 --- a/bin/citrea/src/test_rpc.rs +++ b/bin/citrea/src/test_rpc.rs @@ -99,6 +99,8 @@ fn test_helper( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, + disable_subscriptions: false, + max_subscription_connections: 100, }; queries_test_runner(test_queries, rpc_config).await; diff --git a/bin/citrea/tests/test_helpers/mod.rs b/bin/citrea/tests/test_helpers/mod.rs index b546bb0b4..51778a6e1 100644 --- a/bin/citrea/tests/test_helpers/mod.rs +++ b/bin/citrea/tests/test_helpers/mod.rs @@ -143,6 +143,8 @@ pub fn create_default_rollup_config( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, + disable_subscriptions: false, + max_subscription_connections: 100, }, runner: match node_mode { NodeMode::FullNode(socket_addr) | NodeMode::Prover(socket_addr) => Some(RunnerConfig { diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index 420c44d26..14fbc1afa 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -72,6 +72,8 @@ fn initialize_runner( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, + disable_subscriptions: false, + max_subscription_connections: 100, }, runner: Some(RunnerConfig { sequencer_client_url: "http://127.0.0.1:4444".to_string(), diff --git a/crates/fullnode/tests/runner_reorg_tests.rs b/crates/fullnode/tests/runner_reorg_tests.rs index 8b440a2f2..535bdc3b0 100644 --- a/crates/fullnode/tests/runner_reorg_tests.rs +++ b/crates/fullnode/tests/runner_reorg_tests.rs @@ -130,6 +130,8 @@ async fn runner_execution( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, + disable_subscriptions: false, + max_subscription_connections: 100, }, runner: Some(RunnerConfig { sequencer_client_url: "http://127.0.0.1:4444".to_string(), diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs index 3f0605879..070473356 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs @@ -39,6 +39,12 @@ pub struct RpcConfig { /// Maximum number of batch requests #[serde(default = "default_batch_requests_limit")] pub batch_requests_limit: u32, + /// Disable subscription RPCs + #[serde(default = "default_disable_subscriptions")] + pub disable_subscriptions: bool, + /// Maximum number of subscription connections + #[serde(default = "default_max_subscription_connections")] + pub max_subscription_connections: u32, } #[inline] @@ -66,6 +72,16 @@ const fn default_sync_blocks_count() -> u64 { 10 } +#[inline] +const fn default_disable_subscriptions() -> bool { + false +} + +#[inline] +const fn default_max_subscription_connections() -> u32 { + 100 +} + /// Simple storage configuration #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct StorageConfig { @@ -170,6 +186,8 @@ mod tests { bind_host = "127.0.0.1" bind_port = 12345 max_connections = 500 + disable_subscriptions = false + max_subscription_connections = 200 [da] sender_address = "0000000000000000000000000000000000000000000000000000000000000000" @@ -208,6 +226,8 @@ mod tests { max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, + disable_subscriptions: false, + max_subscription_connections: 200, }, public_keys: RollupPublicKeys { sequencer_public_key: vec![0; 32], From 3f4aa0de5a5be7016636407ac00d3f6da8c09183 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 17:47:22 +0200 Subject: [PATCH 31/34] UPdate log_matches_filter --- crates/ethereum-rpc/src/subscription.rs | 1 - crates/evm/src/query.rs | 9 +-------- crates/evm/src/rpc_helpers/filter.rs | 3 +-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index aba2d4777..3b5bcdc04 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -119,7 +119,6 @@ pub async fn handle_logs_subscription( if log_matches_filter( &log.clone().try_into().unwrap(), &filter, - &filter.topics, log.block_hash.as_ref().unwrap(), &log.block_number.as_ref().unwrap().to::(), ) { diff --git a/crates/evm/src/query.rs b/crates/evm/src/query.rs index c31dfbd83..1a2bf94f6 100644 --- a/crates/evm/src/query.rs +++ b/crates/evm/src/query.rs @@ -1483,7 +1483,6 @@ impl Evm { // TAG - true when the log was removed, due to a chain reorganization. false if its a valid log. let removed = false; - let topics = filter.topics.clone(); let tx_range = block.transactions; for i in tx_range { @@ -1498,13 +1497,7 @@ impl Evm { let logs = receipt.receipt.logs; for log in logs.into_iter() { - if log_matches_filter( - &log, - filter, - &topics, - &block.header.hash(), - &block.header.number, - ) { + if log_matches_filter(&log, filter, &block.header.hash(), &block.header.number) { let log = LogResponse { address: log.address, topics: log.topics().to_vec(), diff --git a/crates/evm/src/rpc_helpers/filter.rs b/crates/evm/src/rpc_helpers/filter.rs index 6f708f1cc..511fb8589 100644 --- a/crates/evm/src/rpc_helpers/filter.rs +++ b/crates/evm/src/rpc_helpers/filter.rs @@ -632,13 +632,12 @@ where pub fn log_matches_filter( log: &reth_primitives::Log, filter: &Filter, - topics: &[FilterSet; 4], block_hash: &BlockHash, block_number: &u64, ) -> bool { if !filter.filter_block_range(block_number) || !filter.filter_block_hash(block_hash) - || !filter.filter_topics(log, topics) + || !filter.filter_topics(log, &filter.topics) || !filter.filter_address(log, &filter.address) { return false; From c94154ba1ede75aedbe1683d67143184dfb72d30 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 17:52:27 +0200 Subject: [PATCH 32/34] recreate working set --- crates/ethereum-rpc/src/subscription.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/ethereum-rpc/src/subscription.rs b/crates/ethereum-rpc/src/subscription.rs index 3b5bcdc04..bd414db17 100644 --- a/crates/ethereum-rpc/src/subscription.rs +++ b/crates/ethereum-rpc/src/subscription.rs @@ -31,15 +31,13 @@ impl SubscriptionManager { return; }; - let mut working_set = None; - if new_heads_tx.receiver_count() != 0 { - working_set = Some(WorkingSet::::new(storage.clone())); + let mut working_set = WorkingSet::::new(storage.clone()); let block = evm .get_block_by_number( Some(BlockNumberOrTag::Number(height)), None, - working_set.as_mut().unwrap(), + &mut working_set, ) .expect("Error querying block from evm") .expect("Received signal but evm block is not found"); @@ -49,8 +47,7 @@ impl SubscriptionManager { } if logs_tx.receiver_count() != 0 { - let mut working_set = - working_set.unwrap_or_else(|| WorkingSet::::new(storage.clone())); + let mut working_set = WorkingSet::::new(storage.clone()); let logs = evm .get_logs_in_block_range( &mut working_set, From e88ecbdf79fa70684f1fc6d0141acbb0d73c1f21 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 19:32:23 +0200 Subject: [PATCH 33/34] Replace disable with enable --- bin/citrea/src/rollup/mod.rs | 6 +++--- bin/citrea/src/test_rpc.rs | 2 +- bin/citrea/tests/test_helpers/mod.rs | 2 +- crates/fullnode/tests/runner_initialization_tests.rs | 2 +- crates/fullnode/tests/runner_reorg_tests.rs | 2 +- .../full-node/sov-stf-runner/src/config.rs | 12 ++++++------ .../bitcoin-regtest/prover_rollup_config.toml | 2 +- resources/configs/bitcoin-regtest/rollup_config.toml | 2 +- .../bitcoin-regtest/sequencer_rollup_config.toml | 2 +- resources/configs/devnet/rollup_config.toml | 2 +- resources/configs/mock-dockerized/rollup_config.toml | 2 +- resources/configs/mock/rollup_config.toml | 2 +- resources/configs/mock/sequencer_rollup_config.toml | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index 31099d970..a36085ebb 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -46,7 +46,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); // If subscriptions disabled, pass None - let soft_confirmation_rx = if !rollup_config.rpc.disable_subscriptions { + let soft_confirmation_rx = if rollup_config.rpc.enable_subscriptions { Some(soft_confirmation_rx) } else { None @@ -126,7 +126,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let runner_config = rollup_config.runner.expect("Runner config is missing"); let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); // If subscriptions disabled, pass None - let soft_confirmation_rx = if !rollup_config.rpc.disable_subscriptions { + let soft_confirmation_rx = if rollup_config.rpc.enable_subscriptions { Some(soft_confirmation_rx) } else { None @@ -212,7 +212,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { let (soft_confirmation_tx, soft_confirmation_rx) = broadcast::channel(10); // If subscriptions disabled, pass None - let soft_confirmation_rx = if !rollup_config.rpc.disable_subscriptions { + let soft_confirmation_rx = if rollup_config.rpc.enable_subscriptions { Some(soft_confirmation_rx) } else { None diff --git a/bin/citrea/src/test_rpc.rs b/bin/citrea/src/test_rpc.rs index a96d5dab1..66ff7b50c 100644 --- a/bin/citrea/src/test_rpc.rs +++ b/bin/citrea/src/test_rpc.rs @@ -99,7 +99,7 @@ fn test_helper( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, - disable_subscriptions: false, + enable_subscriptions: true, max_subscription_connections: 100, }; diff --git a/bin/citrea/tests/test_helpers/mod.rs b/bin/citrea/tests/test_helpers/mod.rs index 51778a6e1..4f77c9407 100644 --- a/bin/citrea/tests/test_helpers/mod.rs +++ b/bin/citrea/tests/test_helpers/mod.rs @@ -143,7 +143,7 @@ pub fn create_default_rollup_config( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, - disable_subscriptions: false, + enable_subscriptions: true, max_subscription_connections: 100, }, runner: match node_mode { diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index 14fbc1afa..496777cad 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -72,7 +72,7 @@ fn initialize_runner( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, - disable_subscriptions: false, + enable_subscriptions: true, max_subscription_connections: 100, }, runner: Some(RunnerConfig { diff --git a/crates/fullnode/tests/runner_reorg_tests.rs b/crates/fullnode/tests/runner_reorg_tests.rs index 535bdc3b0..6dbd86790 100644 --- a/crates/fullnode/tests/runner_reorg_tests.rs +++ b/crates/fullnode/tests/runner_reorg_tests.rs @@ -130,7 +130,7 @@ async fn runner_execution( max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, - disable_subscriptions: false, + enable_subscriptions: true, max_subscription_connections: 100, }, runner: Some(RunnerConfig { diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs index 070473356..f455c036d 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs @@ -40,8 +40,8 @@ pub struct RpcConfig { #[serde(default = "default_batch_requests_limit")] pub batch_requests_limit: u32, /// Disable subscription RPCs - #[serde(default = "default_disable_subscriptions")] - pub disable_subscriptions: bool, + #[serde(default = "default_enable_subscriptions")] + pub enable_subscriptions: bool, /// Maximum number of subscription connections #[serde(default = "default_max_subscription_connections")] pub max_subscription_connections: u32, @@ -73,8 +73,8 @@ const fn default_sync_blocks_count() -> u64 { } #[inline] -const fn default_disable_subscriptions() -> bool { - false +const fn default_enable_subscriptions() -> bool { + true } #[inline] @@ -186,7 +186,7 @@ mod tests { bind_host = "127.0.0.1" bind_port = 12345 max_connections = 500 - disable_subscriptions = false + enable_subscriptions = true max_subscription_connections = 200 [da] @@ -226,7 +226,7 @@ mod tests { max_request_body_size: 10 * 1024 * 1024, max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, - disable_subscriptions: false, + enable_subscriptions: true, max_subscription_connections: 200, }, public_keys: RollupPublicKeys { diff --git a/resources/configs/bitcoin-regtest/prover_rollup_config.toml b/resources/configs/bitcoin-regtest/prover_rollup_config.toml index db4ce681a..944a5d792 100644 --- a/resources/configs/bitcoin-regtest/prover_rollup_config.toml +++ b/resources/configs/bitcoin-regtest/prover_rollup_config.toml @@ -20,7 +20,7 @@ path = "resources/dbs/prover-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12346 -disable_subscriptions = true +enable_subscriptions = false [runner] sequencer_client_url = "http://0.0.0.0:12345" diff --git a/resources/configs/bitcoin-regtest/rollup_config.toml b/resources/configs/bitcoin-regtest/rollup_config.toml index de24377a6..caa47de6f 100644 --- a/resources/configs/bitcoin-regtest/rollup_config.toml +++ b/resources/configs/bitcoin-regtest/rollup_config.toml @@ -20,7 +20,7 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12346 -disable_subscriptions = false +enable_subscriptions = true max_subscription_connections = 2000 [runner] diff --git a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml index 434fd3b34..804920792 100644 --- a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml +++ b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml @@ -20,5 +20,5 @@ path = "resources/dbs/sequencer-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12345 -disable_subscriptions = false +enable_subscriptions = true max_subscription_connections = 2000 diff --git a/resources/configs/devnet/rollup_config.toml b/resources/configs/devnet/rollup_config.toml index 534bbe364..f3ddd58f5 100644 --- a/resources/configs/devnet/rollup_config.toml +++ b/resources/configs/devnet/rollup_config.toml @@ -20,7 +20,7 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "0.0.0.0" bind_port = 12346 -disable_subscriptions = false +enable_subscriptions = true max_subscription_connections = 2000 [runner] diff --git a/resources/configs/mock-dockerized/rollup_config.toml b/resources/configs/mock-dockerized/rollup_config.toml index 3bca71703..471b6c309 100644 --- a/resources/configs/mock-dockerized/rollup_config.toml +++ b/resources/configs/mock-dockerized/rollup_config.toml @@ -15,7 +15,7 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "0.0.0.0" bind_port = 8545 -disable_subscriptions = false +enable_subscriptions = true max_subscription_connections = 2000 [runner] diff --git a/resources/configs/mock/rollup_config.toml b/resources/configs/mock/rollup_config.toml index aa41fe7a6..8545e49e5 100644 --- a/resources/configs/mock/rollup_config.toml +++ b/resources/configs/mock/rollup_config.toml @@ -15,7 +15,7 @@ path = "resources/dbs/full-node-db" # the host and port to bind the rpc server for bind_host = "127.0.0.1" bind_port = 12346 -disable_subscriptions = false +enable_subscriptions = true max_subscription_connections = 2000 [runner] diff --git a/resources/configs/mock/sequencer_rollup_config.toml b/resources/configs/mock/sequencer_rollup_config.toml index 19aff442c..74bd889d6 100644 --- a/resources/configs/mock/sequencer_rollup_config.toml +++ b/resources/configs/mock/sequencer_rollup_config.toml @@ -16,5 +16,5 @@ path = "resources/dbs/sequencer-db" bind_host = "127.0.0.1" bind_port = 12345 max_connections = 10000 -disable_subscriptions = false +enable_subscriptions = true max_subscription_connections = 2000 From d141d6d2006d58cc2d5bba2cf9084162fde5d95a Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Thu, 25 Jul 2024 19:52:04 +0200 Subject: [PATCH 34/34] Rename config --- bin/citrea/src/test_rpc.rs | 2 +- bin/citrea/tests/test_helpers/mod.rs | 2 +- crates/fullnode/src/runner.rs | 2 ++ crates/fullnode/tests/runner_initialization_tests.rs | 2 +- crates/fullnode/tests/runner_reorg_tests.rs | 2 +- crates/prover/src/runner.rs | 2 ++ crates/sequencer/src/sequencer.rs | 2 ++ .../full-node/sov-stf-runner/src/config.rs | 10 +++++----- resources/configs/bitcoin-regtest/rollup_config.toml | 2 +- .../bitcoin-regtest/sequencer_rollup_config.toml | 2 +- resources/configs/devnet/rollup_config.toml | 2 +- resources/configs/mock-dockerized/rollup_config.toml | 2 +- resources/configs/mock/rollup_config.toml | 2 +- resources/configs/mock/sequencer_rollup_config.toml | 2 +- 14 files changed, 21 insertions(+), 15 deletions(-) diff --git a/bin/citrea/src/test_rpc.rs b/bin/citrea/src/test_rpc.rs index 66ff7b50c..cead7b8cf 100644 --- a/bin/citrea/src/test_rpc.rs +++ b/bin/citrea/src/test_rpc.rs @@ -100,7 +100,7 @@ fn test_helper( max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, enable_subscriptions: true, - max_subscription_connections: 100, + max_subscriptions_per_connection: 100, }; queries_test_runner(test_queries, rpc_config).await; diff --git a/bin/citrea/tests/test_helpers/mod.rs b/bin/citrea/tests/test_helpers/mod.rs index 4f77c9407..f9225298e 100644 --- a/bin/citrea/tests/test_helpers/mod.rs +++ b/bin/citrea/tests/test_helpers/mod.rs @@ -144,7 +144,7 @@ pub fn create_default_rollup_config( max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, enable_subscriptions: true, - max_subscription_connections: 100, + max_subscriptions_per_connection: 100, }, runner: match node_mode { NodeMode::FullNode(socket_addr) | NodeMode::Prover(socket_addr) => Some(RunnerConfig { diff --git a/crates/fullnode/src/runner.rs b/crates/fullnode/src/runner.rs index c711088ae..9eca791d3 100644 --- a/crates/fullnode/src/runner.rs +++ b/crates/fullnode/src/runner.rs @@ -170,10 +170,12 @@ where let listen_address = SocketAddr::new(bind_host, self.rpc_config.bind_port); let max_connections = self.rpc_config.max_connections; + let max_subscriptions_per_connection = self.rpc_config.max_subscriptions_per_connection; let _handle = tokio::spawn(async move { let server = jsonrpsee::server::ServerBuilder::default() .max_connections(max_connections) + .max_subscriptions_per_connection(max_subscriptions_per_connection) .build([listen_address].as_ref()) .await; diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index 496777cad..be40a096e 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -73,7 +73,7 @@ fn initialize_runner( max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, enable_subscriptions: true, - max_subscription_connections: 100, + max_subscriptions_per_connection: 100, }, runner: Some(RunnerConfig { sequencer_client_url: "http://127.0.0.1:4444".to_string(), diff --git a/crates/fullnode/tests/runner_reorg_tests.rs b/crates/fullnode/tests/runner_reorg_tests.rs index 6dbd86790..60cb5bdb5 100644 --- a/crates/fullnode/tests/runner_reorg_tests.rs +++ b/crates/fullnode/tests/runner_reorg_tests.rs @@ -131,7 +131,7 @@ async fn runner_execution( max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, enable_subscriptions: true, - max_subscription_connections: 100, + max_subscriptions_per_connection: 100, }, runner: Some(RunnerConfig { sequencer_client_url: "http://127.0.0.1:4444".to_string(), diff --git a/crates/prover/src/runner.rs b/crates/prover/src/runner.rs index 235167bc8..da452975d 100644 --- a/crates/prover/src/runner.rs +++ b/crates/prover/src/runner.rs @@ -175,10 +175,12 @@ where let listen_address = SocketAddr::new(bind_host, self.rpc_config.bind_port); let max_connections = self.rpc_config.max_connections; + let max_subscriptions_per_connection = self.rpc_config.max_subscriptions_per_connection; let _handle = tokio::spawn(async move { let server = jsonrpsee::server::ServerBuilder::default() .max_connections(max_connections) + .max_subscriptions_per_connection(max_subscriptions_per_connection) .build([listen_address].as_ref()) .await; diff --git a/crates/sequencer/src/sequencer.rs b/crates/sequencer/src/sequencer.rs index 6a906702e..1a89af74e 100644 --- a/crates/sequencer/src/sequencer.rs +++ b/crates/sequencer/src/sequencer.rs @@ -202,6 +202,7 @@ where ); let max_connections = self.rpc_config.max_connections; + let max_subscriptions_per_connection = self.rpc_config.max_subscriptions_per_connection; let max_request_body_size = self.rpc_config.max_request_body_size; let max_response_body_size = self.rpc_config.max_response_body_size; let batch_requests_limit = self.rpc_config.batch_requests_limit; @@ -215,6 +216,7 @@ where let _handle = tokio::spawn(async move { let server = ServerBuilder::default() .max_connections(max_connections) + .max_subscriptions_per_connection(max_subscriptions_per_connection) .max_request_body_size(max_request_body_size) .max_response_body_size(max_response_body_size) .set_batch_request_config(BatchRequestConfig::Limit(batch_requests_limit)) diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs index f455c036d..87cda068d 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/config.rs @@ -43,8 +43,8 @@ pub struct RpcConfig { #[serde(default = "default_enable_subscriptions")] pub enable_subscriptions: bool, /// Maximum number of subscription connections - #[serde(default = "default_max_subscription_connections")] - pub max_subscription_connections: u32, + #[serde(default = "default_max_subscriptions_per_connection")] + pub max_subscriptions_per_connection: u32, } #[inline] @@ -78,7 +78,7 @@ const fn default_enable_subscriptions() -> bool { } #[inline] -const fn default_max_subscription_connections() -> u32 { +const fn default_max_subscriptions_per_connection() -> u32 { 100 } @@ -187,7 +187,7 @@ mod tests { bind_port = 12345 max_connections = 500 enable_subscriptions = true - max_subscription_connections = 200 + max_subscriptions_per_connection = 200 [da] sender_address = "0000000000000000000000000000000000000000000000000000000000000000" @@ -227,7 +227,7 @@ mod tests { max_response_body_size: 10 * 1024 * 1024, batch_requests_limit: 50, enable_subscriptions: true, - max_subscription_connections: 200, + max_subscriptions_per_connection: 200, }, public_keys: RollupPublicKeys { sequencer_public_key: vec![0; 32], diff --git a/resources/configs/bitcoin-regtest/rollup_config.toml b/resources/configs/bitcoin-regtest/rollup_config.toml index caa47de6f..828413e07 100644 --- a/resources/configs/bitcoin-regtest/rollup_config.toml +++ b/resources/configs/bitcoin-regtest/rollup_config.toml @@ -21,7 +21,7 @@ path = "resources/dbs/full-node-db" bind_host = "127.0.0.1" bind_port = 12346 enable_subscriptions = true -max_subscription_connections = 2000 +max_subscriptions_per_connection = 100 [runner] sequencer_client_url = "http://0.0.0.0:12345" diff --git a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml index 804920792..f4e1b709c 100644 --- a/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml +++ b/resources/configs/bitcoin-regtest/sequencer_rollup_config.toml @@ -21,4 +21,4 @@ path = "resources/dbs/sequencer-db" bind_host = "127.0.0.1" bind_port = 12345 enable_subscriptions = true -max_subscription_connections = 2000 +max_subscriptions_per_connection = 100 diff --git a/resources/configs/devnet/rollup_config.toml b/resources/configs/devnet/rollup_config.toml index f3ddd58f5..7176301fb 100644 --- a/resources/configs/devnet/rollup_config.toml +++ b/resources/configs/devnet/rollup_config.toml @@ -21,7 +21,7 @@ path = "resources/dbs/full-node-db" bind_host = "0.0.0.0" bind_port = 12346 enable_subscriptions = true -max_subscription_connections = 2000 +max_subscriptions_per_connection = 100 [runner] sequencer_client_url = "https://rpc.devnet.citrea.xyz" diff --git a/resources/configs/mock-dockerized/rollup_config.toml b/resources/configs/mock-dockerized/rollup_config.toml index 471b6c309..25e3636a1 100644 --- a/resources/configs/mock-dockerized/rollup_config.toml +++ b/resources/configs/mock-dockerized/rollup_config.toml @@ -16,7 +16,7 @@ path = "resources/dbs/full-node-db" bind_host = "0.0.0.0" bind_port = 8545 enable_subscriptions = true -max_subscription_connections = 2000 +max_subscriptions_per_connection = 100 [runner] sequencer_client_url = "http://0.0.0.0:8545" diff --git a/resources/configs/mock/rollup_config.toml b/resources/configs/mock/rollup_config.toml index 8545e49e5..9748b4547 100644 --- a/resources/configs/mock/rollup_config.toml +++ b/resources/configs/mock/rollup_config.toml @@ -16,7 +16,7 @@ path = "resources/dbs/full-node-db" bind_host = "127.0.0.1" bind_port = 12346 enable_subscriptions = true -max_subscription_connections = 2000 +max_subscriptions_per_connection = 100 [runner] include_tx_body = false diff --git a/resources/configs/mock/sequencer_rollup_config.toml b/resources/configs/mock/sequencer_rollup_config.toml index 74bd889d6..406b2373e 100644 --- a/resources/configs/mock/sequencer_rollup_config.toml +++ b/resources/configs/mock/sequencer_rollup_config.toml @@ -17,4 +17,4 @@ bind_host = "127.0.0.1" bind_port = 12345 max_connections = 10000 enable_subscriptions = true -max_subscription_connections = 2000 +max_subscriptions_per_connection = 100