diff --git a/doc/historian.md b/doc/historian.md index 8351645688..4f29267e02 100644 --- a/doc/historian.md +++ b/doc/historian.md @@ -11,15 +11,15 @@ with by verifying each entry's hash can be generated from the hash in the previo extern crate silk; use silk::historian::Historian; -use silk::log::{verify_slice, Entry, Sha256Hash}; +use silk::log::{verify_slice, Entry, Hash}; use silk::event::{generate_keypair, get_pubkey, sign_claim_data, Event}; use std::thread::sleep; use std::time::Duration; use std::sync::mpsc::SendError; -fn create_log(hist: &Historian) -> Result<(), SendError>> { +fn create_log(hist: &Historian) -> Result<(), SendError>> { sleep(Duration::from_millis(15)); - let asset = Sha256Hash::default(); + let asset = Hash::default(); let keypair = generate_keypair(); let event0 = Event::new_claim(get_pubkey(&keypair), asset, sign_claim_data(&asset, &keypair)); hist.sender.send(event0)?; @@ -28,11 +28,11 @@ fn create_log(hist: &Historian) -> Result<(), SendError> = hist.receiver.iter().collect(); + let entries: Vec> = hist.receiver.iter().collect(); for entry in &entries { println!("{:?}", entry); } diff --git a/src/accountant.rs b/src/accountant.rs index 3af3949652..96bbb03366 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -2,7 +2,8 @@ //! event log to record transactions. Its users can deposit funds and //! transfer funds to other users. -use log::{Entry, Sha256Hash}; +use hash::Hash; +use entry::Entry; use event::Event; use transaction::Transaction; use signature::{PublicKey, Signature}; @@ -26,8 +27,8 @@ pub type Result = result::Result; pub struct Accountant { pub historian: Historian, pub balances: HashMap, - pub first_id: Sha256Hash, - pub last_id: Sha256Hash, + pub first_id: Hash, + pub last_id: Hash, } impl Accountant { @@ -66,7 +67,7 @@ impl Accountant { Self::new_from_entries(gen.create_entries(), ms_per_tick) } - pub fn sync(self: &mut Self) -> Sha256Hash { + pub fn sync(self: &mut Self) -> Hash { while let Ok(entry) = self.historian.receiver.try_recv() { self.last_id = entry.id; } diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs index 4c6c12a981..c5afe6bc7b 100644 --- a/src/accountant_skel.rs +++ b/src/accountant_skel.rs @@ -2,7 +2,8 @@ use std::io; use accountant::Accountant; use transaction::Transaction; use signature::PublicKey; -use log::{Entry, Sha256Hash}; +use hash::Hash; +use entry::Entry; use std::net::UdpSocket; use bincode::{deserialize, serialize}; @@ -14,7 +15,7 @@ pub struct AccountantSkel { pub enum Request { Transaction(Transaction), GetBalance { key: PublicKey }, - GetEntries { last_id: Sha256Hash }, + GetEntries { last_id: Hash }, GetId { is_last: bool }, } @@ -22,7 +23,7 @@ pub enum Request { pub enum Response { Balance { key: PublicKey, val: Option }, Entries { entries: Vec> }, - Id { id: Sha256Hash, is_last: bool }, + Id { id: Hash, is_last: bool }, } impl AccountantSkel { diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index ea713a9b5f..f36e940480 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -7,14 +7,15 @@ use std::io; use bincode::{deserialize, serialize}; use transaction::Transaction; use signature::{PublicKey, Signature}; -use log::{Entry, Sha256Hash}; +use hash::Hash; +use entry::Entry; use ring::signature::Ed25519KeyPair; use accountant_skel::{Request, Response}; pub struct AccountantStub { pub addr: String, pub socket: UdpSocket, - pub last_id: Option, + pub last_id: Option, } impl AccountantStub { @@ -37,7 +38,7 @@ impl AccountantStub { n: i64, keypair: &Ed25519KeyPair, to: PublicKey, - last_id: &Sha256Hash, + last_id: &Hash, ) -> io::Result { let tr = Transaction::new(keypair, to, n, *last_id); let sig = tr.sig; @@ -58,7 +59,7 @@ impl AccountantStub { Ok(None) } - fn get_id(&self, is_last: bool) -> io::Result { + fn get_id(&self, is_last: bool) -> io::Result { let req = Request::GetId { is_last }; let data = serialize(&req).expect("serialize GetId"); self.socket.send_to(&data, &self.addr)?; @@ -71,7 +72,7 @@ impl AccountantStub { Ok(Default::default()) } - pub fn get_last_id(&self) -> io::Result { + pub fn get_last_id(&self) -> io::Result { self.get_id(true) } diff --git a/src/bin/demo.rs b/src/bin/demo.rs index bdbad0ba23..4aad34f20c 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -1,7 +1,9 @@ extern crate silk; use silk::historian::Historian; -use silk::log::{verify_slice, Entry, Sha256Hash}; +use silk::hash::Hash; +use silk::entry::Entry; +use silk::log::verify_slice; use silk::signature::{generate_keypair, get_pubkey}; use silk::transaction::Transaction; use silk::event::Event; @@ -9,12 +11,9 @@ use std::thread::sleep; use std::time::Duration; use std::sync::mpsc::SendError; -fn create_log( - hist: &Historian, - seed: &Sha256Hash, -) -> Result<(), SendError>> { +fn create_log(hist: &Historian, seed: &Hash) -> Result<(), SendError>> { sleep(Duration::from_millis(15)); - let asset = Sha256Hash::default(); + let asset = Hash::default(); let keypair = generate_keypair(); let tr = Transaction::new(&keypair, get_pubkey(&keypair), asset, *seed); let event0 = Event::Transaction(tr); @@ -24,11 +23,11 @@ fn create_log( } fn main() { - let seed = Sha256Hash::default(); + let seed = Hash::default(); let hist = Historian::new(&seed, Some(10)); create_log(&hist, &seed).expect("send error"); drop(hist.sender); - let entries: Vec> = hist.receiver.iter().collect(); + let entries: Vec> = hist.receiver.iter().collect(); for entry in &entries { println!("{:?}", entry); } diff --git a/src/entry.rs b/src/entry.rs new file mode 100644 index 0000000000..82b691087b --- /dev/null +++ b/src/entry.rs @@ -0,0 +1,102 @@ +use hash::{extend_and_hash, hash, Hash}; +use serde::Serialize; +use event::Event; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct Entry { + pub num_hashes: u64, + pub id: Hash, + pub event: Event, +} + +impl Entry { + /// Creates a Entry from the number of hashes 'num_hashes' since the previous event + /// and that resulting 'id'. + pub fn new_tick(num_hashes: u64, id: &Hash) -> Self { + Entry { + num_hashes, + id: *id, + event: Event::Tick, + } + } + + /// Verifies self.id is the result of hashing a 'start_hash' 'self.num_hashes' times. + /// If the event is not a Tick, then hash that as well. + pub fn verify(&self, start_hash: &Hash) -> bool { + if !self.event.verify() { + return false; + } + self.id == next_hash(start_hash, self.num_hashes, &self.event) + } +} + +/// Creates the hash 'num_hashes' after start_hash. If the event contains +/// signature, the final hash will be a hash of both the previous ID and +/// the signature. +pub fn next_hash(start_hash: &Hash, num_hashes: u64, event: &Event) -> Hash { + let mut id = *start_hash; + let sig = event.get_signature(); + let start_index = if sig.is_some() { 1 } else { 0 }; + for _ in start_index..num_hashes { + id = hash(&id); + } + if let Some(sig) = sig { + id = extend_and_hash(&id, &sig); + } + id +} + +/// Creates the next Entry 'num_hashes' after 'start_hash'. +pub fn create_entry(start_hash: &Hash, cur_hashes: u64, event: Event) -> Entry { + let sig = event.get_signature(); + let num_hashes = cur_hashes + if sig.is_some() { 1 } else { 0 }; + let id = next_hash(start_hash, 0, &event); + Entry { + num_hashes, + id, + event, + } +} + +/// Creates the next Tick Entry 'num_hashes' after 'start_hash'. +pub fn create_entry_mut( + start_hash: &mut Hash, + cur_hashes: &mut u64, + event: Event, +) -> Entry { + let entry = create_entry(start_hash, *cur_hashes, event); + *start_hash = entry.id; + *cur_hashes = 0; + entry +} + +/// Creates the next Tick Entry 'num_hashes' after 'start_hash'. +pub fn next_tick(start_hash: &Hash, num_hashes: u64) -> Entry { + let event = Event::Tick; + Entry { + num_hashes, + id: next_hash(start_hash, num_hashes, &event), + event, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_event_verify() { + let zero = Hash::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() { + let zero = Hash::default(); + assert_eq!(next_tick::(&zero, 1).num_hashes, 1) + } +} diff --git a/src/genesis.rs b/src/genesis.rs index 01bcd1416d..4d95e24eb6 100644 --- a/src/genesis.rs +++ b/src/genesis.rs @@ -3,7 +3,9 @@ use event::Event; use transaction::Transaction; use signature::{generate_keypair, get_pubkey, PublicKey}; -use log::{create_entries, hash, Entry, Sha256Hash}; +use entry::Entry; +use log::create_entries; +use hash::{hash, Hash}; use ring::rand::SystemRandom; use ring::signature::Ed25519KeyPair; use untrusted::Input; @@ -42,7 +44,7 @@ impl Genesis { } } - pub fn get_seed(&self) -> Sha256Hash { + pub fn get_seed(&self) -> Hash { hash(&self.pkcs8) } diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 0000000000..181db33499 --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,19 @@ +use generic_array::GenericArray; +use generic_array::typenum::U32; +use sha2::{Digest, Sha256}; + +pub type Hash = GenericArray; + +/// Return a Sha256 hash for the given data. +pub fn hash(val: &[u8]) -> Hash { + let mut hasher = Sha256::default(); + hasher.input(val); + hasher.result() +} + +/// Return the hash of the given hash extended with the given value. +pub fn extend_and_hash(id: &Hash, val: &[u8]) -> Hash { + let mut hash_data = id.to_vec(); + hash_data.extend_from_slice(val); + hash(&hash_data) +} diff --git a/src/historian.rs b/src/historian.rs index 03605a247c..6d335819e0 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -5,7 +5,8 @@ use std::thread::{spawn, JoinHandle}; use std::collections::HashSet; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; use std::time::Instant; -use log::{hash, Entry, Sha256Hash}; +use hash::{hash, Hash}; +use entry::Entry; use logger::{ExitReason, Logger}; use signature::Signature; use event::Event; @@ -20,7 +21,7 @@ pub struct Historian { } impl Historian { - pub fn new(start_hash: &Sha256Hash, ms_per_tick: Option) -> Self { + pub fn new(start_hash: &Hash, ms_per_tick: Option) -> Self { let (sender, event_receiver) = sync_channel(1000); let (entry_sender, receiver) = sync_channel(1000); let thread_hdl = @@ -37,7 +38,7 @@ impl Historian { /// A background thread that will continue tagging received Event messages and /// sending back Entry messages until either the receiver or sender channel is closed. fn create_logger( - start_hash: Sha256Hash, + start_hash: Hash, ms_per_tick: Option, receiver: Receiver>, sender: SyncSender>, @@ -74,7 +75,7 @@ mod tests { #[test] fn test_historian() { - let zero = Sha256Hash::default(); + let zero = Hash::default(); let hist = Historian::new(&zero, None); hist.sender.send(Event::Tick).unwrap(); @@ -98,7 +99,7 @@ mod tests { #[test] fn test_historian_closed_sender() { - let zero = Sha256Hash::default(); + let zero = Hash::default(); let hist = Historian::::new(&zero, None); drop(hist.receiver); hist.sender.send(Event::Tick).unwrap(); @@ -118,12 +119,12 @@ mod tests { #[test] fn test_ticking_historian() { - let zero = Sha256Hash::default(); + let zero = Hash::default(); let hist = Historian::new(&zero, Some(20)); sleep(Duration::from_millis(30)); hist.sender.send(Event::Tick).unwrap(); drop(hist.sender); - let entries: Vec> = hist.receiver.iter().collect(); + let entries: Vec> = hist.receiver.iter().collect(); // Ensure one entry is sent back for each tick sent in. assert_eq!(entries.len(), 1); diff --git a/src/lib.rs b/src/lib.rs index 91cbf8b703..e452859c99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,12 @@ #![cfg_attr(feature = "unstable", feature(test))] -pub mod log; -pub mod logger; -pub mod event; -pub mod transaction; pub mod signature; +pub mod hash; +pub mod transaction; +pub mod event; +pub mod entry; +pub mod log; pub mod genesis; +pub mod logger; pub mod historian; pub mod accountant; pub mod accountant_skel; diff --git a/src/log.rs b/src/log.rs index 1da66d778f..78f287c742 100644 --- a/src/log.rs +++ b/src/log.rs @@ -13,140 +13,34 @@ /// 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 generic_array::GenericArray; -use generic_array::typenum::U32; +use hash::Hash; use serde::Serialize; +use entry::{create_entry_mut, next_tick, Entry}; use event::Event; -use sha2::{Digest, Sha256}; use rayon::prelude::*; -pub type Sha256Hash = GenericArray; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub struct Entry { - pub num_hashes: u64, - pub id: Sha256Hash, - pub event: Event, -} - -impl Entry { - /// Creates a Entry from the number of hashes 'num_hashes' since the previous event - /// and that resulting 'id'. - pub fn new_tick(num_hashes: u64, id: &Sha256Hash) -> Self { - Entry { - num_hashes, - id: *id, - event: Event::Tick, - } - } - - /// Verifies self.id is the result of hashing a 'start_hash' 'self.num_hashes' times. - /// If the event is not a Tick, then hash that as well. - pub fn verify(&self, start_hash: &Sha256Hash) -> bool { - if !self.event.verify() { - return false; - } - self.id == next_hash(start_hash, self.num_hashes, &self.event) - } -} - -/// Return a Sha256 hash for the given data. -pub fn hash(val: &[u8]) -> Sha256Hash { - let mut hasher = Sha256::default(); - hasher.input(val); - hasher.result() -} - -/// Return the hash of the given hash extended with the given value. -pub fn extend_and_hash(id: &Sha256Hash, val: &[u8]) -> Sha256Hash { - let mut hash_data = id.to_vec(); - hash_data.extend_from_slice(val); - hash(&hash_data) -} - -/// Creates the hash 'num_hashes' after start_hash. If the event contains -/// signature, the final hash will be a hash of both the previous ID and -/// the signature. -pub fn next_hash( - start_hash: &Sha256Hash, - num_hashes: u64, - event: &Event, -) -> Sha256Hash { - let mut id = *start_hash; - let sig = event.get_signature(); - let start_index = if sig.is_some() { 1 } else { 0 }; - for _ in start_index..num_hashes { - id = hash(&id); - } - if let Some(sig) = sig { - id = extend_and_hash(&id, &sig); - } - id -} - -/// Creates the next Entry 'num_hashes' after 'start_hash'. -pub fn create_entry( - start_hash: &Sha256Hash, - cur_hashes: u64, - event: Event, -) -> Entry { - let sig = event.get_signature(); - let num_hashes = cur_hashes + if sig.is_some() { 1 } else { 0 }; - let id = next_hash(start_hash, 0, &event); - Entry { - num_hashes, - id, - event, - } -} - -/// Creates the next Tick Entry 'num_hashes' after 'start_hash'. -pub fn create_entry_mut( - start_hash: &mut Sha256Hash, - cur_hashes: &mut u64, - event: Event, -) -> Entry { - let entry = create_entry(start_hash, *cur_hashes, event); - *start_hash = entry.id; - *cur_hashes = 0; - entry -} - -/// Creates the next Tick Entry 'num_hashes' after 'start_hash'. -pub fn next_tick(start_hash: &Sha256Hash, num_hashes: u64) -> Entry { - let event = Event::Tick; - Entry { - num_hashes, - id: next_hash(start_hash, num_hashes, &event), - event, - } -} - /// Verifies the hashes and counts of a slice of events are all consistent. -pub fn verify_slice(events: &[Entry], start_hash: &Sha256Hash) -> bool { +pub fn verify_slice(events: &[Entry], start_hash: &Hash) -> bool { 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.id)) } /// Verifies the hashes and counts of a slice of events are all consistent. -pub fn verify_slice_i64(events: &[Entry], start_hash: &Sha256Hash) -> bool { +pub fn verify_slice_i64(events: &[Entry], start_hash: &Hash) -> bool { 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.id)) } /// Verifies the hashes and events serially. Exists only for reference. -pub fn verify_slice_seq(events: &[Entry], start_hash: &Sha256Hash) -> bool { +pub fn verify_slice_seq(events: &[Entry], start_hash: &Hash) -> 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.id)) } -pub fn create_entries( - start_hash: &Sha256Hash, - events: Vec>, -) -> Vec> { +pub fn create_entries(start_hash: &Hash, events: Vec>) -> Vec> { let mut id = *start_hash; events .into_iter() @@ -155,7 +49,7 @@ pub fn create_entries( } /// Create a vector of Ticks of length 'len' from 'start_hash' hash and 'num_hashes'. -pub fn next_ticks(start_hash: &Sha256Hash, num_hashes: u64, len: usize) -> Vec> { +pub fn next_ticks(start_hash: &Hash, num_hashes: u64, len: usize) -> Vec> { let mut id = *start_hash; let mut ticks = vec![]; for _ in 0..len { @@ -171,25 +65,10 @@ mod tests { use super::*; use signature::{generate_keypair, get_pubkey}; use transaction::Transaction; + use hash::hash; - #[test] - fn test_event_verify() { - 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() { - let zero = Sha256Hash::default(); - assert_eq!(next_tick::(&zero, 1).num_hashes, 1) - } - - fn verify_slice_generic(verify_slice: fn(&[Entry], &Sha256Hash) -> bool) { - let zero = Sha256Hash::default(); + fn verify_slice_generic(verify_slice: fn(&[Entry], &Hash) -> bool) { + let zero = Hash::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 @@ -208,12 +87,12 @@ mod tests { #[test] fn test_verify_slice_seq() { - verify_slice_generic(verify_slice_seq::); + verify_slice_generic(verify_slice_seq::); } #[test] fn test_reorder_attack() { - let zero = Sha256Hash::default(); + let zero = Hash::default(); let one = hash(&zero); // First, verify entries diff --git a/src/logger.rs b/src/logger.rs index 8fcf101f6d..2d839809a0 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -7,7 +7,8 @@ use std::sync::mpsc::{Receiver, SyncSender, TryRecvError}; use std::time::{Duration, Instant}; -use log::{create_entry_mut, Entry, Sha256Hash}; +use hash::Hash; +use entry::{create_entry_mut, Entry}; use event::Event; use serde::Serialize; use std::fmt::Debug; @@ -22,7 +23,7 @@ pub enum ExitReason { pub struct Logger { pub sender: SyncSender>, pub receiver: Receiver>, - pub last_id: Sha256Hash, + pub last_id: Hash, pub num_hashes: u64, pub num_ticks: u64, } @@ -31,7 +32,7 @@ impl Logger { pub fn new( receiver: Receiver>, sender: SyncSender>, - start_hash: Sha256Hash, + start_hash: Hash, ) -> Self { Logger { receiver, diff --git a/src/transaction.rs b/src/transaction.rs index f0be7c1d89..209a5dc7cb 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -4,24 +4,19 @@ use signature::{get_pubkey, verify_signature, PublicKey, Signature}; use ring::signature::Ed25519KeyPair; use serde::Serialize; use bincode::serialize; -use log::Sha256Hash; +use hash::Hash; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Transaction { pub from: PublicKey, pub to: PublicKey, pub asset: T, - pub last_id: Sha256Hash, + pub last_id: Hash, pub sig: Signature, } impl Transaction { - pub fn new( - from_keypair: &Ed25519KeyPair, - to: PublicKey, - asset: T, - last_id: Sha256Hash, - ) -> Self { + pub fn new(from_keypair: &Ed25519KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { let mut tr = Transaction { from: get_pubkey(&from_keypair), to, @@ -51,21 +46,21 @@ impl Transaction { mod tests { use super::*; use bincode::{deserialize, serialize}; - use log::*; use signature::*; + use hash::hash; #[test] fn test_claim() { let keypair = generate_keypair(); let asset = hash(b"hello, world"); - let zero = Sha256Hash::default(); + let zero = Hash::default(); let tr0 = Transaction::new(&keypair, get_pubkey(&keypair), asset, zero); assert!(tr0.verify()); } #[test] fn test_transfer() { - let zero = Sha256Hash::default(); + let zero = Hash::default(); let keypair0 = generate_keypair(); let keypair1 = generate_keypair(); let pubkey1 = get_pubkey(&keypair1); @@ -90,7 +85,7 @@ mod tests { #[test] fn test_bad_event_signature() { - let zero = Sha256Hash::default(); + let zero = Hash::default(); let keypair = generate_keypair(); let pubkey = get_pubkey(&keypair); let mut tr = Transaction::new(&keypair, pubkey, hash(b"hello, world"), zero); @@ -105,7 +100,7 @@ mod tests { let keypair1 = generate_keypair(); let thief_keypair = generate_keypair(); let pubkey1 = get_pubkey(&keypair1); - let zero = Sha256Hash::default(); + let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, pubkey1, hash(b"hello, world"), zero); tr.sign(&keypair0); tr.to = get_pubkey(&thief_keypair); // <-- attack!