commit
41053b6d0b
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "silk"
|
name = "silk"
|
||||||
description = "A silky smooth implementation of the Loom architecture"
|
description = "A silky smooth implementation of the Loom architecture"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
documentation = "https://docs.rs/silk"
|
documentation = "https://docs.rs/silk"
|
||||||
homepage = "http://loomprotocol.com/"
|
homepage = "http://loomprotocol.com/"
|
||||||
repository = "https://github.com/loomprotocol/silk"
|
repository = "https://github.com/loomprotocol/silk"
|
||||||
|
@ -11,6 +11,10 @@ authors = [
|
||||||
]
|
]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "silk-demo"
|
||||||
|
path = "src/bin/demo.rs"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" }
|
codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" }
|
||||||
|
|
||||||
|
|
48
README.md
48
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
|
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.
|
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<Event>> {
|
||||||
|
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<Entry> = 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
|
# Developing
|
||||||
|
|
||||||
Building
|
Building
|
||||||
|
|
|
@ -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<Event>> {
|
||||||
|
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<Entry> = hist.receiver.iter().collect();
|
||||||
|
for entry in &entries {
|
||||||
|
println!("{:?}", entry);
|
||||||
|
}
|
||||||
|
assert!(verify_slice(&entries, seed));
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use log::{Entry, Event};
|
use log::{hash, Entry, Event};
|
||||||
|
|
||||||
pub struct Historian {
|
pub struct Historian {
|
||||||
pub sender: Sender<Event>,
|
pub sender: Sender<Event>,
|
||||||
|
@ -64,20 +64,16 @@ pub fn create_logger(
|
||||||
receiver: Receiver<Event>,
|
receiver: Receiver<Event>,
|
||||||
sender: Sender<Entry>,
|
sender: Sender<Entry>,
|
||||||
) -> JoinHandle<(Entry, ExitReason)> {
|
) -> JoinHandle<(Entry, ExitReason)> {
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut end_hash = start_hash;
|
let mut end_hash = start_hash;
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
let mut num_hashes = 0;
|
let mut num_hashes = 0;
|
||||||
loop {
|
loop {
|
||||||
match log_events(&receiver, &sender, num_hashes, end_hash) {
|
match log_events(&receiver, &sender, num_hashes, end_hash) {
|
||||||
Ok(n) => num_hashes = n,
|
Ok(n) => num_hashes = n,
|
||||||
Err(err) => return err,
|
Err(err) => return err,
|
||||||
}
|
}
|
||||||
end_hash.hash(&mut hasher);
|
end_hash = hash(end_hash);
|
||||||
end_hash = hasher.finish();
|
|
||||||
num_hashes += 1;
|
num_hashes += 1;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -104,17 +100,22 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_historian() {
|
fn test_historian() {
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
let hist = Historian::new(0);
|
let hist = Historian::new(0);
|
||||||
|
|
||||||
let event = Event::Tick;
|
hist.sender.send(Event::Tick).unwrap();
|
||||||
hist.sender.send(event.clone()).unwrap();
|
sleep(Duration::new(0, 1_000_000));
|
||||||
let entry0 = hist.receiver.recv().unwrap();
|
hist.sender.send(Event::UserDataKey(0xdeadbeef)).unwrap();
|
||||||
assert_eq!(entry0.event, event);
|
sleep(Duration::new(0, 1_000_000));
|
||||||
|
hist.sender.send(Event::Tick).unwrap();
|
||||||
|
|
||||||
let event = Event::UserDataKey(0xdeadbeef);
|
let entry0 = hist.receiver.recv().unwrap();
|
||||||
hist.sender.send(event.clone()).unwrap();
|
|
||||||
let entry1 = 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);
|
drop(hist.sender);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -122,7 +123,7 @@ mod tests {
|
||||||
ExitReason::RecvDisconnected
|
ExitReason::RecvDisconnected
|
||||||
);
|
);
|
||||||
|
|
||||||
verify_slice(&[entry0, entry1], 0);
|
assert!(verify_slice(&[entry0, entry1, entry2], 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
14
src/log.rs
14
src/log.rs
|
@ -48,15 +48,19 @@ impl Entry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the next Tick Entry 'num_hashes' after 'start_hash'.
|
pub fn hash(val: u64) -> u64 {
|
||||||
pub fn next_tick(start_hash: u64, num_hashes: u64) -> Entry {
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
let mut end_hash = start_hash;
|
|
||||||
let mut hasher = DefaultHasher::new();
|
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 {
|
for _ in 0..num_hashes {
|
||||||
end_hash.hash(&mut hasher);
|
end_hash = hash(end_hash);
|
||||||
end_hash = hasher.finish();
|
|
||||||
}
|
}
|
||||||
Entry::new_tick(num_hashes, end_hash)
|
Entry::new_tick(num_hashes, end_hash)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue