Skip to content

Commit a035c64

Browse files
authored
Merge pull request #4136 from ProvableHQ/feat/extended_commit_tracking
[Feat] Log the combined stake at our SHA (in validator mode)
2 parents f104cb3 + 7be6802 commit a035c64

10 files changed

Lines changed: 115 additions & 28 deletions

File tree

Cargo.lock

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ default-features = false
155155
[workspace.dependencies.rand_distr]
156156
version = "0.4"
157157

158+
[workspace.dependencies.smol_str]
159+
version = "=0.3.2" # Update after Rust >1.88
160+
158161
[workspace.dependencies.tracing]
159162
version = "0.1"
160163
default-features = false
@@ -356,8 +359,7 @@ workspace = true
356359
version = "0.11.2"
357360

358361
[build-dependencies.built]
359-
version = "0.8"
360-
features = [ "git2" ]
362+
workspace = true
361363

362364
[build-dependencies.toml]
363365
version = "0.9"

node/bft/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ workspace = true
102102
version = "0.10"
103103
default-features = false
104104

105+
[dependencies.smol_str]
106+
workspace = true
107+
105108
[dependencies.snarkos-account]
106109
workspace = true
107110

node/bft/src/gateway.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::{
2424
helpers::{Cache, PrimarySender, Storage, SyncSender, WorkerSender, assign_to_worker},
2525
spawn_blocking,
2626
};
27+
use smol_str::SmolStr;
2728
use snarkos_account::Account;
2829
use snarkos_node_bft_events::{
2930
BlockRequest,
@@ -50,6 +51,7 @@ use snarkos_node_network::{
5051
bootstrap_peers,
5152
get_repo_commit_hash,
5253
log_repo_sha_comparison,
54+
shorten_snarkos_sha,
5355
};
5456
use snarkos_node_sync::{InsertBlockResponseError, MAX_BLOCKS_BEHIND, communication_service::CommunicationService};
5557
use snarkos_node_tcp::{
@@ -475,6 +477,7 @@ impl<N: Network> Gateway<N> {
475477
address,
476478
NodeType::Validator,
477479
0,
480+
get_repo_commit_hash(),
478481
ConnectionMode::Gateway,
479482
);
480483
}
@@ -886,7 +889,7 @@ impl<N: Network> Gateway<N> {
886889
/// Logs the connected validators.
887890
fn log_connected_validators(&self) {
888891
// Retrieve the connected validators and current committee.
889-
let connected_validators = self.connected_peers();
892+
let connected_validators = self.get_connected_peers();
890893
let committee = match self.ledger.current_committee() {
891894
Ok(c) => c,
892895
Err(err) => {
@@ -908,15 +911,32 @@ impl<N: Network> Gateway<N> {
908911

909912
// Collect the connected validator addresses and stake.
910913
let mut connected_validator_addresses = HashSet::with_capacity(connected_validators.len());
914+
let mut connected_validator_shas: HashMap<SmolStr, u64> = HashMap::with_capacity(connected_validators.len());
915+
// Insert our sha.
916+
let our_sha = shorten_snarkos_sha(&get_repo_commit_hash());
917+
let our_stake = committee.get_stake(self.account.address());
918+
connected_validator_shas.insert(our_sha.clone(), our_stake);
911919
// Include our own address.
912920
connected_validator_addresses.insert(self.account.address());
913921
// Include and log the connected validators.
914-
for peer_ip in &connected_validators {
915-
let address = self.resolve_to_aleo_addr(*peer_ip).map_or("Unknown".to_string(), |a| {
916-
connected_validator_addresses.insert(a);
917-
a.to_string()
918-
});
919-
debug!("{}", format!(" Connected to: {peer_ip} - {address}").dimmed());
922+
for peer in &connected_validators {
923+
let peer_ip = peer.listener_addr;
924+
// Register the Aleo address.
925+
let address = peer.aleo_addr;
926+
connected_validator_addresses.insert(address);
927+
// Register the snarkOS commit SHA and the associated stake.
928+
let address_stake = committee.get_stake(address);
929+
let short_peer_sha = shorten_snarkos_sha(&peer.snarkos_sha);
930+
*connected_validator_shas.entry(short_peer_sha.clone()).or_default() += address_stake;
931+
// Log the connected validator.
932+
debug!("{}", format!(" Connected to: {peer_ip} - {address} @ {short_peer_sha}").dimmed());
933+
}
934+
935+
if let Some(combined_stake) = connected_validator_shas.get(&our_sha) {
936+
let percentage = *combined_stake as f64 / committee.total_stake() as f64 * 100.0;
937+
debug!("{}", format!(" Combined stake @ {our_sha}: {percentage:.2}%").dimmed());
938+
#[cfg(feature = "metrics")]
939+
metrics::gauge(metrics::bft::CONNECTED_STAKE_WITH_MATCHING_SHA, percentage);
920940
}
921941

922942
// Log the validators that are not connected.
@@ -1423,6 +1443,7 @@ impl<N: Network> Handshake for Gateway<N> {
14231443
cr.address,
14241444
node_type,
14251445
cr.version,
1446+
cr.snarkos_sha,
14261447
ConnectionMode::Gateway,
14271448
);
14281449
}
@@ -1508,8 +1529,9 @@ impl<N: Network> Gateway<N> {
15081529
// Determine the snarkOS SHA to send to the peer.
15091530
let current_block_height = self.ledger.latest_block_height();
15101531
let consensus_version = N::CONSENSUS_VERSION(current_block_height).unwrap();
1511-
let snarkos_sha = match (consensus_version >= ConsensusVersion::V12, get_repo_commit_hash()) {
1512-
(true, Some(sha)) => Some(sha),
1532+
let snarkos_sha = match (self.is_dev(), consensus_version >= ConsensusVersion::V12, get_repo_commit_hash()) {
1533+
(true, _, Some(sha)) => Some(sha),
1534+
(_, true, Some(sha)) => Some(sha),
15131535
_ => None,
15141536
};
15151537
// Send a challenge request to the peer.
@@ -1615,8 +1637,9 @@ impl<N: Network> Gateway<N> {
16151637
// Determine the snarkOS SHA to send to the peer.
16161638
let current_block_height = self.ledger.latest_block_height();
16171639
let consensus_version = N::CONSENSUS_VERSION(current_block_height).unwrap();
1618-
let snarkos_sha = match (consensus_version >= ConsensusVersion::V12, get_repo_commit_hash()) {
1619-
(true, Some(sha)) => Some(sha),
1640+
let snarkos_sha = match (self.is_dev(), consensus_version >= ConsensusVersion::V12, get_repo_commit_hash()) {
1641+
(true, _, Some(sha)) => Some(sha),
1642+
(_, true, Some(sha)) => Some(sha),
16201643
_ => None,
16211644
};
16221645
// Send the challenge request.

node/metrics/src/names.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
pub(super) const COUNTER_NAMES: [&str; 3] =
1717
[bft::LEADERS_ELECTED, consensus::STALE_UNCONFIRMED_TRANSACTIONS, consensus::STALE_UNCONFIRMED_SOLUTIONS];
1818

19-
pub(super) const GAUGE_NAMES: [&str; 27] = [
19+
pub(super) const GAUGE_NAMES: [&str; 28] = [
2020
bft::CONNECTED,
2121
bft::CONNECTED_STAKE,
22+
bft::CONNECTED_STAKE_WITH_MATCHING_SHA,
2223
bft::CONNECTING,
2324
bft::LAST_STORED_ROUND,
2425
bft::PROPOSAL_ROUND,
@@ -53,6 +54,7 @@ pub mod bft {
5354
pub const COMMIT_ROUNDS_LATENCY: &str = "snarkos_bft_commit_rounds_latency_secs"; // <-- This one doesn't even make sense.
5455
pub const CONNECTED: &str = "snarkos_bft_connected_total";
5556
pub const CONNECTED_STAKE: &str = "snarkos_bft_connected_stake_as_percentage";
57+
pub const CONNECTED_STAKE_WITH_MATCHING_SHA: &str = "snarkos_bft_connected_stake_with_matching_sha_as_percentage";
5658
pub const CONNECTING: &str = "snarkos_bft_connecting_total";
5759
pub const LAST_STORED_ROUND: &str = "snarkos_bft_last_stored_round";
5860
pub const LEADERS_ELECTED: &str = "snarkos_bft_leaders_elected_total";

node/network/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ workspace = true
3737
[dependencies.serde]
3838
workspace = true
3939

40+
[dependencies.smol_str]
41+
workspace = true
42+
4043
[dependencies.snarkos-node-tcp]
4144
workspace = true
4245

node/network/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub use resolver::*;
2929

3030
use snarkvm::prelude::Network;
3131

32+
use smol_str::SmolStr;
3233
use std::{env::VarError, net::SocketAddr, str::FromStr};
3334
use tracing::*;
3435

@@ -113,3 +114,16 @@ pub fn log_repo_sha_comparison(peer_addr: SocketAddr, peer_sha: &Option<[u8; 40]
113114

114115
debug!("{ctx} Peer '{peer_addr}' uses snarkOS{sha_cmp}");
115116
}
117+
118+
pub fn shorten_snarkos_sha(sha: &Option<[u8; 40]>) -> SmolStr {
119+
if let Some(full_sha) = sha.as_ref().and_then(|s| str::from_utf8(s).ok()) {
120+
let end_idx = full_sha.char_indices()
121+
.nth(7) // GitHub commit SHA shorthand.
122+
.map(|(i, _)| i)
123+
.unwrap_or(full_sha.len()); // Can't really fail.
124+
125+
SmolStr::from(&full_sha[..end_idx])
126+
} else {
127+
"unknown snarkOS SHA".into()
128+
}
129+
}

node/network/src/peer.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ pub struct ConnectedPeer<N: Network> {
7272
pub node_type: NodeType,
7373
/// The message version of the peer.
7474
pub version: u32,
75+
/// The snarkOS commit hash of the peer.
76+
pub snarkos_sha: Option<[u8; 40]>,
7577
/// The latest block height known to be associated with the peer.
7678
pub last_height_seen: Option<u32>,
7779
/// The timestamp of the first message received from the peer.
@@ -105,13 +107,15 @@ impl<N: Network> Peer<N> {
105107
}
106108

107109
/// Promote a connecting peer to a fully connected one.
110+
#[allow(clippy::too_many_arguments)]
108111
pub fn upgrade_to_connected(
109112
&mut self,
110113
connected_addr: SocketAddr,
111114
listener_port: u16,
112115
aleo_address: Address<N>,
113116
node_type: NodeType,
114117
node_version: u32,
118+
snarkos_sha: Option<[u8; 40]>,
115119
connection_mode: ConnectionMode,
116120
) {
117121
let timestamp = Instant::now();
@@ -131,6 +135,7 @@ impl<N: Network> Peer<N> {
131135
node_type,
132136
trusted: self.is_trusted(),
133137
version: node_version,
138+
snarkos_sha,
134139
last_height_seen: None,
135140
first_seen: timestamp,
136141
last_seen: timestamp,

node/router/src/handshake.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ impl<N: Network> Router<N> {
143143
cr.address,
144144
cr.node_type,
145145
cr.version,
146+
cr.snarkos_sha,
146147
ConnectionMode::Router,
147148
);
148149
}

node/src/bootstrap_client/handshake.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl<N: Network> Handshake for BootstrapClient<N> {
118118

119119
if let Some(addr) = listener_addr {
120120
match handshake_result {
121-
Ok((peer_port, peer_aleo_addr, peer_node_type, peer_version, connection_mode)) => {
121+
Ok((peer_port, peer_aleo_addr, peer_node_type, peer_version, peer_snarkos_sha, connection_mode)) => {
122122
if let Some(peer) = self.peer_pool.write().get_mut(&addr) {
123123
// Due to only having a single Resolver, the BootstrapClient only adds an Aleo
124124
// address mapping for Gateway-mode connections, as it is only used there, and
@@ -133,6 +133,7 @@ impl<N: Network> Handshake for BootstrapClient<N> {
133133
peer_aleo_addr,
134134
peer_node_type,
135135
peer_version,
136+
peer_snarkos_sha,
136137
connection_mode,
137138
);
138139
}
@@ -160,24 +161,36 @@ impl<N: Network> BootstrapClient<N> {
160161
peer_addr: SocketAddr,
161162
listener_addr: &mut Option<SocketAddr>,
162163
stream: &'a mut TcpStream,
163-
) -> Result<(u16, Address<N>, NodeType, u32, ConnectionMode), ConnectError> {
164+
) -> Result<(u16, Address<N>, NodeType, u32, Option<[u8; 40]>, ConnectionMode), ConnectError> {
164165
// Construct the stream.
165166
let mut framed = Framed::new(stream, BootstrapClientCodec::<N>::handshake());
166167

167168
/* Step 1: Receive the challenge request. */
168169

169170
// Listen for the challenge request message, which can be either from a regular peer, or a validator.
170171
let peer_request = expect_handshake_msg!(HandshakeMessageKind::ChallengeRequest, framed, peer_addr);
171-
let (peer_port, peer_nonce, peer_aleo_addr, peer_node_type, peer_version, connection_mode) = match peer_request
172-
{
173-
MessageOrEvent::Message(Message::ChallengeRequest(ref msg)) => {
174-
(msg.listener_port, msg.nonce, msg.address, msg.node_type, msg.version, ConnectionMode::Router)
175-
}
176-
MessageOrEvent::Event(Event::ChallengeRequest(ref msg)) => {
177-
(msg.listener_port, msg.nonce, msg.address, NodeType::Validator, msg.version, ConnectionMode::Gateway)
178-
}
179-
_ => unreachable!(),
180-
};
172+
let (peer_port, peer_nonce, peer_aleo_addr, peer_node_type, peer_version, peer_snarkos_sha, connection_mode) =
173+
match peer_request {
174+
MessageOrEvent::Message(Message::ChallengeRequest(ref msg)) => (
175+
msg.listener_port,
176+
msg.nonce,
177+
msg.address,
178+
msg.node_type,
179+
msg.version,
180+
msg.snarkos_sha,
181+
ConnectionMode::Router,
182+
),
183+
MessageOrEvent::Event(Event::ChallengeRequest(ref msg)) => (
184+
msg.listener_port,
185+
msg.nonce,
186+
msg.address,
187+
NodeType::Validator,
188+
msg.version,
189+
msg.snarkos_sha,
190+
ConnectionMode::Gateway,
191+
),
192+
_ => unreachable!(),
193+
};
181194
debug!("Handshake mode: {connection_mode:?}");
182195

183196
// Obtain the peer's listening address.
@@ -261,7 +274,7 @@ impl<N: Network> BootstrapClient<N> {
261274
return Err(ConnectError::application(DisconnectReason::InvalidChallengeResponse));
262275
}
263276

264-
Ok((peer_port, peer_aleo_addr, peer_node_type, peer_version, connection_mode))
277+
Ok((peer_port, peer_aleo_addr, peer_node_type, peer_version, peer_snarkos_sha, connection_mode))
265278
}
266279

267280
async fn verify_challenge_request(

0 commit comments

Comments
 (0)