From 0941d133a838ac5c2ca2953780ff537cccfd2d99 Mon Sep 17 00:00:00 2001 From: behzad nouri Date: Tue, 17 Jan 2023 22:21:14 +0000 Subject: [PATCH] adds new solana_version::Version with ClientId (#29649) --- gossip/src/cluster_info.rs | 4 +- gossip/src/crds_value.rs | 8 +-- sdk/src/lib.rs | 6 +-- version/src/legacy.rs | 80 +++++++++++++++++++++++++++ version/src/lib.rs | 108 +++++++++++++++++++++++++------------ 5 files changed, 163 insertions(+), 43 deletions(-) create mode 100644 version/src/legacy.rs diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs index 8f926eba31..9da25a32bb 100644 --- a/gossip/src/cluster_info.rs +++ b/gossip/src/cluster_info.rs @@ -267,7 +267,7 @@ pub fn make_accounts_hashes_message( pub(crate) type Ping = ping_pong::Ping<[u8; GOSSIP_PING_TOKEN_SIZE]>; // TODO These messages should go through the gpu pipeline for spam filtering -#[frozen_abi(digest = "Hsj6a2bmzxno1RUcSM1gzHAg2zxgw15E3feb2SimieBA")] +#[frozen_abi(digest = "Aui5aMV3SK41tRQN14sgCMK3qp6r9dboLXNAHEBKFzii")] #[derive(Serialize, Deserialize, Debug, AbiEnumVisitor, AbiExample)] #[allow(clippy::large_enum_variant)] pub(crate) enum Protocol { @@ -1240,7 +1240,7 @@ impl ClusterInfo { .collect() } - pub fn get_node_version(&self, pubkey: &Pubkey) -> Option { + pub fn get_node_version(&self, pubkey: &Pubkey) -> Option { let gossip_crds = self.gossip.crds.read().unwrap(); if let Some(version) = gossip_crds.get::<&Version>(*pubkey) { return Some(version.version.clone()); diff --git a/gossip/src/crds_value.rs b/gossip/src/crds_value.rs index 3cdaa23abc..2c2c2802ac 100644 --- a/gossip/src/crds_value.rs +++ b/gossip/src/crds_value.rs @@ -359,7 +359,7 @@ impl<'de> Deserialize<'de> for Vote { pub struct LegacyVersion { pub from: Pubkey, pub wallclock: u64, - pub version: solana_version::LegacyVersion, + pub version: solana_version::LegacyVersion1, } impl Sanitize for LegacyVersion { @@ -374,7 +374,7 @@ impl Sanitize for LegacyVersion { pub struct Version { pub from: Pubkey, pub wallclock: u64, - pub version: solana_version::Version, + pub version: solana_version::LegacyVersion2, } impl Sanitize for Version { @@ -390,7 +390,7 @@ impl Version { Self { from, wallclock: timestamp(), - version: solana_version::Version::default(), + version: solana_version::LegacyVersion2::default(), } } @@ -399,7 +399,7 @@ impl Version { Self { from: pubkey.unwrap_or_else(pubkey::new_rand), wallclock: new_rand_timestamp(rng), - version: solana_version::Version { + version: solana_version::LegacyVersion2 { major: rng.gen(), minor: rng.gen(), patch: rng.gen(), diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index aa4ab7f8a7..d2e743e30c 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -49,9 +49,9 @@ pub use solana_program::{ decode_error, ed25519_program, epoch_schedule, fee_calculator, impl_sysvar_get, incinerator, instruction, keccak, lamports, loader_instruction, loader_upgradeable_instruction, message, msg, native_token, nonce, program, program_error, program_memory, program_option, program_pack, - rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serialize_utils, short_vec, - slot_hashes, slot_history, stake, stake_history, syscalls, system_instruction, system_program, - sysvar, unchecked_div_by_const, vote, wasm_bindgen, + rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serde_varint, serialize_utils, + short_vec, slot_hashes, slot_history, stake, stake_history, syscalls, system_instruction, + system_program, sysvar, unchecked_div_by_const, vote, wasm_bindgen, }; pub mod account; diff --git a/version/src/legacy.rs b/version/src/legacy.rs new file mode 100644 index 0000000000..658ffd5e63 --- /dev/null +++ b/version/src/legacy.rs @@ -0,0 +1,80 @@ +use { + crate::compute_commit, + serde_derive::{Deserialize, Serialize}, + solana_sdk::sanitize::Sanitize, + std::{convert::TryInto, fmt}, +}; + +// Older version structure used earlier 1.3.x releases +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, AbiExample)] +pub struct LegacyVersion1 { + major: u16, + minor: u16, + patch: u16, + commit: Option, // first 4 bytes of the sha1 commit hash +} + +impl Sanitize for LegacyVersion1 {} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, AbiExample)] +pub struct LegacyVersion2 { + pub major: u16, + pub minor: u16, + pub patch: u16, + pub commit: Option, // first 4 bytes of the sha1 commit hash + pub feature_set: u32, // first 4 bytes of the FeatureSet identifier +} + +impl From for LegacyVersion2 { + fn from(legacy_version: LegacyVersion1) -> Self { + Self { + major: legacy_version.major, + minor: legacy_version.minor, + patch: legacy_version.patch, + commit: legacy_version.commit, + feature_set: 0, + } + } +} + +impl Default for LegacyVersion2 { + fn default() -> Self { + let feature_set = u32::from_le_bytes( + solana_sdk::feature_set::ID.as_ref()[..4] + .try_into() + .unwrap(), + ); + 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: compute_commit(option_env!("CI_COMMIT")), + feature_set, + } + } +} + +impl fmt::Display for LegacyVersion2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch,) + } +} + +impl fmt::Debug for LegacyVersion2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}.{}.{} (src:{}; feat:{})", + self.major, + self.minor, + self.patch, + match self.commit { + None => "devbuild".to_string(), + Some(commit) => format!("{commit:08x}"), + }, + self.feature_set, + ) + } +} + +impl Sanitize for LegacyVersion2 {} diff --git a/version/src/lib.rs b/version/src/lib.rs index 0c862d8864..edeca08c96 100644 --- a/version/src/lib.rs +++ b/version/src/lib.rs @@ -1,59 +1,52 @@ #![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(min_specialization))] extern crate serde_derive; +pub use self::legacy::{LegacyVersion1, LegacyVersion2}; use { serde_derive::{Deserialize, Serialize}, - solana_sdk::sanitize::Sanitize, + solana_sdk::{sanitize::Sanitize, serde_varint}, std::{convert::TryInto, fmt}, }; #[macro_use] extern crate solana_frozen_abi_macro; -// Older version structure used earlier 1.3.x releases -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, AbiExample)] -pub struct LegacyVersion { - major: u16, - minor: u16, - patch: u16, - commit: Option, // first 4 bytes of the sha1 commit hash -} +mod legacy; -impl Sanitize for LegacyVersion {} +#[derive(Debug, Eq, PartialEq)] +enum ClientId { + SolanaLabs, + JitoLabs, + Firedancer, + // If new variants are added, update From and TryFrom. + Unknown(u16), +} #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, AbiExample)] pub struct Version { + #[serde(with = "serde_varint")] pub major: u16, + #[serde(with = "serde_varint")] pub minor: u16, + #[serde(with = "serde_varint")] pub patch: u16, - pub commit: Option, // first 4 bytes of the sha1 commit hash - pub feature_set: u32, // first 4 bytes of the FeatureSet identifier + pub commit: u32, // first 4 bytes of the sha1 commit hash + pub feature_set: u32, // first 4 bytes of the FeatureSet identifier + #[serde(with = "serde_varint")] + client: u16, } impl Version { pub fn as_semver_version(&self) -> semver::Version { semver::Version::new(self.major as u64, self.minor as u64, self.patch as u64) } -} -impl From for Version { - fn from(legacy_version: LegacyVersion) -> Self { - Self { - major: legacy_version.major, - minor: legacy_version.minor, - patch: legacy_version.patch, - commit: legacy_version.commit, - feature_set: 0, - } + fn client(&self) -> ClientId { + ClientId::from(self.client) } } fn compute_commit(sha1: Option<&'static str>) -> Option { - let sha1 = sha1?; - if sha1.len() < 8 { - None - } else { - u32::from_str_radix(&sha1[..8], 16).ok() - } + u32::from_str_radix(sha1?.get(..8)?, /*radix:*/ 16).ok() } impl Default for Version { @@ -67,8 +60,10 @@ impl Default for Version { 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: compute_commit(option_env!("CI_COMMIT")), + commit: compute_commit(option_env!("CI_COMMIT")).unwrap_or_default(), feature_set, + // Other client implementations need to modify this line. + client: u16::try_from(ClientId::SolanaLabs).unwrap(), } } } @@ -83,21 +78,44 @@ impl fmt::Debug for Version { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "{}.{}.{} (src:{}; feat:{})", + "{}.{}.{} (src:{:08x}; feat:{}, client:{:?})", self.major, self.minor, self.patch, - match self.commit { - None => "devbuild".to_string(), - Some(commit) => format!("{commit:08x}"), - }, + self.commit, self.feature_set, + self.client(), ) } } impl Sanitize for Version {} +impl From for ClientId { + fn from(client: u16) -> Self { + match client { + 0u16 => Self::SolanaLabs, + 1u16 => Self::JitoLabs, + 2u16 => Self::Firedancer, + _ => Self::Unknown(client), + } + } +} + +impl TryFrom for u16 { + type Error = String; + + fn try_from(client: ClientId) -> Result { + match client { + ClientId::SolanaLabs => Ok(0u16), + ClientId::JitoLabs => Ok(1u16), + ClientId::Firedancer => Ok(2u16), + ClientId::Unknown(client @ 0u16..=2u16) => Err(format!("Invalid client: {client}")), + ClientId::Unknown(client) => Ok(client), + } + } +} + #[macro_export] macro_rules! semver { () => { @@ -123,4 +141,26 @@ mod test { assert_eq!(compute_commit(Some("HEAD")), None); assert_eq!(compute_commit(Some("garbagein")), None); } + + #[test] + fn test_client_id() { + assert_eq!(ClientId::from(0u16), ClientId::SolanaLabs); + assert_eq!(ClientId::from(1u16), ClientId::JitoLabs); + assert_eq!(ClientId::from(2u16), ClientId::Firedancer); + for client in 3u16..=u16::MAX { + assert_eq!(ClientId::from(client), ClientId::Unknown(client)); + } + assert_eq!(u16::try_from(ClientId::SolanaLabs), Ok(0u16)); + assert_eq!(u16::try_from(ClientId::JitoLabs), Ok(1u16)); + assert_eq!(u16::try_from(ClientId::Firedancer), Ok(2u16)); + for client in 0..=2u16 { + assert_eq!( + u16::try_from(ClientId::Unknown(client)), + Err(format!("Invalid client: {client}")) + ); + } + for client in 3u16..=u16::MAX { + assert_eq!(u16::try_from(ClientId::Unknown(client)), Ok(client)); + } + } }