2020-12-18 06:32:43 -08:00
|
|
|
use crate::{
|
|
|
|
cluster_info::MAX_SNAPSHOT_HASHES,
|
|
|
|
contact_info::ContactInfo,
|
|
|
|
deprecated,
|
2021-01-24 07:47:43 -08:00
|
|
|
duplicate_shred::{DuplicateShred, DuplicateShredIndex, MAX_DUPLICATE_SHREDS},
|
2020-12-18 06:32:43 -08:00
|
|
|
epoch_slots::EpochSlots,
|
|
|
|
};
|
2019-07-30 15:43:17 -07:00
|
|
|
use bincode::{serialize, serialized_size};
|
2020-12-10 09:01:55 -08:00
|
|
|
use rand::{CryptoRng, Rng};
|
2021-01-21 05:08:07 -08:00
|
|
|
use serde::de::{Deserialize, Deserializer};
|
2020-04-27 11:06:00 -07:00
|
|
|
use solana_sdk::sanitize::{Sanitize, SanitizeError};
|
2020-03-16 08:37:31 -07:00
|
|
|
use solana_sdk::timing::timestamp;
|
2020-02-18 08:46:11 -08:00
|
|
|
use solana_sdk::{
|
|
|
|
clock::Slot,
|
2020-02-20 11:46:13 -08:00
|
|
|
hash::Hash,
|
2020-11-15 08:38:04 -08:00
|
|
|
pubkey::{self, Pubkey},
|
|
|
|
signature::{Keypair, Signable, Signature, Signer},
|
2020-02-18 08:46:11 -08:00
|
|
|
transaction::Transaction,
|
|
|
|
};
|
2021-01-21 05:08:07 -08:00
|
|
|
use solana_vote_program::vote_transaction::parse_vote_transaction;
|
2020-02-18 08:46:11 -08:00
|
|
|
use std::{
|
|
|
|
borrow::{Borrow, Cow},
|
2021-01-21 05:08:07 -08:00
|
|
|
collections::{hash_map::Entry, BTreeSet, HashMap},
|
2020-02-18 08:46:11 -08:00
|
|
|
fmt,
|
|
|
|
};
|
2018-11-15 13:23:26 -08:00
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
pub const MAX_WALLCLOCK: u64 = 1_000_000_000_000_000;
|
|
|
|
pub const MAX_SLOT: u64 = 1_000_000_000_000_000;
|
|
|
|
|
2019-11-04 16:19:54 -08:00
|
|
|
pub type VoteIndex = u8;
|
2021-01-21 05:08:07 -08:00
|
|
|
// TODO: Remove this in favor of vote_state::MAX_LOCKOUT_HISTORY once
|
|
|
|
// the fleet is updated to the new ClusterInfo::push_vote code.
|
2019-11-04 16:19:54 -08:00
|
|
|
pub const MAX_VOTES: VoteIndex = 32;
|
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
pub type EpochSlotsIndex = u8;
|
|
|
|
pub const MAX_EPOCH_SLOTS: EpochSlotsIndex = 255;
|
2020-02-19 20:24:09 -08:00
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
/// CrdsValue that is replicated across the cluster
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
|
2019-11-03 10:07:51 -08:00
|
|
|
pub struct CrdsValue {
|
|
|
|
pub signature: Signature,
|
|
|
|
pub data: CrdsData,
|
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for CrdsValue {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
|
|
|
self.signature.sanitize()?;
|
|
|
|
self.data.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 10:07:51 -08:00
|
|
|
impl Signable for CrdsValue {
|
|
|
|
fn pubkey(&self) -> Pubkey {
|
|
|
|
self.pubkey()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signable_data(&self) -> Cow<[u8]> {
|
|
|
|
Cow::Owned(serialize(&self.data).expect("failed to serialize CrdsData"))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_signature(&self) -> Signature {
|
|
|
|
self.signature
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_signature(&mut self, signature: Signature) {
|
|
|
|
self.signature = signature
|
|
|
|
}
|
2019-11-04 16:19:54 -08:00
|
|
|
|
|
|
|
fn verify(&self) -> bool {
|
2020-04-27 11:06:00 -07:00
|
|
|
self.get_signature()
|
|
|
|
.verify(&self.pubkey().as_ref(), self.signable_data().borrow())
|
2019-11-04 16:19:54 -08:00
|
|
|
}
|
2019-11-03 10:07:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// CrdsData that defines the different types of items CrdsValues can hold
|
2020-02-18 08:46:11 -08:00
|
|
|
/// * Merge Strategy - Latest wallclock is picked
|
2020-03-11 21:31:50 -07:00
|
|
|
/// * LowestSlot index is deprecated
|
2019-08-20 17:16:06 -07:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample, AbiEnumVisitor)]
|
2019-11-03 10:07:51 -08:00
|
|
|
pub enum CrdsData {
|
2018-11-15 13:23:26 -08:00
|
|
|
ContactInfo(ContactInfo),
|
2019-11-04 16:19:54 -08:00
|
|
|
Vote(VoteIndex, Vote),
|
2020-03-11 21:31:50 -07:00
|
|
|
LowestSlot(u8, LowestSlot),
|
2020-03-16 08:37:31 -07:00
|
|
|
SnapshotHashes(SnapshotHash),
|
|
|
|
AccountsHashes(SnapshotHash),
|
2020-04-01 18:47:50 -07:00
|
|
|
EpochSlots(EpochSlotsIndex, EpochSlots),
|
2020-09-21 19:54:43 -07:00
|
|
|
LegacyVersion(LegacyVersion),
|
2020-05-11 15:02:01 -07:00
|
|
|
Version(Version),
|
2020-12-08 06:19:01 -08:00
|
|
|
NodeInstance(NodeInstance),
|
2021-01-24 07:47:43 -08:00
|
|
|
DuplicateShred(DuplicateShredIndex, DuplicateShred),
|
2019-05-08 13:50:32 -07:00
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for CrdsData {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
|
|
|
match self {
|
|
|
|
CrdsData::ContactInfo(val) => val.sanitize(),
|
|
|
|
CrdsData::Vote(ix, val) => {
|
|
|
|
if *ix >= MAX_VOTES {
|
2020-04-29 18:12:51 -07:00
|
|
|
return Err(SanitizeError::ValueOutOfBounds);
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
val.sanitize()
|
|
|
|
}
|
2020-04-27 20:22:30 -07:00
|
|
|
CrdsData::LowestSlot(ix, val) => {
|
|
|
|
if *ix as usize >= 1 {
|
2020-04-29 18:12:51 -07:00
|
|
|
return Err(SanitizeError::ValueOutOfBounds);
|
2020-04-27 20:22:30 -07:00
|
|
|
}
|
|
|
|
val.sanitize()
|
|
|
|
}
|
2020-04-27 11:06:00 -07:00
|
|
|
CrdsData::SnapshotHashes(val) => val.sanitize(),
|
|
|
|
CrdsData::AccountsHashes(val) => val.sanitize(),
|
|
|
|
CrdsData::EpochSlots(ix, val) => {
|
|
|
|
if *ix as usize >= MAX_EPOCH_SLOTS as usize {
|
2020-04-29 18:12:51 -07:00
|
|
|
return Err(SanitizeError::ValueOutOfBounds);
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
val.sanitize()
|
|
|
|
}
|
2020-09-21 19:54:43 -07:00
|
|
|
CrdsData::LegacyVersion(version) => version.sanitize(),
|
2020-05-11 15:02:01 -07:00
|
|
|
CrdsData::Version(version) => version.sanitize(),
|
2020-12-08 06:19:01 -08:00
|
|
|
CrdsData::NodeInstance(node) => node.sanitize(),
|
2021-01-24 07:47:43 -08:00
|
|
|
CrdsData::DuplicateShred(ix, shred) => {
|
|
|
|
if *ix >= MAX_DUPLICATE_SHREDS {
|
|
|
|
Err(SanitizeError::ValueOutOfBounds)
|
|
|
|
} else {
|
|
|
|
shred.sanitize()
|
|
|
|
}
|
|
|
|
}
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 08:38:04 -08:00
|
|
|
/// Random timestamp for tests and benchmarks.
|
2020-11-19 08:38:01 -08:00
|
|
|
pub(crate) fn new_rand_timestamp<R: Rng>(rng: &mut R) -> u64 {
|
|
|
|
const DELAY: u64 = 10 * 60 * 1000; // 10 minutes
|
|
|
|
timestamp() - DELAY + rng.gen_range(0, 2 * DELAY)
|
2020-11-15 08:38:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CrdsData {
|
|
|
|
/// New random CrdsData for tests and benchmarks.
|
|
|
|
fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> CrdsData {
|
2021-02-26 06:12:04 -08:00
|
|
|
let kind = rng.gen_range(0, 7);
|
2020-11-15 08:38:04 -08:00
|
|
|
// TODO: Implement other kinds of CrdsData here.
|
|
|
|
// TODO: Assign ranges to each arm proportional to their frequency in
|
|
|
|
// the mainnet crds table.
|
|
|
|
match kind {
|
|
|
|
0 => CrdsData::ContactInfo(ContactInfo::new_rand(rng, pubkey)),
|
|
|
|
1 => CrdsData::LowestSlot(rng.gen(), LowestSlot::new_rand(rng, pubkey)),
|
|
|
|
2 => CrdsData::SnapshotHashes(SnapshotHash::new_rand(rng, pubkey)),
|
|
|
|
3 => CrdsData::AccountsHashes(SnapshotHash::new_rand(rng, pubkey)),
|
2020-12-27 05:31:05 -08:00
|
|
|
4 => CrdsData::Version(Version::new_rand(rng, pubkey)),
|
2021-02-26 06:12:04 -08:00
|
|
|
5 => CrdsData::Vote(rng.gen_range(0, MAX_VOTES), Vote::new_rand(rng, pubkey)),
|
|
|
|
_ => CrdsData::EpochSlots(
|
|
|
|
rng.gen_range(0, MAX_EPOCH_SLOTS),
|
|
|
|
EpochSlots::new_rand(rng, pubkey),
|
|
|
|
),
|
2020-11-15 08:38:04 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
|
2020-02-20 11:46:13 -08:00
|
|
|
pub struct SnapshotHash {
|
|
|
|
pub from: Pubkey,
|
|
|
|
pub hashes: Vec<(Slot, Hash)>,
|
|
|
|
pub wallclock: u64,
|
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for SnapshotHash {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
2020-12-18 06:32:43 -08:00
|
|
|
sanitize_wallclock(self.wallclock)?;
|
2020-04-27 11:06:00 -07:00
|
|
|
for (slot, _) in &self.hashes {
|
|
|
|
if *slot >= MAX_SLOT {
|
2020-04-29 18:12:51 -07:00
|
|
|
return Err(SanitizeError::ValueOutOfBounds);
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
self.from.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 11:46:13 -08:00
|
|
|
impl SnapshotHash {
|
2020-03-16 08:37:31 -07:00
|
|
|
pub fn new(from: Pubkey, hashes: Vec<(Slot, Hash)>) -> Self {
|
2020-02-20 11:46:13 -08:00
|
|
|
Self {
|
|
|
|
from,
|
|
|
|
hashes,
|
2020-03-16 08:37:31 -07:00
|
|
|
wallclock: timestamp(),
|
2020-02-20 11:46:13 -08:00
|
|
|
}
|
|
|
|
}
|
2020-11-15 08:38:04 -08:00
|
|
|
|
|
|
|
/// New random SnapshotHash for tests and benchmarks.
|
|
|
|
pub(crate) fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> Self {
|
|
|
|
let num_hashes = rng.gen_range(0, MAX_SNAPSHOT_HASHES) + 1;
|
|
|
|
let hashes = std::iter::repeat_with(|| {
|
|
|
|
let slot = 47825632 + rng.gen_range(0, 512);
|
|
|
|
let hash = solana_sdk::hash::new_rand(rng);
|
|
|
|
(slot, hash)
|
|
|
|
})
|
|
|
|
.take(num_hashes)
|
|
|
|
.collect();
|
|
|
|
Self {
|
|
|
|
from: pubkey.unwrap_or_else(pubkey::new_rand),
|
|
|
|
hashes,
|
|
|
|
wallclock: new_rand_timestamp(rng),
|
|
|
|
}
|
|
|
|
}
|
2020-02-20 11:46:13 -08:00
|
|
|
}
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
|
2020-03-11 21:31:50 -07:00
|
|
|
pub struct LowestSlot {
|
2019-05-08 13:50:32 -07:00
|
|
|
pub from: Pubkey,
|
2020-03-11 21:31:50 -07:00
|
|
|
root: Slot, //deprecated
|
2019-12-05 11:25:13 -08:00
|
|
|
pub lowest: Slot,
|
2020-03-11 21:31:50 -07:00
|
|
|
slots: BTreeSet<Slot>, //deprecated
|
|
|
|
stash: Vec<deprecated::EpochIncompleteSlots>, //deprecated
|
2019-05-08 13:50:32 -07:00
|
|
|
pub wallclock: u64,
|
|
|
|
}
|
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
impl LowestSlot {
|
|
|
|
pub fn new(from: Pubkey, lowest: Slot, wallclock: u64) -> Self {
|
2019-05-08 13:50:32 -07:00
|
|
|
Self {
|
|
|
|
from,
|
2020-03-11 21:31:50 -07:00
|
|
|
root: 0,
|
2019-12-05 11:25:13 -08:00
|
|
|
lowest,
|
2020-03-11 21:31:50 -07:00
|
|
|
slots: BTreeSet::new(),
|
|
|
|
stash: vec![],
|
2019-05-08 13:50:32 -07:00
|
|
|
wallclock,
|
|
|
|
}
|
|
|
|
}
|
2020-11-15 08:38:04 -08:00
|
|
|
|
|
|
|
/// New random LowestSlot for tests and benchmarks.
|
|
|
|
fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> Self {
|
|
|
|
Self {
|
|
|
|
from: pubkey.unwrap_or_else(pubkey::new_rand),
|
|
|
|
root: rng.gen(),
|
|
|
|
lowest: rng.gen(),
|
|
|
|
slots: BTreeSet::default(),
|
|
|
|
stash: Vec::default(),
|
|
|
|
wallclock: new_rand_timestamp(rng),
|
|
|
|
}
|
|
|
|
}
|
2019-05-08 13:50:32 -07:00
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for LowestSlot {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
2020-12-18 06:32:43 -08:00
|
|
|
sanitize_wallclock(self.wallclock)?;
|
2020-04-27 11:06:00 -07:00
|
|
|
if self.lowest >= MAX_SLOT {
|
2020-04-29 18:12:51 -07:00
|
|
|
return Err(SanitizeError::ValueOutOfBounds);
|
2020-04-27 20:22:30 -07:00
|
|
|
}
|
|
|
|
if self.root != 0 {
|
|
|
|
return Err(SanitizeError::InvalidValue);
|
|
|
|
}
|
|
|
|
if !self.slots.is_empty() {
|
|
|
|
return Err(SanitizeError::InvalidValue);
|
|
|
|
}
|
|
|
|
if !self.stash.is_empty() {
|
|
|
|
return Err(SanitizeError::InvalidValue);
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
self.from.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 05:08:07 -08:00
|
|
|
#[derive(Clone, Debug, PartialEq, AbiExample, Serialize)]
|
2018-11-15 13:23:26 -08:00
|
|
|
pub struct Vote {
|
2021-01-21 05:08:07 -08:00
|
|
|
pub(crate) from: Pubkey,
|
|
|
|
transaction: Transaction,
|
|
|
|
pub(crate) wallclock: u64,
|
|
|
|
#[serde(skip_serializing)]
|
|
|
|
slot: Option<Slot>,
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for Vote {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
2020-12-18 06:32:43 -08:00
|
|
|
sanitize_wallclock(self.wallclock)?;
|
2020-04-27 11:06:00 -07:00
|
|
|
self.from.sanitize()?;
|
|
|
|
self.transaction.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-08 13:50:32 -07:00
|
|
|
impl Vote {
|
2021-01-21 05:08:07 -08:00
|
|
|
pub fn new(from: Pubkey, transaction: Transaction, wallclock: u64) -> Self {
|
|
|
|
let slot = parse_vote_transaction(&transaction)
|
|
|
|
.and_then(|(_, vote, _)| vote.slots.last().copied());
|
2019-05-08 13:50:32 -07:00
|
|
|
Self {
|
2021-01-21 05:08:07 -08:00
|
|
|
from,
|
2019-05-08 13:50:32 -07:00
|
|
|
transaction,
|
|
|
|
wallclock,
|
2021-01-21 05:08:07 -08:00
|
|
|
slot,
|
2019-05-08 13:50:32 -07:00
|
|
|
}
|
|
|
|
}
|
2020-12-27 05:31:05 -08:00
|
|
|
|
|
|
|
/// New random Vote for tests and benchmarks.
|
|
|
|
fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> Self {
|
|
|
|
Self {
|
|
|
|
from: pubkey.unwrap_or_else(pubkey::new_rand),
|
|
|
|
transaction: Transaction::default(),
|
|
|
|
wallclock: new_rand_timestamp(rng),
|
2021-01-21 05:08:07 -08:00
|
|
|
slot: None,
|
2020-12-27 05:31:05 -08:00
|
|
|
}
|
|
|
|
}
|
2021-01-21 05:08:07 -08:00
|
|
|
|
|
|
|
pub(crate) fn transaction(&self) -> &Transaction {
|
|
|
|
&self.transaction
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn slot(&self) -> Option<Slot> {
|
|
|
|
self.slot
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Vote {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct Vote {
|
|
|
|
from: Pubkey,
|
|
|
|
transaction: Transaction,
|
|
|
|
wallclock: u64,
|
2021-01-23 11:55:15 -08:00
|
|
|
}
|
2021-01-21 05:08:07 -08:00
|
|
|
let vote = Vote::deserialize(deserializer)?;
|
|
|
|
let vote = match vote.transaction.sanitize() {
|
|
|
|
Ok(_) => Self::new(vote.from, vote.transaction, vote.wallclock),
|
|
|
|
Err(_) => Self {
|
|
|
|
from: vote.from,
|
|
|
|
transaction: vote.transaction,
|
|
|
|
wallclock: vote.wallclock,
|
|
|
|
slot: None,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
Ok(vote)
|
|
|
|
}
|
2019-05-08 13:50:32 -07:00
|
|
|
}
|
|
|
|
|
2020-09-21 19:54:43 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
|
|
|
|
pub struct LegacyVersion {
|
|
|
|
pub from: Pubkey,
|
|
|
|
pub wallclock: u64,
|
|
|
|
pub version: solana_version::LegacyVersion,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sanitize for LegacyVersion {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
2020-12-18 06:32:43 -08:00
|
|
|
sanitize_wallclock(self.wallclock)?;
|
2020-09-21 19:54:43 -07:00
|
|
|
self.from.sanitize()?;
|
|
|
|
self.version.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
|
2020-05-11 15:02:01 -07:00
|
|
|
pub struct Version {
|
|
|
|
pub from: Pubkey,
|
|
|
|
pub wallclock: u64,
|
|
|
|
pub version: solana_version::Version,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sanitize for Version {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
2020-12-18 06:32:43 -08:00
|
|
|
sanitize_wallclock(self.wallclock)?;
|
2020-05-11 15:02:01 -07:00
|
|
|
self.from.sanitize()?;
|
|
|
|
self.version.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Version {
|
|
|
|
pub fn new(from: Pubkey) -> Self {
|
|
|
|
Self {
|
|
|
|
from,
|
|
|
|
wallclock: timestamp(),
|
|
|
|
version: solana_version::Version::default(),
|
|
|
|
}
|
|
|
|
}
|
2020-11-15 08:38:04 -08:00
|
|
|
|
|
|
|
/// New random Version for tests and benchmarks.
|
|
|
|
fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> Self {
|
|
|
|
Self {
|
|
|
|
from: pubkey.unwrap_or_else(pubkey::new_rand),
|
|
|
|
wallclock: new_rand_timestamp(rng),
|
|
|
|
version: solana_version::Version {
|
|
|
|
major: rng.gen(),
|
|
|
|
minor: rng.gen(),
|
|
|
|
patch: rng.gen(),
|
|
|
|
commit: Some(rng.gen()),
|
|
|
|
feature_set: rng.gen(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2020-05-11 15:02:01 -07:00
|
|
|
}
|
|
|
|
|
2020-12-08 06:19:01 -08:00
|
|
|
#[derive(Clone, Debug, PartialEq, AbiExample, Deserialize, Serialize)]
|
|
|
|
pub struct NodeInstance {
|
|
|
|
from: Pubkey,
|
|
|
|
wallclock: u64,
|
|
|
|
timestamp: u64, // Timestamp when the instance was created.
|
|
|
|
token: u64, // Randomly generated value at node instantiation.
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NodeInstance {
|
2020-12-10 09:01:55 -08:00
|
|
|
pub fn new<R>(rng: &mut R, pubkey: Pubkey, now: u64) -> Self
|
|
|
|
where
|
|
|
|
R: Rng + CryptoRng,
|
|
|
|
{
|
2020-12-08 06:19:01 -08:00
|
|
|
Self {
|
|
|
|
from: pubkey,
|
|
|
|
wallclock: now,
|
|
|
|
timestamp: now,
|
2020-12-10 09:01:55 -08:00
|
|
|
token: rng.gen(),
|
2020-12-08 06:19:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-08 12:45:46 -08:00
|
|
|
// Clones the value with an updated wallclock.
|
|
|
|
pub fn with_wallclock(&self, now: u64) -> Self {
|
|
|
|
Self {
|
|
|
|
wallclock: now,
|
|
|
|
..*self
|
2020-12-08 06:19:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the crds-value is a duplicate instance
|
|
|
|
// of this node, with a more recent timestamp.
|
|
|
|
pub fn check_duplicate(&self, other: &CrdsValue) -> bool {
|
|
|
|
match &other.data {
|
|
|
|
CrdsData::NodeInstance(other) => {
|
|
|
|
self.token != other.token
|
|
|
|
&& self.timestamp <= other.timestamp
|
|
|
|
&& self.from == other.from
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sanitize for NodeInstance {
|
|
|
|
fn sanitize(&self) -> Result<(), SanitizeError> {
|
2020-12-18 06:32:43 -08:00
|
|
|
sanitize_wallclock(self.wallclock)?;
|
2020-12-08 06:19:01 -08:00
|
|
|
self.from.sanitize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
/// Type of the replicated value
|
2018-12-01 12:00:30 -08:00
|
|
|
/// These are labels for values in a record that is associated with `Pubkey`
|
2018-11-15 13:23:26 -08:00
|
|
|
#[derive(PartialEq, Hash, Eq, Clone, Debug)]
|
|
|
|
pub enum CrdsValueLabel {
|
|
|
|
ContactInfo(Pubkey),
|
2019-11-04 16:19:54 -08:00
|
|
|
Vote(VoteIndex, Pubkey),
|
2020-03-11 21:31:50 -07:00
|
|
|
LowestSlot(Pubkey),
|
2020-03-16 08:37:31 -07:00
|
|
|
SnapshotHashes(Pubkey),
|
2020-03-11 21:31:50 -07:00
|
|
|
EpochSlots(EpochSlotsIndex, Pubkey),
|
2020-03-16 08:37:31 -07:00
|
|
|
AccountsHashes(Pubkey),
|
2020-09-21 19:54:43 -07:00
|
|
|
LegacyVersion(Pubkey),
|
2020-05-11 15:02:01 -07:00
|
|
|
Version(Pubkey),
|
2020-12-10 09:01:55 -08:00
|
|
|
NodeInstance(Pubkey, u64 /*token*/),
|
2020-12-18 06:32:43 -08:00
|
|
|
DuplicateShred(DuplicateShredIndex, Pubkey),
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for CrdsValueLabel {
|
2018-12-08 21:44:20 -08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2018-11-15 13:23:26 -08:00
|
|
|
match self {
|
|
|
|
CrdsValueLabel::ContactInfo(_) => write!(f, "ContactInfo({})", self.pubkey()),
|
2019-11-04 16:19:54 -08:00
|
|
|
CrdsValueLabel::Vote(ix, _) => write!(f, "Vote({}, {})", ix, self.pubkey()),
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsValueLabel::LowestSlot(_) => write!(f, "LowestSlot({})", self.pubkey()),
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsValueLabel::SnapshotHashes(_) => write!(f, "SnapshotHash({})", self.pubkey()),
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsValueLabel::EpochSlots(ix, _) => write!(f, "EpochSlots({}, {})", ix, self.pubkey()),
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsValueLabel::AccountsHashes(_) => write!(f, "AccountsHashes({})", self.pubkey()),
|
2020-09-21 19:54:43 -07:00
|
|
|
CrdsValueLabel::LegacyVersion(_) => write!(f, "LegacyVersion({})", self.pubkey()),
|
2020-05-11 15:02:01 -07:00
|
|
|
CrdsValueLabel::Version(_) => write!(f, "Version({})", self.pubkey()),
|
2020-12-18 06:32:43 -08:00
|
|
|
CrdsValueLabel::NodeInstance(pk, token) => write!(f, "NodeInstance({}, {})", pk, token),
|
2021-01-24 07:47:43 -08:00
|
|
|
CrdsValueLabel::DuplicateShred(ix, pk) => write!(f, "DuplicateShred({}, {})", ix, pk),
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CrdsValueLabel {
|
|
|
|
pub fn pubkey(&self) -> Pubkey {
|
|
|
|
match self {
|
|
|
|
CrdsValueLabel::ContactInfo(p) => *p,
|
2019-11-04 16:19:54 -08:00
|
|
|
CrdsValueLabel::Vote(_, p) => *p,
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsValueLabel::LowestSlot(p) => *p,
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsValueLabel::SnapshotHashes(p) => *p,
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsValueLabel::EpochSlots(_, p) => *p,
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsValueLabel::AccountsHashes(p) => *p,
|
2020-09-21 19:54:43 -07:00
|
|
|
CrdsValueLabel::LegacyVersion(p) => *p,
|
2020-05-11 15:02:01 -07:00
|
|
|
CrdsValueLabel::Version(p) => *p,
|
2020-12-10 09:01:55 -08:00
|
|
|
CrdsValueLabel::NodeInstance(p, _ /*token*/) => *p,
|
2020-12-18 06:32:43 -08:00
|
|
|
CrdsValueLabel::DuplicateShred(_, p) => *p,
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
}
|
2021-01-08 10:54:40 -08:00
|
|
|
|
|
|
|
/// Returns number of possible distinct labels of the same type for
|
|
|
|
/// a fixed pubkey, and None if that is practically unlimited.
|
|
|
|
pub(crate) fn value_space(&self) -> Option<usize> {
|
|
|
|
match self {
|
|
|
|
CrdsValueLabel::ContactInfo(_) => Some(1),
|
|
|
|
CrdsValueLabel::Vote(_, _) => Some(MAX_VOTES as usize),
|
|
|
|
CrdsValueLabel::LowestSlot(_) => Some(1),
|
|
|
|
CrdsValueLabel::SnapshotHashes(_) => Some(1),
|
|
|
|
CrdsValueLabel::EpochSlots(_, _) => Some(MAX_EPOCH_SLOTS as usize),
|
|
|
|
CrdsValueLabel::AccountsHashes(_) => Some(1),
|
|
|
|
CrdsValueLabel::LegacyVersion(_) => Some(1),
|
|
|
|
CrdsValueLabel::Version(_) => Some(1),
|
|
|
|
CrdsValueLabel::NodeInstance(_, _) => None,
|
2021-01-24 07:47:43 -08:00
|
|
|
CrdsValueLabel::DuplicateShred(_, _) => Some(MAX_DUPLICATE_SHREDS as usize),
|
2021-01-08 10:54:40 -08:00
|
|
|
}
|
|
|
|
}
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
impl CrdsValue {
|
2019-11-03 10:07:51 -08:00
|
|
|
pub fn new_unsigned(data: CrdsData) -> Self {
|
|
|
|
Self {
|
|
|
|
signature: Signature::default(),
|
|
|
|
data,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_signed(data: CrdsData, keypair: &Keypair) -> Self {
|
|
|
|
let mut value = Self::new_unsigned(data);
|
|
|
|
value.sign(keypair);
|
|
|
|
value
|
|
|
|
}
|
2020-09-29 02:04:40 -07:00
|
|
|
|
2020-11-15 08:38:04 -08:00
|
|
|
/// New random CrdsValue for tests and benchmarks.
|
|
|
|
pub fn new_rand<R: Rng>(rng: &mut R, keypair: Option<&Keypair>) -> CrdsValue {
|
|
|
|
match keypair {
|
|
|
|
None => {
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let data = CrdsData::new_rand(rng, Some(keypair.pubkey()));
|
|
|
|
Self::new_signed(data, &keypair)
|
|
|
|
}
|
|
|
|
Some(keypair) => {
|
|
|
|
let data = CrdsData::new_rand(rng, Some(keypair.pubkey()));
|
|
|
|
Self::new_signed(data, keypair)
|
|
|
|
}
|
|
|
|
}
|
2020-09-29 02:04:40 -07:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:02:01 -07:00
|
|
|
/// Totally unsecure unverifiable wallclock of the node that generated this message
|
2018-11-15 13:23:26 -08:00
|
|
|
/// Latest wallclock is always picked.
|
|
|
|
/// This is used to time out push messages.
|
|
|
|
pub fn wallclock(&self) -> u64 {
|
2019-11-03 10:07:51 -08:00
|
|
|
match &self.data {
|
|
|
|
CrdsData::ContactInfo(contact_info) => contact_info.wallclock,
|
2019-11-04 16:19:54 -08:00
|
|
|
CrdsData::Vote(_, vote) => vote.wallclock,
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsData::LowestSlot(_, obj) => obj.wallclock,
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsData::SnapshotHashes(hash) => hash.wallclock,
|
|
|
|
CrdsData::AccountsHashes(hash) => hash.wallclock,
|
2020-04-01 18:47:50 -07:00
|
|
|
CrdsData::EpochSlots(_, p) => p.wallclock,
|
2020-09-21 19:54:43 -07:00
|
|
|
CrdsData::LegacyVersion(version) => version.wallclock,
|
2020-05-11 15:02:01 -07:00
|
|
|
CrdsData::Version(version) => version.wallclock,
|
2020-12-08 06:19:01 -08:00
|
|
|
CrdsData::NodeInstance(node) => node.wallclock,
|
2021-01-24 07:47:43 -08:00
|
|
|
CrdsData::DuplicateShred(_, shred) => shred.wallclock,
|
2019-11-03 10:07:51 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn pubkey(&self) -> Pubkey {
|
|
|
|
match &self.data {
|
|
|
|
CrdsData::ContactInfo(contact_info) => contact_info.id,
|
2019-11-04 16:19:54 -08:00
|
|
|
CrdsData::Vote(_, vote) => vote.from,
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsData::LowestSlot(_, slots) => slots.from,
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsData::SnapshotHashes(hash) => hash.from,
|
|
|
|
CrdsData::AccountsHashes(hash) => hash.from,
|
2020-04-01 18:47:50 -07:00
|
|
|
CrdsData::EpochSlots(_, p) => p.from,
|
2020-09-21 19:54:43 -07:00
|
|
|
CrdsData::LegacyVersion(version) => version.from,
|
2020-05-11 15:02:01 -07:00
|
|
|
CrdsData::Version(version) => version.from,
|
2020-12-08 06:19:01 -08:00
|
|
|
CrdsData::NodeInstance(node) => node.from,
|
2021-01-24 07:47:43 -08:00
|
|
|
CrdsData::DuplicateShred(_, shred) => shred.from,
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn label(&self) -> CrdsValueLabel {
|
2019-11-03 10:07:51 -08:00
|
|
|
match &self.data {
|
|
|
|
CrdsData::ContactInfo(_) => CrdsValueLabel::ContactInfo(self.pubkey()),
|
2019-11-04 16:19:54 -08:00
|
|
|
CrdsData::Vote(ix, _) => CrdsValueLabel::Vote(*ix, self.pubkey()),
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsData::LowestSlot(_, _) => CrdsValueLabel::LowestSlot(self.pubkey()),
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsData::SnapshotHashes(_) => CrdsValueLabel::SnapshotHashes(self.pubkey()),
|
|
|
|
CrdsData::AccountsHashes(_) => CrdsValueLabel::AccountsHashes(self.pubkey()),
|
2020-04-01 18:47:50 -07:00
|
|
|
CrdsData::EpochSlots(ix, _) => CrdsValueLabel::EpochSlots(*ix, self.pubkey()),
|
2020-09-21 19:54:43 -07:00
|
|
|
CrdsData::LegacyVersion(_) => CrdsValueLabel::LegacyVersion(self.pubkey()),
|
2020-05-11 15:02:01 -07:00
|
|
|
CrdsData::Version(_) => CrdsValueLabel::Version(self.pubkey()),
|
2020-12-18 06:32:43 -08:00
|
|
|
CrdsData::NodeInstance(node) => CrdsValueLabel::NodeInstance(node.from, node.token),
|
2021-01-24 07:47:43 -08:00
|
|
|
CrdsData::DuplicateShred(ix, shred) => CrdsValueLabel::DuplicateShred(*ix, shred.from),
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn contact_info(&self) -> Option<&ContactInfo> {
|
2019-11-03 10:07:51 -08:00
|
|
|
match &self.data {
|
|
|
|
CrdsData::ContactInfo(contact_info) => Some(contact_info),
|
2018-11-15 13:23:26 -08:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2019-11-04 16:19:54 -08:00
|
|
|
|
2021-01-21 05:08:07 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
fn vote(&self) -> Option<&Vote> {
|
2019-11-04 16:19:54 -08:00
|
|
|
match &self.data {
|
2021-01-21 05:08:07 -08:00
|
|
|
CrdsData::Vote(_, vote) => Some(vote),
|
2019-11-04 16:19:54 -08:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
pub fn lowest_slot(&self) -> Option<&LowestSlot> {
|
2019-11-03 10:07:51 -08:00
|
|
|
match &self.data {
|
2020-03-11 21:31:50 -07:00
|
|
|
CrdsData::LowestSlot(_, slots) => Some(slots),
|
2019-05-08 13:50:32 -07:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2020-02-20 11:46:13 -08:00
|
|
|
|
|
|
|
pub fn snapshot_hash(&self) -> Option<&SnapshotHash> {
|
|
|
|
match &self.data {
|
2020-03-16 08:37:31 -07:00
|
|
|
CrdsData::SnapshotHashes(slots) => Some(slots),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn accounts_hash(&self) -> Option<&SnapshotHash> {
|
|
|
|
match &self.data {
|
|
|
|
CrdsData::AccountsHashes(slots) => Some(slots),
|
2020-02-20 11:46:13 -08:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
pub fn epoch_slots(&self) -> Option<&EpochSlots> {
|
|
|
|
match &self.data {
|
|
|
|
CrdsData::EpochSlots(_, slots) => Some(slots),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 19:54:43 -07:00
|
|
|
pub fn legacy_version(&self) -> Option<&LegacyVersion> {
|
|
|
|
match &self.data {
|
|
|
|
CrdsData::LegacyVersion(legacy_version) => Some(legacy_version),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 15:02:01 -07:00
|
|
|
pub fn version(&self) -> Option<&Version> {
|
|
|
|
match &self.data {
|
|
|
|
CrdsData::Version(version) => Some(version),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 15:43:17 -07:00
|
|
|
/// Returns the size (in bytes) of a CrdsValue
|
|
|
|
pub fn size(&self) -> u64 {
|
|
|
|
serialized_size(&self).expect("unable to serialize contact info")
|
|
|
|
}
|
2019-11-04 16:19:54 -08:00
|
|
|
|
2020-12-08 06:19:01 -08:00
|
|
|
/// Returns true if, regardless of prunes, this crds-value
|
|
|
|
/// should be pushed to the receiving node.
|
|
|
|
pub fn should_force_push(&self, peer: &Pubkey) -> bool {
|
|
|
|
match &self.data {
|
|
|
|
CrdsData::NodeInstance(node) => node.from == *peer,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2018-12-01 12:00:30 -08:00
|
|
|
|
2020-11-22 09:51:14 -08:00
|
|
|
/// Filters out an iterator of crds values, returning
|
|
|
|
/// the unique ones with the most recent wallclock.
|
|
|
|
pub(crate) fn filter_current<'a, I>(values: I) -> impl Iterator<Item = &'a CrdsValue>
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = &'a CrdsValue>,
|
|
|
|
{
|
|
|
|
let mut out = HashMap::new();
|
|
|
|
for value in values {
|
|
|
|
match out.entry(value.label()) {
|
|
|
|
Entry::Vacant(entry) => {
|
|
|
|
entry.insert((value, value.wallclock()));
|
|
|
|
}
|
|
|
|
Entry::Occupied(mut entry) => {
|
|
|
|
let value_wallclock = value.wallclock();
|
|
|
|
let (_, entry_wallclock) = entry.get();
|
|
|
|
if *entry_wallclock < value_wallclock {
|
|
|
|
entry.insert((value, value_wallclock));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out.into_iter().map(|(_, (v, _))| v)
|
|
|
|
}
|
|
|
|
|
2020-12-18 06:32:43 -08:00
|
|
|
pub(crate) fn sanitize_wallclock(wallclock: u64) -> Result<(), SanitizeError> {
|
|
|
|
if wallclock >= MAX_WALLCLOCK {
|
|
|
|
Err(SanitizeError::ValueOutOfBounds)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::contact_info::ContactInfo;
|
2021-01-21 05:08:07 -08:00
|
|
|
use bincode::{deserialize, Options};
|
2021-01-21 13:53:10 -08:00
|
|
|
use rand::SeedableRng;
|
|
|
|
use rand_chacha::ChaChaRng;
|
2019-11-06 10:52:30 -08:00
|
|
|
use solana_perf::test_tx::test_tx;
|
2020-02-20 13:28:55 -08:00
|
|
|
use solana_sdk::signature::{Keypair, Signer};
|
2018-12-01 12:00:30 -08:00
|
|
|
use solana_sdk::timing::timestamp;
|
2021-01-21 05:08:07 -08:00
|
|
|
use solana_vote_program::{vote_instruction, vote_state};
|
2020-11-22 09:51:14 -08:00
|
|
|
use std::cmp::Ordering;
|
|
|
|
use std::iter::repeat_with;
|
2018-11-15 13:23:26 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_keys_and_values() {
|
2019-11-03 10:07:51 -08:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(v.wallclock(), 0);
|
2020-06-08 17:38:14 -07:00
|
|
|
let key = v.contact_info().unwrap().id;
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(v.label(), CrdsValueLabel::ContactInfo(key));
|
|
|
|
|
2019-11-04 16:19:54 -08:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::Vote(
|
|
|
|
0,
|
2021-01-21 05:08:07 -08:00
|
|
|
Vote::new(Pubkey::default(), test_tx(), 0),
|
2019-11-04 16:19:54 -08:00
|
|
|
));
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(v.wallclock(), 0);
|
2020-06-08 17:38:14 -07:00
|
|
|
let key = v.vote().unwrap().from;
|
2019-11-04 16:19:54 -08:00
|
|
|
assert_eq!(v.label(), CrdsValueLabel::Vote(0, key));
|
2019-05-08 13:50:32 -07:00
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(
|
2019-12-05 11:25:13 -08:00
|
|
|
0,
|
2020-03-11 21:31:50 -07:00
|
|
|
LowestSlot::new(Pubkey::default(), 0, 0),
|
2020-02-19 20:24:09 -08:00
|
|
|
));
|
2019-05-08 13:50:32 -07:00
|
|
|
assert_eq!(v.wallclock(), 0);
|
2020-06-08 17:38:14 -07:00
|
|
|
let key = v.lowest_slot().unwrap().from;
|
2020-03-11 21:31:50 -07:00
|
|
|
assert_eq!(v.label(), CrdsValueLabel::LowestSlot(key));
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-11-04 16:19:54 -08:00
|
|
|
|
2020-04-27 20:22:30 -07:00
|
|
|
#[test]
|
|
|
|
fn test_lowest_slot_sanitize() {
|
|
|
|
let ls = LowestSlot::new(Pubkey::default(), 0, 0);
|
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(0, ls.clone()));
|
|
|
|
assert_eq!(v.sanitize(), Ok(()));
|
|
|
|
|
|
|
|
let mut o = ls.clone();
|
|
|
|
o.root = 1;
|
2020-05-15 09:35:43 -07:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(0, o));
|
2020-04-27 20:22:30 -07:00
|
|
|
assert_eq!(v.sanitize(), Err(SanitizeError::InvalidValue));
|
|
|
|
|
|
|
|
let o = ls.clone();
|
2020-05-15 09:35:43 -07:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(1, o));
|
2020-04-29 18:12:51 -07:00
|
|
|
assert_eq!(v.sanitize(), Err(SanitizeError::ValueOutOfBounds));
|
2020-04-27 20:22:30 -07:00
|
|
|
|
|
|
|
let mut o = ls.clone();
|
|
|
|
o.slots.insert(1);
|
2020-05-15 09:35:43 -07:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(0, o));
|
2020-04-27 20:22:30 -07:00
|
|
|
assert_eq!(v.sanitize(), Err(SanitizeError::InvalidValue));
|
|
|
|
|
2020-05-15 09:35:43 -07:00
|
|
|
let mut o = ls;
|
2020-04-27 20:22:30 -07:00
|
|
|
o.stash.push(deprecated::EpochIncompleteSlots::default());
|
2020-05-15 09:35:43 -07:00
|
|
|
let v = CrdsValue::new_unsigned(CrdsData::LowestSlot(0, o));
|
2020-04-27 20:22:30 -07:00
|
|
|
assert_eq!(v.sanitize(), Err(SanitizeError::InvalidValue));
|
|
|
|
}
|
|
|
|
|
2018-12-01 12:00:30 -08:00
|
|
|
#[test]
|
|
|
|
fn test_signature() {
|
|
|
|
let keypair = Keypair::new();
|
2019-03-11 16:43:30 -07:00
|
|
|
let wrong_keypair = Keypair::new();
|
2019-11-03 10:07:51 -08:00
|
|
|
let mut v = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
|
|
|
&keypair.pubkey(),
|
|
|
|
timestamp(),
|
|
|
|
)));
|
2019-03-11 16:43:30 -07:00
|
|
|
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
2019-11-04 16:19:54 -08:00
|
|
|
v = CrdsValue::new_unsigned(CrdsData::Vote(
|
|
|
|
0,
|
2021-01-21 05:08:07 -08:00
|
|
|
Vote::new(keypair.pubkey(), test_tx(), timestamp()),
|
2019-11-04 16:19:54 -08:00
|
|
|
));
|
2019-03-11 16:43:30 -07:00
|
|
|
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
2020-03-11 21:31:50 -07:00
|
|
|
v = CrdsValue::new_unsigned(CrdsData::LowestSlot(
|
2020-02-17 12:39:30 -08:00
|
|
|
0,
|
2020-03-11 21:31:50 -07:00
|
|
|
LowestSlot::new(keypair.pubkey(), 0, timestamp()),
|
2020-02-19 20:24:09 -08:00
|
|
|
));
|
2019-05-08 13:50:32 -07:00
|
|
|
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
2019-03-11 16:43:30 -07:00
|
|
|
}
|
|
|
|
|
2019-11-04 16:19:54 -08:00
|
|
|
#[test]
|
|
|
|
fn test_max_vote_index() {
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let vote = CrdsValue::new_signed(
|
|
|
|
CrdsData::Vote(
|
|
|
|
MAX_VOTES,
|
2021-01-21 05:08:07 -08:00
|
|
|
Vote::new(keypair.pubkey(), test_tx(), timestamp()),
|
2019-11-04 16:19:54 -08:00
|
|
|
),
|
|
|
|
&keypair,
|
|
|
|
);
|
2020-04-27 11:06:00 -07:00
|
|
|
assert!(vote.sanitize().is_err());
|
2019-11-04 16:19:54 -08:00
|
|
|
}
|
|
|
|
|
2021-01-21 05:08:07 -08:00
|
|
|
#[test]
|
|
|
|
fn test_vote_round_trip() {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let vote = vote_state::Vote::new(
|
|
|
|
vec![1, 3, 7], // slots
|
|
|
|
solana_sdk::hash::new_rand(&mut rng),
|
|
|
|
);
|
|
|
|
let ix = vote_instruction::vote(
|
|
|
|
&Pubkey::new_unique(), // vote_pubkey
|
|
|
|
&Pubkey::new_unique(), // authorized_voter_pubkey
|
|
|
|
vote,
|
|
|
|
);
|
|
|
|
let tx = Transaction::new_with_payer(
|
|
|
|
&[ix], // instructions
|
|
|
|
Some(&Pubkey::new_unique()), // payer
|
|
|
|
);
|
|
|
|
let vote = Vote::new(
|
|
|
|
Pubkey::new_unique(), // from
|
|
|
|
tx,
|
|
|
|
rng.gen(), // wallclock
|
|
|
|
);
|
|
|
|
assert_eq!(vote.slot, Some(7));
|
|
|
|
let bytes = bincode::serialize(&vote).unwrap();
|
|
|
|
let other = bincode::deserialize(&bytes[..]).unwrap();
|
|
|
|
assert_eq!(vote, other);
|
|
|
|
assert_eq!(other.slot, Some(7));
|
|
|
|
let bytes = bincode::options().serialize(&vote).unwrap();
|
|
|
|
let other = bincode::options().deserialize(&bytes[..]).unwrap();
|
|
|
|
assert_eq!(vote, other);
|
|
|
|
assert_eq!(other.slot, Some(7));
|
|
|
|
}
|
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
#[test]
|
|
|
|
fn test_max_epoch_slots_index() {
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let item = CrdsValue::new_signed(
|
|
|
|
CrdsData::EpochSlots(
|
|
|
|
MAX_EPOCH_SLOTS,
|
|
|
|
EpochSlots::new(keypair.pubkey(), timestamp()),
|
|
|
|
),
|
|
|
|
&keypair,
|
|
|
|
);
|
2020-04-29 18:12:51 -07:00
|
|
|
assert_eq!(item.sanitize(), Err(SanitizeError::ValueOutOfBounds));
|
2020-03-11 21:31:50 -07:00
|
|
|
}
|
2019-11-04 16:19:54 -08:00
|
|
|
|
|
|
|
fn serialize_deserialize_value(value: &mut CrdsValue, keypair: &Keypair) {
|
2019-05-23 03:50:41 -07:00
|
|
|
let num_tries = 10;
|
|
|
|
value.sign(keypair);
|
|
|
|
let original_signature = value.get_signature();
|
|
|
|
for _ in 0..num_tries {
|
|
|
|
let serialized_value = serialize(value).unwrap();
|
|
|
|
let deserialized_value: CrdsValue = deserialize(&serialized_value).unwrap();
|
|
|
|
|
|
|
|
// Signatures shouldn't change
|
|
|
|
let deserialized_signature = deserialized_value.get_signature();
|
|
|
|
assert_eq!(original_signature, deserialized_signature);
|
|
|
|
|
|
|
|
// After deserializing, check that the signature is still the same
|
|
|
|
assert!(deserialized_value.verify());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-11 16:43:30 -07:00
|
|
|
fn verify_signatures(
|
|
|
|
value: &mut CrdsValue,
|
|
|
|
correct_keypair: &Keypair,
|
|
|
|
wrong_keypair: &Keypair,
|
|
|
|
) {
|
|
|
|
assert!(!value.verify());
|
|
|
|
value.sign(&correct_keypair);
|
|
|
|
assert!(value.verify());
|
|
|
|
value.sign(&wrong_keypair);
|
|
|
|
assert!(!value.verify());
|
2019-11-04 16:19:54 -08:00
|
|
|
serialize_deserialize_value(value, correct_keypair);
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
2020-11-22 09:51:14 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_filter_current() {
|
2021-01-21 13:53:10 -08:00
|
|
|
let seed = [48u8; 32];
|
|
|
|
let mut rng = ChaChaRng::from_seed(seed);
|
2020-11-22 09:51:14 -08:00
|
|
|
let keys: Vec<_> = repeat_with(Keypair::new).take(16).collect();
|
|
|
|
let values: Vec<_> = repeat_with(|| {
|
|
|
|
let index = rng.gen_range(0, keys.len());
|
|
|
|
CrdsValue::new_rand(&mut rng, Some(&keys[index]))
|
|
|
|
})
|
2020-12-27 05:31:05 -08:00
|
|
|
.take(2048)
|
2020-11-22 09:51:14 -08:00
|
|
|
.collect();
|
|
|
|
let mut currents = HashMap::new();
|
|
|
|
for value in filter_current(&values) {
|
|
|
|
// Assert that filtered values have unique labels.
|
|
|
|
assert!(currents.insert(value.label(), value).is_none());
|
|
|
|
}
|
|
|
|
// Assert that currents are the most recent version of each value.
|
|
|
|
let mut count = 0;
|
|
|
|
for value in &values {
|
|
|
|
let current_value = currents.get(&value.label()).unwrap();
|
|
|
|
match value.wallclock().cmp(¤t_value.wallclock()) {
|
|
|
|
Ordering::Less => (),
|
|
|
|
Ordering::Equal => {
|
2021-01-25 07:57:46 -08:00
|
|
|
// There is a chance that two randomly generated
|
|
|
|
// crds-values have the same label and wallclock.
|
|
|
|
if value == *current_value {
|
|
|
|
count += 1;
|
|
|
|
}
|
2020-11-22 09:51:14 -08:00
|
|
|
}
|
|
|
|
Ordering::Greater => panic!("this should not happen!"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(count, currents.len());
|
|
|
|
// Currently CrdsData::new_rand is only implemented for 5 different
|
2020-12-27 05:31:05 -08:00
|
|
|
// kinds and excludes EpochSlots, and so the unique labels cannot be
|
|
|
|
// more than (5 + MAX_VOTES) times number of keys.
|
|
|
|
assert!(currents.len() <= keys.len() * (5 + MAX_VOTES as usize));
|
2020-11-22 09:51:14 -08:00
|
|
|
}
|
2020-12-08 06:19:01 -08:00
|
|
|
|
2020-12-10 09:01:55 -08:00
|
|
|
#[test]
|
|
|
|
fn test_node_instance_crds_lable() {
|
|
|
|
fn make_crds_value(node: NodeInstance) -> CrdsValue {
|
|
|
|
CrdsValue::new_unsigned(CrdsData::NodeInstance(node))
|
|
|
|
}
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let now = timestamp();
|
|
|
|
let pubkey = Pubkey::new_unique();
|
|
|
|
let node = NodeInstance::new(&mut rng, pubkey, now);
|
|
|
|
assert_eq!(
|
|
|
|
make_crds_value(node.clone()).label(),
|
|
|
|
make_crds_value(node.with_wallclock(now + 8)).label()
|
|
|
|
);
|
|
|
|
let other = NodeInstance {
|
|
|
|
from: Pubkey::new_unique(),
|
|
|
|
..node
|
|
|
|
};
|
|
|
|
assert_ne!(
|
|
|
|
make_crds_value(node.clone()).label(),
|
|
|
|
make_crds_value(other).label()
|
|
|
|
);
|
|
|
|
let other = NodeInstance {
|
|
|
|
wallclock: now + 8,
|
|
|
|
..node
|
|
|
|
};
|
|
|
|
assert_eq!(
|
|
|
|
make_crds_value(node.clone()).label(),
|
|
|
|
make_crds_value(other).label()
|
|
|
|
);
|
|
|
|
let other = NodeInstance {
|
|
|
|
timestamp: now + 8,
|
|
|
|
..node
|
|
|
|
};
|
|
|
|
assert_eq!(
|
|
|
|
make_crds_value(node.clone()).label(),
|
|
|
|
make_crds_value(other).label()
|
|
|
|
);
|
|
|
|
let other = NodeInstance {
|
|
|
|
token: rng.gen(),
|
|
|
|
..node
|
|
|
|
};
|
|
|
|
assert_ne!(
|
|
|
|
make_crds_value(node).label(),
|
|
|
|
make_crds_value(other).label()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-12-08 06:19:01 -08:00
|
|
|
#[test]
|
|
|
|
fn test_check_duplicate_instance() {
|
|
|
|
fn make_crds_value(node: NodeInstance) -> CrdsValue {
|
|
|
|
CrdsValue::new_unsigned(CrdsData::NodeInstance(node))
|
|
|
|
}
|
|
|
|
let now = timestamp();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let pubkey = Pubkey::new_unique();
|
2020-12-10 09:01:55 -08:00
|
|
|
let node = NodeInstance::new(&mut rng, pubkey, now);
|
2020-12-08 06:19:01 -08:00
|
|
|
// Same token is not a duplicate.
|
|
|
|
assert!(!node.check_duplicate(&make_crds_value(NodeInstance {
|
|
|
|
from: pubkey,
|
|
|
|
wallclock: now + 1,
|
|
|
|
timestamp: now + 1,
|
|
|
|
token: node.token,
|
|
|
|
})));
|
|
|
|
// Older timestamp is not a duplicate.
|
|
|
|
assert!(!node.check_duplicate(&make_crds_value(NodeInstance {
|
|
|
|
from: pubkey,
|
|
|
|
wallclock: now + 1,
|
|
|
|
timestamp: now - 1,
|
|
|
|
token: rng.gen(),
|
|
|
|
})));
|
2020-12-08 12:45:46 -08:00
|
|
|
// Updated wallclock is not a duplicate.
|
|
|
|
let other = node.with_wallclock(now + 8);
|
|
|
|
assert_eq!(
|
|
|
|
other,
|
|
|
|
NodeInstance {
|
|
|
|
from: pubkey,
|
|
|
|
wallclock: now + 8,
|
|
|
|
timestamp: now,
|
|
|
|
token: node.token,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert!(!node.check_duplicate(&make_crds_value(other)));
|
2020-12-08 06:19:01 -08:00
|
|
|
// Duplicate instance.
|
|
|
|
assert!(node.check_duplicate(&make_crds_value(NodeInstance {
|
|
|
|
from: pubkey,
|
|
|
|
wallclock: 0,
|
|
|
|
timestamp: now,
|
|
|
|
token: rng.gen(),
|
|
|
|
})));
|
|
|
|
// Different pubkey is not a duplicate.
|
|
|
|
assert!(!node.check_duplicate(&make_crds_value(NodeInstance {
|
|
|
|
from: Pubkey::new_unique(),
|
|
|
|
wallclock: now + 1,
|
|
|
|
timestamp: now + 1,
|
|
|
|
token: rng.gen(),
|
|
|
|
})));
|
|
|
|
// Differnt crds value is not a duplicate.
|
|
|
|
assert!(
|
|
|
|
!node.check_duplicate(&CrdsValue::new_unsigned(CrdsData::ContactInfo(
|
|
|
|
ContactInfo::new_rand(&mut rng, Some(pubkey))
|
|
|
|
)))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_should_force_push() {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let pubkey = Pubkey::new_unique();
|
|
|
|
assert!(
|
|
|
|
!CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_rand(
|
|
|
|
&mut rng,
|
|
|
|
Some(pubkey),
|
|
|
|
)))
|
|
|
|
.should_force_push(&pubkey)
|
|
|
|
);
|
|
|
|
let node = CrdsValue::new_unsigned(CrdsData::NodeInstance(NodeInstance::new(
|
2020-12-10 09:01:55 -08:00
|
|
|
&mut rng,
|
2020-12-08 06:19:01 -08:00
|
|
|
pubkey,
|
|
|
|
timestamp(),
|
|
|
|
)));
|
|
|
|
assert!(node.should_force_push(&pubkey));
|
|
|
|
assert!(!node.should_force_push(&Pubkey::new_unique()));
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|