Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] feat: allow-external-peers #3163

Merged
merged 10 commits into from Mar 18, 2024
2 changes: 1 addition & 1 deletion .devnet/start.sh
Expand Up @@ -37,7 +37,7 @@ start_snarkos_in_tmux() {
tmux new-session -d -s snarkos-session

# Send the snarkOS start command to the tmux session with the NODE_ID
tmux send-keys -t "snarkos-session" "snarkos start --nodisplay --bft 0.0.0.0:5000 --rest 0.0.0.0:3030 --peers $NODE_IP:4130 --validators $NODE_IP:5000 --rest-rps 1000 --verbosity $VERBOSITY --dev $NODE_ID --dev-num-validators $NUM_INSTANCES --validator --metrics" C-m
tmux send-keys -t "snarkos-session" "snarkos start --nodisplay --bft 0.0.0.0:5000 --rest 0.0.0.0:3030 --allow-external-peers --peers $NODE_IP:4130 --validators $NODE_IP:5000 --rest-rps 1000 --verbosity $VERBOSITY --dev $NODE_ID --dev-num-validators $NUM_INSTANCES --validator --metrics" C-m

exit # Exit root user
EOF
Expand Down
6 changes: 5 additions & 1 deletion cli/src/commands/start.rs
Expand Up @@ -147,6 +147,10 @@ pub struct Start {
#[clap(long)]
/// If development mode is enabled, specify the custom bonded balances as a json object. (default: None)
dev_bonded_balances: Option<BondedBalances>,

#[clap(long = "allow-external-peers")]
/// If the flag is set, the validator will allow untrusted peers to connect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[clap(long = "allow-external-peers")]
/// If the flag is set, the validator will allow untrusted peers to connect
/// If the flag is set, the validator will allow untrusted peers to connect
#[clap(long = "allow-external-peers")]

allow_external_peers: bool,
}

impl Start {
Expand Down Expand Up @@ -542,7 +546,7 @@ impl Start {
// Initialize the node.
let bft_ip = if self.dev.is_some() { self.bft } else { None };
match node_type {
NodeType::Validator => Node::new_validator(self.node, bft_ip, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, dev_txs).await,
NodeType::Validator => Node::new_validator(self.node, bft_ip, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, dev_txs, self.allow_external_peers).await,
NodeType::Prover => Node::new_prover(self.node, account, &trusted_peers, genesis, storage_mode).await,
NodeType::Client => Node::new_client(self.node, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode).await,
}
Expand Down
4 changes: 2 additions & 2 deletions devnet.sh
Expand Up @@ -64,12 +64,12 @@ for validator_index in "${validator_indices[@]}"; do

# Send the command to start the validator to the new window and capture output to the log file
if [ "$validator_index" -eq 0 ]; then
tmux send-keys -t "devnet:window$validator_index" "snarkos start --nodisplay --dev $validator_index --dev-num-validators $total_validators --validator --logfile $log_file --metrics" C-m
tmux send-keys -t "devnet:window$validator_index" "snarkos start --nodisplay --dev $validator_index --allow-external-peers --dev-num-validators $total_validators --validator --logfile $log_file --metrics" C-m
else
# Create a new window with a unique name
window_index=$((validator_index + index_offset))
tmux new-window -t "devnet:$window_index" -n "window$validator_index"
tmux send-keys -t "devnet:window$validator_index" "snarkos start --nodisplay --dev $validator_index --dev-num-validators $total_validators --validator --logfile $log_file" C-m
tmux send-keys -t "devnet:window$validator_index" "snarkos start --nodisplay --dev $validator_index --allow-external-peers --dev-num-validators $total_validators --validator --logfile $log_file" C-m
fi
done

Expand Down
6 changes: 6 additions & 0 deletions node/router/src/handshake.rs
Expand Up @@ -264,6 +264,12 @@ impl<N: Network> Router<N> {
if self.is_connected(&peer_ip) {
bail!("Dropping connection request from '{peer_ip}' (already connected)")
}
// Only allow trusted peers to connect if we are a validator
// (unless allow_external_peers is set)
let is_validator = self.node_type().is_validator();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is_validator is no longer necessary as part of the check (because allow_external_peers takes precedence)

if is_validator && !self.allow_external_peers() && !self.is_trusted(&peer_ip) {
bail!("Dropping connection request from '{peer_ip}' (untrusted)")
}
// Ensure the peer is not restricted.
if self.is_restricted(&peer_ip) {
bail!("Dropping connection request from '{peer_ip}' (restricted)")
Expand Down
15 changes: 12 additions & 3 deletions node/router/src/heartbeat.rs
Expand Up @@ -107,6 +107,12 @@ pub trait Heartbeat<N: Network>: Outbound<N> {
return;
}

let is_validator = self.router().node_type().is_validator();
// Skip if the node is not requesting peers.
if is_validator && !self.router().allow_external_peers() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is_validator is no longer necessary as part of the check (because allow_external_peers takes precedence)

return;
}

// Retrieve the trusted peers.
let trusted = self.router().trusted_peers();
// Retrieve the bootstrap peers.
Expand Down Expand Up @@ -216,9 +222,12 @@ pub trait Heartbeat<N: Network>: Outbound<N> {
for peer_ip in self.router().candidate_peers().into_iter().choose_multiple(rng, num_deficient) {
self.router().connect(peer_ip);
}
// Request more peers from the connected peers.
for peer_ip in self.router().connected_peers().into_iter().choose_multiple(rng, 3) {
self.send(peer_ip, Message::PeerRequest(PeerRequest));
let is_validator = self.router().node_type().is_validator();
if !is_validator || self.router().allow_external_peers() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is_validator is no longer necessary as part of the check (because allow_external_peers takes precedence)

// Request more peers from the connected peers.
for peer_ip in self.router().connected_peers().into_iter().choose_multiple(rng, 3) {
self.send(peer_ip, Message::PeerRequest(PeerRequest));
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions node/router/src/inbound.rs
Expand Up @@ -125,6 +125,9 @@ pub trait Inbound<N: Network>: Reading + Outbound<N> {
if !self.router().cache.contains_outbound_peer_request(peer_ip) {
bail!("Peer '{peer_ip}' is not following the protocol (unexpected peer response)")
}
if self.router().node_type().is_validator() && !self.router().allow_external_peers() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is_validator is no longer necessary as part of the check (because allow_external_peers takes precedence)

bail!("Not accepting peer response from '{peer_ip}' (validator gossip is disabled)");
}

match self.peer_response(peer_ip, &message.peers) {
true => Ok(()),
Expand Down
14 changes: 14 additions & 0 deletions node/router/src/lib.rs
Expand Up @@ -95,6 +95,8 @@ pub struct InnerRouter<N: Network> {
handles: Mutex<Vec<JoinHandle<()>>>,
/// The boolean flag for the development mode.
is_dev: bool,
/// If the flag is set, the node will not engage in P2P gossip to request more peers.
allow_external_peers: bool,
}

impl<N: Network> Router<N> {
Expand All @@ -116,6 +118,7 @@ impl<N: Network> Router<N> {
trusted_peers: &[SocketAddr],
max_peers: u16,
is_dev: bool,
allow_external_peers: bool,
) -> Result<Self> {
// Initialize the TCP stack.
let tcp = Tcp::new(Config::new(node_ip, max_peers));
Expand All @@ -133,6 +136,7 @@ impl<N: Network> Router<N> {
restricted_peers: Default::default(),
handles: Default::default(),
is_dev,
allow_external_peers,
})))
}
}
Expand Down Expand Up @@ -251,6 +255,11 @@ impl<N: Network> Router<N> {
self.is_dev
}

/// Returns `true` if the node is not engaging in P2P gossip to request more peers.
pub fn allow_external_peers(&self) -> bool {
self.allow_external_peers
}

/// Returns the listener IP address from the (ambiguous) peer address.
pub fn resolve_to_listener(&self, peer_addr: &SocketAddr) -> Option<SocketAddr> {
self.resolver.get_listener(peer_addr)
Expand Down Expand Up @@ -295,6 +304,11 @@ impl<N: Network> Router<N> {
.unwrap_or(false)
}

/// Returns `true` if the given IP is trusted.
pub fn is_trusted(&self, ip: &SocketAddr) -> bool {
self.trusted_peers.contains(ip)
}

/// Returns the maximum number of connected peers.
pub fn max_connected_peers(&self) -> usize {
self.tcp.config().max_connections as usize
Expand Down
3 changes: 3 additions & 0 deletions node/router/tests/common/mod.rs
Expand Up @@ -78,6 +78,7 @@ pub async fn client(listening_port: u16, max_peers: u16) -> TestRouter<CurrentNe
&[],
max_peers,
true,
true,
)
.await
.expect("couldn't create client router")
Expand All @@ -94,6 +95,7 @@ pub async fn prover(listening_port: u16, max_peers: u16) -> TestRouter<CurrentNe
&[],
max_peers,
true,
true,
)
.await
.expect("couldn't create prover router")
Expand All @@ -110,6 +112,7 @@ pub async fn validator(listening_port: u16, max_peers: u16) -> TestRouter<Curren
&[],
max_peers,
true,
false,
joske marked this conversation as resolved.
Show resolved Hide resolved
)
.await
.expect("couldn't create validator router")
Expand Down
1 change: 1 addition & 0 deletions node/src/client/mod.rs
Expand Up @@ -117,6 +117,7 @@ impl<N: Network, C: ConsensusStorage<N>> Client<N, C> {
trusted_peers,
Self::MAXIMUM_NUMBER_OF_PEERS as u16,
matches!(storage_mode, StorageMode::Development(_)),
true,
)
.await?;
// Load the coinbase puzzle.
Expand Down
2 changes: 2 additions & 0 deletions node/src/node.rs
Expand Up @@ -51,6 +51,7 @@ impl<N: Network> Node<N> {
cdn: Option<String>,
storage_mode: StorageMode,
dev_txs: bool,
allow_external_peers: bool,
) -> Result<Self> {
Ok(Self::Validator(Arc::new(
Validator::new(
Expand All @@ -65,6 +66,7 @@ impl<N: Network> Node<N> {
cdn,
storage_mode,
dev_txs,
allow_external_peers,
)
.await?,
)))
Expand Down
1 change: 1 addition & 0 deletions node/src/prover/mod.rs
Expand Up @@ -110,6 +110,7 @@ impl<N: Network, C: ConsensusStorage<N>> Prover<N, C> {
trusted_peers,
Self::MAXIMUM_NUMBER_OF_PEERS as u16,
matches!(storage_mode, StorageMode::Development(_)),
true,
)
.await?;
// Load the coinbase puzzle.
Expand Down
3 changes: 3 additions & 0 deletions node/src/validator/mod.rs
Expand Up @@ -84,6 +84,7 @@ impl<N: Network, C: ConsensusStorage<N>> Validator<N, C> {
cdn: Option<String>,
storage_mode: StorageMode,
dev_txs: bool,
allow_external_peers: bool,
) -> Result<Self> {
// Prepare the shutdown flag.
let shutdown: Arc<AtomicBool> = Default::default();
Expand Down Expand Up @@ -126,6 +127,7 @@ impl<N: Network, C: ConsensusStorage<N>> Validator<N, C> {
trusted_peers,
Self::MAXIMUM_NUMBER_OF_PEERS as u16,
matches!(storage_mode, StorageMode::Development(_)),
allow_external_peers,
)
.await?;

Expand Down Expand Up @@ -499,6 +501,7 @@ mod tests {
None,
storage_mode,
dev_txs,
false,
)
.await
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions node/tests/common/node.rs
Expand Up @@ -60,6 +60,7 @@ pub async fn validator() -> Validator<CurrentNetwork, ConsensusMemory<CurrentNet
None, // No CDN.
StorageMode::Production,
false, // No dev traffic in production mode.
true,
)
.await
.expect("couldn't create validator instance")
Expand Down