From 6f8dc7958ef3fcfd8891b4dd17f41fd37b83bfb1 Mon Sep 17 00:00:00 2001 From: Marios Christou Date: Fri, 26 Sep 2025 16:47:22 +0300 Subject: [PATCH] Add few comments --- crates/node/src/zombie.rs | 166 ++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 62 deletions(-) diff --git a/crates/node/src/zombie.rs b/crates/node/src/zombie.rs index 1700365..c033991 100644 --- a/crates/node/src/zombie.rs +++ b/crates/node/src/zombie.rs @@ -1,6 +1,33 @@ +//! # ZombieNode Implementation +//! +//! ## Required Binaries +//! This module requires the following binaries to be compiled and available in your PATH: +//! +//! 1. **polkadot-parachain**: +//! ```bash +//! git clone https://github.com/paritytech/polkadot-sdk.git +//! cd polkadot-sdk +//! cargo build --release --locked -p polkadot-parachain-bin --bin polkadot-parachain +//! ``` +//! +//! 2. **eth-rpc** (Revive EVM RPC server): +//! ```bash +//! git clone https://github.com/paritytech/polkadot-sdk.git +//! cd polkadot-sdk +//! cargo build --locked --profile production -p pallet-revive-eth-rpc --bin eth-rpc +//! ``` +//! +//! 3. **polkadot** (for the relay chain): +//! ```bash +//! # In polkadot-sdk directory +//! cargo build --release -p polkadot +//! ``` +//! +//! Make sure to add the build output directories to your PATH or provide +//! the full paths in your configuration. + use std::{ fs::{create_dir_all, remove_dir_all}, - net::TcpListener, path::PathBuf, pin::Pin, process::{Command, Stdio}, @@ -20,12 +47,9 @@ use alloy::{ ext::DebugApi, fillers::{CachedNonceManager, ChainIdFiller, FillProvider, NonceFiller, TxFiller}, }, - rpc::{ - self, - types::{ - EIP1186AccountProofResponse, TransactionReceipt, - trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame}, - }, + rpc::types::{ + EIP1186AccountProofResponse, TransactionReceipt, + trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame}, }, }; @@ -47,6 +71,9 @@ use crate::{ static NODE_COUNT: AtomicU32 = AtomicU32::new(0); +/// A Zombienet network where collator is `polkadot-parachain` node with `eth-rpc` +/// [`ZombieNode`] abstracts away the details of managing the zombienet network and provides +/// an interface to interact with the parachain's Ethereum RPC. #[derive(Debug, Default)] pub struct ZombieNode { id: u32, @@ -61,7 +88,6 @@ pub struct ZombieNode { network_config: Option, network: Option>, eth_rpc_process: Option, - rpc_port: Option, } impl ZombieNode { @@ -106,7 +132,6 @@ impl ZombieNode { network: None, eth_rpc_process: None, connection_string: String::new(), - rpc_port: None, } } @@ -121,7 +146,10 @@ impl ZombieNode { let template_chainspec_path = self.base_directory.join(Self::CHAIN_SPEC_JSON_FILE); self.prepare_chainspec(template_chainspec_path.clone(), genesis)?; - let node_binary = self.node_binary.to_str().unwrap_or_default(); + let node_binary = self + .node_binary + .to_str() + .context("Invalid node binary path")?; let network_config = NetworkConfigBuilder::new() .with_relaychain(|r| { @@ -251,10 +279,10 @@ impl ZombieNode { json!(merged_balances); let writer = std::fs::File::create(&template_chainspec_path) - .context("Failed to create substrate template chainspec file")?; + .context("Failed to create template chainspec file")?; serde_json::to_writer_pretty(writer, &chainspec_json) - .context("Failed to write substrate template chainspec JSON")?; + .context("Failed to write template chainspec JSON")?; Ok(()) } @@ -267,14 +295,14 @@ impl ZombieNode { .alloc .iter() .try_fold(Vec::new(), |mut vec, (address, acc)| { - let substrate_address = Self::eth_to_substrate_address(address); + let polkadot_address = Self::eth_to_polkadot_address(address); let balance = acc.balance.try_into()?; - vec.push((substrate_address, balance)); + vec.push((polkadot_address, balance)); Ok(vec) }) } - fn eth_to_substrate_address(address: &Address) -> String { + fn eth_to_polkadot_address(address: &Address) -> String { let eth_bytes = address.0.0; let mut padded = [0xEEu8; 32]; @@ -448,7 +476,7 @@ impl, P: Provider> ResolverApi Box::pin(async move { self.provider.get_chain_id().await.map_err(Into::into) }) } - #[instrument(level = "info", skip_all, fields(substrate_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(zombie_node_id = self.id))] fn transaction_gas_price( &self, tx_hash: TxHash, @@ -462,7 +490,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(substrate_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(zombie_node_id = self.id))] fn block_gas_limit( &self, number: BlockNumberOrTag, @@ -565,20 +593,29 @@ impl, P: Provider> ResolverApi impl Node for ZombieNode { fn shutdown(&mut self) -> anyhow::Result<()> { - // TODO: destroy the zombienet network properly - - let base_directory = self.base_directory.clone(); - let data_directory = PathBuf::from(Self::DATA_DIRECTORY); - - // Take the process handle - let eth_rpc_process = self.eth_rpc_process.take(); // Kill the eth_rpc process - let _ = eth_rpc_process.map(|mut child| child.kill()); + if let Some(mut child) = self.eth_rpc_process.take() { + child.kill().context("Failed to kill eth-rpc process")?; + } + + // Destroy the network + if let Some(network) = self.network.take() { + // Handle network cleanup here + tokio::task::spawn_blocking(move || { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + if let Err(e) = network.destroy().await { + tracing::warn!("Failed to destroy zombienet network: {e:?}"); + } + }) + }); + } // Remove the database directory - let _ = remove_dir_all(base_directory.join(data_directory)); + if let Err(e) = remove_dir_all(self.base_directory.join(Self::DATA_DIRECTORY)) { + tracing::warn!("Failed to remove database directory: {e:?}"); + } - // Return immediately Ok(()) } @@ -610,37 +647,42 @@ impl Drop for ZombieNode { #[cfg(test)] mod tests { use alloy::rpc::types::TransactionRequest; - use std::sync::Mutex; use super::*; - use crate::Node; - fn test_config() -> TestExecutionContext { - let mut context = TestExecutionContext::default(); - context.zombienet_configuration.use_zombienet = true; - context + mod utils { + use super::*; + use std::sync::Mutex; + + pub fn test_config() -> TestExecutionContext { + let mut context = TestExecutionContext::default(); + context.zombienet_configuration.use_zombienet = true; + context + } + + pub async fn new_node() -> (TestExecutionContext, ZombieNode) { + // Workaround - check substrate.rs for explanation + static NODE_START_MUTEX: Mutex<()> = Mutex::new(()); + let _guard = NODE_START_MUTEX.lock().unwrap(); + + let context = test_config(); + let mut node = ZombieNode::new(context.zombienet_configuration.path.clone(), &context); + let genesis = context.genesis_configuration.genesis().unwrap().clone(); + node.init(genesis).unwrap(); + + // Run spawn_process in a blocking thread + let node = tokio::task::spawn_blocking(move || { + node.spawn_process().unwrap(); + node + }) + .await + .expect("Failed to spawn process"); + + (context, node) + } } - async fn new_node() -> (TestExecutionContext, ZombieNode) { - // Workaround - check substrate.rs for explanation - static NODE_START_MUTEX: Mutex<()> = Mutex::new(()); - let _guard = NODE_START_MUTEX.lock().unwrap(); - - let context = test_config(); - let mut node = ZombieNode::new(context.zombienet_configuration.path.clone(), &context); - let genesis = context.genesis_configuration.genesis().unwrap().clone(); - node.init(genesis).unwrap(); - - // Run spawn_process in a blocking thread - let node = tokio::task::spawn_blocking(move || { - node.spawn_process().unwrap(); - node - }) - .await - .expect("Failed to spawn process"); - - (context, node) - } + use utils::{new_node, test_config}; #[test] fn test_init_generates_chainspec_with_balances() { @@ -675,21 +717,21 @@ mod tests { let contents = std::fs::read_to_string(&final_chainspec_path).expect("Failed to read chainspec"); - // Validate that the Substrate addresses derived from the Ethereum addresses are in the file - let first_eth_addr = ZombieNode::eth_to_substrate_address( + // Validate that the Polkadot addresses derived from the Ethereum addresses are in the file + let first_eth_addr = ZombieNode::eth_to_polkadot_address( &"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap(), ); - let second_eth_addr = ZombieNode::eth_to_substrate_address( + let second_eth_addr = ZombieNode::eth_to_polkadot_address( &"Ab8483F64d9C6d1EcF9b849Ae677dD3315835cb2".parse().unwrap(), ); assert!( contents.contains(&first_eth_addr), - "Chainspec should contain Substrate address for first Ethereum account" + "Chainspec should contain Polkadot address for first Ethereum account" ); assert!( contents.contains(&second_eth_addr), - "Chainspec should contain Substrate address for second Ethereum account" + "Chainspec should contain Polkadot address for second Ethereum account" ); } @@ -740,14 +782,14 @@ mod tests { ]; for eth_addr in eth_addresses { - let ss58 = ZombieNode::eth_to_substrate_address(ð_addr.parse().unwrap()); + let ss58 = ZombieNode::eth_to_polkadot_address(ð_addr.parse().unwrap()); - println!("Ethereum: {eth_addr} -> Substrate SS58: {ss58}"); + println!("Ethereum: {eth_addr} -> Polkadot SS58: {ss58}"); } } #[test] - fn test_eth_to_substrate_address() { + fn test_eth_to_polkadot_address() { let cases = vec![ ( "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1", @@ -768,7 +810,7 @@ mod tests { ]; for (eth_addr, expected_ss58) in cases { - let result = ZombieNode::eth_to_substrate_address(ð_addr.parse().unwrap()); + let result = ZombieNode::eth_to_polkadot_address(ð_addr.parse().unwrap()); assert_eq!( result, expected_ss58, "Mismatch for Ethereum address {eth_addr}"