diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs index cc539f5abe..56b75010f9 100644 --- a/gossip/src/cluster_info.rs +++ b/gossip/src/cluster_info.rs @@ -268,7 +268,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 = "7a2P1GeQjyqCHMyBrhNPTKfPfG4iv32vki7XHahoN55z")] +#[frozen_abi(digest = "ogEqvffeEkPpojAaSiUbCv2HdJcdXDQ1ykgYyvKvLo2")] #[derive(Serialize, Deserialize, Debug, AbiEnumVisitor, AbiExample)] #[allow(clippy::large_enum_variant)] pub(crate) enum Protocol { @@ -395,6 +395,7 @@ fn retain_staked(values: &mut Vec, stakes: &HashMap) { CrdsData::LowestSlot(_, _) | CrdsData::LegacyVersion(_) | CrdsData::DuplicateShred(_, _) + | CrdsData::RestartHeaviestFork(_) | CrdsData::RestartLastVotedForkSlots(_) => { let stake = stakes.get(&value.pubkey()).copied(); stake.unwrap_or_default() >= MIN_STAKE_FOR_GOSSIP diff --git a/gossip/src/cluster_info_metrics.rs b/gossip/src/cluster_info_metrics.rs index fbb7365387..0e474d3cf5 100644 --- a/gossip/src/cluster_info_metrics.rs +++ b/gossip/src/cluster_info_metrics.rs @@ -637,6 +637,8 @@ pub(crate) fn submit_gossip_stats( crds_stats.pull.counts[12], i64 ), + ("RestartHeaviestFork-push", crds_stats.push.counts[13], i64), + ("RestartHeaviestFork-pull", crds_stats.pull.counts[13], i64), ( "all-push", crds_stats.push.counts.iter().sum::(), @@ -684,6 +686,8 @@ pub(crate) fn submit_gossip_stats( crds_stats.pull.fails[12], i64 ), + ("RestartHeaviestFork-push", crds_stats.push.fails[13], i64), + ("RestartHeaviestFork-pull", crds_stats.pull.fails[13], i64), ("all-push", crds_stats.push.fails.iter().sum::(), i64), ("all-pull", crds_stats.pull.fails.iter().sum::(), i64), ); diff --git a/gossip/src/crds.rs b/gossip/src/crds.rs index 5ce3cf5ec5..719bc13847 100644 --- a/gossip/src/crds.rs +++ b/gossip/src/crds.rs @@ -103,7 +103,7 @@ pub enum GossipRoute<'a> { PushMessage(/*from:*/ &'a Pubkey), } -type CrdsCountsArray = [usize; 13]; +type CrdsCountsArray = [usize; 14]; pub(crate) struct CrdsDataStats { pub(crate) counts: CrdsCountsArray, @@ -722,6 +722,7 @@ impl CrdsDataStats { CrdsData::SnapshotHashes(_) => 10, CrdsData::ContactInfo(_) => 11, CrdsData::RestartLastVotedForkSlots(_) => 12, + CrdsData::RestartHeaviestFork(_) => 13, // Update CrdsCountsArray if new items are added here. } } diff --git a/gossip/src/crds_value.rs b/gossip/src/crds_value.rs index 61d916e76b..ad6422fc2e 100644 --- a/gossip/src/crds_value.rs +++ b/gossip/src/crds_value.rs @@ -6,7 +6,7 @@ use { duplicate_shred::{DuplicateShred, DuplicateShredIndex, MAX_DUPLICATE_SHREDS}, epoch_slots::EpochSlots, legacy_contact_info::LegacyContactInfo, - restart_crds_values::RestartLastVotedForkSlots, + restart_crds_values::{RestartHeaviestFork, RestartLastVotedForkSlots}, }, bincode::{serialize, serialized_size}, rand::{CryptoRng, Rng}, @@ -96,6 +96,7 @@ pub enum CrdsData { SnapshotHashes(SnapshotHashes), ContactInfo(ContactInfo), RestartLastVotedForkSlots(RestartLastVotedForkSlots), + RestartHeaviestFork(RestartHeaviestFork), } impl Sanitize for CrdsData { @@ -135,6 +136,7 @@ impl Sanitize for CrdsData { CrdsData::SnapshotHashes(val) => val.sanitize(), CrdsData::ContactInfo(node) => node.sanitize(), CrdsData::RestartLastVotedForkSlots(slots) => slots.sanitize(), + CrdsData::RestartHeaviestFork(fork) => fork.sanitize(), } } } @@ -148,7 +150,7 @@ pub(crate) fn new_rand_timestamp(rng: &mut R) -> u64 { impl CrdsData { /// New random CrdsData for tests and benchmarks. fn new_rand(rng: &mut R, pubkey: Option) -> CrdsData { - let kind = rng.gen_range(0..8); + let kind = rng.gen_range(0..9); // TODO: Implement other kinds of CrdsData here. // TODO: Assign ranges to each arm proportional to their frequency in // the mainnet crds table. @@ -163,6 +165,7 @@ impl CrdsData { 6 => CrdsData::RestartLastVotedForkSlots(RestartLastVotedForkSlots::new_rand( rng, pubkey, )), + 7 => CrdsData::RestartHeaviestFork(RestartHeaviestFork::new_rand(rng, pubkey)), _ => CrdsData::EpochSlots( rng.gen_range(0..MAX_EPOCH_SLOTS), EpochSlots::new_rand(rng, pubkey), @@ -508,6 +511,7 @@ pub enum CrdsValueLabel { SnapshotHashes(Pubkey), ContactInfo(Pubkey), RestartLastVotedForkSlots(Pubkey), + RestartHeaviestFork(Pubkey), } impl fmt::Display for CrdsValueLabel { @@ -534,6 +538,9 @@ impl fmt::Display for CrdsValueLabel { CrdsValueLabel::RestartLastVotedForkSlots(_) => { write!(f, "RestartLastVotedForkSlots({})", self.pubkey()) } + CrdsValueLabel::RestartHeaviestFork(_) => { + write!(f, "RestartHeaviestFork({})", self.pubkey()) + } } } } @@ -554,6 +561,7 @@ impl CrdsValueLabel { CrdsValueLabel::SnapshotHashes(p) => *p, CrdsValueLabel::ContactInfo(pubkey) => *pubkey, CrdsValueLabel::RestartLastVotedForkSlots(p) => *p, + CrdsValueLabel::RestartHeaviestFork(p) => *p, } } } @@ -605,6 +613,7 @@ impl CrdsValue { CrdsData::SnapshotHashes(hash) => hash.wallclock, CrdsData::ContactInfo(node) => node.wallclock(), CrdsData::RestartLastVotedForkSlots(slots) => slots.wallclock, + CrdsData::RestartHeaviestFork(fork) => fork.wallclock, } } pub fn pubkey(&self) -> Pubkey { @@ -622,6 +631,7 @@ impl CrdsValue { CrdsData::SnapshotHashes(hash) => hash.from, CrdsData::ContactInfo(node) => *node.pubkey(), CrdsData::RestartLastVotedForkSlots(slots) => slots.from, + CrdsData::RestartHeaviestFork(fork) => fork.from, } } pub fn label(&self) -> CrdsValueLabel { @@ -643,6 +653,7 @@ impl CrdsValue { CrdsData::RestartLastVotedForkSlots(_) => { CrdsValueLabel::RestartLastVotedForkSlots(self.pubkey()) } + CrdsData::RestartHeaviestFork(_) => CrdsValueLabel::RestartHeaviestFork(self.pubkey()), } } pub fn contact_info(&self) -> Option<&LegacyContactInfo> { diff --git a/gossip/src/restart_crds_values.rs b/gossip/src/restart_crds_values.rs index 02f9359cce..4a2606335b 100644 --- a/gossip/src/restart_crds_values.rs +++ b/gossip/src/restart_crds_values.rs @@ -1,5 +1,5 @@ use { - crate::crds_value::new_rand_timestamp, + crate::crds_value::{new_rand_timestamp, sanitize_wallclock}, bv::BitVec, itertools::Itertools, rand::Rng, @@ -29,6 +29,16 @@ pub enum RestartLastVotedForkSlotsError { LastVotedForkEmpty, } +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, AbiExample, Debug)] +pub struct RestartHeaviestFork { + pub from: Pubkey, + pub wallclock: u64, + pub last_slot: Slot, + pub last_slot_hash: Hash, + pub observed_stake: u64, + pub shred_version: u16, +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, AbiExample, AbiEnumVisitor)] enum SlotsOffsets { RunLengthEncoding(RunLengthEncoding), @@ -48,6 +58,7 @@ struct RawOffsets(BitVec); impl Sanitize for RestartLastVotedForkSlots { fn sanitize(&self) -> std::result::Result<(), SanitizeError> { + sanitize_wallclock(self.wallclock)?; self.last_voted_hash.sanitize() } } @@ -94,7 +105,7 @@ impl RestartLastVotedForkSlots { } /// New random Version for tests and benchmarks. - pub fn new_rand(rng: &mut R, pubkey: Option) -> Self { + pub(crate) fn new_rand(rng: &mut R, pubkey: Option) -> Self { let pubkey = pubkey.unwrap_or_else(solana_sdk::pubkey::new_rand); let num_slots = rng.gen_range(2..20); let slots = std::iter::repeat_with(|| 47825632 + rng.gen_range(0..512)) @@ -122,6 +133,27 @@ impl RestartLastVotedForkSlots { } } +impl Sanitize for RestartHeaviestFork { + fn sanitize(&self) -> Result<(), SanitizeError> { + sanitize_wallclock(self.wallclock)?; + self.last_slot_hash.sanitize() + } +} + +impl RestartHeaviestFork { + pub(crate) fn new_rand(rng: &mut R, from: Option) -> Self { + let from = from.unwrap_or_else(solana_sdk::pubkey::new_rand); + Self { + from, + wallclock: new_rand_timestamp(rng), + last_slot: rng.gen_range(0..1000), + last_slot_hash: Hash::new_unique(), + observed_stake: rng.gen_range(1..u64::MAX), + shred_version: 1, + } + } +} + impl RunLengthEncoding { fn new(bits: &BitVec) -> Self { let encoded = (0..bits.len()) @@ -317,4 +349,22 @@ mod test { let range: Vec = make_rand_slots(&mut rng).take(large_length).collect(); check_run_length_encoding(range); } + + #[test] + fn test_restart_heaviest_fork() { + let keypair = Keypair::new(); + let slot = 53; + let mut fork = RestartHeaviestFork { + from: keypair.pubkey(), + wallclock: timestamp(), + last_slot: slot, + last_slot_hash: Hash::default(), + observed_stake: 800_000, + shred_version: 1, + }; + assert_eq!(fork.sanitize(), Ok(())); + assert_eq!(fork.observed_stake, 800_000); + fork.wallclock = crate::crds_value::MAX_WALLCLOCK; + assert_eq!(fork.sanitize(), Err(SanitizeError::ValueOutOfBounds)); + } }