feat(network): Configurable external address for Version network messages (#8488)
* add configurable external address for Version network messages * apply suggestions from code review Co-authored-by: Arya <aryasolhi@gmail.com> --------- Co-authored-by: Arya <aryasolhi@gmail.com>
This commit is contained in:
parent
8a786fe6ce
commit
239fcc85ba
|
@ -72,6 +72,14 @@ pub struct Config {
|
||||||
/// their address books.
|
/// their address books.
|
||||||
pub listen_addr: SocketAddr,
|
pub listen_addr: SocketAddr,
|
||||||
|
|
||||||
|
/// The external address of this node if any.
|
||||||
|
///
|
||||||
|
/// Zebra bind to `listen_addr` but this can be an internal address if the node
|
||||||
|
/// is behind a firewall, load balancer or NAT. This field can be used to
|
||||||
|
/// advertise a different address to peers making it possible to receive inbound
|
||||||
|
/// connections and contribute to the P2P network from behind a firewall, load balancer, or NAT.
|
||||||
|
pub external_addr: Option<SocketAddr>,
|
||||||
|
|
||||||
/// The network to connect to.
|
/// The network to connect to.
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
|
|
||||||
|
@ -601,6 +609,7 @@ impl Default for Config {
|
||||||
listen_addr: "0.0.0.0:8233"
|
listen_addr: "0.0.0.0:8233"
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Hardcoded address should be parseable"),
|
.expect("Hardcoded address should be parseable"),
|
||||||
|
external_addr: None,
|
||||||
network: Network::Mainnet,
|
network: Network::Mainnet,
|
||||||
initial_mainnet_peers: mainnet_peers,
|
initial_mainnet_peers: mainnet_peers,
|
||||||
initial_testnet_peers: testnet_peers,
|
initial_testnet_peers: testnet_peers,
|
||||||
|
@ -635,6 +644,7 @@ impl<'de> Deserialize<'de> for Config {
|
||||||
#[serde(deny_unknown_fields, default)]
|
#[serde(deny_unknown_fields, default)]
|
||||||
struct DConfig {
|
struct DConfig {
|
||||||
listen_addr: String,
|
listen_addr: String,
|
||||||
|
external_addr: Option<String>,
|
||||||
network: NetworkKind,
|
network: NetworkKind,
|
||||||
testnet_parameters: Option<DTestnetParameters>,
|
testnet_parameters: Option<DTestnetParameters>,
|
||||||
initial_mainnet_peers: IndexSet<String>,
|
initial_mainnet_peers: IndexSet<String>,
|
||||||
|
@ -651,6 +661,7 @@ impl<'de> Deserialize<'de> for Config {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
Self {
|
Self {
|
||||||
listen_addr: "0.0.0.0".to_string(),
|
listen_addr: "0.0.0.0".to_string(),
|
||||||
|
external_addr: None,
|
||||||
network: Default::default(),
|
network: Default::default(),
|
||||||
testnet_parameters: None,
|
testnet_parameters: None,
|
||||||
initial_mainnet_peers: config.initial_mainnet_peers,
|
initial_mainnet_peers: config.initial_mainnet_peers,
|
||||||
|
@ -665,6 +676,7 @@ impl<'de> Deserialize<'de> for Config {
|
||||||
|
|
||||||
let DConfig {
|
let DConfig {
|
||||||
listen_addr,
|
listen_addr,
|
||||||
|
external_addr,
|
||||||
network: network_kind,
|
network: network_kind,
|
||||||
testnet_parameters,
|
testnet_parameters,
|
||||||
initial_mainnet_peers,
|
initial_mainnet_peers,
|
||||||
|
@ -737,6 +749,20 @@ impl<'de> Deserialize<'de> for Config {
|
||||||
},
|
},
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
let external_socket_addr = if let Some(address) = &external_addr {
|
||||||
|
match address.parse::<SocketAddr>() {
|
||||||
|
Ok(socket) => Ok(Some(socket)),
|
||||||
|
Err(_) => match address.parse::<IpAddr>() {
|
||||||
|
Ok(ip) => Ok(Some(SocketAddr::new(ip, network.default_port()))),
|
||||||
|
Err(err) => Err(de::Error::custom(format!(
|
||||||
|
"{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
}?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let [max_connections_per_ip, peerset_initial_target_size] = [
|
let [max_connections_per_ip, peerset_initial_target_size] = [
|
||||||
("max_connections_per_ip", max_connections_per_ip, DEFAULT_MAX_CONNS_PER_IP),
|
("max_connections_per_ip", max_connections_per_ip, DEFAULT_MAX_CONNS_PER_IP),
|
||||||
// If we want Zebra to operate with no network,
|
// If we want Zebra to operate with no network,
|
||||||
|
@ -756,6 +782,7 @@ impl<'de> Deserialize<'de> for Config {
|
||||||
|
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
listen_addr: canonical_socket_addr(listen_addr),
|
listen_addr: canonical_socket_addr(listen_addr),
|
||||||
|
external_addr: external_socket_addr,
|
||||||
network,
|
network,
|
||||||
initial_mainnet_peers,
|
initial_mainnet_peers,
|
||||||
initial_testnet_peers,
|
initial_testnet_peers,
|
||||||
|
|
|
@ -654,7 +654,17 @@ where
|
||||||
let their_addr = connected_addr
|
let their_addr = connected_addr
|
||||||
.get_transient_addr()
|
.get_transient_addr()
|
||||||
.expect("non-Isolated connections have a remote addr");
|
.expect("non-Isolated connections have a remote addr");
|
||||||
(their_addr, our_services, config.listen_addr)
|
|
||||||
|
// Include the configured external address in our version message, if any, otherwise, include our listen address.
|
||||||
|
let advertise_addr = match config.external_addr {
|
||||||
|
Some(external_addr) => {
|
||||||
|
info!(?their_addr, ?config.listen_addr, "using external address for Version messages");
|
||||||
|
external_addr
|
||||||
|
}
|
||||||
|
None => config.listen_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
(their_addr, our_services, advertise_addr)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,8 @@ use common::{
|
||||||
check::{is_zebrad_version, EphemeralCheck, EphemeralConfig},
|
check::{is_zebrad_version, EphemeralCheck, EphemeralConfig},
|
||||||
config::random_known_rpc_port_config,
|
config::random_known_rpc_port_config,
|
||||||
config::{
|
config::{
|
||||||
config_file_full_path, configs_dir, default_test_config, persistent_test_config, testdir,
|
config_file_full_path, configs_dir, default_test_config, external_address_test_config,
|
||||||
|
persistent_test_config, testdir,
|
||||||
},
|
},
|
||||||
launch::{
|
launch::{
|
||||||
spawn_zebrad_for_rpc, spawn_zebrad_without_rpc, ZebradTestDirExt, BETWEEN_NODES_DELAY,
|
spawn_zebrad_for_rpc, spawn_zebrad_without_rpc, ZebradTestDirExt, BETWEEN_NODES_DELAY,
|
||||||
|
@ -3128,6 +3129,33 @@ async fn validate_regtest_genesis_block() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that Version messages are sent with the external address when configured to do so.
|
||||||
|
#[test]
|
||||||
|
fn external_address() -> Result<()> {
|
||||||
|
let _init_guard = zebra_test::init();
|
||||||
|
let testdir = testdir()?.with_config(&mut external_address_test_config(&Mainnet)?)?;
|
||||||
|
let mut child = testdir.spawn_child(args!["start"])?;
|
||||||
|
|
||||||
|
// Give enough time to start connecting to some peers.
|
||||||
|
std::thread::sleep(Duration::from_secs(10));
|
||||||
|
|
||||||
|
child.kill(false)?;
|
||||||
|
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
let output = output.assert_failure()?;
|
||||||
|
|
||||||
|
// Zebra started
|
||||||
|
output.stdout_line_contains("Starting zebrad")?;
|
||||||
|
|
||||||
|
// Make sure we are using external address for Version messages.
|
||||||
|
output.stdout_line_contains("using external address for Version messages")?;
|
||||||
|
|
||||||
|
// Make sure the command was killed.
|
||||||
|
output.assert_was_killed()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Test successful `getblocktemplate` and `submitblock` RPC calls on Regtest on Canopy.
|
/// Test successful `getblocktemplate` and `submitblock` RPC calls on Regtest on Canopy.
|
||||||
///
|
///
|
||||||
/// See [`common::regtest::submit_blocks`] for more information.
|
/// See [`common::regtest::submit_blocks`] for more information.
|
||||||
|
|
|
@ -119,6 +119,12 @@ pub fn persistent_test_config(network: &Network) -> Result<ZebradConfig> {
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn external_address_test_config(network: &Network) -> Result<ZebradConfig> {
|
||||||
|
let mut config = default_test_config(network)?;
|
||||||
|
config.network.external_addr = Some("127.0.0.1:0".parse()?);
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn testdir() -> Result<TempDir> {
|
pub fn testdir() -> Result<TempDir> {
|
||||||
tempfile::Builder::new()
|
tempfile::Builder::new()
|
||||||
.prefix("zebrad_tests")
|
.prefix("zebrad_tests")
|
||||||
|
|
Loading…
Reference in New Issue