Batch events

It's now a Tick that locks down event order. Before this change, the
event order would be locked down in the order the server sees it.

Fixes #59
Fixes #61
This commit is contained in:
Greg Fitzgerald 2018-03-09 16:16:29 -07:00
parent 209910299d
commit cc9f0788aa
8 changed files with 91 additions and 76 deletions

2
Cargo.lock generated
View File

@ -355,7 +355,7 @@ dependencies = [
[[package]] [[package]]
name = "silk" name = "silk"
version = "0.3.3" version = "0.4.0"
dependencies = [ dependencies = [
"bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -61,10 +61,12 @@ impl Accountant {
// fields are the same. That entry should be treated as a deposit, not a // fields are the same. That entry should be treated as a deposit, not a
// transfer to oneself. // transfer to oneself.
let entry1 = entries.next().unwrap(); 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 { 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 acc
} }

View File

@ -93,11 +93,13 @@ impl AccountantStub {
self.socket.recv_from(&mut buf)?; self.socket.recv_from(&mut buf)?;
let resp = deserialize(&buf).expect("deserialize signature"); let resp = deserialize(&buf).expect("deserialize signature");
if let Response::Entries { entries } = resp { if let Response::Entries { entries } = resp {
for Entry { id, event, .. } in entries { for Entry { id, events, .. } in entries {
self.last_id = Some(id); self.last_id = Some(id);
if let Some(sig) = event.get_signature() { for event in events {
if sig == *wait_sig { if let Some(sig) = event.get_signature() {
return Ok(()); if sig == *wait_sig {
return Ok(());
}
} }
} }
} }

View File

@ -4,7 +4,7 @@ extern crate silk;
use silk::mint::Mint; use silk::mint::Mint;
use silk::event::Event; use silk::event::Event;
use silk::transaction::Transaction; use silk::transaction::Transaction;
use silk::log::create_entries; use silk::entry::create_entry;
use silk::signature::{KeyPair, KeyPairUtil, PublicKey}; use silk::signature::{KeyPair, KeyPairUtil, PublicKey};
use silk::hash::Hash; use silk::hash::Hash;
use std::io::stdin; use std::io::stdin;
@ -14,17 +14,17 @@ fn transfer(from: &KeyPair, (to, tokens): (PublicKey, i64), last_id: Hash) -> Ev
} }
fn main() { fn main() {
let alice = (KeyPair::new().pubkey(), 200);
let bob = (KeyPair::new().pubkey(), 100);
let mint: Mint = serde_json::from_reader(stdin()).unwrap(); let mint: Mint = serde_json::from_reader(stdin()).unwrap();
let mut entries = mint.create_entries();
let from = mint.keypair(); let from = mint.keypair();
let seed = mint.seed(); let seed = mint.seed();
let mut events = mint.create_events(); let alice = (KeyPair::new().pubkey(), 200);
events.push(transfer(&from, alice, seed)); let bob = (KeyPair::new().pubkey(), 100);
events.push(transfer(&from, bob, seed)); 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()); println!("{}", serde_json::to_string(&entry).unwrap());
} }
} }

View File

@ -5,7 +5,7 @@ use event::Event;
pub struct Entry { pub struct Entry {
pub num_hashes: u64, pub num_hashes: u64,
pub id: Hash, pub id: Hash,
pub event: Event, pub events: Vec<Event>,
} }
impl Entry { impl Entry {
@ -15,51 +15,61 @@ impl Entry {
Entry { Entry {
num_hashes, num_hashes,
id: *id, id: *id,
event: Event::Tick, events: vec![],
} }
} }
/// Verifies self.id is the result of hashing a 'start_hash' 'self.num_hashes' times. /// 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. /// If the event is not a Tick, then hash that as well.
pub fn verify(&self, start_hash: &Hash) -> bool { pub fn verify(&self, start_hash: &Hash) -> bool {
if !self.event.verify() { for event in &self.events {
return false; 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 /// 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 /// signature, the final hash will be a hash of both the previous ID and
/// the signature. /// 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 mut id = *start_hash;
let sig = event.get_signature(); for _ in 1..num_hashes {
let start_index = if sig.is_some() { 1 } else { 0 };
for _ in start_index..num_hashes {
id = hash(&id); 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 id
} }
/// Creates the next Entry 'num_hashes' after 'start_hash'. /// Creates the next Entry 'num_hashes' after 'start_hash'.
pub fn create_entry(start_hash: &Hash, cur_hashes: u64, event: Event) -> Entry { pub fn create_entry(start_hash: &Hash, cur_hashes: u64, events: Vec<Event>) -> Entry {
let sig = event.get_signature(); let num_hashes = cur_hashes + if events.is_empty() { 0 } else { 1 };
let num_hashes = cur_hashes + if sig.is_some() { 1 } else { 0 }; let id = next_hash(start_hash, 0, &events);
let id = next_hash(start_hash, 0, &event);
Entry { Entry {
num_hashes, num_hashes,
id, id,
event, events,
} }
} }
/// Creates the next Tick Entry 'num_hashes' after 'start_hash'. /// 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 { pub fn create_entry_mut(start_hash: &mut Hash, cur_hashes: &mut u64, events: Vec<Event>) -> Entry {
let entry = create_entry(start_hash, *cur_hashes, event); let entry = create_entry(start_hash, *cur_hashes, events);
*start_hash = entry.id; *start_hash = entry.id;
*cur_hashes = 0; *cur_hashes = 0;
entry 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'. /// Creates the next Tick Entry 'num_hashes' after 'start_hash'.
pub fn next_tick(start_hash: &Hash, num_hashes: u64) -> Entry { pub fn next_tick(start_hash: &Hash, num_hashes: u64) -> Entry {
let event = Event::Tick;
Entry { Entry {
num_hashes, num_hashes,
id: next_hash(start_hash, num_hashes, &event), id: next_hash(start_hash, num_hashes, &[]),
event, events: vec![],
} }
} }

View File

@ -14,23 +14,19 @@
/// was generated by the fastest processor at the time the entry was logged. /// was generated by the fastest processor at the time the entry was logged.
use hash::Hash; use hash::Hash;
use entry::{create_entry_mut, next_tick, Entry}; use entry::{create_entry, next_tick, Entry};
use event::Event; use event::Event;
use rayon::prelude::*; use rayon::prelude::*;
/// Verifies the hashes and counts of a slice of events are all consistent. /// 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 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)) event_pairs.all(|(x0, x1)| x1.verify(&x0.id))
} }
pub fn create_entries(start_hash: &Hash, events: Vec<Event>) -> Vec<Entry> { pub fn create_entries(start_hash: &Hash, events: Vec<Event>) -> Vec<Entry> {
let mut id = *start_hash; vec![create_entry(start_hash, 0, events)]
events
.into_iter()
.map(|event| create_entry_mut(&mut id, &mut 0, event))
.collect()
} }
/// Create a vector of Ticks of length 'len' from 'start_hash' hash and 'num_hashes'. /// 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<Entry>
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use signature::{KeyPair, KeyPairUtil};
use transaction::Transaction;
use hash::hash; use hash::hash;
#[test] #[test]
@ -66,25 +60,26 @@ mod tests {
assert!(!verify_slice(&bad_ticks, &zero)); // inductive step, bad assert!(!verify_slice(&bad_ticks, &zero)); // inductive step, bad
} }
#[test] // TODO: This is no longer relevant. Instead, test for reordered ticks.
fn test_reorder_attack() { //#[test]
let zero = Hash::default(); //fn test_reorder_attack() {
// let zero = Hash::default();
// First, verify entries // // First, verify entries
let keypair = KeyPair::new(); // let keypair = KeyPair::new();
let tr0 = Transaction::new(&keypair, keypair.pubkey(), 0, zero); // let tr0 = Transaction::new(&keypair, keypair.pubkey(), 0, zero);
let tr1 = Transaction::new(&keypair, keypair.pubkey(), 1, zero); // let tr1 = Transaction::new(&keypair, keypair.pubkey(), 1, zero);
let events = vec![Event::Transaction(tr0), Event::Transaction(tr1)]; // let events = vec![Event::Transaction(tr0), Event::Transaction(tr1)];
let mut entries = create_entries(&zero, events); // let mut entries = create_entries(&zero, events);
assert!(verify_slice(&entries, &zero)); // assert!(verify_slice(&entries, &zero));
// Next, swap two events and ensure verification fails. // // Next, swap two events and ensure verification fails.
let event0 = entries[0].event.clone(); // let event0 = entries[0].event.clone();
let event1 = entries[1].event.clone(); // let event1 = entries[1].event.clone();
entries[0].event = event1; // entries[0].event = event1;
entries[1].event = event0; // entries[1].event = event0;
assert!(!verify_slice(&entries, &zero)); // assert!(!verify_slice(&entries, &zero));
} //}
} }
#[cfg(all(feature = "unstable", test))] #[cfg(all(feature = "unstable", test))]

View File

@ -7,6 +7,7 @@
use std::sync::mpsc::{Receiver, SyncSender, TryRecvError}; use std::sync::mpsc::{Receiver, SyncSender, TryRecvError};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::mem;
use hash::Hash; use hash::Hash;
use entry::{create_entry_mut, Entry}; use entry::{create_entry_mut, Entry};
use event::Event; use event::Event;
@ -22,6 +23,7 @@ pub struct Logger {
pub sender: SyncSender<Entry>, pub sender: SyncSender<Entry>,
pub receiver: Receiver<Event>, pub receiver: Receiver<Event>,
pub last_id: Hash, pub last_id: Hash,
pub events: Vec<Event>,
pub num_hashes: u64, pub num_hashes: u64,
pub num_ticks: u64, pub num_ticks: u64,
} }
@ -32,13 +34,15 @@ impl Logger {
receiver, receiver,
sender, sender,
last_id: start_hash, last_id: start_hash,
events: vec![],
num_hashes: 0, num_hashes: 0,
num_ticks: 0, num_ticks: 0,
} }
} }
pub fn log_event(&mut self, event: Event) -> Result<Entry, ExitReason> { pub fn log_entry(&mut self) -> Result<Entry, ExitReason> {
let entry = create_entry_mut(&mut self.last_id, &mut self.num_hashes, event); 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()); println!("{}", serde_json::to_string(&entry).unwrap());
Ok(entry) Ok(entry)
} }
@ -51,17 +55,21 @@ impl Logger {
loop { loop {
if let Some(ms) = ms_per_tick { if let Some(ms) = ms_per_tick {
if epoch.elapsed() > Duration::from_millis((self.num_ticks + 1) * ms) { if epoch.elapsed() > Duration::from_millis((self.num_ticks + 1) * ms) {
self.log_event(Event::Tick)?; self.log_entry()?;
self.num_ticks += 1; self.num_ticks += 1;
} }
} }
match self.receiver.try_recv() { match self.receiver.try_recv() {
Ok(event) => { Ok(event) => {
let entry = self.log_event(event)?; if let Event::Tick = event {
self.sender let entry = self.log_entry()?;
.send(entry) self.sender
.or(Err(ExitReason::SendDisconnected))?; .send(entry)
.or(Err(ExitReason::SendDisconnected))?;
} else {
self.events.push(event);
}
} }
Err(TryRecvError::Empty) => return Ok(()), Err(TryRecvError::Empty) => return Ok(()),
Err(TryRecvError::Disconnected) => return Err(ExitReason::RecvDisconnected), Err(TryRecvError::Disconnected) => return Err(ExitReason::RecvDisconnected),

View File

@ -4,7 +4,7 @@ use event::Event;
use transaction::Transaction; use transaction::Transaction;
use signature::{KeyPair, KeyPairUtil, PublicKey}; use signature::{KeyPair, KeyPairUtil, PublicKey};
use entry::Entry; use entry::Entry;
use log::create_entries; use entry::create_entry;
use hash::{hash, Hash}; use hash::{hash, Hash};
use ring::rand::SystemRandom; use ring::rand::SystemRandom;
use untrusted::Input; use untrusted::Input;
@ -44,11 +44,13 @@ impl Mint {
pub fn create_events(&self) -> Vec<Event> { pub fn create_events(&self) -> Vec<Event> {
let keypair = self.keypair(); let keypair = self.keypair();
let tr = Transaction::new(&keypair, self.pubkey(), self.tokens, self.seed()); 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<Entry> { pub fn create_entries(&self) -> Vec<Entry> {
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] #[test]
fn test_create_events() { fn test_create_events() {
let mut events = Mint::new(100).create_events().into_iter(); 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() { if let Event::Transaction(tr) = events.next().unwrap() {
assert_eq!(tr.from, tr.to); assert_eq!(tr.from, tr.to);
} else {
assert!(false);
} }
assert_eq!(events.next(), None); assert_eq!(events.next(), None);
} }