solana/src/crds_value.rs

277 lines
8.0 KiB
Rust

use crate::contact_info::ContactInfo;
use bincode::serialize;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signable, Signature};
use solana_sdk::transaction::Transaction;
use std::fmt;
/// CrdsValue that is replicated across the cluster
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum CrdsValue {
/// * Merge Strategy - Latest wallclock is picked
ContactInfo(ContactInfo),
/// * Merge Strategy - Latest wallclock is picked
Vote(Vote),
/// * Merge Strategy - Latest wallclock is picked
LeaderId(LeaderId),
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct LeaderId {
pub id: Pubkey,
pub signature: Signature,
pub leader_id: Pubkey,
pub wallclock: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Vote {
pub transaction: Transaction,
pub signature: Signature,
pub wallclock: u64,
}
impl Signable for LeaderId {
fn pubkey(&self) -> Pubkey {
self.id
}
fn signable_data(&self) -> Vec<u8> {
#[derive(Serialize)]
struct SignData {
id: Pubkey,
leader_id: Pubkey,
wallclock: u64,
}
let data = SignData {
id: self.id,
leader_id: self.leader_id,
wallclock: self.wallclock,
};
serialize(&data).expect("unable to serialize LeaderId")
}
fn get_signature(&self) -> Signature {
self.signature
}
fn set_signature(&mut self, signature: Signature) {
self.signature = signature
}
}
impl Signable for Vote {
fn pubkey(&self) -> Pubkey {
self.transaction.account_keys[0]
}
fn signable_data(&self) -> Vec<u8> {
#[derive(Serialize)]
struct SignData {
transaction: Transaction,
wallclock: u64,
}
let data = SignData {
transaction: self.transaction.clone(),
wallclock: self.wallclock,
};
serialize(&data).expect("unable to serialize Vote")
}
fn get_signature(&self) -> Signature {
self.signature
}
fn set_signature(&mut self, signature: Signature) {
self.signature = signature
}
}
/// Type of the replicated value
/// These are labels for values in a record that is associated with `Pubkey`
#[derive(PartialEq, Hash, Eq, Clone, Debug)]
pub enum CrdsValueLabel {
ContactInfo(Pubkey),
Vote(Pubkey),
LeaderId(Pubkey),
}
impl fmt::Display for CrdsValueLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CrdsValueLabel::ContactInfo(_) => write!(f, "ContactInfo({})", self.pubkey()),
CrdsValueLabel::Vote(_) => write!(f, "Vote({})", self.pubkey()),
CrdsValueLabel::LeaderId(_) => write!(f, "LeaderId({})", self.pubkey()),
}
}
}
impl CrdsValueLabel {
pub fn pubkey(&self) -> Pubkey {
match self {
CrdsValueLabel::ContactInfo(p) => *p,
CrdsValueLabel::Vote(p) => *p,
CrdsValueLabel::LeaderId(p) => *p,
}
}
}
impl LeaderId {
pub fn new(id: Pubkey, leader_id: Pubkey, wallclock: u64) -> Self {
LeaderId {
id,
signature: Signature::default(),
leader_id,
wallclock,
}
}
}
impl Vote {
// TODO: it might make sense for the transaction to encode the wallclock in the userdata
pub fn new(transaction: Transaction, wallclock: u64) -> Self {
Vote {
transaction,
signature: Signature::default(),
wallclock,
}
}
}
impl CrdsValue {
/// Totally unsecure unverfiable wallclock of the node that generated this message
/// 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,
CrdsValue::LeaderId(leader_id) => leader_id.wallclock,
}
}
pub fn label(&self) -> CrdsValueLabel {
match self {
CrdsValue::ContactInfo(contact_info) => {
CrdsValueLabel::ContactInfo(contact_info.pubkey())
}
CrdsValue::Vote(vote) => CrdsValueLabel::Vote(vote.pubkey()),
CrdsValue::LeaderId(leader_id) => CrdsValueLabel::LeaderId(leader_id.pubkey()),
}
}
pub fn contact_info(&self) -> Option<&ContactInfo> {
match self {
CrdsValue::ContactInfo(contact_info) => Some(contact_info),
_ => None,
}
}
pub fn leader_id(&self) -> Option<&LeaderId> {
match self {
CrdsValue::LeaderId(leader_id) => Some(leader_id),
_ => None,
}
}
pub fn vote(&self) -> Option<&Vote> {
match self {
CrdsValue::Vote(vote) => Some(vote),
_ => None,
}
}
/// Return all the possible labels for a record identified by Pubkey.
pub fn record_labels(key: Pubkey) -> [CrdsValueLabel; 3] {
[
CrdsValueLabel::ContactInfo(key),
CrdsValueLabel::Vote(key),
CrdsValueLabel::LeaderId(key),
]
}
}
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),
CrdsValue::LeaderId(leader_id) => leader_id.sign(keypair),
};
}
fn verify(&self) -> bool {
match self {
CrdsValue::ContactInfo(contact_info) => contact_info.verify(),
CrdsValue::Vote(vote) => vote.verify(),
CrdsValue::LeaderId(leader_id) => leader_id.verify(),
}
}
fn pubkey(&self) -> Pubkey {
match self {
CrdsValue::ContactInfo(contact_info) => contact_info.pubkey(),
CrdsValue::Vote(vote) => vote.pubkey(),
CrdsValue::LeaderId(leader_id) => leader_id.pubkey(),
}
}
fn signable_data(&self) -> Vec<u8> {
unimplemented!()
}
fn get_signature(&self) -> Signature {
unimplemented!()
}
fn set_signature(&mut self, _: Signature) {
unimplemented!()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::contact_info::ContactInfo;
use crate::test_tx::test_tx;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::timing::timestamp;
#[test]
fn test_labels() {
let mut hits = [false; 3];
// this method should cover all the possible labels
for v in &CrdsValue::record_labels(Pubkey::default()) {
match v {
CrdsValueLabel::ContactInfo(_) => hits[0] = true,
CrdsValueLabel::Vote(_) => hits[1] = true,
CrdsValueLabel::LeaderId(_) => hits[2] = true,
}
}
assert!(hits.iter().all(|x| *x));
}
#[test]
fn test_keys_and_values() {
let v = CrdsValue::LeaderId(LeaderId::default());
let key = v.clone().leader_id().unwrap().id;
assert_eq!(v.wallclock(), 0);
assert_eq!(v.label(), CrdsValueLabel::LeaderId(key));
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));
let v = CrdsValue::Vote(Vote::new(test_tx(), 0));
assert_eq!(v.wallclock(), 0);
let key = v.clone().vote().unwrap().transaction.account_keys[0];
assert_eq!(v.label(), CrdsValueLabel::Vote(key));
}
#[test]
fn test_signature() {
let keypair = Keypair::new();
let fake_keypair = Keypair::new();
let leader = LeaderId::new(keypair.pubkey(), Pubkey::default(), timestamp());
let mut v = CrdsValue::LeaderId(leader);
v.sign(&keypair);
assert!(v.verify());
v.sign(&fake_keypair);
assert!(!v.verify());
}
}