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]]
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)",

View File

@ -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
}

View File

@ -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(());
}
}
}
}

View File

@ -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());
}
}

View File

@ -5,7 +5,7 @@ use event::Event;
pub struct Entry {
pub num_hashes: u64,
pub id: Hash,
pub event: Event,
pub events: Vec<Event>,
}
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<Event>) -> 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<Event>) -> 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![],
}
}

View File

@ -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<Event>) -> Vec<Entry> {
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<Entry>
#[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))]

View File

@ -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<Entry>,
pub receiver: Receiver<Event>,
pub last_id: Hash,
pub events: Vec<Event>,
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<Entry, ExitReason> {
let entry = create_entry_mut(&mut self.last_id, &mut self.num_hashes, event);
pub fn log_entry(&mut self) -> Result<Entry, ExitReason> {
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),

View File

@ -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<Event> {
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<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]
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);
}