diff --git a/chain/chain/src/store/mod.rs b/chain/chain/src/store/mod.rs index 1a0992c61d4..d51809fb040 100644 --- a/chain/chain/src/store/mod.rs +++ b/chain/chain/src/store/mod.rs @@ -1957,121 +1957,6 @@ impl<'a> ChainStoreUpdate<'a> { Ok(()) } - /// Only used in mock network - /// Create a new ChainStoreUpdate that copies the necessary chain state related to `block_hash` - /// from `source_store` to the current store. - pub fn copy_chain_state_as_of_block( - chain_store: &'a mut ChainStore, - block_hash: &CryptoHash, - source_epoch_manager: &dyn EpochManagerAdapter, - source_store: &ChainStore, - ) -> Result, Error> { - let mut chain_store_update = ChainStoreUpdate::new(chain_store); - let block = source_store.get_block(block_hash)?; - let header = block.header().clone(); - let height = header.height(); - let tip = Tip { - height, - last_block_hash: *block_hash, - prev_block_hash: *header.prev_hash(), - epoch_id: *header.epoch_id(), - next_epoch_id: *header.next_epoch_id(), - }; - chain_store_update.head = Some(tip.clone()); - chain_store_update.tail = Some(height); - chain_store_update.chunk_tail = Some(height); - chain_store_update.fork_tail = Some(height); - chain_store_update.header_head = Some(tip.clone()); - chain_store_update.final_head = Some(tip); - chain_store_update.chain_store_cache_update.blocks.insert(*block_hash, block.clone()); - chain_store_update.chain_store_cache_update.headers.insert(*block_hash, header.clone()); - // store all headers until header.last_final_block - // needed to light client - let mut prev_hash = *header.prev_hash(); - let last_final_hash = header.last_final_block(); - loop { - let header = source_store.get_block_header(&prev_hash)?; - chain_store_update.chain_store_cache_update.headers.insert(prev_hash, header.clone()); - if &prev_hash == last_final_hash { - break; - } else { - chain_store_update - .chain_store_cache_update - .next_block_hashes - .insert(*header.prev_hash(), prev_hash); - prev_hash = *header.prev_hash(); - } - } - chain_store_update - .chain_store_cache_update - .block_extras - .insert(*block_hash, source_store.get_block_extra(block_hash)?); - let shard_layout = source_epoch_manager.get_shard_layout(&header.epoch_id())?; - for shard_uid in shard_layout.shard_uids() { - chain_store_update.chain_store_cache_update.chunk_extras.insert( - (*block_hash, shard_uid), - source_store.get_chunk_extra(block_hash, &shard_uid)?.clone(), - ); - } - for (shard_index, chunk_header) in block.chunks().iter_deprecated().enumerate() { - let shard_id = shard_layout.get_shard_id(shard_index)?; - let chunk_hash = chunk_header.chunk_hash(); - chain_store_update - .chain_store_cache_update - .chunks - .insert(chunk_hash.clone(), source_store.get_chunk(&chunk_hash)?.clone()); - chain_store_update.chain_store_cache_update.outgoing_receipts.insert( - (*block_hash, shard_id), - source_store.get_outgoing_receipts(block_hash, shard_id)?.clone(), - ); - chain_store_update.chain_store_cache_update.incoming_receipts.insert( - (*block_hash, shard_id), - source_store.get_incoming_receipts(block_hash, shard_id)?.clone(), - ); - let outcome_ids = - source_store.get_outcomes_by_block_hash_and_shard_id(block_hash, shard_id)?; - for id in outcome_ids.iter() { - if let Some(existing_outcome) = - source_store.get_outcome_by_id_and_block_hash(id, block_hash)? - { - chain_store_update - .chain_store_cache_update - .outcomes - .insert((*id, *block_hash), existing_outcome); - } - } - chain_store_update - .chain_store_cache_update - .outcome_ids - .insert((*block_hash, shard_id), outcome_ids); - } - chain_store_update - .chain_store_cache_update - .height_to_hashes - .insert(height, Some(*block_hash)); - chain_store_update - .chain_store_cache_update - .next_block_hashes - .insert(*header.prev_hash(), *block_hash); - let block_merkle_tree = source_store.get_block_merkle_tree(block_hash)?; - chain_store_update - .chain_store_cache_update - .block_merkle_tree - .insert(*block_hash, block_merkle_tree.clone()); - chain_store_update - .chain_store_cache_update - .block_ordinal_to_hash - .insert(block_merkle_tree.size(), *block_hash); - chain_store_update.chain_store_cache_update.processed_block_heights.insert(height); - - // other information not directly related to this block - chain_store_update.chain_store_cache_update.height_to_hashes.insert( - source_store.get_genesis_height(), - Some(source_store.get_block_hash_by_height(source_store.get_genesis_height())?), - ); - Ok(chain_store_update) - } - #[tracing::instrument(level = "debug", target = "store", "ChainUpdate::finalize", skip_all)] fn finalize(&mut self) -> Result { let mut store_update = self.store().store_update(); diff --git a/tools/mock-node/src/lib.rs b/tools/mock-node/src/lib.rs index 7a705419245..ad8723999ca 100644 --- a/tools/mock-node/src/lib.rs +++ b/tools/mock-node/src/lib.rs @@ -429,6 +429,7 @@ impl MockPeer { // listen on the addr passed to MockPeer::new() and wait til someone connects. // Then respond to messages indefinitely until an error occurs async fn run(mut self, target_height: BlockHeight) -> anyhow::Result<()> { + // TODO: should just keep accepting incoming conns let mut conn = self.listener.accept().await?; let messages = InFlightMessages::new(self.network_config.response_delay); tokio::pin!(messages); diff --git a/tools/mock-node/src/main.rs b/tools/mock-node/src/main.rs index 1fdcaf5ec0d..6f17a289e70 100644 --- a/tools/mock-node/src/main.rs +++ b/tools/mock-node/src/main.rs @@ -3,127 +3,45 @@ //! network, responding to the client's network requests by reading from a //! pre-generated chain history in storage. -use actix::System; use anyhow::Context; -use mock_node::setup::{setup_mock_node, MockNode}; +use mock_node::setup::setup_mock_node; use mock_node::MockNetworkConfig; -use near_actix_test_utils::run_actix; -use near_chain_configs::{GenesisValidationMode, MutableConfigValue}; -use near_crypto::{InMemorySigner, KeyType}; -use near_jsonrpc_client::JsonRpcClient; -use near_network::tcp; +use near_actix_test_utils::{block_on_interruptible, setup_actix}; use near_o11y::testonly::init_integration_logger; use near_primitives::types::BlockHeight; -use std::net::SocketAddr; -use std::path::{Path, PathBuf}; -use std::time::{Duration, Instant}; +use std::path::Path; +use std::time::Duration; -/// Program to start a mock node, which runs a regular client in a mock network environment. -/// The mock network simulates the entire network by replaying a pre-generated chain history -/// from storage and responds to the client's network requests. +/// Program to start a mock node, which starts a TCP server and accepts incoming +/// connections from NEAR nodes. Once connected, it will respond to block and chunk +/// requests, but not do anything else unless periodic outgoing messages are +/// are specified in $home/mock.json. /// -/// There are two ways to replay the stored history: -/// * catchup: client is behind the network and applies the blocks as fast as possible -/// * normal block production: client accept "new" blocks as they are produced -/// (in reality, blocks are just fetched from the pre-generated store). -/// -/// This is controlled by two flags: -/// * `--client-height` specifies the height the client starts at. Defaults to 0. -/// * `--network-height` specifies the hight the rest of the (simulated) -/// network starts at. Defaults to the latest recorded height. -/// -/// As a shortcut, `--start-height` sets both. -/// -/// -/// Examples -/// -/// ```console -/// # Pure catchup from genesis height to the end of the recorded history. -/// $ mock-node ~/.near/localnet/node0 -/// -/// # Pure block production starting from block height 61. -/// $ mock-node ~/.near/localnet/node0 --start-height 61 -/// -/// # Mixed: client starts at genesis and tries to catch up with the network, which starts at height 20. -/// $ mock-node ~/.near/localnet/node0 --network-height 20 -/// ``` #[derive(clap::Parser)] struct Cli { /// Existing home dir for the pre-generated chain history. For example, you can use /// the home dir of a near node. - chain_history_home_dir: String, - /// Home dir for the new client that will be started. If not specified, the binary will - /// generate a temporary directory - client_home_dir: Option, - /// Simulated network delay (in ms) + #[clap(long)] + home: String, + /// If set, the mock node will wait this many millis before sending messages #[clap(short = 'd', long)] network_delay: Option, - /// If specified, the binary will set up client home dir before starting the - /// client node so head of the client chain will be the specified height - /// when the client starts. The given height must be the last block in an - /// epoch. - #[clap(long, default_value = "0")] - client_height: BlockHeight, /// The height at which the mock network starts. The client would have to /// catch up to this height before participating in new block production. /// /// Defaults to the largest height in history. #[clap(long)] network_height: Option, - /// Shortcut to set both `--client-height` and `--network-height`. - #[clap(long, conflicts_with_all(&["client-height", "network-height"]))] - start_height: Option, /// Target height that the client should sync to before stopping. If not specified, /// use the height of the last block in chain history #[clap(long)] target_height: Option, - /// If true, use in memory storage instead of rocksdb for the client - #[clap(short = 'i', long)] - in_memory_storage: bool, - /// port the mock node should listen on - #[clap(long)] - mock_port: Option, -} - -async fn target_height_reached(client: &JsonRpcClient, target_height: BlockHeight) -> bool { - let t = Instant::now(); - let status = client.status().await; - let latency = t.elapsed(); - if latency > Duration::from_millis(100) { - tracing::warn!( - target: "mock_node", latency = %format_args!("{latency:0.2?}"), - "client is unresponsive, took too long to handle status request" - ); - } - match status { - Ok(status) => status.sync_info.latest_block_height >= target_height, - Err(_) => false, - } } fn main() -> anyhow::Result<()> { init_integration_logger(); let args: Cli = clap::Parser::parse(); - let home_dir = Path::new(&args.chain_history_home_dir); - let mut near_config = nearcore::config::load_config(home_dir, GenesisValidationMode::Full) - .context("Error loading config")?; - near_config.validator_signer = MutableConfigValue::new(None, "validator_signer"); - near_config.client_config.min_num_peers = 1; - let signer = InMemorySigner::from_random("mock_node".parse().unwrap(), KeyType::ED25519); - near_config.network_config.node_key = signer.secret_key; - near_config.client_config.tracked_shards = - near_config.genesis.config.shard_layout.shard_ids().collect(); - if near_config.rpc_config.is_none() { - near_config.rpc_config = Some(near_jsonrpc::RpcConfig::default()); - } - let tempdir; - let client_home_dir = match &args.client_home_dir { - Some(it) => it.as_path(), - None => { - tempdir = tempfile::Builder::new().prefix("mock_node").tempdir().unwrap(); - tempdir.path() - } - }; + let home_dir = Path::new(&args.home); let mock_config_path = home_dir.join("mock.json"); let mut network_config = if mock_config_path.exists() { @@ -137,66 +55,14 @@ fn main() -> anyhow::Result<()> { network_config.response_delay = Duration::from_millis(delay); } - let client_height = args.start_height.unwrap_or(args.client_height); - let network_height = args.start_height.or(args.network_height); - let addr = tcp::ListenerAddr::new(SocketAddr::new( - "127.0.0.1".parse().unwrap(), - args.mock_port.unwrap_or(24566), - )); + let sys = setup_actix(); + let res = block_on_interruptible(&sys, async move { + let mock_peer = + setup_mock_node(home_dir, network_config, args.network_height, args.target_height) + .context("failed setting up mock node")?; - run_actix(async move { - let MockNode { target_height, mut mock_peer, rpc_client } = setup_mock_node( - Path::new(&client_home_dir), - home_dir, - near_config, - &network_config, - client_height, - network_height, - args.target_height, - args.in_memory_storage, - addr, - ); - - // TODO: would be nice to be able to somehow quit right after the target block - // is applied rather than polling like this - let mut interval = tokio::time::interval(Duration::from_millis(100)); - interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); - - let start = Instant::now(); - // Let's set the timeout to 5 seconds per block - just in case we test on very full blocks. - let timeout = target_height * 5; - let timeout = u32::try_from(timeout).unwrap_or(u32::MAX) * Duration::from_secs(1); - - loop { - if start.elapsed() > timeout { - tracing::error!( - "node still hasn't made it to #{} after {:?}", - target_height, - timeout - ); - mock_peer.abort(); - break; - } - tokio::select! { - _ = interval.tick() => { - if target_height_reached(&rpc_client, target_height).await { - tracing::info!("node reached target height"); - mock_peer.abort(); - break; - } - } - result = &mut mock_peer => { - match result { - Ok(Ok(_)) => tracing::info!("mock peer exited"), - Ok(Err(e)) => tracing::error!("mock peer exited with error: {:?}", e), - Err(e) => tracing::error!("failed running mock peer task: {:?}", e), - }; - break; - } - } - } - - System::current().stop(); + mock_peer.await.context("failed running mock peer task")?.context("mock peer failed") }); - Ok(()) + + res } diff --git a/tools/mock-node/src/setup.rs b/tools/mock-node/src/setup.rs index d65cf65e317..566e20a5183 100644 --- a/tools/mock-node/src/setup.rs +++ b/tools/mock-node/src/setup.rs @@ -2,86 +2,44 @@ use crate::{MockNetworkConfig, MockPeer}; use anyhow::Context; -use near_chain::types::RuntimeAdapter; -use near_chain::ChainStoreUpdate; -use near_chain::{Chain, ChainGenesis, ChainStore, ChainStoreAccess, DoomslugThresholdMode}; -use near_crypto::{KeyType, SecretKey}; +use near_chain::{Chain, ChainGenesis, DoomslugThresholdMode}; +use near_chain_configs::GenesisValidationMode; use near_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; -use near_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; -use near_jsonrpc_client::JsonRpcClient; +use near_epoch_manager::{EpochManager, EpochManagerAdapter}; use near_network::tcp; -use near_network::types::PeerInfo; -use near_primitives::network::PeerId; use near_primitives::shard_layout::ShardLayout; -use near_primitives::state_part::PartId; -use near_primitives::state_sync::get_num_state_parts; use near_primitives::types::BlockHeight; -use near_store::test_utils::create_test_store; use near_time::Clock; use nearcore::{NearConfig, NightshadeRuntime, NightshadeRuntimeExt}; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::cmp::min; use std::path::Path; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; -fn setup_runtime( - home_dir: &Path, - near_config: &NearConfig, - in_memory_storage: bool, -) -> (Arc, ShardTracker, Arc) { - let store = if in_memory_storage { - create_test_store() - } else { - near_store::NodeStorage::opener( - home_dir, - &near_config.config.store, - near_config.config.archival_config(), - ) - .open() - .unwrap() - .get_hot_store() - }; - let epoch_manager = - EpochManager::new_arc_handle(store.clone(), &near_config.genesis.config, Some(home_dir)); - let shard_tracker = ShardTracker::new( - TrackedConfig::from_config(&near_config.client_config), - epoch_manager.clone(), - ); - let runtime = - NightshadeRuntime::from_config(home_dir, store, near_config, epoch_manager.clone()) - .expect("could not create transaction runtime"); - (epoch_manager, shard_tracker, runtime) -} - -fn setup_mock_peer( +pub(crate) fn setup_mock_peer( chain: Chain, - config: &mut NearConfig, + config: NearConfig, network_start_height: Option, network_config: MockNetworkConfig, target_height: BlockHeight, shard_layout: ShardLayout, - mock_listen_addr: tcp::ListenerAddr, ) -> tokio::task::JoinHandle> { let network_start_height = match network_start_height { None => target_height, Some(0) => chain.genesis_block().header().height(), Some(it) => it, }; - let secret_key = SecretKey::from_random(KeyType::ED25519); - config - .network_config - .peer_store - .boot_nodes - .push(PeerInfo::new(PeerId::new(secret_key.public_key()), *mock_listen_addr)); - let chain_id = config.genesis.config.chain_id.clone(); + let secret_key = config.network_config.node_key; + let chain_id = config.genesis.config.chain_id; let block_production_delay = config.client_config.min_block_production_delay; let archival = config.client_config.archive; - actix::spawn(async move { + let listen_addr = match config.network_config.node_addr { + Some(a) => a, + None => tcp::ListenerAddr::new("127.0.0.1".parse().unwrap()), + }; + let mock_peer = actix::spawn(async move { let mock = MockPeer::new( chain, secret_key, - mock_listen_addr, + listen_addr, chain_id, archival, block_production_delay.unsigned_abs(), @@ -91,196 +49,66 @@ fn setup_mock_peer( ) .await?; mock.run(target_height).await - }) -} - -pub struct MockNode { - // target height actually available to sync to in the chain history database - pub target_height: BlockHeight, - pub mock_peer: tokio::task::JoinHandle>, - // client that allows making RPC requests to the node under test - pub rpc_client: JsonRpcClient, + }); + mock_peer } -/// Set up a mock node, including setting up -/// a MockPeerManagerActor and a ClientActor and a ViewClientActor -/// `client_home_dir`: home dir for the new client -/// `network_home_dir`: home dir that contains the pre-generated chain history, will be used -/// to construct `MockPeerManagerActor` -/// `config`: config for the new client -/// `network_delay`: delay for getting response from the simulated network -/// `client_start_height`: start height for client -/// `network_start_height`: height at which the simulated network starts producing blocks -/// `target_height`: height that the simulated peers will produce blocks until. If None, will -/// use the height from the chain head in storage -/// `in_memory_storage`: if true, make client use in memory storage instead of rocksdb -/// -/// Returns a struct representing the node under test +/// Set up a mock node, which will read blocks and chunks from the DB at +/// `home_dir`, and provide them to any node that connects. If `network_start_height` is +/// Some(), it will first send a block at that height, so that the connecting node sees +/// that its head is at that height, and it will then send higher heights periodically. +/// If target_height is Some(), it will not send any blocks or chunks of higher height. pub fn setup_mock_node( - client_home_dir: &Path, - network_home_dir: &Path, - mut config: NearConfig, - network_config: &MockNetworkConfig, - client_start_height: BlockHeight, + home_dir: &Path, + network_config: MockNetworkConfig, network_start_height: Option, target_height: Option, - in_memory_storage: bool, - mock_listen_addr: tcp::ListenerAddr, -) -> MockNode { - let parent_span = tracing::debug_span!(target: "mock_node", "setup_mock_node").entered(); - let (mock_network_epoch_manager, mock_network_shard_tracker, mock_network_runtime) = - setup_runtime(network_home_dir, &config, false); - tracing::info!(target: "mock_node", ?network_home_dir, "Setup network runtime"); - - let chain_genesis = ChainGenesis::new(&config.genesis.config); - - // set up client dir to be ready to process blocks from client_start_height - if client_start_height > 0 { - tracing::info!(target: "mock_node", "Preparing client data dir to be able to start at the specified start height {}", client_start_height); - let (client_epoch_manager, _, client_runtime) = - setup_runtime(client_home_dir, &config, in_memory_storage); - tracing::info!(target: "mock_node", ?client_home_dir, "Setup client runtime"); - let mut chain_store = ChainStore::new( - client_runtime.store().clone(), - config.client_config.save_trie_changes, - config.genesis.config.transaction_validity_period, - ); - let mut network_chain_store = ChainStore::new( - mock_network_runtime.store().clone(), - config.client_config.save_trie_changes, - config.genesis.config.transaction_validity_period, - ); - - let network_tail_height = network_chain_store.tail().unwrap(); - let network_head_height = network_chain_store.head().unwrap().height; - tracing::info!(target: "mock_node", network_tail_height, network_head_height, "network data chain"); - assert!( - client_start_height <= network_head_height - && client_start_height >= network_tail_height, - "client start height {} is not within the network chain range [{}, {}]", - client_start_height, - network_tail_height, - network_head_height - ); - let hash = network_chain_store.get_block_hash_by_height(client_start_height).unwrap(); - tracing::info!(target: "mock_node", "Checking whether the given start height is the last block of an epoch."); - if !mock_network_epoch_manager.is_next_block_epoch_start(&hash).unwrap() { - let epoch_start_height = - mock_network_epoch_manager.get_epoch_start_height(&hash).unwrap(); - panic!( - "start height must be the last block of an epoch, try using {} instead.", - epoch_start_height - 1 - ); - } - - // copy chain info - let chain_store_update = ChainStoreUpdate::copy_chain_state_as_of_block( - &mut chain_store, - &hash, - mock_network_epoch_manager.as_ref(), - &mut network_chain_store, - ) - .unwrap(); - chain_store_update.commit().unwrap(); - tracing::info!(target: "mock_node", "Done preparing chain state"); - - client_epoch_manager - .write() - .copy_epoch_info_as_of_block(&hash, &mock_network_epoch_manager.read()) - .unwrap(); - tracing::info!(target: "mock_node", "Done preparing epoch info"); - - // copy state for all shards - let block = network_chain_store.get_block(&hash).unwrap(); - let prev_hash = *block.header().prev_hash(); - let epoch_id = block.header().epoch_id(); - let shard_layout = client_epoch_manager.get_shard_layout(epoch_id).unwrap(); - for (shard_index, chunk_header) in block.chunks().iter_deprecated().enumerate() { - let shard_id = shard_layout.get_shard_id(shard_index).unwrap(); - let state_root = chunk_header.prev_state_root(); - let state_root_node = - mock_network_runtime.get_state_root_node(shard_id, &hash, &state_root).unwrap(); - let num_parts = get_num_state_parts(state_root_node.memory_usage); - let finished_parts_count = Arc::new(AtomicUsize::new(0)); - tracing::info!(target: "mock_node", ?shard_id, ?state_root, num_parts, "Preparing state for a shard"); - - (0..num_parts) - .into_par_iter() - .try_for_each(|part_id| -> anyhow::Result<()> { - let _span = tracing::debug_span!( - target: "mock_node", - parent: &parent_span, - "obtain_and_apply_state_part", - part_id, - ?shard_id) - .entered(); - - let state_part = mock_network_runtime - .obtain_state_part( - shard_id, - &prev_hash, - &state_root, - PartId::new(part_id, num_parts), - ) - .with_context(|| { - format!("Obtaining state part {} in shard {}", part_id, shard_id) - })?; - client_runtime - .apply_state_part( - shard_id, - &state_root, - PartId::new(part_id, num_parts), - &state_part, - &mock_network_epoch_manager.get_epoch_id_from_prev_block(&hash)?, - ) - .with_context(|| { - format!("Applying state part {} in shard {}", part_id, shard_id) - })?; - finished_parts_count.fetch_add(1, Ordering::SeqCst); - tracing::info!( - target: "mock_node", - "Done {}/{} parts for shard {}", - finished_parts_count.load(Ordering::SeqCst), - num_parts, - shard_id, - ); - Ok(()) - }) - .unwrap(); - } - } +) -> anyhow::Result>> { + let near_config = nearcore::config::load_config(home_dir, GenesisValidationMode::Full) + .context("Error loading config")?; + + let store = near_store::NodeStorage::opener( + home_dir, + &near_config.config.store, + near_config.config.archival_config(), + ) + .open() + .context("failed opening storage")? + .get_hot_store(); + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &near_config.genesis.config, Some(home_dir)); + let shard_tracker = ShardTracker::new( + TrackedConfig::from_config(&near_config.client_config), + epoch_manager.clone(), + ); + let runtime = + NightshadeRuntime::from_config(home_dir, store, &near_config, epoch_manager.clone()) + .context("could not create transaction runtime")?; + let chain_genesis = ChainGenesis::new(&near_config.genesis.config); let chain = Chain::new_for_view_client( Clock::real(), - mock_network_epoch_manager.clone(), - mock_network_shard_tracker, - mock_network_runtime, + epoch_manager.clone(), + shard_tracker, + runtime, &chain_genesis, DoomslugThresholdMode::NoApprovals, - config.client_config.save_trie_changes, + near_config.client_config.save_trie_changes, ) - .unwrap(); - let head = chain.head().unwrap(); + .context("failed creating Chain")?; + + let head = chain.head().context("failed getting chain head")?; let epoch_id = head.epoch_id; - let shard_layout = mock_network_epoch_manager.get_shard_layout(&epoch_id).unwrap(); + let shard_layout = epoch_manager.get_shard_layout(&epoch_id).unwrap(); + let target_height = min(target_height.unwrap_or(head.height), head.height); - config.network_config.peer_store.boot_nodes.clear(); - let mock_peer = setup_mock_peer( + Ok(setup_mock_peer( chain, - &mut config, + near_config, network_start_height, - network_config.clone(), + network_config, target_height, shard_layout, - mock_listen_addr, - ); - - let rpc_client = near_jsonrpc_client::new_client(&format!( - "http://{}", - &config.rpc_config.as_ref().expect("the JSON RPC config must be set").addr - )); - let _node = nearcore::start_with_config(client_home_dir, config).unwrap(); - - MockNode { target_height, mock_peer, rpc_client } + )) }