From fa72160c954c82ae7afc4881df98c65b785ed71d Mon Sep 17 00:00:00 2001 From: Rob Walker Date: Mon, 12 Nov 2018 17:03:23 -0800 Subject: [PATCH] add last_id to Entry, PohEntry (#1783) add prev_id to Entry, PohEntry --- src/cluster_info.rs | 8 ++++++-- src/entry.rs | 36 +++++++++++++++++++++++++---------- src/ledger.rs | 7 ++++--- src/poh.rs | 46 ++++++++++++++++++++++++++++++--------------- src/poh_recorder.rs | 32 +++++++++++++++---------------- 5 files changed, 82 insertions(+), 47 deletions(-) diff --git a/src/cluster_info.rs b/src/cluster_info.rs index 3f81d7c30..cbdd85cad 100644 --- a/src/cluster_info.rs +++ b/src/cluster_info.rs @@ -1670,8 +1670,12 @@ mod tests { let zero = Hash::default(); let one = hash(&zero.as_ref()); writer - .write_entries(&vec![Entry::new_tick(0, &zero), Entry::new_tick(0, &one)].to_vec()) - .unwrap(); + .write_entries( + &vec![ + Entry::new_tick(&zero, 0, &zero), + Entry::new_tick(&one, 0, &one), + ].to_vec(), + ).unwrap(); path } diff --git a/src/entry.rs b/src/entry.rs index 4198a194c..2ec57c4e4 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -33,6 +33,9 @@ pub type EntryReceiver = Receiver>; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Entry { + /// The the previous Entry ID. + pub prev_id: Hash, + /// The number of hashes since the previous Entry ID. pub num_hashes: u64, @@ -47,19 +50,21 @@ pub struct Entry { impl Entry { /// Creates the next Entry `num_hashes` after `start_hash`. - pub fn new(start_hash: &Hash, num_hashes: u64, transactions: Vec) -> Self { + pub fn new(prev_id: &Hash, num_hashes: u64, transactions: Vec) -> Self { let entry = { if num_hashes == 0 && transactions.is_empty() { Entry { + prev_id: *prev_id, num_hashes: 0, - id: *start_hash, + id: *prev_id, transactions, } } else if num_hashes == 0 { // If you passed in transactions, but passed in num_hashes == 0, then // next_hash will generate the next hash and set num_hashes == 1 - let id = next_hash(start_hash, 1, &transactions); + let id = next_hash(prev_id, 1, &transactions); Entry { + prev_id: *prev_id, num_hashes: 1, id, transactions, @@ -68,8 +73,9 @@ impl Entry { // Otherwise, the next Entry `num_hashes` after `start_hash`. // If you wanted a tick for instance, then pass in num_hashes = 1 // and transactions = empty - let id = next_hash(start_hash, num_hashes, &transactions); + let id = next_hash(prev_id, num_hashes, &transactions); Entry { + prev_id: *prev_id, num_hashes, id, transactions, @@ -121,7 +127,10 @@ impl Entry { /// Estimate serialized_size of Entry without creating an Entry. pub fn serialized_size(transactions: &[Transaction]) -> u64 { let txs_size = serialized_size(transactions).unwrap(); - size_of::() as u64 + size_of::() as u64 + txs_size + + // num_hashes + id + prev_id + txs + + (size_of::() + 2 * size_of::()) as u64 + txs_size } pub fn num_will_fit(transactions: &[Transaction]) -> usize { @@ -175,14 +184,19 @@ impl Entry { /// Creates a Entry from the number of hashes `num_hashes` since the previous transaction /// and that resulting `id`. - pub fn new_tick(num_hashes: u64, id: &Hash) -> Self { + pub fn new_tick(prev_id: &Hash, num_hashes: u64, id: &Hash) -> Self { Entry { + prev_id: *prev_id, num_hashes, id: *id, transactions: vec![], } } + pub fn verify_self(&self) -> bool { + self.id == next_hash(&self.prev_id, self.num_hashes, &self.transactions) + } + /// Verifies self.id is the result of hashing a `start_hash` `self.num_hashes` times. /// If the transaction is not a Tick, then hash that as well. pub fn verify(&self, start_hash: &Hash) -> bool { @@ -225,11 +239,12 @@ fn next_hash(start_hash: &Hash, num_hashes: u64, transactions: &[Transaction]) - } /// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`. -pub fn next_entry(start_hash: &Hash, num_hashes: u64, transactions: Vec) -> Entry { +pub fn next_entry(prev_id: &Hash, num_hashes: u64, transactions: Vec) -> Entry { assert!(num_hashes > 0 || transactions.is_empty()); Entry { + prev_id: *prev_id, num_hashes, - id: next_hash(start_hash, num_hashes, &transactions), + id: next_hash(prev_id, num_hashes, &transactions), transactions, } } @@ -249,9 +264,10 @@ mod tests { fn test_entry_verify() { let zero = Hash::default(); let one = hash(&zero.as_ref()); - assert!(Entry::new_tick(0, &zero).verify(&zero)); // base case - assert!(!Entry::new_tick(0, &zero).verify(&one)); // base case, bad + assert!(Entry::new_tick(&zero, 0, &zero).verify(&zero)); // base case, never used + assert!(!Entry::new_tick(&zero, 0, &zero).verify(&one)); // base case, bad assert!(next_entry(&zero, 1, vec![]).verify(&zero)); // inductive step + assert!(next_entry(&zero, 1, vec![]).verify_self()); // also inductive step assert!(!next_entry(&zero, 1, vec![]).verify(&one)); // inductive step, bad } diff --git a/src/ledger.rs b/src/ledger.rs index a6b248814..ece67648c 100644 --- a/src/ledger.rs +++ b/src/ledger.rs @@ -463,7 +463,7 @@ pub trait Block { impl Block for [Entry] { fn verify(&self, start_hash: &Hash) -> bool { - let genesis = [Entry::new_tick(0, start_hash)]; + let genesis = [Entry::new_tick(start_hash, 0, start_hash)]; let entry_pairs = genesis.par_iter().chain(self).zip(self); entry_pairs.all(|(x0, x1)| { let r = x1.verify(&x0.id); @@ -710,8 +710,8 @@ mod tests { let zero = Hash::default(); let one = hash(&zero.as_ref()); assert!(vec![][..].verify(&zero)); // base case - assert!(vec![Entry::new_tick(0, &zero)][..].verify(&zero)); // singleton case 1 - assert!(!vec![Entry::new_tick(0, &zero)][..].verify(&one)); // singleton case 2, bad + assert!(vec![Entry::new_tick(&zero, 0, &zero)][..].verify(&zero)); // singleton case 1 + assert!(!vec![Entry::new_tick(&zero, 0, &zero)][..].verify(&one)); // singleton case 2, bad assert!(vec![next_entry(&zero, 0, vec![]); 2][..].verify(&zero)); // inductive step let mut bad_ticks = vec![next_entry(&zero, 0, vec![]); 2]; @@ -781,6 +781,7 @@ mod tests { let tx_small_size = serialized_size(&tx_small).unwrap() as usize; let tx_large_size = serialized_size(&tx_large).unwrap() as usize; let entry_size = serialized_size(&Entry { + prev_id: Hash::default(), num_hashes: 0, id: Hash::default(), transactions: vec![], diff --git a/src/poh.rs b/src/poh.rs index 701f72163..934c29998 100644 --- a/src/poh.rs +++ b/src/poh.rs @@ -3,40 +3,48 @@ use hash::{hash, hashv, Hash}; pub struct Poh { - last_hash: Hash, + prev_id: Hash, + id: Hash, num_hashes: u64, pub tick_height: u64, } #[derive(Debug)] pub struct PohEntry { + pub prev_id: Hash, pub num_hashes: u64, pub id: Hash, pub mixin: Option, } impl Poh { - pub fn new(last_hash: Hash, tick_height: u64) -> Self { + pub fn new(prev_id: Hash, tick_height: u64) -> Self { Poh { - last_hash, + prev_id, num_hashes: 0, + id: prev_id, tick_height, } } pub fn hash(&mut self) { - self.last_hash = hash(&self.last_hash.as_ref()); + self.id = hash(&self.id.as_ref()); self.num_hashes += 1; } pub fn record(&mut self, mixin: Hash) -> PohEntry { - let num_hashes = self.num_hashes + 1; - self.last_hash = hashv(&[&self.last_hash.as_ref(), &mixin.as_ref()]); + self.id = hashv(&[&self.id.as_ref(), &mixin.as_ref()]); + let prev_id = self.prev_id; + self.prev_id = self.id; + + let num_hashes = self.num_hashes + 1; self.num_hashes = 0; + PohEntry { + prev_id, num_hashes, - id: self.last_hash, + id: self.id, mixin: Some(mixin), } } @@ -48,11 +56,16 @@ impl Poh { let num_hashes = self.num_hashes; self.num_hashes = 0; + + let prev_id = self.prev_id; + self.prev_id = self.id; + self.tick_height += 1; PohEntry { + prev_id, num_hashes, - id: self.last_hash, + id: self.id, mixin: None, } } @@ -60,21 +73,22 @@ impl Poh { #[cfg(test)] pub fn verify(initial: Hash, entries: &[PohEntry]) -> bool { - let mut last_hash = initial; + let mut prev_id = initial; for entry in entries { assert!(entry.num_hashes != 0); + assert!(prev_id == entry.prev_id); + for _ in 1..entry.num_hashes { - last_hash = hash(&last_hash.as_ref()); + prev_id = hash(&prev_id.as_ref()); } - let id = match entry.mixin { - Some(mixin) => hashv(&[&last_hash.as_ref(), &mixin.as_ref()]), - None => hash(&last_hash.as_ref()), + prev_id = match entry.mixin { + Some(mixin) => hashv(&[&prev_id.as_ref(), &mixin.as_ref()]), + None => hash(&prev_id.as_ref()), }; - if id != entry.id { + if prev_id != entry.id { return false; } - last_hash = id; } true @@ -91,10 +105,12 @@ mod tests { poh::verify( Hash::default(), &[PohEntry { + prev_id: Hash::default(), num_hashes: 0, id: Hash::default(), mixin: None, }], ); } + } diff --git a/src/poh_recorder.rs b/src/poh_recorder.rs index bfee2039d..a1b1c8368 100644 --- a/src/poh_recorder.rs +++ b/src/poh_recorder.rs @@ -80,15 +80,6 @@ impl PohRecorder { } } - fn generate_tick_entry(&self, poh: &mut Poh) -> Entry { - let tick = poh.tick(); - Entry { - num_hashes: tick.num_hashes, - id: tick.id, - transactions: vec![], - } - } - fn is_max_tick_height_reached(&self, poh: &Poh) -> bool { if let Some(max_tick_height) = self.max_tick_height { poh.tick_height >= max_tick_height @@ -98,11 +89,12 @@ impl PohRecorder { } fn record_and_send_txs(&self, poh: &mut Poh, mixin: Hash, txs: Vec) -> Result<()> { - let tick = poh.record(mixin); + let entry = poh.record(mixin); assert!(!txs.is_empty(), "Entries without transactions are used to track real-time passing in the ledger and can only be generated with PohRecorder::tick function"); let entry = Entry { - num_hashes: tick.num_hashes, - id: tick.id, + prev_id: entry.prev_id, + num_hashes: entry.num_hashes, + id: entry.id, transactions: txs, }; self.sender.send(vec![entry])?; @@ -110,9 +102,15 @@ impl PohRecorder { } fn register_and_send_tick(&self, poh: &mut Poh) -> Result<()> { - let tick_entry = self.generate_tick_entry(poh); - self.bank.register_tick(&tick_entry.id); - self.sender.send(vec![tick_entry])?; + let tick = poh.tick(); + let tick = Entry { + prev_id: tick.prev_id, + num_hashes: tick.num_hashes, + id: tick.id, + transactions: vec![], + }; + self.bank.register_tick(&tick.id); + self.sender.send(vec![tick])?; Ok(()) } } @@ -130,9 +128,9 @@ mod tests { fn test_poh() { let mint = Mint::new(1); let bank = Arc::new(Bank::new(&mint)); - let last_id = bank.last_id(); + let prev_id = bank.last_id(); let (entry_sender, entry_receiver) = channel(); - let mut poh_recorder = PohRecorder::new(bank, entry_sender, last_id, None); + let mut poh_recorder = PohRecorder::new(bank, entry_sender, prev_id, None); //send some data let h1 = hash(b"hello world!");