From 0297edaf1fca3c0821ae2c2d5e713c4bf49114e9 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Mon, 19 Feb 2018 16:17:13 -0700 Subject: [PATCH] Use sha256 hashes instead of Rust's builtin hasher. Causes a 20x performance degradation. Enabling asm did not speed things up. --- .travis.yml | 2 +- Cargo.toml | 4 +++ src/bin/demo.rs | 8 ++--- src/historian.rs | 20 ++++++----- src/lib.rs | 2 ++ src/log.rs | 91 ++++++++++++++++++++++++++---------------------- 6 files changed, 72 insertions(+), 55 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ee3205fc4..da4cd01db3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ matrix: - rust: stable - rust: nightly env: - - FEATURES='unstable' + - FEATURES='asm,unstable' before_script: | export PATH="$PATH:$HOME/.cargo/bin" rustup component add rustfmt-preview diff --git a/Cargo.toml b/Cargo.toml index 0f6900c6b4..21fc26483e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,11 @@ codecov = { repository = "loomprotocol/silk", branch = "master", service = "gith [features] unstable = [] +asm = ["sha2-asm"] [dependencies] rayon = "1.0.0" itertools = "0.7.6" +sha2 = "0.7.0" +sha2-asm = {version="0.3", optional=true} +digest = "0.7.2" diff --git a/src/bin/demo.rs b/src/bin/demo.rs index 4715d2e083..962781e3f1 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -1,7 +1,7 @@ extern crate silk; use silk::historian::Historian; -use silk::log::{verify_slice, Entry, Event}; +use silk::log::{verify_slice, Entry, Event, Sha256Hash}; use std::{thread, time}; use std::sync::mpsc::SendError; @@ -15,13 +15,13 @@ fn create_log(hist: &Historian) -> Result<(), SendError> { } fn main() { - let seed = 0; - let hist = Historian::new(seed); + let seed = Sha256Hash::default(); + let hist = Historian::new(&seed); create_log(&hist).expect("send error"); drop(hist.sender); let entries: Vec = hist.receiver.iter().collect(); for entry in &entries { println!("{:?}", entry); } - assert!(verify_slice(&entries, seed)); + assert!(verify_slice(&entries, &seed)); } diff --git a/src/historian.rs b/src/historian.rs index d0a282589f..8f410e4a80 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -7,7 +7,7 @@ use std::thread::JoinHandle; use std::sync::mpsc::{Receiver, Sender}; -use log::{hash, Entry, Event}; +use log::{hash, Entry, Event, Sha256Hash}; pub struct Historian { pub sender: Sender, @@ -25,7 +25,7 @@ fn log_events( receiver: &Receiver, sender: &Sender, num_hashes: u64, - end_hash: u64, + end_hash: Sha256Hash, ) -> Result { use std::sync::mpsc::TryRecvError; let mut num_hashes = num_hashes; @@ -60,7 +60,7 @@ fn log_events( /// A background thread that will continue tagging received Event messages and /// sending back Entry messages until either the receiver or sender channel is closed. pub fn create_logger( - start_hash: u64, + start_hash: Sha256Hash, receiver: Receiver, sender: Sender, ) -> JoinHandle<(Entry, ExitReason)> { @@ -73,18 +73,18 @@ pub fn create_logger( Ok(n) => num_hashes = n, Err(err) => return err, } - end_hash = hash(end_hash); + end_hash = hash(&end_hash); num_hashes += 1; } }) } impl Historian { - pub fn new(start_hash: u64) -> Self { + pub fn new(start_hash: &Sha256Hash) -> Self { use std::sync::mpsc::channel; let (sender, event_receiver) = channel(); let (entry_sender, receiver) = channel(); - let thread_hdl = create_logger(start_hash, event_receiver, entry_sender); + let thread_hdl = create_logger(*start_hash, event_receiver, entry_sender); Historian { sender, receiver, @@ -103,7 +103,8 @@ mod tests { use std::thread::sleep; use std::time::Duration; - let hist = Historian::new(0); + let zero = Sha256Hash::default(); + let hist = Historian::new(&zero); hist.sender.send(Event::Tick).unwrap(); sleep(Duration::new(0, 1_000_000)); @@ -121,12 +122,13 @@ mod tests { ExitReason::RecvDisconnected ); - assert!(verify_slice(&[entry0, entry1, entry2], 0)); + assert!(verify_slice(&[entry0, entry1, entry2], &zero)); } #[test] fn test_historian_closed_sender() { - let hist = Historian::new(0); + let zero = Sha256Hash::default(); + let hist = Historian::new(&zero); drop(hist.receiver); hist.sender.send(Event::Tick).unwrap(); assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index f6952625a8..5087d40cf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![cfg_attr(feature = "unstable", feature(test))] pub mod log; pub mod historian; +extern crate digest; extern crate itertools; extern crate rayon; +extern crate sha2; diff --git a/src/log.rs b/src/log.rs index 5db5437e28..4eef785269 100644 --- a/src/log.rs +++ b/src/log.rs @@ -12,10 +12,15 @@ /// 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. + +use digest::generic_array::GenericArray; +use digest::generic_array::typenum::U32; +pub type Sha256Hash = GenericArray; + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Entry { pub num_hashes: u64, - pub end_hash: u64, + pub end_hash: Sha256Hash, pub event: Event, } @@ -33,58 +38,57 @@ pub enum Event { impl Entry { /// Creates a Entry from the number of hashes 'num_hashes' since the previous event /// and that resulting 'end_hash'. - pub fn new_tick(num_hashes: u64, end_hash: u64) -> Self { + pub fn new_tick(num_hashes: u64, end_hash: &Sha256Hash) -> Self { let event = Event::Tick; Entry { num_hashes, - end_hash, + end_hash: *end_hash, event, } } /// Verifies self.end_hash is the result of hashing a 'start_hash' 'self.num_hashes' times. - pub fn verify(self: &Self, start_hash: u64) -> bool { + pub fn verify(self: &Self, start_hash: &Sha256Hash) -> bool { self.end_hash == next_tick(start_hash, self.num_hashes).end_hash } } -pub fn hash(val: u64) -> u64 { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - let mut hasher = DefaultHasher::new(); - val.hash(&mut hasher); - hasher.finish() +pub fn hash(val: &[u8]) -> Sha256Hash { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::default(); + hasher.input(val); + hasher.result() } /// Creates the next Tick Entry 'num_hashes' after 'start_hash'. -pub fn next_tick(start_hash: u64, num_hashes: u64) -> Entry { - let mut end_hash = start_hash; +pub fn next_tick(start_hash: &Sha256Hash, num_hashes: u64) -> Entry { + let mut end_hash = *start_hash; for _ in 0..num_hashes { - end_hash = hash(end_hash); + end_hash = hash(&end_hash); } - Entry::new_tick(num_hashes, end_hash) + Entry::new_tick(num_hashes, &end_hash) } /// Verifies the hashes and counts of a slice of events are all consistent. -pub fn verify_slice(events: &[Entry], start_hash: u64) -> bool { +pub fn verify_slice(events: &[Entry], start_hash: &Sha256Hash) -> bool { use rayon::prelude::*; - let genesis = [Entry::new_tick(0, start_hash)]; + let genesis = [Entry::new_tick(Default::default(), start_hash)]; let event_pairs = genesis.par_iter().chain(events).zip(events); - event_pairs.all(|(x0, x1)| x1.verify(x0.end_hash)) + event_pairs.all(|(x0, x1)| x1.verify(&x0.end_hash)) } /// Verifies the hashes and events serially. Exists only for reference. -pub fn verify_slice_seq(events: &[Entry], start_hash: u64) -> bool { +pub fn verify_slice_seq(events: &[Entry], start_hash: &Sha256Hash) -> bool { let genesis = [Entry::new_tick(0, start_hash)]; let mut event_pairs = genesis.iter().chain(events).zip(events); - event_pairs.all(|(x0, x1)| x1.verify(x0.end_hash)) + event_pairs.all(|(x0, x1)| x1.verify(&x0.end_hash)) } /// Create a vector of Ticks of length 'len' from 'start_hash' hash and 'num_hashes'. -pub fn create_ticks(start_hash: u64, num_hashes: u64, len: usize) -> Vec { +pub fn create_ticks(start_hash: &Sha256Hash, num_hashes: u64, len: usize) -> Vec { use itertools::unfold; - let mut events = unfold(start_hash, |state| { - let event = next_tick(*state, num_hashes); + let mut events = unfold(*start_hash, |state| { + let event = next_tick(state, num_hashes); *state = event.end_hash; return Some(event); }); @@ -97,26 +101,31 @@ mod tests { #[test] fn test_event_verify() { - assert!(Entry::new_tick(0, 0).verify(0)); // base case - assert!(!Entry::new_tick(0, 0).verify(1)); // base case, bad - assert!(next_tick(0, 1).verify(0)); // inductive step - assert!(!next_tick(0, 1).verify(1)); // inductive step, bad + let zero = Sha256Hash::default(); + let one = hash(&zero); + assert!(Entry::new_tick(0, &zero).verify(&zero)); // base case + assert!(!Entry::new_tick(0, &zero).verify(&one)); // base case, bad + assert!(next_tick(&zero, 1).verify(&zero)); // inductive step + assert!(!next_tick(&zero, 1).verify(&one)); // inductive step, bad } #[test] fn test_next_tick() { - assert_eq!(next_tick(0, 1).num_hashes, 1) + let zero = Sha256Hash::default(); + assert_eq!(next_tick(&zero, 1).num_hashes, 1) } - fn verify_slice_generic(verify_slice: fn(&[Entry], u64) -> bool) { - assert!(verify_slice(&vec![], 0)); // base case - assert!(verify_slice(&vec![Entry::new_tick(0, 0)], 0)); // singleton case 1 - assert!(!verify_slice(&vec![Entry::new_tick(0, 0)], 1)); // singleton case 2, bad - assert!(verify_slice(&create_ticks(0, 0, 2), 0)); // inductive step + fn verify_slice_generic(verify_slice: fn(&[Entry], &Sha256Hash) -> bool) { + let zero = Sha256Hash::default(); + let one = hash(&zero); + assert!(verify_slice(&vec![], &zero)); // base case + assert!(verify_slice(&vec![Entry::new_tick(0, &zero)], &zero)); // singleton case 1 + assert!(!verify_slice(&vec![Entry::new_tick(0, &zero)], &one)); // singleton case 2, bad + assert!(verify_slice(&create_ticks(&zero, 0, 2), &zero)); // inductive step - let mut bad_ticks = create_ticks(0, 0, 2); - bad_ticks[1].end_hash = 1; - assert!(!verify_slice(&bad_ticks, 0)); // inductive step, bad + let mut bad_ticks = create_ticks(&zero, 0, 2); + bad_ticks[1].end_hash = one; + assert!(!verify_slice(&bad_ticks, &zero)); // inductive step, bad } #[test] @@ -139,19 +148,19 @@ mod bench { #[bench] fn event_bench(bencher: &mut Bencher) { - let start_hash = 0; - let events = create_ticks(start_hash, 100_000, 8); + let start_hash = Default::default(); + let events = create_ticks(&start_hash, 100_000, 8); bencher.iter(|| { - assert!(verify_slice(&events, start_hash)); + assert!(verify_slice(&events, &start_hash)); }); } #[bench] fn event_bench_seq(bencher: &mut Bencher) { - let start_hash = 0; - let events = create_ticks(start_hash, 100_000, 8); + let start_hash = Default::default(); + let events = create_ticks(&start_hash, 100_000, 8); bencher.iter(|| { - assert!(verify_slice_seq(&events, start_hash)); + assert!(verify_slice_seq(&events, &start_hash)); }); } }