From a74540470ae0e3be384323c588cc2ab696b9aeb5 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Thu, 15 Feb 2018 10:13:56 -0700 Subject: [PATCH] Rename Tick to Event * Define a tick to be an event with no user data. * Use the term "event log" for now. ** Reserve the word "entry" for hash entries, and "item" for array items. ** Reserve the word "blockchain" for when the event is a block of something. ** Reserve the word "ledger" for when the event is of a particular type, such as transactions. --- src/event.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- src/tick.rs | 96 ------------------------------------------ 3 files changed, 116 insertions(+), 97 deletions(-) create mode 100644 src/event.rs delete mode 100644 src/tick.rs diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 000000000..0c6e9b603 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,115 @@ +//! The `event` crate provides the foundational data structures for Proof-of-History + +/// A Proof-of-History is an ordered log of events in time. Each entry contains three +/// pieces of data. The 'n' field is the number of hashes performed since the previous +/// entry. The 'hash' field is the result of hashing 'hash' from the previous entry 'n' +/// times. The 'data' field is an optional foreign key (a hash) pointing to some arbitrary +/// data that a client is looking to associate with the entry. +/// +/// If you divide 'n' by the amount of time it takes to generate a new hash, you +/// get a duration estimate since the last event. Since processing power increases +/// over time, one should expect the duration 'n' represents to decrease proportionally. +/// Though processing power varies across nodes, the network gives priority to the +/// fastest processor. Duration should therefore be estimated by assuming that the hash +/// was generated by the fastest processor at the time the entry was logged. +/// +/// When 'data' is None, the event represents a simple "tick", and exists for the +/// sole purpose of improving the performance of event log verification. A tick can +/// be generated in 'n' hashes and verified in 'n' hashes. By logging a hash alongside +/// the tick, each tick and be verified in parallel using the 'hash' of the preceding +/// tick to seed its hashing. +pub struct Event { + pub hash: u64, + pub n: u64, + pub data: Option, +} + +impl Event { + /// Creates an Event from the number of hashes 'n' since the previous event + /// and that resulting 'hash'. + pub fn new(hash: u64, n: u64) -> Self { + let data = None; + Event { hash, n, data } + } + + /// Creates an Event from by hashing 'seed' 'n' times. + /// + /// ``` + /// use loomination::event::Event; + /// assert_eq!(Event::run(0, 1).n, 1) + /// ``` + pub fn run(seed: u64, n: u64) -> Self { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + let mut hash = seed; + let mut hasher = DefaultHasher::new(); + for _ in 0..n { + hash.hash(&mut hasher); + hash = hasher.finish(); + } + Self::new(hash, n) + } + /// Verifies self.hash is the result of hashing a 'seed' 'self.n' times. + /// + /// ``` + /// use loomination::event::Event; + /// assert!(Event::run(0, 0).verify(0)); // base case + /// assert!(!Event::run(0, 0).verify(1)); // base case, bad + /// assert!(Event::run(0, 1).verify(0)); // inductive case + /// assert!(!Event::run(0, 1).verify(1)); // inductive case, bad + /// ``` + pub fn verify(self: &Self, seed: u64) -> bool { + self.hash == Self::run(seed, self.n).hash + } +} + +/// Verifies the hashes and counts of a slice of events are all consistent. +/// +/// ``` +/// use loomination::event::{verify_slice, Event}; +/// assert!(verify_slice(&vec![], 0)); // base case +/// assert!(verify_slice(&vec![Event::run(0, 0)], 0)); // singleton case 1 +/// assert!(!verify_slice(&vec![Event::run(0, 0)], 1)); // singleton case 2, bad +/// assert!(verify_slice(&vec![Event::run(0, 0), Event::run(0, 0)], 0)); // lazy inductive case +/// assert!(!verify_slice(&vec![Event::run(0, 0), Event::run(1, 0)], 0)); // lazy inductive case, bad +/// ``` +pub fn verify_slice(events: &[Event], seed: u64) -> bool { + use rayon::prelude::*; + let genesis = [Event::run(seed, 0)]; + let event_pairs = genesis.par_iter().chain(events).zip(events); + event_pairs.all(|(x, x1)| x1.verify(x.hash)) +} + +/// Verifies the hashes and events serially. Exists only for reference. +pub fn verify_slice_seq(events: &[Event], seed: u64) -> bool { + let genesis = [Event::run(seed, 0)]; + let event_pairs = genesis.iter().chain(events).zip(events); + event_pairs.into_iter().all(|(x, x1)| x1.verify(x.hash)) +} + +/// Create a vector of Ticks of length 'len' from 'seed' hash and 'hashes_since_prev'. +pub fn create_events(seed: u64, hashes_since_prev: u64, len: usize) -> Vec { + use itertools::unfold; + let mut events = unfold(seed, |state| { + let event = Event::run(*state, hashes_since_prev); + *state = event.hash; + return Some(event); + }); + events.by_ref().take(len).collect() +} + +#[cfg(all(feature = "unstable", test))] +mod bench { + extern crate test; + use self::test::Bencher; + use event; + + #[bench] + fn event_bench(bencher: &mut Bencher) { + let seed = 0; + let events = event::create_events(seed, 100_000, 4); + bencher.iter(|| { + assert!(event::verify_slice(&events, seed)); + }); + } +} diff --git a/src/lib.rs b/src/lib.rs index d08f10f3b..a8ae4177d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ #![cfg_attr(feature = "unstable", feature(test))] -pub mod tick; +pub mod event; extern crate itertools; extern crate rayon; diff --git a/src/tick.rs b/src/tick.rs deleted file mode 100644 index 4d3fa9c91..000000000 --- a/src/tick.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! The `tick` crate provides the foundational data structures for Proof of History - -pub struct Tick { - pub hash: u64, - pub n: u64, - pub data: Option, -} - -impl Tick { - /// Creates a Tick from a 'hash' and how many times it hashed the previous entry 'n'. - pub fn new(hash: u64, n: u64) -> Self { - let data = None; - Tick { hash, n, data } - } - - /// Creates a Tick from by hashing 'seed' 'n' times. - /// - /// ``` - /// use loomination::tick::Tick; - /// assert_eq!(Tick::run(0, 1).n, 1) - /// ``` - pub fn run(seed: u64, n: u64) -> Self { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - let mut hash = seed; - let mut hasher = DefaultHasher::new(); - for _ in 0..n { - hash.hash(&mut hasher); - hash = hasher.finish(); - } - Self::new(hash, n) - } - /// Verifies self.hash is the result of hashing a 'seed' 'self.n' times. - /// - /// ``` - /// use loomination::tick::Tick; - /// assert!(Tick::run(0, 0).verify(0)); // base case - /// assert!(!Tick::run(0, 0).verify(1)); // base case, bad - /// assert!(Tick::run(0, 1).verify(0)); // inductive case - /// assert!(!Tick::run(0, 1).verify(1)); // inductive case, bad - /// ``` - pub fn verify(self: &Self, seed: u64) -> bool { - self.hash == Self::run(seed, self.n).hash - } -} - -/// Verifies the hashes and counts of a slice of ticks are all consistent. -/// -/// ``` -/// use loomination::tick::{verify_slice, Tick}; -/// assert!(verify_slice(&vec![], 0)); // base case -/// assert!(verify_slice(&vec![Tick::run(0, 0)], 0)); // singleton case 1 -/// assert!(!verify_slice(&vec![Tick::run(0, 0)], 1)); // singleton case 2, bad -/// assert!(verify_slice(&vec![Tick::run(0, 0), Tick::run(0, 0)], 0)); // lazy inductive case -/// assert!(!verify_slice(&vec![Tick::run(0, 0), Tick::run(1, 0)], 0)); // lazy inductive case, bad -/// ``` -pub fn verify_slice(ticks: &[Tick], seed: u64) -> bool { - use rayon::prelude::*; - let genesis = [Tick::run(seed, 0)]; - let tick_pairs = genesis.par_iter().chain(ticks).zip(ticks); - tick_pairs.all(|(x, x1)| x1.verify(x.hash)) -} - -/// Verifies the hashes and ticks serially. Exists only for reference. -pub fn verify_slice_seq(ticks: &[Tick], seed: u64) -> bool { - let genesis = [Tick::run(seed, 0)]; - let tick_pairs = genesis.iter().chain(ticks).zip(ticks); - tick_pairs.into_iter().all(|(x, x1)| x1.verify(x.hash)) -} - -/// Create a vector of Ticks of length 'len' from 'seed' hash and 'hashes_per_tick'. -pub fn create_ticks(seed: u64, hashes_per_tick: u64, len: usize) -> Vec { - use itertools::unfold; - let mut ticks_iter = unfold(seed, |state| { - let tick = Tick::run(*state, hashes_per_tick); - *state = tick.hash; - return Some(tick); - }); - ticks_iter.by_ref().take(len).collect() -} - -#[cfg(all(feature = "unstable", test))] -mod bench { - extern crate test; - use self::test::Bencher; - use tick; - - #[bench] - fn tick_bench(bencher: &mut Bencher) { - let seed = 0; - let ticks = tick::create_ticks(seed, 100_000, 4); - bencher.iter(|| { - assert!(tick::verify_slice(&ticks, seed)); - }); - } -}