From be5873ef642c68c4895e94ede95719fbc62484c0 Mon Sep 17 00:00:00 2001 From: jayt106 Date: Fri, 25 Oct 2024 11:28:19 -0400 Subject: [PATCH] feat: L2 network cannot filter transactions (#79) --- core/bin/zksync_server/src/main.rs | 4 +- core/bin/zksync_server/src/node_builder.rs | 27 +++++++-- core/lib/config/src/configs/general.rs | 2 + core/lib/config/src/configs/mod.rs | 2 + core/lib/config/src/configs/tx_sink.rs | 19 +++++++ core/lib/config/src/testonly.rs | 9 +++ core/lib/env_config/src/lib.rs | 1 + core/lib/env_config/src/tx_sink.rs | 35 ++++++++++++ core/lib/protobuf_config/src/general.rs | 2 + core/lib/protobuf_config/src/lib.rs | 1 + .../src/proto/config/general.proto | 2 + .../src/proto/config/tx_sink.proto | 7 +++ core/lib/protobuf_config/src/tests.rs | 1 + core/lib/protobuf_config/src/tx_sink.rs | 19 +++++++ core/lib/zksync_core_leftovers/src/lib.rs | 3 + .../src/temp_config_store/mod.rs | 4 ++ .../src/tx_sender/deny_list_pool_sink.rs | 40 +++++++++++++ core/node/api_server/src/tx_sender/mod.rs | 57 +++++++++++++------ core/node/api_server/src/tx_sender/result.rs | 5 +- core/node/api_server/src/web3/testonly.rs | 13 +++-- .../web3_api/tx_sink/deny_list_pool_sink.rs | 55 ++++++++++++++++++ .../layers/web3_api/tx_sink/mod.rs | 6 +- 22 files changed, 283 insertions(+), 31 deletions(-) create mode 100644 core/lib/config/src/configs/tx_sink.rs create mode 100644 core/lib/env_config/src/tx_sink.rs create mode 100644 core/lib/protobuf_config/src/proto/config/tx_sink.proto create mode 100644 core/lib/protobuf_config/src/tx_sink.rs create mode 100644 core/node/api_server/src/tx_sender/deny_list_pool_sink.rs create mode 100644 core/node/node_framework/src/implementations/layers/web3_api/tx_sink/deny_list_pool_sink.rs diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 9e1a1b594..9371d1bca 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -12,6 +12,7 @@ use zksync_config::{ fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, secrets::DataAvailabilitySecrets, + tx_sink::TxSinkConfig, BasicWitnessInputProducerConfig, ContractsConfig, DatabaseSecrets, ExperimentalVmConfig, ExternalPriceApiClientConfig, FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, @@ -45,7 +46,7 @@ struct Cli { /// Comma-separated list of components to launch. #[arg( long, - default_value = "api,tree,eth,state_keeper,housekeeper,commitment_generator,da_dispatcher,vm_runner_protective_reads" + default_value = "api,tree,eth,state_keeper,housekeeper,commitment_generator,da_dispatcher,vm_runner_protective_reads,deny_list" )] components: ComponentsToRun, /// Path to the yaml config. If set, it will be used instead of env vars. @@ -196,5 +197,6 @@ fn load_env_config() -> anyhow::Result { external_proof_integration_api_config: ExternalProofIntegrationApiConfig::from_env().ok(), experimental_vm_config: ExperimentalVmConfig::from_env().ok(), prover_job_monitor_config: None, + tx_sink_config: TxSinkConfig::from_env().ok(), }) } diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 234e22894..64b1655ff 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -64,7 +64,7 @@ use zksync_node_framework::{ server::{Web3ServerLayer, Web3ServerOptionalConfig}, tree_api_client::TreeApiClientLayer, tx_sender::{PostgresStorageCachesConfig, TxSenderLayer}, - tx_sink::MasterPoolSinkLayer, + tx_sink::{DenyListPoolSinkLayer, MasterPoolSinkLayer}, }, }, service::{ZkStackService, ZkStackServiceBuilder}, @@ -300,7 +300,7 @@ impl MainNodeBuilder { Ok(self) } - fn add_tx_sender_layer(mut self) -> anyhow::Result { + fn add_tx_sender_layer(mut self, deny_list_enabled: bool) -> anyhow::Result { let sk_config = try_load_config!(self.configs.state_keeper_config); let rpc_config = try_load_config!(self.configs.api_config).web3_json_rpc; let postgres_storage_caches_config = PostgresStorageCachesConfig { @@ -310,8 +310,17 @@ impl MainNodeBuilder { latest_values_max_block_lag: rpc_config.latest_values_max_block_lag(), }; - // On main node we always use master pool sink. - self.node.add_layer(MasterPoolSinkLayer); + let tx_sink_config = try_load_config!(self.configs.tx_sink_config); + if deny_list_enabled && tx_sink_config.deny_list().is_some() { + tracing::info!("run DenyListPoolSinkLayer {:?}", tx_sink_config.deny_list()); + self.node.add_layer(DenyListPoolSinkLayer::new( + tx_sink_config.deny_list().unwrap(), + )); + } else { + tracing::info!("run MasterPoolSinkLayer"); + self.node.add_layer(MasterPoolSinkLayer); + } + self.node.add_layer(TxSenderLayer::new( TxSenderConfig::new( &sk_config, @@ -667,6 +676,8 @@ impl MainNodeBuilder { _ => 0, }); + let mut deny_list_enabled = false; + // Add "component-specific" layers. // Note that the layers are added only once, so it's fine to add the same layer multiple times. for component in &components { @@ -683,7 +694,7 @@ impl MainNodeBuilder { Component::HttpApi => { self = self .add_l1_gas_layer()? - .add_tx_sender_layer()? + .add_tx_sender_layer(deny_list_enabled)? .add_tree_api_client_layer()? .add_api_caches_layer()? .add_http_web3_api_layer()?; @@ -691,7 +702,7 @@ impl MainNodeBuilder { Component::WsApi => { self = self .add_l1_gas_layer()? - .add_tx_sender_layer()? + .add_tx_sender_layer(deny_list_enabled)? .add_tree_api_client_layer()? .add_api_caches_layer()? .add_ws_web3_api_layer()?; @@ -756,6 +767,10 @@ impl MainNodeBuilder { Component::ExternalProofIntegrationApi => { self = self.add_external_proof_integration_api_layer()?; } + Component::TxSinkDenyList => { + tracing::info!("L2 denylist enabled."); + deny_list_enabled = true; + } } } Ok(self.node.build()) diff --git a/core/lib/config/src/configs/general.rs b/core/lib/config/src/configs/general.rs index bb733510f..bbaf31937 100644 --- a/core/lib/config/src/configs/general.rs +++ b/core/lib/config/src/configs/general.rs @@ -10,6 +10,7 @@ use crate::{ prover_job_monitor::ProverJobMonitorConfig, pruning::PruningConfig, snapshot_recovery::SnapshotRecoveryConfig, + tx_sink::TxSinkConfig, vm_runner::{BasicWitnessInputProducerConfig, ProtectiveReadsWriterConfig}, CommitmentGeneratorConfig, ExperimentalVmConfig, ExternalPriceApiClientConfig, FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, @@ -56,4 +57,5 @@ pub struct GeneralConfig { pub external_proof_integration_api_config: Option, pub experimental_vm_config: Option, pub prover_job_monitor_config: Option, + pub tx_sink_config: Option, } diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index a8d136d63..0a6b624d8 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -28,6 +28,7 @@ pub use self::{ secrets::{DatabaseSecrets, L1Secrets, Secrets}, snapshot_recovery::SnapshotRecoveryConfig, snapshots_creator::SnapshotsCreatorConfig, + tx_sink::TxSinkConfig, utils::PrometheusConfig, vm_runner::{BasicWitnessInputProducerConfig, ProtectiveReadsWriterConfig}, }; @@ -66,6 +67,7 @@ pub mod pruning; pub mod secrets; pub mod snapshot_recovery; pub mod snapshots_creator; +pub mod tx_sink; pub mod utils; pub mod vm_runner; pub mod wallets; diff --git a/core/lib/config/src/configs/tx_sink.rs b/core/lib/config/src/configs/tx_sink.rs new file mode 100644 index 000000000..2d3b981aa --- /dev/null +++ b/core/lib/config/src/configs/tx_sink.rs @@ -0,0 +1,19 @@ +use std::{collections::HashSet, str::FromStr}; + +use serde::Deserialize; +use zksync_basic_types::Address; + +#[derive(Debug, Deserialize, Clone, PartialEq)] +pub struct TxSinkConfig { + pub deny_list: Option, +} + +impl TxSinkConfig { + pub fn deny_list(&self) -> Option> { + self.deny_list.as_ref().map(|list| { + list.split(',') + .map(|element| Address::from_str(element).unwrap()) + .collect() + }) + } +} diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 9b1ec13e2..f04b7d2af 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -1166,6 +1166,15 @@ impl Distribution for EncodeDist { external_proof_integration_api_config: self.sample(rng), experimental_vm_config: self.sample(rng), prover_job_monitor_config: self.sample(rng), + tx_sink_config: self.sample(rng), + } + } +} + +impl Distribution for EncodeDist { + fn sample(&self, rng: &mut R) -> configs::TxSinkConfig { + configs::TxSinkConfig { + deny_list: self.sample(rng), } } } diff --git a/core/lib/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index b72c2c5d5..8f7a35d76 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -19,6 +19,7 @@ pub mod object_store; mod observability; mod proof_data_handler; mod snapshots_creator; +mod tx_sink; mod utils; mod base_token_adjuster; diff --git a/core/lib/env_config/src/tx_sink.rs b/core/lib/env_config/src/tx_sink.rs new file mode 100644 index 000000000..92890bac8 --- /dev/null +++ b/core/lib/env_config/src/tx_sink.rs @@ -0,0 +1,35 @@ +use zksync_config::configs::TxSinkConfig; + +use crate::{envy_load, FromEnv}; + +impl FromEnv for TxSinkConfig { + fn from_env() -> anyhow::Result { + envy_load("tx_sink", "TX_SINK_") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::EnvMutex; + + static MUTEX: EnvMutex = EnvMutex::new(); + + fn expected_config() -> TxSinkConfig { + TxSinkConfig { + deny_list: Some("0x1234567890abcdef".to_string()), + } + } + + #[test] + fn from_env() { + let mut lock = MUTEX.lock(); + let config = r#" + TX_SINK_DENY_LIST="0x1234567890abcdef" + "#; + lock.set_env(config); + + let actual = TxSinkConfig::from_env().unwrap(); + assert_eq!(actual, expected_config()); + } +} diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index b73539a08..ec62f3d47 100644 --- a/core/lib/protobuf_config/src/general.rs +++ b/core/lib/protobuf_config/src/general.rs @@ -46,6 +46,7 @@ impl ProtoRepr for proto::GeneralConfig { ), experimental_vm_config: read_optional_repr(&self.experimental_vm), prover_job_monitor_config: read_optional_repr(&self.prover_job_monitor), + tx_sink_config: read_optional_repr(&self.tx_sink), }) } @@ -106,6 +107,7 @@ impl ProtoRepr for proto::GeneralConfig { .prover_job_monitor_config .as_ref() .map(ProtoRepr::build), + tx_sink: this.tx_sink_config.as_ref().map(ProtoRepr::build), } } } diff --git a/core/lib/protobuf_config/src/lib.rs b/core/lib/protobuf_config/src/lib.rs index 68f7f699d..2dd057ec8 100644 --- a/core/lib/protobuf_config/src/lib.rs +++ b/core/lib/protobuf_config/src/lib.rs @@ -36,6 +36,7 @@ mod snapshot_recovery; mod snapshots_creator; #[cfg(test)] mod tests; +mod tx_sink; mod utils; mod vm_runner; mod wallets; diff --git a/core/lib/protobuf_config/src/proto/config/general.proto b/core/lib/protobuf_config/src/proto/config/general.proto index ee70b61b1..586ad1e12 100644 --- a/core/lib/protobuf_config/src/proto/config/general.proto +++ b/core/lib/protobuf_config/src/proto/config/general.proto @@ -26,6 +26,7 @@ import "zksync/config/external_proof_integration_api.proto"; import "zksync/core/consensus.proto"; import "zksync/config/prover_job_monitor.proto"; import "zksync/config/da_client.proto"; +import "zksync/config/tx_sink.proto"; message GeneralConfig { optional database.Postgres postgres = 1; @@ -62,4 +63,5 @@ message GeneralConfig { optional experimental.Vm experimental_vm = 44; optional prover_job_monitor.ProverJobMonitor prover_job_monitor = 45; optional da_client.DataAvailabilityClient da_client = 46; + optional tx_sink.TxSink tx_sink = 100; } diff --git a/core/lib/protobuf_config/src/proto/config/tx_sink.proto b/core/lib/protobuf_config/src/proto/config/tx_sink.proto new file mode 100644 index 000000000..24a9d1b0e --- /dev/null +++ b/core/lib/protobuf_config/src/proto/config/tx_sink.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package zksync.config.tx_sink; + +message TxSink { + optional string deny_list = 1; // optional +} diff --git a/core/lib/protobuf_config/src/tests.rs b/core/lib/protobuf_config/src/tests.rs index c72bce0bf..0be6eca83 100644 --- a/core/lib/protobuf_config/src/tests.rs +++ b/core/lib/protobuf_config/src/tests.rs @@ -55,6 +55,7 @@ fn test_encoding() { rng, ); test_encode_all_formats::>(rng); + test_encode_all_formats::>(rng); } #[test] diff --git a/core/lib/protobuf_config/src/tx_sink.rs b/core/lib/protobuf_config/src/tx_sink.rs new file mode 100644 index 000000000..7d6114093 --- /dev/null +++ b/core/lib/protobuf_config/src/tx_sink.rs @@ -0,0 +1,19 @@ +use zksync_config::configs; +use zksync_protobuf::repr::ProtoRepr; + +use crate::proto::tx_sink as proto; + +impl ProtoRepr for proto::TxSink { + type Type = configs::tx_sink::TxSinkConfig; + fn read(&self) -> anyhow::Result { + Ok(Self::Type { + deny_list: self.deny_list.clone(), + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + deny_list: this.deny_list.clone(), + } + } +} diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index 87fb7ea28..278424993 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -63,6 +63,8 @@ pub enum Component { ExternalProofIntegrationApi, /// VM runner-based component that allows to test experimental VM features. Doesn't save any data to Postgres. VmPlayground, + /// Component for filtering L2 transactions by denylist + TxSinkDenyList, } #[derive(Debug)] @@ -108,6 +110,7 @@ impl FromStr for Components { "external_proof_integration_api" => { Ok(Components(vec![Component::ExternalProofIntegrationApi])) } + "deny_list" => Ok(Components(vec![Component::TxSinkDenyList])), other => Err(format!("{} is not a valid component name", other)), } } diff --git a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs index eb2170bcc..2a3179afc 100644 --- a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs +++ b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs @@ -17,6 +17,7 @@ use zksync_config::{ FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, GeneralConfig, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, ProtectiveReadsWriterConfig, ProverJobMonitorConfig, PruningConfig, SnapshotRecoveryConfig, + TxSinkConfig, }, ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, DAClientConfig, DADispatcherConfig, DBConfig, EthConfig, EthWatchConfig, ExternalProofIntegrationApiConfig, GasAdjusterConfig, @@ -81,6 +82,7 @@ pub struct TempConfigStore { pub external_proof_integration_api_config: Option, pub experimental_vm_config: Option, pub prover_job_monitor_config: Option, + pub tx_sink_config: Option, } impl TempConfigStore { @@ -122,6 +124,7 @@ impl TempConfigStore { .clone(), experimental_vm_config: self.experimental_vm_config.clone(), prover_job_monitor_config: self.prover_job_monitor_config.clone(), + tx_sink_config: self.tx_sink_config.clone(), } } @@ -203,6 +206,7 @@ fn load_env_config() -> anyhow::Result { external_proof_integration_api_config: ExternalProofIntegrationApiConfig::from_env().ok(), experimental_vm_config: ExperimentalVmConfig::from_env().ok(), prover_job_monitor_config: ProverJobMonitorConfig::from_env().ok(), + tx_sink_config: TxSinkConfig::from_env().ok(), }) } diff --git a/core/node/api_server/src/tx_sender/deny_list_pool_sink.rs b/core/node/api_server/src/tx_sender/deny_list_pool_sink.rs new file mode 100644 index 000000000..81c1735e0 --- /dev/null +++ b/core/node/api_server/src/tx_sender/deny_list_pool_sink.rs @@ -0,0 +1,40 @@ +use std::collections::HashSet; + +use zksync_dal::transactions_dal::L2TxSubmissionResult; +use zksync_multivm::interface::TransactionExecutionMetrics; +use zksync_types::{l2::L2Tx, Address}; + +use super::{master_pool_sink::MasterPoolSink, tx_sink::TxSink, SubmitTxError}; +//use crate::api_server::tx_sender::master_pool_sink::MasterPoolSink; + +/// Wrapper for the master DB pool that allows to submit transactions to the mempool. +#[derive(Debug)] +pub struct DenyListPoolSink { + deny_list: HashSet
, + master_pool_sync: MasterPoolSink, +} + +impl DenyListPoolSink { + pub fn new(master_pool_sync: MasterPoolSink, deny_list: HashSet
) -> Self { + Self { + master_pool_sync, + deny_list, + } + } +} + +#[async_trait::async_trait] +impl TxSink for DenyListPoolSink { + async fn submit_tx( + &self, + tx: &L2Tx, + execution_metrics: TransactionExecutionMetrics, + ) -> Result { + let address_and_nonce = (tx.initiator_account(), tx.nonce()); + if self.deny_list.contains(&address_and_nonce.0) { + return Err(SubmitTxError::SenderInDenyList(tx.initiator_account())); + } + + self.master_pool_sync.submit_tx(tx, execution_metrics).await + } +} diff --git a/core/node/api_server/src/tx_sender/mod.rs b/core/node/api_server/src/tx_sender/mod.rs index 38794fe71..56e4db384 100644 --- a/core/node/api_server/src/tx_sender/mod.rs +++ b/core/node/api_server/src/tx_sender/mod.rs @@ -1,10 +1,10 @@ //! Helper module to submit transactions into the ZKsync Network. -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; use anyhow::Context as _; use tokio::sync::RwLock; -use zksync_config::configs::{api::Web3JsonRpcConfig, chain::StateKeeperConfig}; +use zksync_config::configs::{api::Web3JsonRpcConfig, chain::StateKeeperConfig, TxSinkConfig}; use zksync_dal::{ transactions_dal::L2TxSubmissionResult, Connection, ConnectionPool, Core, CoreDal, }; @@ -34,13 +34,14 @@ use zksync_vm_executor::oneshot::{ }; pub(super) use self::{gas_estimation::BinarySearchKind, result::SubmitTxError}; -use self::{master_pool_sink::MasterPoolSink, result::ApiCallResult, tx_sink::TxSink}; +use self::{deny_list_pool_sink::DenyListPoolSink, master_pool_sink::MasterPoolSink, result::ApiCallResult, tx_sink::TxSink}; use crate::execution_sandbox::{ BlockArgs, SandboxAction, SandboxExecutor, SubmitTxStage, VmConcurrencyBarrier, VmConcurrencyLimiter, SANDBOX_METRICS, }; mod gas_estimation; +pub mod deny_list_pool_sink; pub mod master_pool_sink; pub mod proxy; mod result; @@ -48,33 +49,53 @@ mod result; pub(crate) mod tests; pub mod tx_sink; +pub struct TxSenderBuilderConfigs { + pub tx_sender_config: TxSenderConfig, + pub web3_json_config: Web3JsonRpcConfig, + pub state_keeper_config: StateKeeperConfig, + pub tx_sink_config: Option, +} + pub async fn build_tx_sender( - tx_sender_config: &TxSenderConfig, - web3_json_config: &Web3JsonRpcConfig, - state_keeper_config: &StateKeeperConfig, + builder_config: TxSenderBuilderConfigs, replica_pool: ConnectionPool, master_pool: ConnectionPool, batch_fee_model_input_provider: Arc, storage_caches: PostgresStorageCaches, ) -> anyhow::Result<(TxSender, VmConcurrencyBarrier)> { - let sequencer_sealer = SequencerSealer::new(state_keeper_config.clone()); - let master_pool_sink = MasterPoolSink::new(master_pool); - let tx_sender_builder = TxSenderBuilder::new( - tx_sender_config.clone(), - replica_pool.clone(), - Arc::new(master_pool_sink), - ) - .with_sealer(Arc::new(sequencer_sealer)); + let sequencer_sealer = SequencerSealer::new(builder_config.state_keeper_config); + + let tx_sender_builder = if let Some(config) = builder_config.tx_sink_config { + let deny_list_pool_sink = if let Some(list) = config.deny_list() { + DenyListPoolSink::new(MasterPoolSink::new(master_pool), list) + } else { + DenyListPoolSink::new(MasterPoolSink::new(master_pool), HashSet::
::new()) + }; - let max_concurrency = web3_json_config.vm_concurrency_limit(); + TxSenderBuilder::new( + builder_config.tx_sender_config.clone(), + replica_pool.clone(), + Arc::new(deny_list_pool_sink), + ) + .with_sealer(Arc::new(sequencer_sealer)) + } else { + TxSenderBuilder::new( + builder_config.tx_sender_config.clone(), + replica_pool.clone(), + Arc::new(MasterPoolSink::new(master_pool)), + ) + .with_sealer(Arc::new(sequencer_sealer)) + }; + + let max_concurrency = builder_config.web3_json_config.vm_concurrency_limit(); let (vm_concurrency_limiter, vm_barrier) = VmConcurrencyLimiter::new(max_concurrency); let batch_fee_input_provider = ApiFeeInputProvider::new(batch_fee_model_input_provider, replica_pool); let executor_options = SandboxExecutorOptions::new( - tx_sender_config.chain_id, - AccountTreeId::new(tx_sender_config.fee_account_addr), - tx_sender_config.validation_computational_gas_limit, + builder_config.tx_sender_config.chain_id, + AccountTreeId::new(builder_config.tx_sender_config.fee_account_addr), + builder_config.tx_sender_config.validation_computational_gas_limit, ) .await?; let tx_sender = tx_sender_builder.build( diff --git a/core/node/api_server/src/tx_sender/result.rs b/core/node/api_server/src/tx_sender/result.rs index e2a51ae8e..364ae4ac1 100644 --- a/core/node/api_server/src/tx_sender/result.rs +++ b/core/node/api_server/src/tx_sender/result.rs @@ -1,6 +1,6 @@ use thiserror::Error; use zksync_multivm::interface::{ExecutionResult, VmExecutionResultAndLogs}; -use zksync_types::{l2::error::TxCheckError, U256}; +use zksync_types::{l2::error::TxCheckError, Address, U256}; use zksync_web3_decl::error::EnrichedClientError; use crate::execution_sandbox::{SandboxExecutionError, ValidationError}; @@ -67,6 +67,8 @@ pub enum SubmitTxError { /// Catch-all internal error (e.g., database error) that should not be exposed to the caller. #[error("internal error")] Internal(#[from] anyhow::Error), + #[error("sender address {0} is in deny list")] + SenderInDenyList(Address), } impl SubmitTxError { @@ -96,6 +98,7 @@ impl SubmitTxError { Self::MintedAmountOverflow => "minted-amount-overflow", Self::ProxyError(_) => "proxy-error", Self::Internal(_) => "internal", + Self::SenderInDenyList(_) => "sender-in-deny-list", } } diff --git a/core/node/api_server/src/web3/testonly.rs b/core/node/api_server/src/web3/testonly.rs index 2d642b9a0..5d9721da1 100644 --- a/core/node/api_server/src/web3/testonly.rs +++ b/core/node/api_server/src/web3/testonly.rs @@ -15,7 +15,7 @@ use zksync_vm_executor::oneshot::MockOneshotExecutor; use super::{metrics::ApiTransportLabel, *}; use crate::{ execution_sandbox::SandboxExecutor, - tx_sender::{SandboxExecutorOptions, TxSenderConfig}, + tx_sender::{SandboxExecutorOptions, TxSenderConfig,TxSenderBuilderConfigs}, }; const TEST_TIMEOUT: Duration = Duration::from_secs(90); @@ -36,12 +36,17 @@ pub(crate) async fn create_test_tx_sender( l2_chain_id, ); + let config = TxSenderBuilderConfigs { + tx_sender_config: tx_sender_config.clone(), + web3_json_config: web3_config.clone(), + state_keeper_config: state_keeper_config.clone(), + tx_sink_config: None, + }; + let storage_caches = PostgresStorageCaches::new(1, 1); let batch_fee_model_input_provider = Arc::::default(); let (mut tx_sender, vm_barrier) = crate::tx_sender::build_tx_sender( - &tx_sender_config, - &web3_config, - &state_keeper_config, + config, pool.clone(), pool, batch_fee_model_input_provider, diff --git a/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/deny_list_pool_sink.rs b/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/deny_list_pool_sink.rs new file mode 100644 index 000000000..050b31142 --- /dev/null +++ b/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/deny_list_pool_sink.rs @@ -0,0 +1,55 @@ +use std::collections::HashSet; + +use zksync_node_api_server::tx_sender::{ + deny_list_pool_sink::DenyListPoolSink, master_pool_sink::MasterPoolSink, +}; +use zksync_types::Address; + +use crate::{ + implementations::resources::{ + pools::{MasterPool, PoolResource}, + web3_api::TxSinkResource, + }, + wiring_layer::{WiringError, WiringLayer}, + FromContext, IntoContext, +}; + +/// Wiring layer for [`DenyListPoolSink`], [`TxSink`](zksync_node_api_server::tx_sender::tx_sink::TxSink) implementation. +pub struct DenyListPoolSinkLayer { + deny_list: HashSet
, +} + +impl DenyListPoolSinkLayer { + pub fn new(deny_list: HashSet
) -> Self { + Self { deny_list } + } +} + +#[derive(Debug, FromContext)] +#[context(crate = crate)] +pub struct Input { + pub pool: PoolResource, +} + +#[derive(Debug, IntoContext)] +#[context(crate = crate)] +pub struct Output { + pub tx_sink: TxSinkResource, +} + +#[async_trait::async_trait] +impl WiringLayer for DenyListPoolSinkLayer { + type Input = Input; + type Output = Output; + + fn layer_name(&self) -> &'static str { + "deny_list_pool_sink_layer" + } + + async fn wire(self, input: Self::Input) -> Result { + let pool = input.pool.get().await?; + Ok(Output { + tx_sink: DenyListPoolSink::new(MasterPoolSink::new(pool), self.deny_list).into(), + }) + } +} diff --git a/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/mod.rs b/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/mod.rs index 61b9fb1d9..daadacb8f 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/mod.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/tx_sink/mod.rs @@ -1,4 +1,8 @@ -pub use self::{master_pool_sink::MasterPoolSinkLayer, proxy_sink::ProxySinkLayer}; +pub use self::{ + deny_list_pool_sink::DenyListPoolSinkLayer, master_pool_sink::MasterPoolSinkLayer, + proxy_sink::ProxySinkLayer, +}; +pub mod deny_list_pool_sink; pub mod master_pool_sink; pub mod proxy_sink;