Skip to content

Commit

Permalink
Allow running tree API w/o tree on EN
Browse files Browse the repository at this point in the history
  • Loading branch information
slowli committed Oct 24, 2024
1 parent 6fd87a5 commit 5f3904f
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 47 deletions.
39 changes: 32 additions & 7 deletions core/bin/external_node/src/node_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use zksync_config::{
},
PostgresConfig,
};
use zksync_metadata_calculator::{MetadataCalculatorConfig, MetadataCalculatorRecoveryConfig};
use zksync_metadata_calculator::{
MerkleTreeReaderConfig, MetadataCalculatorConfig, MetadataCalculatorRecoveryConfig,
};
use zksync_node_api_server::web3::Namespace;
use zksync_node_framework::{
implementations::layers::{
Expand All @@ -25,7 +27,7 @@ use zksync_node_framework::{
logs_bloom_backfill::LogsBloomBackfillLayer,
main_node_client::MainNodeClientLayer,
main_node_fee_params_fetcher::MainNodeFeeParamsFetcherLayer,
metadata_calculator::MetadataCalculatorLayer,
metadata_calculator::{MetadataCalculatorLayer, TreeApiServerLayer},
node_storage_init::{
external_node_strategy::{ExternalNodeInitStrategyLayer, SnapshotRecoveryConfig},
NodeStorageInitializerLayer,
Expand Down Expand Up @@ -385,6 +387,29 @@ impl ExternalNodeBuilder {
Ok(self)
}

fn add_isolated_tree_api_layer(mut self) -> anyhow::Result<Self> {
let reader_config = MerkleTreeReaderConfig {
db_path: self.config.required.merkle_tree_path.clone(),
max_open_files: self.config.optional.merkle_tree_max_open_files,
multi_get_chunk_size: self.config.optional.merkle_tree_multi_get_chunk_size,
block_cache_capacity: self.config.optional.merkle_tree_block_cache_size(),
include_indices_and_filters_in_block_cache: self
.config
.optional
.merkle_tree_include_indices_and_filters_in_block_cache,
};
let api_config = MerkleTreeApiConfig {
port: self
.config
.tree_component
.api_port
.context("should contain tree api port")?,
};
self.node
.add_layer(TreeApiServerLayer::new(reader_config, api_config));
Ok(self)
}

fn add_tx_sender_layer(mut self) -> anyhow::Result<Self> {
let postgres_storage_config = PostgresStorageCachesConfig {
factory_deps_cache_size: self.config.optional.factory_deps_cache_size() as u64,
Expand Down Expand Up @@ -607,11 +632,11 @@ impl ExternalNodeBuilder {
self = self.add_metadata_calculator_layer(with_tree_api)?;
}
Component::TreeApi => {
anyhow::ensure!(
components.contains(&Component::Tree),
"Merkle tree API cannot be started without a tree component"
);
// Do nothing, will be handled by the `Tree` component.
if components.contains(&Component::Tree) {
// Do nothing, will be handled by the `Tree` component.
} else {
self = self.add_isolated_tree_api_layer()?;
}
}
Component::TreeFetcher => {
self = self.add_tree_data_fetcher_layer()?;
Expand Down
39 changes: 1 addition & 38 deletions core/bin/external_node/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod utils;
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(10);
const POLL_INTERVAL: Duration = Duration::from_millis(100);

#[test_casing(3, ["all", "core", "api"])]
#[test_casing(4, ["all", "core", "api", "core,tree_api"])]
#[tokio::test]
#[tracing::instrument] // Add args to the test logs
async fn external_node_basics(components_str: &'static str) {
Expand Down Expand Up @@ -170,40 +170,3 @@ async fn running_tree_without_core_is_not_allowed() {
err
);
}

#[tokio::test]
async fn running_tree_api_without_tree_is_not_allowed() {
let _guard = zksync_vlog::ObservabilityBuilder::new().try_build().ok(); // Enable logging to simplify debugging
let (env, _env_handles) = utils::TestEnvironment::with_genesis_block("core,tree_api").await;

let l2_client = utils::mock_l2_client(&env);
let eth_client = utils::mock_eth_client(env.config.diamond_proxy_address());

let node_handle = tokio::task::spawn_blocking(move || {
std::thread::spawn(move || {
let mut node = ExternalNodeBuilder::new(env.config)?;
inject_test_layers(
&mut node,
env.sigint_receiver,
env.app_health_sender,
eth_client,
l2_client,
);

// We're only interested in the error, so we drop the result.
node.build(env.components.0.into_iter().collect()).map(drop)
})
.join()
.unwrap()
});

// Check that we cannot build the node without the core component.
let result = node_handle.await.expect("Building the node panicked");
let err = result.expect_err("Building the node with tree api but without tree should fail");
assert!(
err.to_string()
.contains("Merkle tree API cannot be started without a tree component"),
"Unexpected errror: {}",
err
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use std::{
use anyhow::Context as _;
use zksync_config::configs::{api::MerkleTreeApiConfig, database::MerkleTreeMode};
use zksync_metadata_calculator::{
LazyAsyncTreeReader, MerkleTreePruningTask, MetadataCalculator, MetadataCalculatorConfig,
LazyAsyncTreeReader, MerkleTreePruningTask, MerkleTreeReaderConfig, MetadataCalculator,
MetadataCalculatorConfig, TreeReaderTask,
};
use zksync_storage::RocksDB;

Expand All @@ -19,7 +20,7 @@ use crate::{
web3_api::TreeApiClientResource,
},
service::{ShutdownHook, StopReceiver},
task::{Task, TaskId},
task::{Task, TaskId, TaskKind},
wiring_layer::{WiringError, WiringLayer},
FromContext, IntoContext,
};
Expand Down Expand Up @@ -205,3 +206,65 @@ impl Task for MerkleTreePruningTask {
(*self).run(stop_receiver.0).await
}
}

/// Mutually exclusive with [`MetadataCalculatorLayer`].
#[derive(Debug)]
pub struct TreeApiServerLayer {
config: MerkleTreeReaderConfig,
api_config: MerkleTreeApiConfig,
}

impl TreeApiServerLayer {
pub fn new(config: MerkleTreeReaderConfig, api_config: MerkleTreeApiConfig) -> Self {
Self { config, api_config }
}
}

#[derive(Debug, IntoContext)]
#[context(crate = crate)]
pub struct TreeApiServerOutput {
tree_api_client: TreeApiClientResource,
#[context(task)]
tree_reader_task: TreeReaderTask,
#[context(task)]
tree_api_task: TreeApiTask,
}

#[async_trait::async_trait]
impl WiringLayer for TreeApiServerLayer {
type Input = ();
type Output = TreeApiServerOutput;

fn layer_name(&self) -> &'static str {
"tree_api_server"
}

async fn wire(self, (): Self::Input) -> Result<Self::Output, WiringError> {
let tree_reader_task = TreeReaderTask::new(self.config);
let bind_addr = (Ipv4Addr::UNSPECIFIED, self.api_config.port).into();
let tree_api_task = TreeApiTask {
bind_addr,
tree_reader: tree_reader_task.tree_reader(),
};
Ok(TreeApiServerOutput {
tree_api_client: TreeApiClientResource(Arc::new(tree_reader_task.tree_reader())),
tree_api_task,
tree_reader_task,
})
}
}

#[async_trait::async_trait]
impl Task for TreeReaderTask {
fn kind(&self) -> TaskKind {
TaskKind::OneshotTask
}

fn id(&self) -> TaskId {
"merkle_tree_reader_task".into()
}

async fn run(self: Box<Self>, stop_receiver: StopReceiver) -> anyhow::Result<()> {
(*self).run(stop_receiver.0).await
}
}

0 comments on commit 5f3904f

Please sign in to comment.