diff --git a/Cargo.lock b/Cargo.lock index a38d95457..9de0b7c05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,7 +355,7 @@ dependencies = [ [[package]] name = "silk" -version = "0.3.3" +version = "0.4.0" dependencies = [ "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/accountant.rs b/src/accountant.rs index d63672adf..efb80f349 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -61,10 +61,12 @@ impl Accountant { // fields are the same. That entry should be treated as a deposit, not a // transfer to oneself. let entry1 = entries.next().unwrap(); - acc.process_verified_event(&entry1.event, true).unwrap(); + acc.process_verified_event(&entry1.events[0], true).unwrap(); for entry in entries { - acc.process_verified_event(&entry.event, false).unwrap(); + for event in entry.events { + acc.process_verified_event(&event, false).unwrap(); + } } acc } diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index bc842474f..cc8740d45 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -93,11 +93,13 @@ impl AccountantStub { self.socket.recv_from(&mut buf)?; let resp = deserialize(&buf).expect("deserialize signature"); if let Response::Entries { entries } = resp { - for Entry { id, event, .. } in entries { + for Entry { id, events, .. } in entries { self.last_id = Some(id); - if let Some(sig) = event.get_signature() { - if sig == *wait_sig { - return Ok(()); + for event in events { + if let Some(sig) = event.get_signature() { + if sig == *wait_sig { + return Ok(()); + } } } } diff --git a/src/bin/genesis-demo.rs b/src/bin/genesis-demo.rs index ad3772d53..ca53378a9 100644 --- a/src/bin/genesis-demo.rs +++ b/src/bin/genesis-demo.rs @@ -4,7 +4,7 @@ extern crate silk; use silk::mint::Mint; use silk::event::Event; use silk::transaction::Transaction; -use silk::log::create_entries; +use silk::entry::create_entry; use silk::signature::{KeyPair, KeyPairUtil, PublicKey}; use silk::hash::Hash; use std::io::stdin; @@ -14,17 +14,17 @@ fn transfer(from: &KeyPair, (to, tokens): (PublicKey, i64), last_id: Hash) -> Ev } fn main() { - let alice = (KeyPair::new().pubkey(), 200); - let bob = (KeyPair::new().pubkey(), 100); - let mint: Mint = serde_json::from_reader(stdin()).unwrap(); + let mut entries = mint.create_entries(); + let from = mint.keypair(); let seed = mint.seed(); - let mut events = mint.create_events(); - events.push(transfer(&from, alice, seed)); - events.push(transfer(&from, bob, seed)); + let alice = (KeyPair::new().pubkey(), 200); + let bob = (KeyPair::new().pubkey(), 100); + let events = vec![transfer(&from, alice, seed), transfer(&from, bob, seed)]; + entries.push(create_entry(&seed, 0, events)); - for entry in create_entries(&seed, events) { + for entry in entries { println!("{}", serde_json::to_string(&entry).unwrap()); } } diff --git a/src/entry.rs b/src/entry.rs index 67a6ea7ae..14bc6e8c7 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -5,7 +5,7 @@ use event::Event; pub struct Entry { pub num_hashes: u64, pub id: Hash, - pub event: Event, + pub events: Vec, } impl Entry { @@ -15,51 +15,61 @@ impl Entry { Entry { num_hashes, id: *id, - event: Event::Tick, + events: vec![], } } /// 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; + for event in &self.events { + if !event.verify() { + return false; + } } - self.id == next_hash(start_hash, self.num_hashes, &self.event) + self.id == next_hash(start_hash, self.num_hashes, &self.events) } } /// 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 { +pub fn next_hash(start_hash: &Hash, num_hashes: u64, events: &[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 { + for _ in 1..num_hashes { id = hash(&id); } - if let Some(sig) = sig { - id = extend_and_hash(&id, &sig); + + // Hash all the event data + let mut hash_data = vec![]; + for event in events { + let sig = event.get_signature(); + if let Some(sig) = sig { + hash_data.extend_from_slice(&sig); + } } + + if !hash_data.is_empty() { + return extend_and_hash(&id, &hash_data); + } + 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); +pub fn create_entry(start_hash: &Hash, cur_hashes: u64, events: Vec) -> Entry { + let num_hashes = cur_hashes + if events.is_empty() { 0 } else { 1 }; + let id = next_hash(start_hash, 0, &events); Entry { num_hashes, id, - event, + events, } } /// 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); +pub fn create_entry_mut(start_hash: &mut Hash, cur_hashes: &mut u64, events: Vec) -> Entry { + let entry = create_entry(start_hash, *cur_hashes, events); *start_hash = entry.id; *cur_hashes = 0; entry @@ -67,11 +77,10 @@ pub fn create_entry_mut(start_hash: &mut Hash, cur_hashes: &mut u64, event: Even /// 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, + id: next_hash(start_hash, num_hashes, &[]), + events: vec![], } } diff --git a/src/log.rs b/src/log.rs index b01450cb5..963271463 100644 --- a/src/log.rs +++ b/src/log.rs @@ -14,23 +14,19 @@ /// was generated by the fastest processor at the time the entry was logged. use hash::Hash; -use entry::{create_entry_mut, next_tick, Entry}; +use entry::{create_entry, next_tick, Entry}; use event::Event; use rayon::prelude::*; /// Verifies the hashes and counts of a slice of events are all consistent. -pub fn verify_slice(events: &[Entry], start_hash: &Hash) -> bool { +pub fn verify_slice(entries: &[Entry], start_hash: &Hash) -> bool { let genesis = [Entry::new_tick(Default::default(), start_hash)]; - let event_pairs = genesis.par_iter().chain(events).zip(events); + let event_pairs = genesis.par_iter().chain(entries).zip(entries); event_pairs.all(|(x0, x1)| x1.verify(&x0.id)) } pub fn create_entries(start_hash: &Hash, events: Vec) -> Vec { - let mut id = *start_hash; - events - .into_iter() - .map(|event| create_entry_mut(&mut id, &mut 0, event)) - .collect() + vec![create_entry(start_hash, 0, events)] } /// Create a vector of Ticks of length 'len' from 'start_hash' hash and 'num_hashes'. @@ -48,8 +44,6 @@ pub fn next_ticks(start_hash: &Hash, num_hashes: u64, len: usize) -> Vec #[cfg(test)] mod tests { use super::*; - use signature::{KeyPair, KeyPairUtil}; - use transaction::Transaction; use hash::hash; #[test] @@ -66,25 +60,26 @@ mod tests { assert!(!verify_slice(&bad_ticks, &zero)); // inductive step, bad } - #[test] - fn test_reorder_attack() { - let zero = Hash::default(); + // TODO: This is no longer relevant. Instead, test for reordered ticks. + //#[test] + //fn test_reorder_attack() { + // let zero = Hash::default(); - // First, verify entries - let keypair = KeyPair::new(); - let tr0 = Transaction::new(&keypair, keypair.pubkey(), 0, zero); - let tr1 = Transaction::new(&keypair, keypair.pubkey(), 1, zero); - let events = vec![Event::Transaction(tr0), Event::Transaction(tr1)]; - let mut entries = create_entries(&zero, events); - assert!(verify_slice(&entries, &zero)); + // // First, verify entries + // let keypair = KeyPair::new(); + // let tr0 = Transaction::new(&keypair, keypair.pubkey(), 0, zero); + // let tr1 = Transaction::new(&keypair, keypair.pubkey(), 1, zero); + // let events = vec![Event::Transaction(tr0), Event::Transaction(tr1)]; + // let mut entries = create_entries(&zero, events); + // assert!(verify_slice(&entries, &zero)); - // Next, swap two events and ensure verification fails. - let event0 = entries[0].event.clone(); - let event1 = entries[1].event.clone(); - entries[0].event = event1; - entries[1].event = event0; - assert!(!verify_slice(&entries, &zero)); - } + // // Next, swap two events and ensure verification fails. + // let event0 = entries[0].event.clone(); + // let event1 = entries[1].event.clone(); + // entries[0].event = event1; + // entries[1].event = event0; + // assert!(!verify_slice(&entries, &zero)); + //} } #[cfg(all(feature = "unstable", test))] diff --git a/src/logger.rs b/src/logger.rs index b5affd290..6a90c340c 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -7,6 +7,7 @@ use std::sync::mpsc::{Receiver, SyncSender, TryRecvError}; use std::time::{Duration, Instant}; +use std::mem; use hash::Hash; use entry::{create_entry_mut, Entry}; use event::Event; @@ -22,6 +23,7 @@ pub struct Logger { pub sender: SyncSender, pub receiver: Receiver, pub last_id: Hash, + pub events: Vec, pub num_hashes: u64, pub num_ticks: u64, } @@ -32,13 +34,15 @@ impl Logger { receiver, sender, last_id: start_hash, + events: vec![], num_hashes: 0, num_ticks: 0, } } - pub fn log_event(&mut self, event: Event) -> Result { - let entry = create_entry_mut(&mut self.last_id, &mut self.num_hashes, event); + pub fn log_entry(&mut self) -> Result { + let events = mem::replace(&mut self.events, vec![]); + let entry = create_entry_mut(&mut self.last_id, &mut self.num_hashes, events); println!("{}", serde_json::to_string(&entry).unwrap()); Ok(entry) } @@ -51,17 +55,21 @@ impl Logger { loop { if let Some(ms) = ms_per_tick { if epoch.elapsed() > Duration::from_millis((self.num_ticks + 1) * ms) { - self.log_event(Event::Tick)?; + self.log_entry()?; self.num_ticks += 1; } } match self.receiver.try_recv() { Ok(event) => { - let entry = self.log_event(event)?; - self.sender - .send(entry) - .or(Err(ExitReason::SendDisconnected))?; + if let Event::Tick = event { + let entry = self.log_entry()?; + self.sender + .send(entry) + .or(Err(ExitReason::SendDisconnected))?; + } else { + self.events.push(event); + } } Err(TryRecvError::Empty) => return Ok(()), Err(TryRecvError::Disconnected) => return Err(ExitReason::RecvDisconnected), diff --git a/src/mint.rs b/src/mint.rs index 6beeeea98..991a1cb8c 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -4,7 +4,7 @@ use event::Event; use transaction::Transaction; use signature::{KeyPair, KeyPairUtil, PublicKey}; use entry::Entry; -use log::create_entries; +use entry::create_entry; use hash::{hash, Hash}; use ring::rand::SystemRandom; use untrusted::Input; @@ -44,11 +44,13 @@ impl Mint { pub fn create_events(&self) -> Vec { let keypair = self.keypair(); let tr = Transaction::new(&keypair, self.pubkey(), self.tokens, self.seed()); - vec![Event::Tick, Event::Transaction(tr)] + vec![Event::Transaction(tr)] } pub fn create_entries(&self) -> Vec { - create_entries(&self.seed(), self.create_events()) + let e0 = create_entry(&self.seed(), 0, vec![]); + let e1 = create_entry(&e0.id, 0, self.create_events()); + vec![e0, e1] } } @@ -60,11 +62,8 @@ mod tests { #[test] fn test_create_events() { let mut events = Mint::new(100).create_events().into_iter(); - assert_eq!(events.next().unwrap(), Event::Tick); if let Event::Transaction(tr) = events.next().unwrap() { assert_eq!(tr.from, tr.to); - } else { - assert!(false); } assert_eq!(events.next(), None); }