diff --git a/jormungandr-lib/src/interfaces/config/mod.rs b/jormungandr-lib/src/interfaces/config/mod.rs index 47f2dabe9e..1fd01fe32e 100644 --- a/jormungandr-lib/src/interfaces/config/mod.rs +++ b/jormungandr-lib/src/interfaces/config/mod.rs @@ -6,7 +6,7 @@ mod secret; pub use log::{Log, LogEntry, LogOutput}; pub use mempool::{LogMaxEntries, Mempool, PersistentLog, PoolMaxEntries}; pub use node::{ - Bootstrap, Connection, Cors, CorsOrigin, JRpc, LayersConfig, NodeConfig, NodeId, P2p, Policy, PreferredListConfig, - Rest, Tls, TopicsOfInterest, TrustedPeer, + Bootstrap, Connection, Cors, CorsOrigin, JRpc, LayersConfig, NodeConfig, NodeId, P2p, Policy, + PreferredListConfig, Rest, Tls, TopicsOfInterest, TrustedPeer, }; pub use secret::{Bft, GenesisPraos, NodeSecret}; diff --git a/jormungandr-lib/src/interfaces/config/node.rs b/jormungandr-lib/src/interfaces/config/node.rs index 013222fad6..b5fce9384b 100644 --- a/jormungandr-lib/src/interfaces/config/node.rs +++ b/jormungandr-lib/src/interfaces/config/node.rs @@ -196,7 +196,6 @@ pub struct P2p { pub layers: Option, } - /// Bootstrap contains meta data for initial startup #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Bootstrap { diff --git a/jormungandr/src/network/p2p/comm.rs b/jormungandr/src/network/p2p/comm.rs index 703c7462b8..0c39e9f99b 100644 --- a/jormungandr/src/network/p2p/comm.rs +++ b/jormungandr/src/network/p2p/comm.rs @@ -658,6 +658,11 @@ impl Peers { } } + pub async fn get_peer_addr(&self, peer: &NodeId) -> Option { + let mut map = self.inner().await; + map.peer_comms(peer).map(|peer| peer.remote_addr()) + } + pub async fn refresh_peer_on_gossip(&self, peer: &NodeId) -> bool { let timestamp = SystemTime::now(); let mut map = self.inner().await; diff --git a/jormungandr/src/network/p2p/comm/peer_map.rs b/jormungandr/src/network/p2p/comm/peer_map.rs index 00488ed5bd..5f752de114 100644 --- a/jormungandr/src/network/p2p/comm/peer_map.rs +++ b/jormungandr/src/network/p2p/comm/peer_map.rs @@ -265,7 +265,7 @@ impl PeerMap { .iter() .map(|(&id, data)| PeerInfo { id, - addr: None, + addr: Some(data.comms.remote_addr), stats: data.stats.clone(), }) .collect() diff --git a/jormungandr/src/network/subscription.rs b/jormungandr/src/network/subscription.rs index b6a644ab0b..a0e3f35f64 100644 --- a/jormungandr/src/network/subscription.rs +++ b/jormungandr/src/network/subscription.rs @@ -8,11 +8,11 @@ use crate::{ }; use chain_network::data as net_data; use chain_network::error::{Code, Error}; -use jormungandr_lib::interfaces::FragmentOrigin; - +use futures::executor; use futures::future::BoxFuture; use futures::prelude::*; use futures::ready; +use jormungandr_lib::interfaces::FragmentOrigin; use std::error::Error as _; use std::mem; @@ -24,7 +24,7 @@ fn filter_gossip_node(node: &Gossip, config: &Configuration) -> bool { if config.allow_private_addresses { node.has_valid_address() } else { - node.is_global() + !node.is_global() } } @@ -165,6 +165,12 @@ impl FragmentProcessor { } } + fn get_ingress_addr(&self) -> Option { + let state = self.global_state.clone(); + let node_id = self.node_id; + executor::block_on(state.peers.get_peer_addr(&node_id)) + } + fn refresh_stat(&mut self) { let state = self.global_state.clone(); let node_id = self.node_id; @@ -301,7 +307,22 @@ impl Sink for FragmentProcessor { e })?; tracing::debug!(hash = %fragment.hash(), "received fragment"); - self.buffered_fragments.push(fragment); + if let Some(whitelist) = &self.global_state.config.whitelist { + match self.get_ingress_addr() { + Some(ingress_addr) => { + if whitelist.contains(&ingress_addr) { + self.buffered_fragments.push(fragment); + } else { + tracing::info!("dropping fragments from {}", ingress_addr); + } + } + None => tracing::warn!("unable to resolve address of ingress client"), + } + } else { + // if no whitelist config, normal behaviour, no filtering + self.buffered_fragments.push(fragment); + } + Ok(()) } diff --git a/jormungandr/src/settings/start/config.rs b/jormungandr/src/settings/start/config.rs index c1b9e752f3..69bfa32055 100644 --- a/jormungandr/src/settings/start/config.rs +++ b/jormungandr/src/settings/start/config.rs @@ -11,7 +11,7 @@ use multiaddr::Multiaddr; use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use tracing::level_filters::LevelFilter; -use std::path::PathBuf; +use std::{net::SocketAddr, path::PathBuf}; #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] @@ -132,7 +132,7 @@ pub struct Connection { pub allow_private_addresses: bool, /// contains addrs of nodes which we can accept fragments from - //pub whitelist: Option>, + pub whitelist: Option>, /// interval to start gossiping with new nodes, changing the value will /// affect the bandwidth. The more often the node will gossip the more diff --git a/jormungandr/src/settings/start/mod.rs b/jormungandr/src/settings/start/mod.rs index 4bea201843..8dfcfe61af 100644 --- a/jormungandr/src/settings/start/mod.rs +++ b/jormungandr/src/settings/start/mod.rs @@ -298,7 +298,8 @@ fn generate_network( } let trusted_peers = p2p - .bootstrap.trusted_peers + .bootstrap + .trusted_peers .as_ref() .map_or_else(Vec::new, |peers| resolve_trusted_peers(peers)); @@ -350,19 +351,24 @@ fn generate_network( rings, }, max_connections: p2p - .connection.max_connections + .connection + .max_connections .unwrap_or(network::DEFAULT_MAX_CONNECTIONS), max_client_connections: p2p - .connection.max_client_connections + .connection + .max_client_connections .unwrap_or(network::DEFAULT_MAX_CLIENT_CONNECTIONS), timeout: std::time::Duration::from_secs(15), allow_private_addresses: p2p.connection.allow_private_addresses, + whitelist: p2p.connection.whitelist, gossip_interval: p2p - .connection.gossip_interval + .connection + .gossip_interval .map(|d| d.into()) .unwrap_or_else(|| std::time::Duration::from_secs(10)), network_stuck_check: p2p - .connection.network_stuck_check + .connection + .network_stuck_check .map(Into::into) .unwrap_or(crate::topology::DEFAULT_NETWORK_STUCK_INTERVAL), max_bootstrap_attempts: p2p.bootstrap.max_bootstrap_attempts, diff --git a/jormungandr/src/settings/start/network.rs b/jormungandr/src/settings/start/network.rs index 0dc2b915d6..9207be8598 100644 --- a/jormungandr/src/settings/start/network.rs +++ b/jormungandr/src/settings/start/network.rs @@ -86,6 +86,8 @@ pub struct Configuration { /// Whether to allow non-public IP addresses in gossip pub allow_private_addresses: bool, + pub whitelist: Option>, + pub gossip_interval: Duration, pub network_stuck_check: Duration, diff --git a/testing/hersir/src/builder/settings.rs b/testing/hersir/src/builder/settings.rs index b29317c507..73d7d667d3 100644 --- a/testing/hersir/src/builder/settings.rs +++ b/testing/hersir/src/builder/settings.rs @@ -136,7 +136,7 @@ impl Settings { ) where RNG: RngCore + CryptoRng, { - let mut blockchain_configuration = &mut self.block0.blockchain_configuration; + let blockchain_configuration = &mut self.block0.blockchain_configuration; // TODO blockchain_configuration.block0_date = ; blockchain_configuration.linear_fees = blockchain.linear_fee(); diff --git a/testing/hersir/src/builder/spawn_params.rs b/testing/hersir/src/builder/spawn_params.rs index 0838c3442f..55b7436627 100644 --- a/testing/hersir/src/builder/spawn_params.rs +++ b/testing/hersir/src/builder/spawn_params.rs @@ -26,6 +26,8 @@ pub struct SpawnParams { log_level: Option, max_bootstrap_attempts: Option, max_connections: Option, + allow_private_addresses: Option, + whitelist: Option>, max_inbound_connections: Option, mempool: Option, network_stuck_check: Option, @@ -57,6 +59,8 @@ impl SpawnParams { log_level: None, max_bootstrap_attempts: None, max_connections: None, + allow_private_addresses: None, + whitelist: None, max_inbound_connections: None, mempool: None, network_stuck_check: None, @@ -136,6 +140,16 @@ impl SpawnParams { self } + pub fn allow_private_addresses(mut self, switch: bool) -> Self { + self.allow_private_addresses = Some(switch); + self + } + + pub fn whitelist(mut self, nodes: Vec) -> Self { + self.whitelist = Some(nodes); + self + } + pub fn max_inbound_connections(mut self, max_inbound_connections: u32) -> Self { self.max_inbound_connections = Some(max_inbound_connections); self @@ -271,6 +285,14 @@ impl SpawnParams { node_config.p2p.connection.max_inbound_connections = Some(*max_inbound_connections); } + if let Some(allow_private_addresses) = &self.allow_private_addresses { + node_config.p2p.connection.allow_private_addresses = *allow_private_addresses; + } + + if let Some(whitelist) = &self.whitelist { + node_config.p2p.connection.whitelist = Some(whitelist.clone()); + } + if let Some(max_connections) = &self.max_connections { node_config.p2p.connection.max_connections = Some(*max_connections); } diff --git a/testing/jormungandr-automation/src/jormungandr/configuration/node_config_builder.rs b/testing/jormungandr-automation/src/jormungandr/configuration/node_config_builder.rs index 4debd55bee..6cb548949b 100644 --- a/testing/jormungandr-automation/src/jormungandr/configuration/node_config_builder.rs +++ b/testing/jormungandr-automation/src/jormungandr/configuration/node_config_builder.rs @@ -2,8 +2,8 @@ use jormungandr_lib::{ interfaces::{ - Bootstrap, Connection,Cors, JRpc, LayersConfig, Log, Mempool, NodeConfig, P2p, Policy, Rest, Tls, - TopicsOfInterest, TrustedPeer, + Bootstrap, Connection, Cors, JRpc, LayersConfig, Log, Mempool, NodeConfig, P2p, Policy, + Rest, Tls, TopicsOfInterest, TrustedPeer, }, time::Duration, }; diff --git a/testing/jormungandr-automation/src/jormungandr/legacy/config/configuration_builder.rs b/testing/jormungandr-automation/src/jormungandr/legacy/config/configuration_builder.rs index 8a8c866ab6..1033a5e29c 100644 --- a/testing/jormungandr-automation/src/jormungandr/legacy/config/configuration_builder.rs +++ b/testing/jormungandr-automation/src/jormungandr/legacy/config/configuration_builder.rs @@ -80,7 +80,8 @@ impl LegacyNodeConfigConverter { let trusted_peers: Vec = source .p2p - .bootstrap.trusted_peers + .bootstrap + .trusted_peers .iter() .map(|peer| { let id = NodeId::from( @@ -116,6 +117,7 @@ impl LegacyNodeConfigConverter { policy: source.p2p.policy.clone(), layers: source.p2p.layers.clone(), public_id: None, + whitelist: source.p2p.connection.whitelist.clone(), }, mempool: source.mempool.clone(), bootstrap_from_trusted_peers: source.bootstrap_from_trusted_peers, @@ -126,7 +128,8 @@ impl LegacyNodeConfigConverter { fn build_node_config_after_0_12_0(&self, source: &NewestNodeConfig) -> NodeConfig { let trusted_peers: Vec = source .p2p - .bootstrap.trusted_peers + .bootstrap + .trusted_peers .iter() .map(|peer| TrustedPeer { id: None, @@ -155,6 +158,7 @@ impl LegacyNodeConfigConverter { policy: source.p2p.policy.clone(), layers: source.p2p.layers.clone(), public_id: None, + whitelist: source.p2p.connection.whitelist.clone(), }, mempool: source.mempool.clone(), bootstrap_from_trusted_peers: source.bootstrap_from_trusted_peers, @@ -172,7 +176,8 @@ impl LegacyNodeConfigConverter { let mut rng = OsRng; let trusted_peers: Vec = source .p2p - .bootstrap.trusted_peers + .bootstrap + .trusted_peers .iter() .map(|peer| { let id = { @@ -215,6 +220,7 @@ impl LegacyNodeConfigConverter { policy: source.p2p.policy.clone(), layers: None, public_id: None, + whitelist: source.p2p.connection.whitelist.clone(), }, mempool: source.mempool.clone(), bootstrap_from_trusted_peers: source.bootstrap_from_trusted_peers, diff --git a/testing/jormungandr-automation/src/jormungandr/legacy/config/mod.rs b/testing/jormungandr-automation/src/jormungandr/legacy/config/mod.rs index ca260dbdb2..145c917b9f 100644 --- a/testing/jormungandr-automation/src/jormungandr/legacy/config/mod.rs +++ b/testing/jormungandr-automation/src/jormungandr/legacy/config/mod.rs @@ -34,6 +34,8 @@ pub struct P2p { pub allow_private_addresses: bool, + pub whitelist: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub topics_of_interest: Option, diff --git a/testing/jormungandr-integration-tests/src/jormungandr/genesis/fragments.rs b/testing/jormungandr-integration-tests/src/jormungandr/genesis/fragments.rs index 8e1e4df440..39000dc4d5 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/genesis/fragments.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/genesis/fragments.rs @@ -119,7 +119,7 @@ pub fn test_all_fragments() { ); let mut new_stake_pool = stake_pool.clone(); - let mut stake_pool_info = new_stake_pool.info_mut(); + let stake_pool_info = new_stake_pool.info_mut(); stake_pool_info.serial = 100u128; time::wait_for_epoch(1, jormungandr.rest()); diff --git a/testing/jormungandr-integration-tests/src/jormungandr/genesis/pool_update.rs b/testing/jormungandr-integration-tests/src/jormungandr/genesis/pool_update.rs index a1ddf9b187..7c5900edb0 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/genesis/pool_update.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/genesis/pool_update.rs @@ -24,7 +24,7 @@ pub fn update_pool_fees_is_not_allowed() { let stake_pool = stake_pools.get(0).unwrap(); let mut new_stake_pool = stake_pool.clone(); - let mut stake_pool_info = new_stake_pool.info_mut(); + let stake_pool_info = new_stake_pool.info_mut(); stake_pool_info.rewards = TaxType::zero(); // 6. send pool update certificate diff --git a/testing/jormungandr-integration-tests/src/jormungandr/legacy.rs b/testing/jormungandr-integration-tests/src/jormungandr/legacy.rs index 6c74f06243..07c5b96af6 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/legacy.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/legacy.rs @@ -163,7 +163,7 @@ pub fn test_legacy_node_all_fragments() { ); let mut new_stake_pool = first_stake_pool.clone(); - let mut stake_pool_info = new_stake_pool.info_mut(); + let stake_pool_info = new_stake_pool.info_mut(); stake_pool_info.reward_account = Some(AccountIdentifier::Single( second_stake_pool_owner diff --git a/testing/jormungandr-integration-tests/src/networking/p2p/mod.rs b/testing/jormungandr-integration-tests/src/networking/p2p/mod.rs index b93440f408..da689ed1ec 100644 --- a/testing/jormungandr-integration-tests/src/networking/p2p/mod.rs +++ b/testing/jormungandr-integration-tests/src/networking/p2p/mod.rs @@ -1,4 +1,5 @@ pub mod connections; +pub mod public_traffic; pub mod quarantine; pub mod stats; diff --git a/testing/jormungandr-integration-tests/src/networking/p2p/public_traffic.rs b/testing/jormungandr-integration-tests/src/networking/p2p/public_traffic.rs new file mode 100644 index 0000000000..482602a1ea --- /dev/null +++ b/testing/jormungandr-integration-tests/src/networking/p2p/public_traffic.rs @@ -0,0 +1,433 @@ +use crate::networking::utils; +use hersir::{ + builder::{NetworkBuilder, Node, Topology}, + config::{ + BlockchainBuilder, BlockchainConfiguration, NodeConfig, SpawnParams, WalletTemplateBuilder, + }, +}; +use jormungandr_automation::{ + jormungandr::{explorer::configuration::ExplorerParams, LogLevel}, + testing::{ensure_nodes_are_in_sync, SyncWaitParams}, +}; + +use jormungandr_lib::{ + interfaces::{Policy, PreferredListConfig, SlotDuration, TrustedPeer}, + time::{Duration, SystemTime}, +}; +use multiaddr::Multiaddr; +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}, + path::PathBuf, +}; +use thor::{FragmentSender, FragmentVerifier}; + +const GATEWAY: &str = "GATEWAY"; + +const PUBLIC_NODE: &str = "PUBLIC"; +const INTERNAL_NODE: &str = "INTERNAL"; +const INTERNAL_NODE_2: &str = "INTERNAL_2"; + +const ALICE: &str = "ALICE"; +const BOB: &str = "BOB"; + +#[ignore] +#[test] +fn public_gossip_rejection() { + const SERVER_GOSSIP_INTERVAL_SECS: u64 = 10; + + let mut network_controller = NetworkBuilder::default() + .topology( + Topology::default() + .with_node(Node::new(INTERNAL_NODE)) + .with_node(Node::new(GATEWAY).with_trusted_peer(INTERNAL_NODE)) + .with_node(Node::new(PUBLIC_NODE).with_trusted_peer(GATEWAY)), + ) + .blockchain_config(BlockchainConfiguration::default().with_leader(GATEWAY)) + .wallet_template( + WalletTemplateBuilder::new(ALICE) + .with(1_000_000) + .delegated_to(INTERNAL_NODE) + .build(), + ) + .wallet_template( + WalletTemplateBuilder::new(BOB) + .with(1_000_000) + .delegated_to(GATEWAY) + .build(), + ) + .build() + .unwrap(); + + // spin up node within the intranet + // gossip from public node should be dropped + + let _client_internal = network_controller + .spawn( + SpawnParams::new(INTERNAL_NODE) + .gossip_interval(Duration::new(5, 0)) + .allow_private_addresses(false), + ) + .unwrap(); + + // node from internal network exposed to public + let _gateway = network_controller + .spawn( + SpawnParams::new(GATEWAY) + .gossip_interval(Duration::new(SERVER_GOSSIP_INTERVAL_SECS, 0)) + .allow_private_addresses(true) + .log_level(LogLevel::TRACE), + ) + .unwrap(); + + // simulate node in the wild + let address: Multiaddr = "/ip4/80.9.12.3/tcp/0".parse().unwrap(); + + let _client_public = network_controller + .spawn( + SpawnParams::new(PUBLIC_NODE) + .gossip_interval(Duration::new(5, 0)) + .public_address(address) + .allow_private_addresses(true), + ) + .unwrap(); + + utils::wait(20); + + let mut gossip_dropped = false; + // internal node should drop gossip from public node + for i in _client_internal.logger.get_lines_as_string().into_iter() { + if i.contains("nodes dropped from gossip") && i.contains("80.9.12.3") { + gossip_dropped = true + } + } + + assert!(gossip_dropped); +} + +#[ignore] +#[test] +pub fn test_public_node_cannot_publish() { + let mut network_controller = NetworkBuilder::default() + .topology( + Topology::default() + .with_node(Node::new(INTERNAL_NODE)) + .with_node(Node::new(INTERNAL_NODE_2).with_trusted_peer(INTERNAL_NODE)) + .with_node(Node::new(GATEWAY).with_trusted_peer(INTERNAL_NODE)) + .with_node(Node::new(PUBLIC_NODE).with_trusted_peer(GATEWAY)), + ) + .blockchain_config(BlockchainConfiguration::default().with_leader(INTERNAL_NODE)) + .wallet_template( + WalletTemplateBuilder::new(ALICE) + .with(1_000_000) + .delegated_to(INTERNAL_NODE) + .build(), + ) + .wallet_template( + WalletTemplateBuilder::new(BOB) + .with(1_000_000) + .delegated_to(INTERNAL_NODE_2) + .build(), + ) + .build() + .unwrap(); + + // + // + // Get addresses of nodes for whitelisting + let internal_node_addr = network_controller + .node_config(INTERNAL_NODE) + .unwrap() + .p2p + .get_listen_addr() + .unwrap(); + + let internal_node_2_addr = network_controller + .node_config(INTERNAL_NODE_2) + .unwrap() + .p2p + .get_listen_addr() + .unwrap(); + + let gateway_addr = network_controller + .node_config(GATEWAY) + .unwrap() + .p2p + .get_listen_addr() + .unwrap(); + + let whitelist = vec![internal_node_addr, internal_node_2_addr, gateway_addr]; + + println!("whitelist {:?}", whitelist); + + // + // + // add whitelists to nodes config + let mut internal_node_config = network_controller.node_config(INTERNAL_NODE).unwrap(); + + internal_node_config.p2p.whitelist = Some(whitelist.clone()); + + let mut internal_node_2_config = network_controller.node_config(INTERNAL_NODE_2).unwrap(); + + internal_node_2_config.p2p.whitelist = Some(whitelist.clone()); + + let mut gateway_node_config = network_controller.node_config(GATEWAY).unwrap(); + + gateway_node_config.p2p.whitelist = Some(whitelist.clone()); + + let mut public_node_config = network_controller.node_config(PUBLIC_NODE).unwrap(); + + public_node_config.p2p.whitelist = Some(whitelist.clone()); + + // + // + // spin up internal nodes + let params = SpawnParams::new(INTERNAL_NODE) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(false) + .whitelist(whitelist.clone()); + + params.override_settings(&mut internal_node_config); + + let _client_internal = network_controller.spawn(params).unwrap(); + + let params = SpawnParams::new(INTERNAL_NODE_2) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(false) + .whitelist(whitelist.clone()); + + params.override_settings(&mut internal_node_2_config); + + let _client_internal_2 = network_controller.spawn(params).unwrap(); + + // + // + // node from internal network exposed to public + + let params = SpawnParams::new(GATEWAY) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(true) + .whitelist(whitelist.clone()); + + params.override_settings(&mut gateway_node_config); + + let _gateway = network_controller.spawn(params).unwrap(); + + // + // + // simulate node in the wild + let address: Multiaddr = "/ip4/80.9.12.3/tcp/0".parse().unwrap(); + + let params = SpawnParams::new(PUBLIC_NODE) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(true) + .public_address(address) + .whitelist(whitelist.clone()); + + params.override_settings(&mut public_node_config); + + let _client_public = network_controller.spawn(params).unwrap(); + + // + // + // public node sends fragments to network + // it should fail as they public node is not whitelisted + let mut alice = network_controller.controlled_wallet(ALICE).unwrap(); + let mut bob = network_controller.controlled_wallet(BOB).unwrap(); + + let fragment_sender = FragmentSender::from(&network_controller.settings().block0); + + match fragment_sender.send_transactions_round_trip( + 5, + &mut alice, + &mut bob, + &_client_public, + 100.into(), + ) { + Ok(_) => panic!("public node is not whitelisted, fragments should not send!"), + Err(err) => assert_eq!( + "Too many attempts failed (1) while trying to send fragment to node: ".to_string(), + err.to_string() + ), + }; +} + +#[ignore] +#[test] +pub fn test_public_node_synced_with_internal() { + let mut network_controller = NetworkBuilder::default() + .topology( + Topology::default() + .with_node(Node::new(INTERNAL_NODE)) + .with_node(Node::new(INTERNAL_NODE_2).with_trusted_peer(INTERNAL_NODE)) + .with_node(Node::new(GATEWAY).with_trusted_peer(INTERNAL_NODE)) + .with_node(Node::new(PUBLIC_NODE).with_trusted_peer(GATEWAY)), + ) + .blockchain_config(BlockchainConfiguration::default().with_leader(INTERNAL_NODE)) + .wallet_template( + WalletTemplateBuilder::new(ALICE) + .with(1_000_000) + .delegated_to(INTERNAL_NODE) + .build(), + ) + .wallet_template( + WalletTemplateBuilder::new(BOB) + .with(1_000_000) + .delegated_to(INTERNAL_NODE_2) + .build(), + ) + .build() + .unwrap(); + + // + // + // Get addresses of nodes for whitelisting + let internal_node_addr = network_controller + .node_config(INTERNAL_NODE) + .unwrap() + .p2p + .get_listen_addr() + .unwrap(); + + let internal_node_2_addr = network_controller + .node_config(INTERNAL_NODE_2) + .unwrap() + .p2p + .get_listen_addr() + .unwrap(); + + let gateway_addr = network_controller + .node_config(GATEWAY) + .unwrap() + .p2p + .get_listen_addr() + .unwrap(); + + let whitelist = vec![internal_node_addr, internal_node_2_addr, gateway_addr]; + + println!("whitelist {:?}", whitelist); + + // + // + // add whitelists to nodes config + let mut internal_node_config = network_controller.node_config(INTERNAL_NODE).unwrap(); + + internal_node_config.p2p.whitelist = Some(whitelist.clone()); + + let mut internal_node_2_config = network_controller.node_config(INTERNAL_NODE_2).unwrap(); + + internal_node_2_config.p2p.whitelist = Some(whitelist.clone()); + + let mut gateway_node_config = network_controller.node_config(GATEWAY).unwrap(); + + gateway_node_config.p2p.whitelist = Some(whitelist.clone()); + + let mut public_node_config = network_controller.node_config(PUBLIC_NODE).unwrap(); + + public_node_config.p2p.whitelist = Some(whitelist.clone()); + + // + // + // spin up internal nodes + let params = SpawnParams::new(INTERNAL_NODE) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(false) + .whitelist(whitelist.clone()); + + params.override_settings(&mut internal_node_config); + + let _client_internal = network_controller.spawn(params).unwrap(); + + let params = SpawnParams::new(INTERNAL_NODE_2) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(false) + .whitelist(whitelist.clone()); + + params.override_settings(&mut internal_node_2_config); + + let _client_internal_2 = network_controller.spawn(params).unwrap(); + + // + // + // node from internal network exposed to public + + let params = SpawnParams::new(GATEWAY) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(true) + .whitelist(whitelist.clone()); + + params.override_settings(&mut gateway_node_config); + + let _gateway = network_controller.spawn(params).unwrap(); + + // + // + // simulate node in the wild + let address: Multiaddr = "/ip4/80.9.12.3/tcp/0".parse().unwrap(); + + let params = SpawnParams::new(PUBLIC_NODE) + .gossip_interval(Duration::new(1, 0)) + .allow_private_addresses(true) + .public_address(address) + .whitelist(whitelist.clone()); + + params.override_settings(&mut public_node_config); + + let _client_public = network_controller.spawn(params).unwrap(); + + // + // + // internal node sends fragments to network + // fragments should be propagated to the publish node (which can consume but not publish) + let mut alice = network_controller.controlled_wallet(ALICE).unwrap(); + let mut bob = network_controller.controlled_wallet(BOB).unwrap(); + + let fragment_sender = FragmentSender::from(&network_controller.settings().block0); + + match fragment_sender.send_transactions_round_trip( + 5, + &mut alice, + &mut bob, + &_client_internal, + 100.into(), + ) { + Ok(_) => println!("fragments sent"), + Err(err) => panic!("{}", err), + }; + + utils::wait(10); + + // + // + // account states should be the same + + let public_state_a = _client_internal_2 + .rest() + .account_state(&alice.account_id()) + .unwrap(); + + let public_state_b = _client_public + .rest() + .account_state(&bob.account_id()) + .unwrap(); + + let internal_state_a = _client_internal_2 + .rest() + .account_state(&alice.account_id()) + .unwrap(); + + let internal_state_b = _client_public + .rest() + .account_state(&bob.account_id()) + .unwrap(); + + assert_eq!(public_state_a, internal_state_a); + assert_eq!(public_state_b, internal_state_b); + + // based on this test; nodes will never be fully synced as gossip from the public node is dropped by internal nodes + // which do not allow private addresses + ensure_nodes_are_in_sync( + SyncWaitParams::ZeroWait, + &[&_client_internal_2, &_client_public], + ) + .unwrap(); +} diff --git a/testing/thor/src/cli/controller.rs b/testing/thor/src/cli/controller.rs index cd97119b2a..df47c47165 100644 --- a/testing/thor/src/cli/controller.rs +++ b/testing/thor/src/cli/controller.rs @@ -79,7 +79,7 @@ impl CliController { } pub fn refresh_state(&mut self) -> Result<(), Error> { - let mut wallet = self.wallets.wallet_mut()?; + let wallet = self.wallets.wallet_mut()?; let new_state = self.client.account_state_by_pk(&wallet.pk_bech32())?; wallet.spending_counters = new_state.counters(); wallet.value = (*new_state.value()).into();