Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use sha256 hashes instead of Rust's builtin hasher. #13

Merged
merged 1 commit into from
Feb 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
8 changes: 4 additions & 4 deletions src/bin/demo.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -15,13 +15,13 @@ fn create_log(hist: &Historian) -> Result<(), SendError<Event>> {
}

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<Entry> = hist.receiver.iter().collect();
for entry in &entries {
println!("{:?}", entry);
}
assert!(verify_slice(&entries, seed));
assert!(verify_slice(&entries, &seed));
}
20 changes: 11 additions & 9 deletions src/historian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Event>,
Expand All @@ -25,7 +25,7 @@ fn log_events(
receiver: &Receiver<Event>,
sender: &Sender<Entry>,
num_hashes: u64,
end_hash: u64,
end_hash: Sha256Hash,
) -> Result<u64, (Entry, ExitReason)> {
use std::sync::mpsc::TryRecvError;
let mut num_hashes = num_hashes;
Expand Down Expand Up @@ -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<Event>,
sender: Sender<Entry>,
) -> JoinHandle<(Entry, ExitReason)> {
Expand All @@ -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,
Expand All @@ -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));
Expand All @@ -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!(
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
93 changes: 51 additions & 42 deletions src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8, U32>;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Entry {
pub num_hashes: u64,
pub end_hash: u64,
pub end_hash: Sha256Hash,
pub event: Event,
}

Expand All @@ -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<Entry> {
pub fn create_ticks(start_hash: &Sha256Hash, num_hashes: u64, len: usize) -> Vec<Entry> {
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);
});
Expand All @@ -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

let mut bad_ticks = create_ticks(0, 0, 2);
bad_ticks[1].end_hash = 1;
assert!(!verify_slice(&bad_ticks, 0)); // inductive step, bad
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(&zero, 0, 2);
bad_ticks[1].end_hash = one;
assert!(!verify_slice(&bad_ticks, &zero)); // inductive step, bad
}

#[test]
Expand All @@ -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));
});
}
}