diff --git a/Cargo.toml b/Cargo.toml index 26b5d18da8..0f6900c6b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "silk" description = "A silky smooth implementation of the Loom architecture" -version = "0.1.3" +version = "0.2.0" documentation = "https://docs.rs/silk" homepage = "http://loomprotocol.com/" repository = "https://github.com/loomprotocol/silk" @@ -11,6 +11,10 @@ authors = [ ] license = "Apache-2.0" +[[bin]] +name = "silk-demo" +path = "src/bin/demo.rs" + [badges] codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" } diff --git a/README.md b/README.md index 2367f5d92e..8eda7bff09 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,54 @@ corresponding benchmarks are also added that demonstrate real performance boots. feature set here will always be a ways behind the loom repo, but that this is an implementation you can take to the bank, literally. +# Usage + +Add the latest [silk package](https://crates.io/crates/silk) to the `[dependencies]` section +of your Cargo.toml. + +Create a *Historian* and send it *events* to generate an *event log*, where each log *entry* +is tagged with the historian's latest *hash*. Then ensure the order of events was not tampered +with by verifying each entry's hash can be generated from the hash in the previous entry: + +```rust +extern crate silk; + +use silk::historian::Historian; +use silk::log::{verify_slice, Entry, Event}; +use std::{thread, time}; +use std::sync::mpsc::SendError; + +fn create_log(hist: &Historian) -> Result<(), SendError> { + hist.sender.send(Event::Tick)?; + thread::sleep(time::Duration::new(0, 100_000)); + hist.sender.send(Event::UserDataKey(0xdeadbeef))?; + thread::sleep(time::Duration::new(0, 100_000)); + hist.sender.send(Event::Tick)?; + Ok(()) +} + +fn main() { + let seed = 0; + 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)); +} +``` + +Running the program should produce a log similar to: + +``` +Entry { num_hashes: 0, end_hash: 0, event: Tick } +Entry { num_hashes: 245, end_hash: 11504657626326377539, event: UserDataKey(3735928559) } +Entry { num_hashes: 154, end_hash: 13410333856574024888, event: Tick } +``` + + # Developing Building diff --git a/src/bin/demo.rs b/src/bin/demo.rs new file mode 100644 index 0000000000..4715d2e083 --- /dev/null +++ b/src/bin/demo.rs @@ -0,0 +1,27 @@ +extern crate silk; + +use silk::historian::Historian; +use silk::log::{verify_slice, Entry, Event}; +use std::{thread, time}; +use std::sync::mpsc::SendError; + +fn create_log(hist: &Historian) -> Result<(), SendError> { + hist.sender.send(Event::Tick)?; + thread::sleep(time::Duration::new(0, 100_000)); + hist.sender.send(Event::UserDataKey(0xdeadbeef))?; + thread::sleep(time::Duration::new(0, 100_000)); + hist.sender.send(Event::Tick)?; + Ok(()) +} + +fn main() { + let seed = 0; + 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)); +} diff --git a/src/historian.rs b/src/historian.rs index 24bd545c20..380d432f22 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::{Entry, Event}; +use log::{hash, Entry, Event}; pub struct Historian { pub sender: Sender, @@ -64,20 +64,16 @@ pub fn create_logger( receiver: Receiver, sender: Sender, ) -> JoinHandle<(Entry, ExitReason)> { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; use std::thread; thread::spawn(move || { let mut end_hash = start_hash; - let mut hasher = DefaultHasher::new(); let mut num_hashes = 0; loop { match log_events(&receiver, &sender, num_hashes, end_hash) { Ok(n) => num_hashes = n, Err(err) => return err, } - end_hash.hash(&mut hasher); - end_hash = hasher.finish(); + end_hash = hash(end_hash); num_hashes += 1; } }) @@ -104,17 +100,22 @@ mod tests { #[test] fn test_historian() { + use std::thread::sleep; + use std::time::Duration; + let hist = Historian::new(0); - let event = Event::Tick; - hist.sender.send(event.clone()).unwrap(); - let entry0 = hist.receiver.recv().unwrap(); - assert_eq!(entry0.event, event); + hist.sender.send(Event::Tick).unwrap(); + sleep(Duration::new(0, 1_000_000)); + hist.sender.send(Event::UserDataKey(0xdeadbeef)).unwrap(); + sleep(Duration::new(0, 1_000_000)); + hist.sender.send(Event::Tick).unwrap(); - let event = Event::UserDataKey(0xdeadbeef); - hist.sender.send(event.clone()).unwrap(); + let entry0 = hist.receiver.recv().unwrap(); let entry1 = hist.receiver.recv().unwrap(); - assert_eq!(entry1.event, event); + let entry2 = hist.receiver.recv().unwrap(); + assert!(entry1.num_hashes != 0); + assert!(entry2.num_hashes != 0); drop(hist.sender); assert_eq!( @@ -122,7 +123,7 @@ mod tests { ExitReason::RecvDisconnected ); - verify_slice(&[entry0, entry1], 0); + assert!(verify_slice(&[entry0, entry1, entry2], 0)); } #[test] diff --git a/src/log.rs b/src/log.rs index 007de3807c..5db5437e28 100644 --- a/src/log.rs +++ b/src/log.rs @@ -48,15 +48,19 @@ impl Entry { } } -/// Creates the next Tick Entry 'num_hashes' after 'start_hash'. -pub fn next_tick(start_hash: u64, num_hashes: u64) -> Entry { +pub fn hash(val: u64) -> u64 { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; - let mut end_hash = start_hash; let mut hasher = DefaultHasher::new(); + val.hash(&mut hasher); + hasher.finish() +} + +/// 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; for _ in 0..num_hashes { - end_hash.hash(&mut hasher); - end_hash = hasher.finish(); + end_hash = hash(end_hash); } Entry::new_tick(num_hashes, end_hash) }