Add fullnode --dynamic-port-range option

This commit is contained in:
Michael Vines 2019-04-12 18:17:34 -07:00
parent c7a7d6db84
commit f6aa90e193
11 changed files with 203 additions and 101 deletions

View File

@ -1,6 +1,6 @@
## Testnet Participation ## Testnet Participation
This document describes how to participate in a public testnet as a This document describes how to participate in the beta testnet as a
validator node using the *Grandview v0.13* release. validator node.
Please note some of the information and instructions described here may change Please note some of the information and instructions described here may change
in future releases. in future releases.
@ -125,6 +125,12 @@ $ RUST_LOG=info solana-gossip --network ${ip:?}:8001
Congratulations, you're now participating in the testnet cluster! Congratulations, you're now participating in the testnet cluster!
#### Controlling local network port allocation
By default the validator will dynamically select available network ports in the
8000-10000 range, and may be overridden with `--dynamic-port-range`. For
example, `fullnode-x.sh --dynamic-port-range 11000-11010 ...` will restrict the
validator to ports 11000-11011.
### Sharing Metrics From Your Validator ### Sharing Metrics From Your Validator
If you'd like to share metrics perform the following steps before starting the If you'd like to share metrics perform the following steps before starting the
validator node: validator node:

View File

@ -66,6 +66,19 @@ echo --- Creating tarball
cp solana-release-cuda/bin/solana-fullnode solana-release/bin/solana-fullnode-cuda cp solana-release-cuda/bin/solana-fullnode solana-release/bin/solana-fullnode-cuda
cp -a scripts multinode-demo solana-release/ cp -a scripts multinode-demo solana-release/
# Add a wrapper script for fullnode.sh
# TODO: Remove multinode/... from tarball
cat > solana-release/bin/fullnode.sh <<'EOF'
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")"/..
export USE_INSTALL=1
exec multinode-demo/fullnode.sh "$@"
EOF
chmod +x solana-release/bin/fullnode.sh
# Add a wrapper script for fullnode-x.sh
# TODO: Remove multinode/... from tarball
cat > solana-release/bin/fullnode-x.sh <<'EOF' cat > solana-release/bin/fullnode-x.sh <<'EOF'
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e

View File

@ -31,11 +31,12 @@ use rand::{thread_rng, Rng};
use rayon::prelude::*; use rayon::prelude::*;
use solana_metrics::counter::Counter; use solana_metrics::counter::Counter;
use solana_metrics::{influxdb, submit}; use solana_metrics::{influxdb, submit};
use solana_netutil::{bind_in_range, bind_to, find_available_port_in_range, multi_bind_in_range}; use solana_netutil::{
bind_in_range, bind_to, find_available_port_in_range, multi_bind_in_range, PortRange,
};
use solana_runtime::bloom::Bloom; use solana_runtime::bloom::Bloom;
use solana_sdk::hash::Hash; use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::rpc_port;
use solana_sdk::signature::{Keypair, KeypairUtil, Signable, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signable, Signature};
use solana_sdk::timing::{duration_as_ms, timestamp}; use solana_sdk::timing::{duration_as_ms, timestamp};
use solana_sdk::transaction::Transaction; use solana_sdk::transaction::Transaction;
@ -48,7 +49,7 @@ use std::sync::{Arc, RwLock};
use std::thread::{sleep, Builder, JoinHandle}; use std::thread::{sleep, Builder, JoinHandle};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
pub const FULLNODE_PORT_RANGE: (u16, u16) = (8000, 10_000); pub const FULLNODE_PORT_RANGE: PortRange = (8000, 10_000);
/// The fanout for Ledger Replication /// The fanout for Ledger Replication
pub const DATA_PLANE_FANOUT: usize = 200; pub const DATA_PLANE_FANOUT: usize = 200;
@ -1510,7 +1511,7 @@ impl Node {
}, },
} }
} }
fn get_gossip_port(gossip_addr: &SocketAddr) -> (u16, UdpSocket) { fn get_gossip_port(gossip_addr: &SocketAddr, port_range: PortRange) -> (u16, UdpSocket) {
if gossip_addr.port() != 0 { if gossip_addr.port() != 0 {
( (
gossip_addr.port(), gossip_addr.port(),
@ -1519,27 +1520,29 @@ impl Node {
}), }),
) )
} else { } else {
Self::bind() Self::bind(port_range)
} }
} }
fn bind() -> (u16, UdpSocket) { fn bind(port_range: PortRange) -> (u16, UdpSocket) {
bind_in_range(FULLNODE_PORT_RANGE).expect("Failed to bind") bind_in_range(port_range).expect("Failed to bind")
} }
pub fn new_with_external_ip(pubkey: &Pubkey, gossip_addr: &SocketAddr) -> Node { pub fn new_with_external_ip(
let (gossip_port, gossip) = Self::get_gossip_port(gossip_addr); pubkey: &Pubkey,
gossip_addr: &SocketAddr,
port_range: PortRange,
) -> Node {
let (gossip_port, gossip) = Self::get_gossip_port(gossip_addr, port_range);
let (tvu_port, tvu_sockets) = let (tvu_port, tvu_sockets) = multi_bind_in_range(port_range, 8).expect("tvu multi_bind");
multi_bind_in_range(FULLNODE_PORT_RANGE, 8).expect("tvu multi_bind");
let (tpu_port, tpu_sockets) = let (tpu_port, tpu_sockets) = multi_bind_in_range(port_range, 32).expect("tpu multi_bind");
multi_bind_in_range(FULLNODE_PORT_RANGE, 32).expect("tpu multi_bind");
let (tpu_via_blobs_port, tpu_via_blobs_sockets) = let (tpu_via_blobs_port, tpu_via_blobs_sockets) =
multi_bind_in_range(FULLNODE_PORT_RANGE, 8).expect("tpu multi_bind"); multi_bind_in_range(port_range, 8).expect("tpu multi_bind");
let (_, repair) = Self::bind(); let (_, repair) = Self::bind(port_range);
let (_, broadcast) = Self::bind(); let (_, broadcast) = Self::bind(port_range);
let (_, retransmit) = Self::bind(); let (_, retransmit) = Self::bind(port_range);
let info = ContactInfo::new( let info = ContactInfo::new(
pubkey, pubkey,
@ -1547,9 +1550,9 @@ impl Node {
SocketAddr::new(gossip_addr.ip(), tvu_port), SocketAddr::new(gossip_addr.ip(), tvu_port),
SocketAddr::new(gossip_addr.ip(), tpu_port), SocketAddr::new(gossip_addr.ip(), tpu_port),
SocketAddr::new(gossip_addr.ip(), tpu_via_blobs_port), SocketAddr::new(gossip_addr.ip(), tpu_via_blobs_port),
"0.0.0.0:0".parse().unwrap(), socketaddr_any!(),
SocketAddr::new(gossip_addr.ip(), rpc_port::DEFAULT_RPC_PORT), socketaddr_any!(),
SocketAddr::new(gossip_addr.ip(), rpc_port::DEFAULT_RPC_PUBSUB_PORT), socketaddr_any!(),
0, 0,
); );
trace!("new ContactInfo: {:?}", info); trace!("new ContactInfo: {:?}", info);
@ -1568,14 +1571,18 @@ impl Node {
}, },
} }
} }
pub fn new_replicator_with_external_ip(pubkey: &Pubkey, gossip_addr: &SocketAddr) -> Node { pub fn new_replicator_with_external_ip(
let mut new = Self::new_with_external_ip(pubkey, gossip_addr); pubkey: &Pubkey,
let (storage_port, storage_socket) = Self::bind(); gossip_addr: &SocketAddr,
port_range: PortRange,
) -> Node {
let mut new = Self::new_with_external_ip(pubkey, gossip_addr, port_range);
let (storage_port, storage_socket) = Self::bind(port_range);
new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port); new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port);
new.sockets.storage = Some(storage_socket); new.sockets.storage = Some(storage_socket);
let empty = "0.0.0.0:0".parse().unwrap(); let empty = socketaddr_any!();
new.info.tpu = empty; new.info.tpu = empty;
new.info.tpu_via_blobs = empty; new.info.tpu_via_blobs = empty;
new.sockets.tpu = vec![]; new.sockets.tpu = vec![];
@ -1904,7 +1911,11 @@ mod tests {
#[test] #[test]
fn new_with_external_ip_test_random() { fn new_with_external_ip_test_random() {
let ip = Ipv4Addr::from(0); let ip = Ipv4Addr::from(0);
let node = Node::new_with_external_ip(&Pubkey::new_rand(), &socketaddr!(ip, 0)); let node = Node::new_with_external_ip(
&Pubkey::new_rand(),
&socketaddr!(ip, 0),
FULLNODE_PORT_RANGE,
);
check_node_sockets(&node, IpAddr::V4(ip), FULLNODE_PORT_RANGE); check_node_sockets(&node, IpAddr::V4(ip), FULLNODE_PORT_RANGE);
} }
@ -1917,7 +1928,11 @@ mod tests {
.expect("Failed to bind") .expect("Failed to bind")
.0 .0
}; };
let node = Node::new_with_external_ip(&Pubkey::new_rand(), &socketaddr!(0, port)); let node = Node::new_with_external_ip(
&Pubkey::new_rand(),
&socketaddr!(0, port),
FULLNODE_PORT_RANGE,
);
check_node_sockets(&node, ip, FULLNODE_PORT_RANGE); check_node_sockets(&node, ip, FULLNODE_PORT_RANGE);
@ -1927,7 +1942,11 @@ mod tests {
#[test] #[test]
fn new_replicator_external_ip_test() { fn new_replicator_external_ip_test() {
let ip = Ipv4Addr::from(0); let ip = Ipv4Addr::from(0);
let node = Node::new_replicator_with_external_ip(&Pubkey::new_rand(), &socketaddr!(ip, 0)); let node = Node::new_replicator_with_external_ip(
&Pubkey::new_rand(),
&socketaddr!(ip, 0),
FULLNODE_PORT_RANGE,
);
let ip = IpAddr::V4(ip); let ip = IpAddr::V4(ip);
check_socket(&node.sockets.storage.unwrap(), ip, FULLNODE_PORT_RANGE); check_socket(&node.sockets.storage.unwrap(), ip, FULLNODE_PORT_RANGE);

View File

@ -1,7 +1,10 @@
use bincode::serialize; use bincode::serialize;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
#[cfg(test)]
use solana_sdk::rpc_port; use solana_sdk::rpc_port;
use solana_sdk::signature::{Keypair, KeypairUtil, Signable, Signature}; #[cfg(test)]
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::signature::{Signable, Signature};
use solana_sdk::timing::timestamp; use solana_sdk::timing::timestamp;
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
@ -141,16 +144,19 @@ impl ContactInfo {
0, 0,
) )
} }
fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr {
let mut nxt_addr = *addr; #[cfg(test)]
nxt_addr.set_port(addr.port() + nxt);
nxt_addr
}
fn new_with_pubkey_socketaddr(pubkey: &Pubkey, bind_addr: &SocketAddr) -> Self { fn new_with_pubkey_socketaddr(pubkey: &Pubkey, bind_addr: &SocketAddr) -> Self {
fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr {
let mut nxt_addr = *addr;
nxt_addr.set_port(addr.port() + nxt);
nxt_addr
}
let tpu_addr = *bind_addr; let tpu_addr = *bind_addr;
let gossip_addr = Self::next_port(&bind_addr, 1); let gossip_addr = next_port(&bind_addr, 1);
let tvu_addr = Self::next_port(&bind_addr, 2); let tvu_addr = next_port(&bind_addr, 2);
let tpu_via_blobs_addr = Self::next_port(&bind_addr, 3); let tpu_via_blobs_addr = next_port(&bind_addr, 3);
let rpc_addr = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PORT); let rpc_addr = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PORT);
let rpc_pubsub_addr = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PUBSUB_PORT); let rpc_pubsub_addr = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PUBSUB_PORT);
Self::new( Self::new(
@ -165,7 +171,9 @@ impl ContactInfo {
timestamp(), timestamp(),
) )
} }
pub fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
#[cfg(test)]
pub(crate) fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
let keypair = Keypair::new(); let keypair = Keypair::new();
Self::new_with_pubkey_socketaddr(&keypair.pubkey(), bind_addr) Self::new_with_pubkey_socketaddr(&keypair.pubkey(), bind_addr)
} }
@ -185,11 +193,13 @@ impl ContactInfo {
timestamp(), timestamp(),
) )
} }
fn is_valid_ip(addr: IpAddr) -> bool { fn is_valid_ip(addr: IpAddr) -> bool {
!(addr.is_unspecified() || addr.is_multicast()) !(addr.is_unspecified() || addr.is_multicast())
// || (addr.is_loopback() && !cfg_test)) // || (addr.is_loopback() && !cfg_test))
// TODO: boot loopback in production networks // TODO: boot loopback in production networks
} }
/// port must not be 0 /// port must not be 0
/// ip must be specified and not mulitcast /// ip must be specified and not mulitcast
/// loopback ip is only allowed in tests /// loopback ip is only allowed in tests

View File

@ -142,24 +142,32 @@ impl Fullnode {
let storage_state = StorageState::new(); let storage_state = StorageState::new();
let rpc_service = JsonRpcService::new( let rpc_service = if node.info.rpc.port() == 0 {
&cluster_info, None
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), node.info.rpc.port()), } else {
storage_state.clone(), Some(JsonRpcService::new(
config.rpc_config.clone(), &cluster_info,
bank_forks.clone(), SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), node.info.rpc.port()),
&exit, storage_state.clone(),
); config.rpc_config.clone(),
bank_forks.clone(),
&exit,
))
};
let subscriptions = Arc::new(RpcSubscriptions::default()); let subscriptions = Arc::new(RpcSubscriptions::default());
let rpc_pubsub_service = PubSubService::new( let rpc_pubsub_service = if node.info.rpc_pubsub.port() == 0 {
&subscriptions, None
SocketAddr::new( } else {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), Some(PubSubService::new(
node.info.rpc_pubsub.port(), &subscriptions,
), SocketAddr::new(
&exit, IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
); node.info.rpc_pubsub.port(),
),
&exit,
))
};
let gossip_service = GossipService::new( let gossip_service = GossipService::new(
&cluster_info, &cluster_info,
@ -243,8 +251,8 @@ impl Fullnode {
Self { Self {
id, id,
gossip_service, gossip_service,
rpc_service: Some(rpc_service), rpc_service,
rpc_pubsub_service: Some(rpc_pubsub_service), rpc_pubsub_service,
tpu, tpu,
tvu, tvu,
exit, exit,

View File

@ -1,7 +1,7 @@
//! The `local_vote_signer_service` can be started locally to sign fullnode votes //! The `local_vote_signer_service` can be started locally to sign fullnode votes
use crate::cluster_info::FULLNODE_PORT_RANGE;
use crate::service::Service; use crate::service::Service;
use solana_netutil::PortRange;
use solana_vote_signer::rpc::VoteSignerRpcService; use solana_vote_signer::rpc::VoteSignerRpcService;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -24,8 +24,8 @@ impl Service for LocalVoteSignerService {
impl LocalVoteSignerService { impl LocalVoteSignerService {
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new() -> (Self, SocketAddr) { pub fn new(port_range: PortRange) -> (Self, SocketAddr) {
let addr = match solana_netutil::find_available_port_in_range(FULLNODE_PORT_RANGE) { let addr = match solana_netutil::find_available_port_in_range(port_range) {
Ok(port) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port), Ok(port) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port),
Err(_e) => panic!("Failed to find an available port for local vote signer service"), Err(_e) => panic!("Failed to find an available port for local vote signer service"),
}; };

View File

@ -5,15 +5,27 @@ use solana::contact_info::ContactInfo;
use solana::fullnode::{Fullnode, FullnodeConfig}; use solana::fullnode::{Fullnode, FullnodeConfig};
use solana::local_vote_signer_service::LocalVoteSignerService; use solana::local_vote_signer_service::LocalVoteSignerService;
use solana::service::Service; use solana::service::Service;
use solana_netutil::parse_port_range;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use std::fs::File; use std::fs::File;
use std::process::exit; use std::process::exit;
use std::sync::Arc; use std::sync::Arc;
fn port_range_validator(port_range: String) -> Result<(), String> {
if parse_port_range(&port_range).is_some() {
Ok(())
} else {
Err("Invalid port range".to_string())
}
}
fn main() { fn main() {
solana_logger::setup(); solana_logger::setup();
solana_metrics::set_panic_hook("fullnode"); solana_metrics::set_panic_hook("fullnode");
let default_dynamic_port_range =
&format!("{}-{}", FULLNODE_PORT_RANGE.0, FULLNODE_PORT_RANGE.1);
let matches = App::new(crate_name!()).about(crate_description!()) let matches = App::new(crate_name!()).about(crate_description!())
.version(crate_version!()) .version(crate_version!())
.arg( .arg(
@ -131,6 +143,15 @@ fn main() {
.takes_value(true) .takes_value(true)
.help("Gossip port number for the node"), .help("Gossip port number for the node"),
) )
.arg(
clap::Arg::with_name("dynamic_port_range")
.long("dynamic-port-range")
.value_name("MIN_PORT-MAX_PORT")
.takes_value(true)
.default_value(default_dynamic_port_range)
.validator(port_range_validator)
.help("Range to use for dynamically assigned ports"),
)
.get_matches(); .get_matches();
let mut fullnode_config = FullnodeConfig::default(); let mut fullnode_config = FullnodeConfig::default();
@ -170,10 +191,14 @@ fn main() {
.value_of("rpc_drone_address") .value_of("rpc_drone_address")
.map(|address| address.parse().expect("failed to parse drone address")); .map(|address| address.parse().expect("failed to parse drone address"));
let dynamic_port_range = parse_port_range(matches.value_of("dynamic_port_range").unwrap())
.expect("invalid dynamic_port_range");
let gossip_addr = { let gossip_addr = {
let mut addr = solana_netutil::parse_port_or_addr( let mut addr = solana_netutil::parse_port_or_addr(
matches.value_of("gossip_port"), matches.value_of("gossip_port"),
FULLNODE_PORT_RANGE.0 + 1, solana_netutil::find_available_port_in_range(dynamic_port_range)
.expect("unable to allocate gossip_port"),
); );
if matches.is_present("public_address") { if matches.is_present("public_address") {
addr.set_ip(solana_netutil::get_public_ip_addr().unwrap()); addr.set_ip(solana_netutil::get_public_ip_addr().unwrap());
@ -199,31 +224,23 @@ fn main() {
) )
} else { } else {
// Run a local vote signer if a vote signer service address was not provided // Run a local vote signer if a vote signer service address was not provided
let (signer_service, signer_addr) = LocalVoteSignerService::new(); let (signer_service, signer_addr) = LocalVoteSignerService::new(dynamic_port_range);
(Some(signer_service), signer_addr) (Some(signer_service), signer_addr)
}; };
let (rpc_port, rpc_pubsub_port) = if let Some(port) = matches.value_of("rpc_port") {
let port_number = port.to_string().parse().expect("integer");
if port_number == 0 {
eprintln!("Invalid RPC port requested: {:?}", port);
exit(1);
}
(port_number, port_number + 1)
} else {
(
solana_netutil::find_available_port_in_range(FULLNODE_PORT_RANGE)
.expect("unable to allocate rpc_port"),
solana_netutil::find_available_port_in_range(FULLNODE_PORT_RANGE)
.expect("unable to allocate rpc_pubsub_port"),
)
};
let init_complete_file = matches.value_of("init_complete_file"); let init_complete_file = matches.value_of("init_complete_file");
fullnode_config.blockstream = matches.value_of("blockstream").map(|s| s.to_string()); fullnode_config.blockstream = matches.value_of("blockstream").map(|s| s.to_string());
let keypair = Arc::new(keypair); let keypair = Arc::new(keypair);
let mut node = Node::new_with_external_ip(&keypair.pubkey(), &gossip_addr); let mut node = Node::new_with_external_ip(&keypair.pubkey(), &gossip_addr, dynamic_port_range);
node.info.rpc.set_port(rpc_port); if let Some(port) = matches.value_of("rpc_port") {
node.info.rpc_pubsub.set_port(rpc_pubsub_port); let port_number = port.to_string().parse().expect("integer");
if port_number == 0 {
eprintln!("Invalid RPC port requested: {:?}", port);
exit(1);
}
node.info.rpc.set_port(port_number);
node.info.rpc_pubsub.set_port(port_number + 1);
};
let fullnode = Fullnode::new( let fullnode = Fullnode::new(
node, node,

View File

@ -35,6 +35,9 @@ while [[ ${1:0:1} = - ]]; do
elif [[ $1 = --rpc-port ]]; then elif [[ $1 = --rpc-port ]]; then
extra_fullnode_args+=("$1" "$2") extra_fullnode_args+=("$1" "$2")
shift 2 shift 2
elif [[ $1 = --dynamic-port-range ]]; then
extra_fullnode_args+=("$1" "$2")
shift 2
else else
echo "Unknown argument: $1" echo "Unknown argument: $1"
exit 1 exit 1
@ -74,6 +77,7 @@ $program \
--vote-account "$bootstrap_leader_vote_id" \ --vote-account "$bootstrap_leader_vote_id" \
--ledger "$SOLANA_CONFIG_DIR"/bootstrap-leader-ledger \ --ledger "$SOLANA_CONFIG_DIR"/bootstrap-leader-ledger \
--accounts "$SOLANA_CONFIG_DIR"/bootstrap-leader-accounts \ --accounts "$SOLANA_CONFIG_DIR"/bootstrap-leader-accounts \
--gossip-port 8001 \
--rpc-port 8899 \ --rpc-port 8899 \
--rpc-drone-address 127.0.0.1:9900 \ --rpc-drone-address 127.0.0.1:9900 \
"${extra_fullnode_args[@]}" \ "${extra_fullnode_args[@]}" \

View File

@ -13,7 +13,7 @@ if [[ $1 = -h ]]; then
fullnode_usage "$@" fullnode_usage "$@"
fi fi
gossip_port=9000 gossip_port=
extra_fullnode_args=() extra_fullnode_args=()
self_setup=0 self_setup=0
setup_stakes=1 setup_stakes=1
@ -51,10 +51,14 @@ while [[ ${1:0:1} = - ]]; do
shift shift
elif [[ $1 = --gossip-port ]]; then elif [[ $1 = --gossip-port ]]; then
gossip_port=$2 gossip_port=$2
extra_fullnode_args+=("$1" "$2")
shift 2 shift 2
elif [[ $1 = --rpc-port ]]; then elif [[ $1 = --rpc-port ]]; then
extra_fullnode_args+=("$1" "$2") extra_fullnode_args+=("$1" "$2")
shift 2 shift 2
elif [[ $1 = --dynamic-port-range ]]; then
extra_fullnode_args+=("$1" "$2")
shift 2
else else
echo "Unknown argument: $1" echo "Unknown argument: $1"
exit 1 exit 1
@ -117,26 +121,16 @@ if ((!self_setup)); then
fullnode_vote_id_path=$SOLANA_CONFIG_DIR/fullnode-vote-id.json fullnode_vote_id_path=$SOLANA_CONFIG_DIR/fullnode-vote-id.json
ledger_config_dir=$SOLANA_CONFIG_DIR/fullnode-ledger ledger_config_dir=$SOLANA_CONFIG_DIR/fullnode-ledger
accounts_config_dir=$SOLANA_CONFIG_DIR/fullnode-accounts accounts_config_dir=$SOLANA_CONFIG_DIR/fullnode-accounts
if [[ -z $gossip_port ]]; then
extra_fullnode_args+=("--gossip-port" 9000)
fi
else else
mkdir -p "$SOLANA_CONFIG_DIR" mkdir -p "$SOLANA_CONFIG_DIR"
fullnode_id_path=$SOLANA_CONFIG_DIR/fullnode-id-x$self_setup_label.json fullnode_id_path=$SOLANA_CONFIG_DIR/fullnode-id-x$self_setup_label.json
fullnode_vote_id_path=$SOLANA_CONFIG_DIR/fullnode-vote-id-x$self_setup_label.json fullnode_vote_id_path=$SOLANA_CONFIG_DIR/fullnode-vote-id-x$self_setup_label.json
[[ -f "$fullnode_id_path" ]] || $solana_keygen -o "$fullnode_id_path" [[ -f "$fullnode_id_path" ]] || $solana_keygen -o "$fullnode_id_path"
[[ -f "$fullnode_vote_id_path" ]] || $solana_keygen -o "$fullnode_vote_id_path" [[ -f "$fullnode_vote_id_path" ]] || $solana_keygen -o "$fullnode_vote_id_path"
echo "Finding a port.."
# Find an available port in the range 9100-9899
(( gossip_port = 9100 + ($$ % 800) ))
while true; do
(( gossip_port = gossip_port >= 9900 ? 9100 : ++gossip_port ))
echo "Testing $gossip_port"
if ! nc -w 10 -z 127.0.0.1 $gossip_port; then
echo "Selected gossip_port $gossip_port"
break;
fi
echo "Port $gossip_port is in use"
done
ledger_config_dir=$SOLANA_CONFIG_DIR/fullnode-ledger-x$self_setup_label ledger_config_dir=$SOLANA_CONFIG_DIR/fullnode-ledger-x$self_setup_label
accounts_config_dir=$SOLANA_CONFIG_DIR/fullnode-accounts-x$self_setup_label accounts_config_dir=$SOLANA_CONFIG_DIR/fullnode-accounts-x$self_setup_label
fi fi
@ -188,7 +182,6 @@ while true; do
trap 'kill "$pid" && wait "$pid"' INT TERM ERR trap 'kill "$pid" && wait "$pid"' INT TERM ERR
$program \ $program \
--gossip-port "$gossip_port" \
--identity "$fullnode_id_path" \ --identity "$fullnode_id_path" \
--voting-keypair "$fullnode_vote_id_path" \ --voting-keypair "$fullnode_vote_id_path" \
--vote-account "$fullnode_vote_id" \ --vote-account "$fullnode_vote_id" \

View File

@ -17,6 +17,8 @@ pub struct UdpSocketPair {
pub sender: UdpSocket, // Locally bound socket to send via public address pub sender: UdpSocket, // Locally bound socket to send via public address
} }
pub type PortRange = (u16, u16);
/// Tries to determine the public IP address of this machine /// Tries to determine the public IP address of this machine
pub fn get_public_ip_addr() -> Result<IpAddr, String> { pub fn get_public_ip_addr() -> Result<IpAddr, String> {
let body = reqwest::get("http://ifconfig.co/ip") let body = reqwest::get("http://ifconfig.co/ip")
@ -48,6 +50,26 @@ pub fn parse_port_or_addr(optstr: Option<&str>, default_port: u16) -> SocketAddr
} }
} }
pub fn parse_port_range(port_range: &str) -> Option<PortRange> {
let ports: Vec<&str> = port_range.split('-').collect();
if ports.len() != 2 {
return None;
}
let start_port = ports[0].parse();
let end_port = ports[1].parse();
if start_port.is_err() || end_port.is_err() {
return None;
}
let start_port = start_port.unwrap();
let end_port = end_port.unwrap();
if end_port < start_port {
return None;
}
Some((start_port, end_port))
}
fn find_eth0ish_ip_addr( fn find_eth0ish_ip_addr(
ifaces: &mut Vec<datalink::NetworkInterface>, ifaces: &mut Vec<datalink::NetworkInterface>,
enable_ipv6: bool, enable_ipv6: bool,
@ -122,7 +144,7 @@ fn udp_socket(reuseaddr: bool) -> io::Result<Socket> {
Ok(sock) Ok(sock)
} }
pub fn bind_in_range(range: (u16, u16)) -> io::Result<(u16, UdpSocket)> { pub fn bind_in_range(range: PortRange) -> io::Result<(u16, UdpSocket)> {
let sock = udp_socket(false)?; let sock = udp_socket(false)?;
let (start, end) = range; let (start, end) = range;
@ -151,7 +173,7 @@ pub fn bind_in_range(range: (u16, u16)) -> io::Result<(u16, UdpSocket)> {
} }
// binds many sockets to the same port in a range // binds many sockets to the same port in a range
pub fn multi_bind_in_range(range: (u16, u16), num: usize) -> io::Result<(u16, Vec<UdpSocket>)> { pub fn multi_bind_in_range(range: PortRange, num: usize) -> io::Result<(u16, Vec<UdpSocket>)> {
let mut sockets = Vec::with_capacity(num); let mut sockets = Vec::with_capacity(num);
let port = { let port = {
@ -176,7 +198,7 @@ pub fn bind_to(port: u16, reuseaddr: bool) -> io::Result<UdpSocket> {
} }
} }
pub fn find_available_port_in_range(range: (u16, u16)) -> io::Result<u16> { pub fn find_available_port_in_range(range: PortRange) -> io::Result<u16> {
let (start, end) = range; let (start, end) = range;
let mut tries_left = end - start; let mut tries_left = end - start;
let mut rand_port = thread_rng().gen_range(start, end); let mut rand_port = thread_rng().gen_range(start, end);
@ -303,6 +325,15 @@ mod tests {
assert_eq!(p3.port(), 1); assert_eq!(p3.port(), 1);
} }
#[test]
fn test_parse_port_range() {
assert_eq!(parse_port_range("garbage"), None);
assert_eq!(parse_port_range("1-"), None);
assert_eq!(parse_port_range("1-2"), Some((1, 2)));
assert_eq!(parse_port_range("1-2-3"), None);
assert_eq!(parse_port_range("2-1"), None);
}
#[test] #[test]
fn test_bind() { fn test_bind() {
assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000); assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000);

View File

@ -1,5 +1,5 @@
use clap::{crate_description, crate_name, crate_version, App, Arg}; use clap::{crate_description, crate_name, crate_version, App, Arg};
use solana::cluster_info::Node; use solana::cluster_info::{Node, FULLNODE_PORT_RANGE};
use solana::contact_info::ContactInfo; use solana::contact_info::ContactInfo;
use solana::replicator::Replicator; use solana::replicator::Replicator;
use solana::socketaddr; use solana::socketaddr;
@ -67,7 +67,8 @@ fn main() {
} }
addr addr
}; };
let node = Node::new_replicator_with_external_ip(&keypair.pubkey(), &gossip_addr); let node =
Node::new_replicator_with_external_ip(&keypair.pubkey(), &gossip_addr, FULLNODE_PORT_RANGE);
println!( println!(
"replicating the data with keypair={:?} gossip_addr={:?}", "replicating the data with keypair={:?} gossip_addr={:?}",