2018-12-07 19:16:27 -08:00
|
|
|
use crate::contact_info::ContactInfo;
|
2019-07-30 15:43:17 -07:00
|
|
|
use bincode::{serialize, serialized_size};
|
2018-11-15 13:23:26 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2018-12-03 10:26:28 -08:00
|
|
|
use solana_sdk::signature::{Keypair, Signable, Signature};
|
2018-11-29 16:18:47 -08:00
|
|
|
use solana_sdk::transaction::Transaction;
|
2019-06-12 16:43:05 -07:00
|
|
|
use std::borrow::Cow;
|
2019-05-23 03:50:41 -07:00
|
|
|
use std::collections::BTreeSet;
|
2018-11-15 13:23:26 -08:00
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
/// CrdsValue that is replicated across the cluster
|
2019-08-20 17:16:06 -07:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2018-11-15 13:23:26 -08:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
|
|
pub enum CrdsValue {
|
|
|
|
/// * Merge Strategy - Latest wallclock is picked
|
|
|
|
ContactInfo(ContactInfo),
|
2019-01-31 15:51:29 -08:00
|
|
|
/// * Merge Strategy - Latest wallclock is picked
|
2018-11-15 13:23:26 -08:00
|
|
|
Vote(Vote),
|
2019-05-08 13:50:32 -07:00
|
|
|
/// * Merge Strategy - Latest wallclock is picked
|
|
|
|
EpochSlots(EpochSlots),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
|
|
pub struct EpochSlots {
|
|
|
|
pub from: Pubkey,
|
|
|
|
pub root: u64,
|
2019-05-23 03:50:41 -07:00
|
|
|
pub slots: BTreeSet<u64>,
|
2019-05-08 13:50:32 -07:00
|
|
|
pub signature: Signature,
|
|
|
|
pub wallclock: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EpochSlots {
|
2019-05-23 03:50:41 -07:00
|
|
|
pub fn new(from: Pubkey, root: u64, slots: BTreeSet<u64>, wallclock: u64) -> Self {
|
2019-05-08 13:50:32 -07:00
|
|
|
Self {
|
|
|
|
from,
|
|
|
|
root,
|
|
|
|
slots,
|
|
|
|
signature: Signature::default(),
|
|
|
|
wallclock,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Signable for EpochSlots {
|
|
|
|
fn pubkey(&self) -> Pubkey {
|
|
|
|
self.from
|
|
|
|
}
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
fn signable_data(&self) -> Cow<[u8]> {
|
2019-05-08 13:50:32 -07:00
|
|
|
#[derive(Serialize)]
|
|
|
|
struct SignData<'a> {
|
|
|
|
root: u64,
|
2019-05-23 03:50:41 -07:00
|
|
|
slots: &'a BTreeSet<u64>,
|
2019-05-08 13:50:32 -07:00
|
|
|
wallclock: u64,
|
|
|
|
}
|
|
|
|
let data = SignData {
|
|
|
|
root: self.root,
|
|
|
|
slots: &self.slots,
|
|
|
|
wallclock: self.wallclock,
|
|
|
|
};
|
2019-06-12 16:43:05 -07:00
|
|
|
Cow::Owned(serialize(&data).expect("unable to serialize EpochSlots"))
|
2019-05-08 13:50:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_signature(&self) -> Signature {
|
|
|
|
self.signature
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_signature(&mut self, signature: Signature) {
|
|
|
|
self.signature = signature;
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
|
|
pub struct Vote {
|
2019-03-11 16:43:30 -07:00
|
|
|
pub from: Pubkey,
|
2018-11-15 13:23:26 -08:00
|
|
|
pub transaction: Transaction,
|
2019-03-11 16:43:30 -07:00
|
|
|
pub signature: Signature,
|
2018-11-15 13:23:26 -08:00
|
|
|
pub wallclock: u64,
|
|
|
|
}
|
|
|
|
|
2019-05-08 13:50:32 -07:00
|
|
|
impl Vote {
|
|
|
|
pub fn new(from: &Pubkey, transaction: Transaction, wallclock: u64) -> Self {
|
|
|
|
Self {
|
|
|
|
from: *from,
|
|
|
|
transaction,
|
|
|
|
signature: Signature::default(),
|
|
|
|
wallclock,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-01 12:00:30 -08:00
|
|
|
impl Signable for Vote {
|
|
|
|
fn pubkey(&self) -> Pubkey {
|
2019-03-11 16:43:30 -07:00
|
|
|
self.from
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
fn signable_data(&self) -> Cow<[u8]> {
|
2019-03-11 16:43:30 -07:00
|
|
|
#[derive(Serialize)]
|
2019-05-08 13:50:32 -07:00
|
|
|
struct SignData<'a> {
|
|
|
|
transaction: &'a Transaction,
|
2019-03-11 16:43:30 -07:00
|
|
|
wallclock: u64,
|
|
|
|
}
|
|
|
|
let data = SignData {
|
2019-05-08 13:50:32 -07:00
|
|
|
transaction: &self.transaction,
|
2019-03-11 16:43:30 -07:00
|
|
|
wallclock: self.wallclock,
|
|
|
|
};
|
2019-06-12 16:43:05 -07:00
|
|
|
Cow::Owned(serialize(&data).expect("unable to serialize Vote"))
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_signature(&self) -> Signature {
|
2019-03-11 16:43:30 -07:00
|
|
|
self.signature
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
2019-03-11 16:43:30 -07:00
|
|
|
fn set_signature(&mut self, signature: Signature) {
|
|
|
|
self.signature = signature
|
|
|
|
}
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
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),
|
|
|
|
Vote(Pubkey),
|
2019-05-08 13:50:32 -07:00
|
|
|
EpochSlots(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()),
|
|
|
|
CrdsValueLabel::Vote(_) => write!(f, "Vote({})", self.pubkey()),
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValueLabel::EpochSlots(_) => write!(f, "EpochSlots({})", self.pubkey()),
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CrdsValueLabel {
|
|
|
|
pub fn pubkey(&self) -> Pubkey {
|
|
|
|
match self {
|
|
|
|
CrdsValueLabel::ContactInfo(p) => *p,
|
|
|
|
CrdsValueLabel::Vote(p) => *p,
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValueLabel::EpochSlots(p) => *p,
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
impl CrdsValue {
|
2018-12-01 12:00:30 -08:00
|
|
|
/// Totally unsecure unverfiable 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 {
|
|
|
|
match self {
|
|
|
|
CrdsValue::ContactInfo(contact_info) => contact_info.wallclock,
|
|
|
|
CrdsValue::Vote(vote) => vote.wallclock,
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValue::EpochSlots(vote) => vote.wallclock,
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn label(&self) -> CrdsValueLabel {
|
|
|
|
match self {
|
2018-12-01 12:00:30 -08:00
|
|
|
CrdsValue::ContactInfo(contact_info) => {
|
|
|
|
CrdsValueLabel::ContactInfo(contact_info.pubkey())
|
|
|
|
}
|
|
|
|
CrdsValue::Vote(vote) => CrdsValueLabel::Vote(vote.pubkey()),
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValue::EpochSlots(slots) => CrdsValueLabel::EpochSlots(slots.pubkey()),
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn contact_info(&self) -> Option<&ContactInfo> {
|
|
|
|
match self {
|
|
|
|
CrdsValue::ContactInfo(contact_info) => Some(contact_info),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn vote(&self) -> Option<&Vote> {
|
|
|
|
match self {
|
|
|
|
CrdsValue::Vote(vote) => Some(vote),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2019-05-08 13:50:32 -07:00
|
|
|
pub fn epoch_slots(&self) -> Option<&EpochSlots> {
|
|
|
|
match self {
|
|
|
|
CrdsValue::EpochSlots(slots) => Some(slots),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
/// Return all the possible labels for a record identified by Pubkey.
|
2019-05-08 13:50:32 -07:00
|
|
|
pub fn record_labels(key: &Pubkey) -> [CrdsValueLabel; 3] {
|
2019-03-09 19:28:43 -08:00
|
|
|
[
|
|
|
|
CrdsValueLabel::ContactInfo(*key),
|
|
|
|
CrdsValueLabel::Vote(*key),
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValueLabel::EpochSlots(*key),
|
2019-03-09 19:28:43 -08:00
|
|
|
]
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
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")
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2018-12-01 12:00:30 -08:00
|
|
|
|
|
|
|
impl Signable for CrdsValue {
|
|
|
|
fn sign(&mut self, keypair: &Keypair) {
|
|
|
|
match self {
|
|
|
|
CrdsValue::ContactInfo(contact_info) => contact_info.sign(keypair),
|
|
|
|
CrdsValue::Vote(vote) => vote.sign(keypair),
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValue::EpochSlots(epoch_slots) => epoch_slots.sign(keypair),
|
2018-12-01 12:00:30 -08:00
|
|
|
};
|
|
|
|
}
|
2019-05-08 13:50:32 -07:00
|
|
|
|
2018-12-01 12:00:30 -08:00
|
|
|
fn verify(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
CrdsValue::ContactInfo(contact_info) => contact_info.verify(),
|
|
|
|
CrdsValue::Vote(vote) => vote.verify(),
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValue::EpochSlots(epoch_slots) => epoch_slots.verify(),
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pubkey(&self) -> Pubkey {
|
|
|
|
match self {
|
|
|
|
CrdsValue::ContactInfo(contact_info) => contact_info.pubkey(),
|
|
|
|
CrdsValue::Vote(vote) => vote.pubkey(),
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValue::EpochSlots(epoch_slots) => epoch_slots.pubkey(),
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
fn signable_data(&self) -> Cow<[u8]> {
|
2018-12-01 12:00:30 -08:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_signature(&self) -> Signature {
|
2019-05-23 03:50:41 -07:00
|
|
|
match self {
|
|
|
|
CrdsValue::ContactInfo(contact_info) => contact_info.get_signature(),
|
|
|
|
CrdsValue::Vote(vote) => vote.get_signature(),
|
|
|
|
CrdsValue::EpochSlots(epoch_slots) => epoch_slots.get_signature(),
|
|
|
|
}
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn set_signature(&mut self, _: Signature) {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
use crate::test_tx::test_tx;
|
2019-05-23 03:50:41 -07:00
|
|
|
use bincode::deserialize;
|
2018-12-01 12:00:30 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
|
|
|
use solana_sdk::timing::timestamp;
|
2018-11-15 13:23:26 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_labels() {
|
2019-05-08 13:50:32 -07:00
|
|
|
let mut hits = [false; 3];
|
2018-11-15 13:23:26 -08:00
|
|
|
// this method should cover all the possible labels
|
2019-03-09 19:28:43 -08:00
|
|
|
for v in &CrdsValue::record_labels(&Pubkey::default()) {
|
2018-11-15 13:23:26 -08:00
|
|
|
match v {
|
|
|
|
CrdsValueLabel::ContactInfo(_) => hits[0] = true,
|
|
|
|
CrdsValueLabel::Vote(_) => hits[1] = true,
|
2019-05-08 13:50:32 -07:00
|
|
|
CrdsValueLabel::EpochSlots(_) => hits[2] = true,
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
assert!(hits.iter().all(|x| *x));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_keys_and_values() {
|
|
|
|
let v = CrdsValue::ContactInfo(ContactInfo::default());
|
|
|
|
assert_eq!(v.wallclock(), 0);
|
|
|
|
let key = v.clone().contact_info().unwrap().id;
|
|
|
|
assert_eq!(v.label(), CrdsValueLabel::ContactInfo(key));
|
|
|
|
|
2019-03-11 16:43:30 -07:00
|
|
|
let v = CrdsValue::Vote(Vote::new(&Pubkey::default(), test_tx(), 0));
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(v.wallclock(), 0);
|
2019-03-11 16:43:30 -07:00
|
|
|
let key = v.clone().vote().unwrap().from;
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(v.label(), CrdsValueLabel::Vote(key));
|
2019-05-08 13:50:32 -07:00
|
|
|
|
2019-05-23 03:50:41 -07:00
|
|
|
let v = CrdsValue::EpochSlots(EpochSlots::new(Pubkey::default(), 0, BTreeSet::new(), 0));
|
2019-05-08 13:50:32 -07:00
|
|
|
assert_eq!(v.wallclock(), 0);
|
|
|
|
let key = v.clone().epoch_slots().unwrap().from;
|
|
|
|
assert_eq!(v.label(), CrdsValueLabel::EpochSlots(key));
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
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-03-08 19:28:19 -08:00
|
|
|
let mut v =
|
2019-03-09 19:28:43 -08:00
|
|
|
CrdsValue::ContactInfo(ContactInfo::new_localhost(&keypair.pubkey(), timestamp()));
|
2019-03-11 16:43:30 -07:00
|
|
|
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
|
|
|
v = CrdsValue::Vote(Vote::new(&keypair.pubkey(), test_tx(), timestamp()));
|
|
|
|
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
2019-05-23 03:50:41 -07:00
|
|
|
let btreeset: BTreeSet<u64> = vec![1, 2, 3, 6, 8].into_iter().collect();
|
|
|
|
v = CrdsValue::EpochSlots(EpochSlots::new(keypair.pubkey(), 0, btreeset, timestamp()));
|
2019-05-08 13:50:32 -07:00
|
|
|
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
2019-03-11 16:43:30 -07:00
|
|
|
}
|
|
|
|
|
2019-05-23 03:50:41 -07:00
|
|
|
fn test_serialize_deserialize_value(value: &mut CrdsValue, keypair: &Keypair) {
|
|
|
|
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-05-23 03:50:41 -07:00
|
|
|
test_serialize_deserialize_value(value, correct_keypair);
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|