Advertise node version in gossip (#9986)

automerge
This commit is contained in:
Michael Vines
2020-05-11 17:45:19 -07:00
committed by GitHub
parent eb11db3e3e
commit 8d7e90e9b8
10 changed files with 166 additions and 20 deletions

18
Cargo.lock generated
View File

@ -2711,9 +2711,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.106" version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -2729,9 +2729,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.106" version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
dependencies = [ dependencies = [
"proc-macro2 1.0.12", "proc-macro2 1.0.12",
"quote 1.0.1", "quote 1.0.1",
@ -3290,6 +3290,7 @@ dependencies = [
"solana-streamer", "solana-streamer",
"solana-sys-tuner", "solana-sys-tuner",
"solana-transaction-status", "solana-transaction-status",
"solana-version",
"solana-vote-program", "solana-vote-program",
"solana-vote-signer", "solana-vote-signer",
"systemstat", "systemstat",
@ -4007,6 +4008,15 @@ dependencies = [
"solana-vote-signer", "solana-vote-signer",
] ]
[[package]]
name = "solana-version"
version = "1.1.11"
dependencies = [
"serde",
"serde_derive",
"solana-sdk",
]
[[package]] [[package]]
name = "solana-vest-program" name = "solana-vest-program"
version = "1.1.11" version = "1.1.11"

View File

@ -58,6 +58,7 @@ members = [
"transaction-status", "transaction-status",
"upload-perf", "upload-perf",
"net-utils", "net-utils",
"version",
"vote-signer", "vote-signer",
"cli", "cli",
"rayon-threadlimit", "rayon-threadlimit",

View File

@ -61,6 +61,7 @@ solana-sdk = { path = "../sdk", version = "1.1.11" }
solana-stake-program = { path = "../programs/stake", version = "1.1.11" } solana-stake-program = { path = "../programs/stake", version = "1.1.11" }
solana-storage-program = { path = "../programs/storage", version = "1.1.11" } solana-storage-program = { path = "../programs/storage", version = "1.1.11" }
solana-streamer = { path = "../streamer", version = "1.1.11" } solana-streamer = { path = "../streamer", version = "1.1.11" }
solana-version = { path = "../version", version = "1.1.11" }
solana-vote-program = { path = "../programs/vote", version = "1.1.11" } solana-vote-program = { path = "../programs/vote", version = "1.1.11" }
solana-vote-signer = { path = "../vote-signer", version = "1.1.11" } solana-vote-signer = { path = "../vote-signer", version = "1.1.11" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.1.11" } solana-sys-tuner = { path = "../sys-tuner", version = "1.1.11" }

View File

@ -18,8 +18,8 @@ use crate::{
crds_gossip_error::CrdsGossipError, crds_gossip_error::CrdsGossipError,
crds_gossip_pull::{CrdsFilter, CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS}, crds_gossip_pull::{CrdsFilter, CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS},
crds_value::{ crds_value::{
self, CrdsData, CrdsValue, CrdsValueLabel, EpochSlotsIndex, LowestSlot, SnapshotHash, Vote, self, CrdsData, CrdsValue, CrdsValueLabel, EpochSlotsIndex, LowestSlot, SnapshotHash,
MAX_WALLCLOCK, Version, Vote, MAX_WALLCLOCK,
}, },
epoch_slots::EpochSlots, epoch_slots::EpochSlots,
result::{Error, Result}, result::{Error, Result},
@ -378,6 +378,7 @@ impl ClusterInfo {
archivers += 1; archivers += 1;
} }
let node_version = self.get_node_version(&node.id);
if my_shred_version != 0 && (node.shred_version != 0 && node.shred_version != my_shred_version) { if my_shred_version != 0 && (node.shred_version != 0 && node.shred_version != my_shred_version) {
different_shred_nodes += 1; different_shred_nodes += 1;
None None
@ -393,10 +394,9 @@ impl ClusterInfo {
"none".to_string() "none".to_string()
} }
} }
let ip_addr = node.gossip.ip(); let ip_addr = node.gossip.ip();
Some(format!( Some(format!(
"{:15} {:2}| {:5} | {:44} | {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {}\n", "{:15} {:2}| {:5} | {:44} |{:^15}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {}\n",
if ContactInfo::is_valid_address(&node.gossip) { if ContactInfo::is_valid_address(&node.gossip) {
ip_addr.to_string() ip_addr.to_string()
} else { } else {
@ -405,6 +405,11 @@ impl ClusterInfo {
if node.id == my_pubkey { "me" } else { "" }.to_string(), if node.id == my_pubkey { "me" } else { "" }.to_string(),
now.saturating_sub(last_updated), now.saturating_sub(last_updated),
node.id.to_string(), node.id.to_string(),
if let Some(node_version) = node_version {
node_version.to_string()
} else {
"-".to_string()
},
addr_to_string(&ip_addr, &node.gossip), addr_to_string(&ip_addr, &node.gossip),
addr_to_string(&ip_addr, &node.tpu), addr_to_string(&ip_addr, &node.tpu),
addr_to_string(&ip_addr, &node.tpu_forwards), addr_to_string(&ip_addr, &node.tpu_forwards),
@ -412,7 +417,6 @@ impl ClusterInfo {
addr_to_string(&ip_addr, &node.tvu_forwards), addr_to_string(&ip_addr, &node.tvu_forwards),
addr_to_string(&ip_addr, &node.repair), addr_to_string(&ip_addr, &node.repair),
addr_to_string(&ip_addr, &node.serve_repair), addr_to_string(&ip_addr, &node.serve_repair),
addr_to_string(&ip_addr, &node.storage_addr),
addr_to_string(&ip_addr, &node.rpc), addr_to_string(&ip_addr, &node.rpc),
addr_to_string(&ip_addr, &node.rpc_pubsub), addr_to_string(&ip_addr, &node.rpc_pubsub),
node.shred_version, node.shred_version,
@ -423,9 +427,9 @@ impl ClusterInfo {
format!( format!(
"IP Address |Age(ms)| Node identifier \ "IP Address |Age(ms)| Node identifier \
|Gossip| TPU |TPUfwd| TVU |TVUfwd|Repair|ServeR|Storag| RPC |PubSub|ShredVer\n\ | Version |Gossip| TPU |TPUfwd| TVU |TVUfwd|Repair|ServeR| RPC |PubSub|ShredVer\n\
------------------+-------+----------------------------------------------+\ ------------------+-------+----------------------------------------------+---------------+\
------+------+------+------+------+------+------+------+------+------+--------\n\ ------+------+------+------+------+------+------+------+------+--------\n\
{}\ {}\
Nodes: {}{}{}{}", Nodes: {}{}{}{}",
nodes.join(""), nodes.join(""),
@ -440,7 +444,7 @@ impl ClusterInfo {
} else { } else {
"".to_string() "".to_string()
}, },
if spy_nodes > 0 { if different_shred_nodes > 0 {
format!( format!(
"\nNodes with different shred version: {}", "\nNodes with different shred version: {}",
different_shred_nodes different_shred_nodes
@ -703,6 +707,18 @@ impl ClusterInfo {
(vec, max) (vec, max)
} }
pub fn get_node_version(&self, pubkey: &Pubkey) -> Option<solana_version::Version> {
self.gossip
.read()
.unwrap()
.crds
.table
.get(&CrdsValueLabel::Version(*pubkey))
.map(|x| x.value.version())
.flatten()
.map(|version| version.version.clone())
}
/// all validators that have a valid rpc port regardless of `shred_version`. /// all validators that have a valid rpc port regardless of `shred_version`.
pub fn all_rpc_peers(&self) -> Vec<ContactInfo> { pub fn all_rpc_peers(&self) -> Vec<ContactInfo> {
self.gossip self.gossip
@ -1313,6 +1329,9 @@ impl ClusterInfo {
let mut last_contact_info_trace = timestamp(); let mut last_contact_info_trace = timestamp();
let mut adopt_shred_version = obj.my_shred_version() == 0; let mut adopt_shred_version = obj.my_shred_version() == 0;
let recycler = PacketsRecycler::default(); let recycler = PacketsRecycler::default();
let message = CrdsData::Version(Version::new(obj.id()));
obj.push_message(CrdsValue::new_signed(message, &obj.keypair));
loop { loop {
let start = timestamp(); let start = timestamp();
thread_mem_usage::datapoint("solana-gossip"); thread_mem_usage::datapoint("solana-gossip");

View File

@ -75,6 +75,7 @@ pub enum CrdsData {
SnapshotHashes(SnapshotHash), SnapshotHashes(SnapshotHash),
AccountsHashes(SnapshotHash), AccountsHashes(SnapshotHash),
EpochSlots(EpochSlotsIndex, EpochSlots), EpochSlots(EpochSlotsIndex, EpochSlots),
Version(Version),
} }
impl Sanitize for CrdsData { impl Sanitize for CrdsData {
@ -101,6 +102,7 @@ impl Sanitize for CrdsData {
} }
val.sanitize() val.sanitize()
} }
CrdsData::Version(version) => version.sanitize(),
} }
} }
} }
@ -206,6 +208,33 @@ impl Vote {
} }
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Version {
pub from: Pubkey,
pub wallclock: u64,
pub version: solana_version::Version,
}
impl Sanitize for Version {
fn sanitize(&self) -> Result<(), SanitizeError> {
if self.wallclock >= MAX_WALLCLOCK {
return Err(SanitizeError::ValueOutOfBounds);
}
self.from.sanitize()?;
self.version.sanitize()
}
}
impl Version {
pub fn new(from: Pubkey) -> Self {
Self {
from,
wallclock: timestamp(),
version: solana_version::Version::default(),
}
}
}
/// Type of the replicated value /// Type of the replicated value
/// These are labels for values in a record that is associated with `Pubkey` /// These are labels for values in a record that is associated with `Pubkey`
#[derive(PartialEq, Hash, Eq, Clone, Debug)] #[derive(PartialEq, Hash, Eq, Clone, Debug)]
@ -216,6 +245,7 @@ pub enum CrdsValueLabel {
SnapshotHashes(Pubkey), SnapshotHashes(Pubkey),
EpochSlots(EpochSlotsIndex, Pubkey), EpochSlots(EpochSlotsIndex, Pubkey),
AccountsHashes(Pubkey), AccountsHashes(Pubkey),
Version(Pubkey),
} }
impl fmt::Display for CrdsValueLabel { impl fmt::Display for CrdsValueLabel {
@ -227,6 +257,7 @@ impl fmt::Display for CrdsValueLabel {
CrdsValueLabel::SnapshotHashes(_) => write!(f, "SnapshotHash({})", self.pubkey()), CrdsValueLabel::SnapshotHashes(_) => write!(f, "SnapshotHash({})", self.pubkey()),
CrdsValueLabel::EpochSlots(ix, _) => write!(f, "EpochSlots({}, {})", ix, self.pubkey()), CrdsValueLabel::EpochSlots(ix, _) => write!(f, "EpochSlots({}, {})", ix, self.pubkey()),
CrdsValueLabel::AccountsHashes(_) => write!(f, "AccountsHashes({})", self.pubkey()), CrdsValueLabel::AccountsHashes(_) => write!(f, "AccountsHashes({})", self.pubkey()),
CrdsValueLabel::Version(_) => write!(f, "Version({})", self.pubkey()),
} }
} }
} }
@ -240,6 +271,7 @@ impl CrdsValueLabel {
CrdsValueLabel::SnapshotHashes(p) => *p, CrdsValueLabel::SnapshotHashes(p) => *p,
CrdsValueLabel::EpochSlots(_, p) => *p, CrdsValueLabel::EpochSlots(_, p) => *p,
CrdsValueLabel::AccountsHashes(p) => *p, CrdsValueLabel::AccountsHashes(p) => *p,
CrdsValueLabel::Version(p) => *p,
} }
} }
} }
@ -257,7 +289,7 @@ impl CrdsValue {
value.sign(keypair); value.sign(keypair);
value value
} }
/// Totally unsecure unverfiable wallclock of the node that generated this message /// Totally unsecure unverifiable wallclock of the node that generated this message
/// Latest wallclock is always picked. /// Latest wallclock is always picked.
/// This is used to time out push messages. /// This is used to time out push messages.
pub fn wallclock(&self) -> u64 { pub fn wallclock(&self) -> u64 {
@ -268,6 +300,7 @@ impl CrdsValue {
CrdsData::SnapshotHashes(hash) => hash.wallclock, CrdsData::SnapshotHashes(hash) => hash.wallclock,
CrdsData::AccountsHashes(hash) => hash.wallclock, CrdsData::AccountsHashes(hash) => hash.wallclock,
CrdsData::EpochSlots(_, p) => p.wallclock, CrdsData::EpochSlots(_, p) => p.wallclock,
CrdsData::Version(version) => version.wallclock,
} }
} }
pub fn pubkey(&self) -> Pubkey { pub fn pubkey(&self) -> Pubkey {
@ -278,6 +311,7 @@ impl CrdsValue {
CrdsData::SnapshotHashes(hash) => hash.from, CrdsData::SnapshotHashes(hash) => hash.from,
CrdsData::AccountsHashes(hash) => hash.from, CrdsData::AccountsHashes(hash) => hash.from,
CrdsData::EpochSlots(_, p) => p.from, CrdsData::EpochSlots(_, p) => p.from,
CrdsData::Version(version) => version.from,
} }
} }
pub fn label(&self) -> CrdsValueLabel { pub fn label(&self) -> CrdsValueLabel {
@ -288,6 +322,7 @@ impl CrdsValue {
CrdsData::SnapshotHashes(_) => CrdsValueLabel::SnapshotHashes(self.pubkey()), CrdsData::SnapshotHashes(_) => CrdsValueLabel::SnapshotHashes(self.pubkey()),
CrdsData::AccountsHashes(_) => CrdsValueLabel::AccountsHashes(self.pubkey()), CrdsData::AccountsHashes(_) => CrdsValueLabel::AccountsHashes(self.pubkey()),
CrdsData::EpochSlots(ix, _) => CrdsValueLabel::EpochSlots(*ix, self.pubkey()), CrdsData::EpochSlots(ix, _) => CrdsValueLabel::EpochSlots(*ix, self.pubkey()),
CrdsData::Version(_) => CrdsValueLabel::Version(self.pubkey()),
} }
} }
pub fn contact_info(&self) -> Option<&ContactInfo> { pub fn contact_info(&self) -> Option<&ContactInfo> {
@ -338,6 +373,13 @@ impl CrdsValue {
} }
} }
pub fn version(&self) -> Option<&Version> {
match &self.data {
CrdsData::Version(version) => Some(version),
_ => None,
}
}
/// Return all the possible labels for a record identified by Pubkey. /// Return all the possible labels for a record identified by Pubkey.
pub fn record_labels(key: &Pubkey) -> Vec<CrdsValueLabel> { pub fn record_labels(key: &Pubkey) -> Vec<CrdsValueLabel> {
let mut labels = vec![ let mut labels = vec![
@ -345,6 +387,7 @@ impl CrdsValue {
CrdsValueLabel::LowestSlot(*key), CrdsValueLabel::LowestSlot(*key),
CrdsValueLabel::SnapshotHashes(*key), CrdsValueLabel::SnapshotHashes(*key),
CrdsValueLabel::AccountsHashes(*key), CrdsValueLabel::AccountsHashes(*key),
CrdsValueLabel::Version(*key),
]; ];
labels.extend((0..MAX_VOTES).map(|ix| CrdsValueLabel::Vote(ix, *key))); labels.extend((0..MAX_VOTES).map(|ix| CrdsValueLabel::Vote(ix, *key)));
labels.extend((0..MAX_EPOCH_SLOTS).map(|ix| CrdsValueLabel::EpochSlots(ix, *key))); labels.extend((0..MAX_EPOCH_SLOTS).map(|ix| CrdsValueLabel::EpochSlots(ix, *key)));
@ -395,7 +438,7 @@ mod test {
#[test] #[test]
fn test_labels() { fn test_labels() {
let mut hits = [false; 4 + MAX_VOTES as usize + MAX_EPOCH_SLOTS as usize]; let mut hits = [false; 5 + MAX_VOTES as usize + MAX_EPOCH_SLOTS as usize];
// this method should cover all the possible labels // this method should cover all the possible labels
for v in &CrdsValue::record_labels(&Pubkey::default()) { for v in &CrdsValue::record_labels(&Pubkey::default()) {
match v { match v {
@ -403,9 +446,10 @@ mod test {
CrdsValueLabel::LowestSlot(_) => hits[1] = true, CrdsValueLabel::LowestSlot(_) => hits[1] = true,
CrdsValueLabel::SnapshotHashes(_) => hits[2] = true, CrdsValueLabel::SnapshotHashes(_) => hits[2] = true,
CrdsValueLabel::AccountsHashes(_) => hits[3] = true, CrdsValueLabel::AccountsHashes(_) => hits[3] = true,
CrdsValueLabel::Vote(ix, _) => hits[*ix as usize + 4] = true, CrdsValueLabel::Version(_) => hits[4] = true,
CrdsValueLabel::Vote(ix, _) => hits[*ix as usize + 5] = true,
CrdsValueLabel::EpochSlots(ix, _) => { CrdsValueLabel::EpochSlots(ix, _) => {
hits[*ix as usize + MAX_VOTES as usize + 4] = true hits[*ix as usize + MAX_VOTES as usize + 5] = true
} }
} }
} }

View File

@ -1474,7 +1474,7 @@ impl RpcSol for RpcSolImpl {
fn get_version(&self, _: Self::Metadata) -> Result<RpcVersionInfo> { fn get_version(&self, _: Self::Metadata) -> Result<RpcVersionInfo> {
Ok(RpcVersionInfo { Ok(RpcVersionInfo {
solana_core: solana_clap_utils::version!().to_string(), solana_core: solana_version::Version::default().to_string(),
}) })
} }
@ -2637,7 +2637,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"solana-core": solana_clap_utils::version!().to_string() "solana-core": solana_version::version!().to_string()
}, },
"id": 1 "id": 1
}); });

View File

@ -46,7 +46,7 @@ fn test_rpc_client() {
assert_eq!( assert_eq!(
client.get_version().unwrap().solana_core, client.get_version().unwrap().solana_core,
solana_clap_utils::version!() solana_version::version!()
); );
assert!(client.get_account(&bob_pubkey).is_err()); assert!(client.get_account(&bob_pubkey).is_err());

2
version/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target/
/farf/

20
version/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "solana-version"
version = "1.1.11"
description = "Solana Version"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
serde = "1.0.105"
serde_derive = "1.0.103"
solana-sdk = { path = "../sdk", version = "1.1.11" }
[lib]
name = "solana_version"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

49
version/src/lib.rs Normal file
View File

@ -0,0 +1,49 @@
extern crate serde_derive;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::sanitize::Sanitize;
use std::fmt;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Version {
major: u16,
minor: u16,
patch: u16,
commit: Option<u32>, // first 4 bytes of the sha1 commit hash
}
impl Default for Version {
fn default() -> Self {
Self {
major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
commit: option_env!("CI_COMMIT")
.map(|sha1| u32::from_str_radix(&sha1[..8], 16).unwrap()),
}
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}.{}.{} {}",
self.major,
self.minor,
self.patch,
match self.commit {
None => "devbuild".to_string(),
Some(commit) => format!("{:08x}", commit),
}
)
}
}
impl Sanitize for Version {}
#[macro_export]
macro_rules! version {
() => {
&*format!("{}", $crate::Version::default())
};
}