From 1b77e8a69a154430d5e705a4c55aac801bbae464 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 08:43:54 -0700 Subject: [PATCH 01/13] Move Event into its own crate The log crate was starting to be the catch-all for all things related to entries, events, signatures, and hashes. This split shows us that: * Event depends only on signatures, not on hashes [directly] * All event testing was done via log testing (shame on me) * Accounting depends only on events --- src/accountant.rs | 9 ++-- src/accountant_skel.rs | 2 +- src/accountant_stub.rs | 9 ++-- src/bin/client-demo.rs | 2 +- src/bin/demo.rs | 4 +- src/event.rs | 114 +++++++++++++++++++++++++++++++++++++++++ src/historian.rs | 4 +- src/lib.rs | 1 + src/log.rs | 98 +---------------------------------- 9 files changed, 134 insertions(+), 109 deletions(-) create mode 100644 src/event.rs diff --git a/src/accountant.rs b/src/accountant.rs index 627f214badabf4..bf2a4a302be939 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, Event, PublicKey, Sha256Hash, Signature}; +use log::{Entry, Sha256Hash}; +use event::{Event, PublicKey, Signature}; use historian::Historian; use ring::signature::Ed25519KeyPair; use std::sync::mpsc::{RecvError, SendError}; @@ -86,7 +87,7 @@ impl Accountant { n: u64, keypair: &Ed25519KeyPair, ) -> Result>> { - use log::{get_pubkey, sign_serialized}; + use event::{get_pubkey, sign_serialized}; let key = get_pubkey(keypair); let sig = sign_serialized(&n, keypair); self.deposit_signed(key, n, sig).map(|_| sig) @@ -119,7 +120,7 @@ impl Accountant { keypair: &Ed25519KeyPair, to: PublicKey, ) -> Result>> { - use log::{get_pubkey, sign_transaction_data}; + use event::{get_pubkey, sign_transaction_data}; let from = get_pubkey(keypair); let sig = sign_transaction_data(&n, keypair, &to); @@ -153,7 +154,7 @@ impl Accountant { #[cfg(test)] mod tests { use super::*; - use log::{generate_keypair, get_pubkey}; + use event::{generate_keypair, get_pubkey}; use historian::ExitReason; #[test] diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs index 0573299a7897f1..6dec3f3ea90f7c 100644 --- a/src/accountant_skel.rs +++ b/src/accountant_skel.rs @@ -1,6 +1,6 @@ use std::io; use accountant::Accountant; -use log::{PublicKey, Signature}; +use event::{PublicKey, Signature}; //use serde::Serialize; pub struct AccountantSkel { diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index ac1945fa62f86a..57b9e6e36c8ed4 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -5,7 +5,7 @@ use std::net::UdpSocket; use std::io; use bincode::{deserialize, serialize}; -use log::{PublicKey, Signature}; +use event::{PublicKey, Signature}; use ring::signature::Ed25519KeyPair; use accountant_skel::{Request, Response}; @@ -34,7 +34,7 @@ impl AccountantStub { } pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> io::Result { - use log::{get_pubkey, sign_serialized}; + use event::{get_pubkey, sign_serialized}; let key = get_pubkey(keypair); let sig = sign_serialized(&n, keypair); self.deposit_signed(key, n, sig).map(|_| sig) @@ -58,7 +58,7 @@ impl AccountantStub { keypair: &Ed25519KeyPair, to: PublicKey, ) -> io::Result { - use log::{get_pubkey, sign_transaction_data}; + use event::{get_pubkey, sign_transaction_data}; let from = get_pubkey(keypair); let sig = sign_transaction_data(&n, keypair, &to); self.transfer_signed(from, to, n, sig).map(|_| sig) @@ -100,7 +100,8 @@ mod tests { use accountant_skel::AccountantSkel; use std::thread::{sleep, spawn}; use std::time::Duration; - use log::{generate_keypair, get_pubkey, Sha256Hash}; + use log::Sha256Hash; + use event::{generate_keypair, get_pubkey}; #[test] fn test_accountant_stub() { diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index cbc0d6dd308c71..ba0d4829421b9d 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -4,7 +4,7 @@ fn main() { use silk::accountant_stub::AccountantStub; use std::time::Instant; use std::net::UdpSocket; - use silk::log::{generate_keypair, get_pubkey}; + use silk::event::{generate_keypair, get_pubkey}; let addr = "127.0.0.1:8000"; let send_addr = "127.0.0.1:8001"; diff --git a/src/bin/demo.rs b/src/bin/demo.rs index a8f502a8755c68..c7ce456951b71d 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -1,8 +1,8 @@ extern crate silk; use silk::historian::Historian; -use silk::log::{generate_keypair, get_pubkey, sign_serialized, verify_slice, Entry, Event, - Sha256Hash}; +use silk::log::{verify_slice, Entry, Sha256Hash}; +use silk::event::{generate_keypair, get_pubkey, sign_serialized, Event}; use std::thread::sleep; use std::time::Duration; use std::sync::mpsc::SendError; diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 00000000000000..8597394c549d9a --- /dev/null +++ b/src/event.rs @@ -0,0 +1,114 @@ +//! The `log` crate provides the foundational data structures for Proof-of-History, +//! an ordered log of events in time. + +/// Each log entry contains three pieces of data. The 'num_hashes' field is the number +/// of hashes performed since the previous entry. The 'end_hash' field is the result +/// of hashing 'end_hash' from the previous entry 'num_hashes' times. The 'event' +/// field points to an Event that took place shortly after 'end_hash' was generated. +/// +/// If you divide 'num_hashes' by the amount of time it takes to generate a new hash, you +/// get a duration estimate since the last event. Since processing power increases +/// over time, one should expect the duration 'num_hashes' represents to decrease proportionally. +/// Though processing power varies across nodes, the network gives priority to the +/// 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, U64}; +use ring::signature::Ed25519KeyPair; +use serde::Serialize; + +pub type PublicKey = GenericArray; +pub type Signature = GenericArray; + +/// When 'event' is Tick, the event represents a simple clock tick, and exists for the +/// sole purpose of improving the performance of event log verification. A tick can +/// be generated in 'num_hashes' hashes and verified in 'num_hashes' hashes. By logging +/// a hash alongside the tick, each tick and be verified in parallel using the 'end_hash' +/// of the preceding tick to seed its hashing. +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum Event { + Tick, + Claim { + key: PublicKey, + data: T, + sig: Signature, + }, + Transaction { + from: PublicKey, + to: PublicKey, + data: T, + sig: Signature, + }, +} + +/// Return a new ED25519 keypair +pub fn generate_keypair() -> Ed25519KeyPair { + use ring::{rand, signature}; + use untrusted; + let rng = rand::SystemRandom::new(); + let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); + signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap() +} + +/// Return the public key for the given keypair +pub fn get_pubkey(keypair: &Ed25519KeyPair) -> PublicKey { + GenericArray::clone_from_slice(keypair.public_key_bytes()) +} + +/// Return a signature for the given data using the private key from the given keypair. +pub fn sign_serialized(data: &T, keypair: &Ed25519KeyPair) -> Signature { + use bincode::serialize; + let serialized = serialize(data).unwrap(); + GenericArray::clone_from_slice(keypair.sign(&serialized).as_ref()) +} + +/// Return a signature for the given transaction data using the private key from the given keypair. +pub fn sign_transaction_data( + data: &T, + keypair: &Ed25519KeyPair, + to: &PublicKey, +) -> Signature { + sign_serialized(&(data, to), keypair) +} + +/// Verify a signed message with the given public key. +pub fn verify_signature(peer_public_key_bytes: &[u8], msg_bytes: &[u8], sig_bytes: &[u8]) -> bool { + use untrusted; + use ring::signature; + let peer_public_key = untrusted::Input::from(peer_public_key_bytes); + let msg = untrusted::Input::from(msg_bytes); + let sig = untrusted::Input::from(sig_bytes); + signature::verify(&signature::ED25519, peer_public_key, msg, sig).is_ok() +} + +pub fn get_signature(event: &Event) -> Option { + match *event { + Event::Tick => None, + Event::Claim { sig, .. } => Some(sig), + Event::Transaction { sig, .. } => Some(sig), + } +} + +pub fn verify_event(event: &Event) -> bool { + use bincode::serialize; + if let Event::Claim { key, ref data, sig } = *event { + let mut claim_data = serialize(&data).unwrap(); + if !verify_signature(&key, &claim_data, &sig) { + return false; + } + } + if let Event::Transaction { + from, + to, + ref data, + sig, + } = *event + { + let sign_data = serialize(&(&data, &to)).unwrap(); + if !verify_signature(&from, &sign_data, &sig) { + return false; + } + } + true +} diff --git a/src/historian.rs b/src/historian.rs index 1a0441451c502b..be30f6686747ea 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -9,7 +9,8 @@ use std::thread::JoinHandle; use std::collections::HashMap; use std::sync::mpsc::{Receiver, SyncSender}; use std::time::{Duration, SystemTime}; -use log::{get_signature, hash, hash_event, verify_event, Entry, Event, Sha256Hash, Signature}; +use log::{hash, hash_event, Entry, Sha256Hash}; +use event::{get_signature, verify_event, Event, Signature}; use serde::Serialize; use std::fmt::Debug; @@ -141,6 +142,7 @@ impl Historian { mod tests { use super::*; use log::*; + use event::*; use std::thread::sleep; use std::time::Duration; diff --git a/src/lib.rs b/src/lib.rs index 6deb92941323d4..851262000a3466 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(feature = "unstable", feature(test))] pub mod log; +pub mod event; pub mod historian; pub mod accountant; pub mod accountant_skel; diff --git a/src/log.rs b/src/log.rs index 04a3b2482301ca..56d930b523a727 100644 --- a/src/log.rs +++ b/src/log.rs @@ -14,13 +14,11 @@ /// was generated by the fastest processor at the time the entry was logged. use generic_array::GenericArray; -use generic_array::typenum::{U32, U64}; -use ring::signature::Ed25519KeyPair; +use generic_array::typenum::U32; use serde::Serialize; +use event::*; pub type Sha256Hash = GenericArray; -pub type PublicKey = GenericArray; -pub type Signature = GenericArray; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Entry { @@ -29,27 +27,6 @@ pub struct Entry { pub event: Event, } -/// When 'event' is Tick, the event represents a simple clock tick, and exists for the -/// sole purpose of improving the performance of event log verification. A tick can -/// be generated in 'num_hashes' hashes and verified in 'num_hashes' hashes. By logging -/// a hash alongside the tick, each tick and be verified in parallel using the 'end_hash' -/// of the preceding tick to seed its hashing. -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum Event { - Tick, - Claim { - key: PublicKey, - data: T, - sig: Signature, - }, - Transaction { - from: PublicKey, - to: PublicKey, - data: T, - sig: Signature, - }, -} - impl Entry { /// Creates a Entry from the number of hashes 'num_hashes' since the previous event /// and that resulting 'end_hash'. @@ -62,36 +39,6 @@ impl Entry { } } -/// Return a new ED25519 keypair -pub fn generate_keypair() -> Ed25519KeyPair { - use ring::{rand, signature}; - use untrusted; - let rng = rand::SystemRandom::new(); - let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); - signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap() -} - -/// Return the public key for the given keypair -pub fn get_pubkey(keypair: &Ed25519KeyPair) -> PublicKey { - GenericArray::clone_from_slice(keypair.public_key_bytes()) -} - -/// Return a signature for the given data using the private key from the given keypair. -pub fn sign_serialized(data: &T, keypair: &Ed25519KeyPair) -> Signature { - use bincode::serialize; - let serialized = serialize(data).unwrap(); - GenericArray::clone_from_slice(keypair.sign(&serialized).as_ref()) -} - -/// Return a signature for the given transaction data using the private key from the given keypair. -pub fn sign_transaction_data( - data: &T, - keypair: &Ed25519KeyPair, - to: &PublicKey, -) -> Signature { - sign_serialized(&(data, to), keypair) -} - /// Return a Sha256 hash for the given data. pub fn hash(val: &[u8]) -> Sha256Hash { use sha2::{Digest, Sha256}; @@ -107,14 +54,6 @@ pub fn extend_and_hash(end_hash: &Sha256Hash, val: &[u8]) -> Sha256Hash { hash(&hash_data) } -pub fn get_signature(event: &Event) -> Option { - match *event { - Event::Tick => None, - Event::Claim { sig, .. } => Some(sig), - Event::Transaction { sig, .. } => Some(sig), - } -} - pub fn hash_event(end_hash: &Sha256Hash, event: &Event) -> Sha256Hash { match get_signature(event) { None => *end_hash, @@ -164,29 +103,6 @@ pub fn next_tick(start_hash: &Sha256Hash, num_hashes: u64) -> Entr next_entry(start_hash, num_hashes, Event::Tick) } -pub fn verify_event(event: &Event) -> bool { - use bincode::serialize; - if let Event::Claim { key, ref data, sig } = *event { - let mut claim_data = serialize(&data).unwrap(); - if !verify_signature(&key, &claim_data, &sig) { - return false; - } - } - if let Event::Transaction { - from, - to, - ref data, - sig, - } = *event - { - let sign_data = serialize(&(&data, &to)).unwrap(); - if !verify_signature(&from, &sign_data, &sig) { - return false; - } - } - true -} - /// Verifies self.end_hash 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_entry(entry: &Entry, start_hash: &Sha256Hash) -> bool { @@ -219,16 +135,6 @@ pub fn verify_slice_seq(events: &[Entry], start_hash: &Sha256Ha event_pairs.all(|(x0, x1)| verify_entry(&x1, &x0.end_hash)) } -/// Verify a signed message with the given public key. -pub fn verify_signature(peer_public_key_bytes: &[u8], msg_bytes: &[u8], sig_bytes: &[u8]) -> bool { - use untrusted; - use ring::signature; - let peer_public_key = untrusted::Input::from(peer_public_key_bytes); - let msg = untrusted::Input::from(msg_bytes); - let sig = untrusted::Input::from(sig_bytes); - signature::verify(&signature::ED25519, peer_public_key, msg, sig).is_ok() -} - pub fn create_entries( start_hash: &Sha256Hash, num_hashes: u64, From 684f4c59e0b6d5eeb28cb27ff152f276320f7837 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 08:51:27 -0700 Subject: [PATCH 02/13] Delete commented out code accountant crate shouldn't verify the log. Instead, it should only add valid entries and leave verification to network nodes. --- src/accountant.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index bf2a4a302be939..98e2d82dda49ac 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -57,14 +57,11 @@ impl Accountant { while let Ok(entry) = self.historian.receiver.try_recv() { entries.push(entry); } - // TODO: Does this cause the historian's channel to get blocked? - //use log::verify_slice_u64; - //println!("accountant: verifying {} entries...", entries.len()); - //assert!(verify_slice_u64(&entries, &self.end_hash)); - //println!("accountant: Done verifying {} entries.", entries.len()); + if let Some(last_entry) = entries.last() { self.end_hash = last_entry.end_hash; } + for e in &entries { self.process_event(&e.event); } From 36bb1f989d0c57cdbff9b31ed72cfa17d5221037 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 09:10:10 -0700 Subject: [PATCH 03/13] More defense against a double-spend attack Before this change, a client could spend funds before the accountant processed a previous spend. With this change in place, the accountant updates balances immediately, but that comes at an architectural cost. The accountant now verifies signatures on behalf of the historian, so that it can ensure logging will not fail. --- src/accountant.rs | 86 +++++++++++++++++++++++------------------------ src/historian.rs | 67 ++++++++++++++++++++++-------------- 2 files changed, 85 insertions(+), 68 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 98e2d82dda49ac..f244f788a7bc0b 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -6,7 +6,7 @@ use log::{Entry, Sha256Hash}; use event::{Event, PublicKey, Signature}; use historian::Historian; use ring::signature::Ed25519KeyPair; -use std::sync::mpsc::{RecvError, SendError}; +use std::sync::mpsc::SendError; use std::collections::HashMap; pub struct Accountant { @@ -25,33 +25,6 @@ impl Accountant { } } - pub fn process_event(self: &mut Self, event: &Event) { - match *event { - Event::Claim { key, data, .. } => { - if self.balances.contains_key(&key) { - if let Some(x) = self.balances.get_mut(&key) { - *x += data; - } - } else { - self.balances.insert(key, data); - } - } - Event::Transaction { from, to, data, .. } => { - if let Some(x) = self.balances.get_mut(&from) { - *x -= data; - } - if self.balances.contains_key(&to) { - if let Some(x) = self.balances.get_mut(&to) { - *x += data; - } - } else { - self.balances.insert(to, data); - } - } - _ => (), - } - } - pub fn sync(self: &mut Self) -> Vec> { let mut entries = vec![]; while let Ok(entry) = self.historian.receiver.try_recv() { @@ -62,25 +35,35 @@ impl Accountant { self.end_hash = last_entry.end_hash; } - for e in &entries { - self.process_event(&e.event); - } - entries } pub fn deposit_signed( - self: &Self, + self: &mut Self, key: PublicKey, data: u64, sig: Signature, ) -> Result<(), SendError>> { let event = Event::Claim { key, data, sig }; + if !self.historian.verify_event(&event) { + // TODO: Replace the SendError result with a custom one. + println!("Rejecting transaction: Invalid event"); + return Ok(()); + } + + if self.balances.contains_key(&key) { + if let Some(x) = self.balances.get_mut(&key) { + *x += data; + } + } else { + self.balances.insert(key, data); + } + self.historian.sender.send(event) } pub fn deposit( - self: &Self, + self: &mut Self, n: u64, keypair: &Ed25519KeyPair, ) -> Result>> { @@ -97,17 +80,36 @@ impl Accountant { data: u64, sig: Signature, ) -> Result<(), SendError>> { - if self.get_balance(&from).unwrap() < data { - // TODO: Replace the SendError result with a custom one. - println!("Error: Insufficient funds"); - return Ok(()); - } let event = Event::Transaction { from, to, data, sig, }; + if !self.historian.verify_event(&event) { + // TODO: Replace the SendError result with a custom one. + println!("Rejecting transaction: Invalid event"); + return Ok(()); + } + + if self.get_balance(&from).unwrap_or(0) < data { + // TODO: Replace the SendError result with a custom one. + println!("Rejecting transaction: Insufficient funds"); + return Ok(()); + } + + if let Some(x) = self.balances.get_mut(&from) { + *x -= data; + } + + if self.balances.contains_key(&to) { + if let Some(x) = self.balances.get_mut(&to) { + *x += data; + } + } else { + self.balances.insert(to, data); + } + self.historian.sender.send(event) } @@ -118,15 +120,13 @@ impl Accountant { to: PublicKey, ) -> Result>> { use event::{get_pubkey, sign_transaction_data}; - let from = get_pubkey(keypair); let sig = sign_transaction_data(&n, keypair, &to); self.transfer_signed(from, to, n, sig).map(|_| sig) } - pub fn get_balance(self: &mut Self, pubkey: &PublicKey) -> Result { - self.sync(); - Ok(*self.balances.get(pubkey).unwrap_or(&0)) + pub fn get_balance(self: &Self, pubkey: &PublicKey) -> Option { + self.balances.get(pubkey).map(|x| *x) } pub fn wait_on_signature(self: &mut Self, wait_sig: &Signature) { diff --git a/src/historian.rs b/src/historian.rs index be30f6686747ea..c01947c1dc4d11 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -6,7 +6,7 @@ //! The resulting stream of entries represents ordered events in time. use std::thread::JoinHandle; -use std::collections::HashMap; +use std::collections::HashSet; use std::sync::mpsc::{Receiver, SyncSender}; use std::time::{Duration, SystemTime}; use log::{hash, hash_event, Entry, Sha256Hash}; @@ -18,6 +18,7 @@ pub struct Historian { pub sender: SyncSender>, pub receiver: Receiver>, pub thread_hdl: JoinHandle<(Entry, ExitReason)>, + pub signatures: HashSet, } #[derive(Debug, PartialEq, Eq)] @@ -44,10 +45,25 @@ fn log_event( Ok(()) } +fn verify_event_and_reserve_signature( + signatures: &mut HashSet, + event: &Event, +) -> bool { + if !verify_event(&event) { + return false; + } + if let Some(sig) = get_signature(&event) { + if signatures.contains(&sig) { + return false; + } + signatures.insert(sig); + } + true +} + fn log_events( receiver: &Receiver>, sender: &SyncSender>, - signatures: &mut HashMap, num_hashes: &mut u64, end_hash: &mut Sha256Hash, epoch: SystemTime, @@ -65,15 +81,7 @@ fn log_events( } match receiver.try_recv() { Ok(event) => { - if verify_event(&event) { - if let Some(sig) = get_signature(&event) { - if signatures.contains_key(&sig) { - continue; - } - signatures.insert(sig, true); - } - log_event(sender, num_hashes, end_hash, event)?; - } + log_event(sender, num_hashes, end_hash, event)?; } Err(TryRecvError::Empty) => { return Ok(()); @@ -103,13 +111,11 @@ pub fn create_logger( let mut end_hash = start_hash; let mut num_hashes = 0; let mut num_ticks = 0; - let mut signatures = HashMap::new(); let epoch = SystemTime::now(); loop { if let Err(err) = log_events( &receiver, &sender, - &mut signatures, &mut num_hashes, &mut end_hash, epoch, @@ -130,12 +136,17 @@ impl Historian { let (sender, event_receiver) = sync_channel(1000); let (entry_sender, receiver) = sync_channel(1000); let thread_hdl = create_logger(*start_hash, ms_per_tick, event_receiver, entry_sender); + let signatures = HashSet::new(); Historian { sender, receiver, thread_hdl, + signatures, } } + pub fn verify_event(self: &mut Self, event: &Event) -> bool { + return verify_event_and_reserve_signature(&mut self.signatures, event); + } } #[cfg(test)] @@ -201,22 +212,28 @@ mod tests { } #[test] - fn test_bad_event_attack() { - let zero = Sha256Hash::default(); - let hist = Historian::new(&zero, None); + fn test_bad_event_signature() { let keypair = generate_keypair(); + let sig = sign_serialized(&hash(b"hello, world"), &keypair); let event0 = Event::Claim { key: get_pubkey(&keypair), data: hash(b"goodbye cruel world"), - sig: sign_serialized(&hash(b"hello, world"), &keypair), + sig, }; - hist.sender.send(event0).unwrap(); - drop(hist.sender); - assert_eq!( - hist.thread_hdl.join().unwrap().1, - ExitReason::RecvDisconnected - ); - let entries: Vec> = hist.receiver.iter().collect(); - assert_eq!(entries.len(), 0); + let mut sigs = HashSet::new(); + assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); + assert!(!sigs.contains(&sig)); + } + + #[test] + fn test_duplicate_event_signature() { + let keypair = generate_keypair(); + let key = get_pubkey(&keypair); + let data = &hash(b"hello, world"); + let sig = sign_serialized(data, &keypair); + let event0 = Event::Claim { key, data, sig }; + let mut sigs = HashSet::new(); + assert!(verify_event_and_reserve_signature(&mut sigs, &event0)); + assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); } } From dfd1c4eab34b01b2b15e54731a875ddb3be2b678 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 10:16:39 -0700 Subject: [PATCH 04/13] Don't process transaction if channel.send() fails. Do all input validation first, then log (which can fail). If all goes swimmingly, process the transaction. --- src/accountant.rs | 53 +++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index f244f788a7bc0b..2120650d310a28 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -8,6 +8,16 @@ use historian::Historian; use ring::signature::Ed25519KeyPair; use std::sync::mpsc::SendError; use std::collections::HashMap; +use std::result; + +#[derive(Debug, PartialEq, Eq)] +pub enum AccountingError { + InsufficientFunds, + InvalidEvent, + SendError, +} + +pub type Result = result::Result; pub struct Accountant { pub historian: Historian, @@ -43,12 +53,13 @@ impl Accountant { key: PublicKey, data: u64, sig: Signature, - ) -> Result<(), SendError>> { + ) -> Result<()> { let event = Event::Claim { key, data, sig }; if !self.historian.verify_event(&event) { - // TODO: Replace the SendError result with a custom one. - println!("Rejecting transaction: Invalid event"); - return Ok(()); + return Err(AccountingError::InvalidEvent); + } + if let Err(SendError(_)) = self.historian.sender.send(event) { + return Err(AccountingError::SendError); } if self.balances.contains_key(&key) { @@ -59,14 +70,10 @@ impl Accountant { self.balances.insert(key, data); } - self.historian.sender.send(event) + Ok(()) } - pub fn deposit( - self: &mut Self, - n: u64, - keypair: &Ed25519KeyPair, - ) -> Result>> { + pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> Result { use event::{get_pubkey, sign_serialized}; let key = get_pubkey(keypair); let sig = sign_serialized(&n, keypair); @@ -79,7 +86,11 @@ impl Accountant { to: PublicKey, data: u64, sig: Signature, - ) -> Result<(), SendError>> { + ) -> Result<()> { + if self.get_balance(&from).unwrap_or(0) < data { + return Err(AccountingError::InsufficientFunds); + } + let event = Event::Transaction { from, to, @@ -87,15 +98,10 @@ impl Accountant { sig, }; if !self.historian.verify_event(&event) { - // TODO: Replace the SendError result with a custom one. - println!("Rejecting transaction: Invalid event"); - return Ok(()); + return Err(AccountingError::InvalidEvent); } - - if self.get_balance(&from).unwrap_or(0) < data { - // TODO: Replace the SendError result with a custom one. - println!("Rejecting transaction: Insufficient funds"); - return Ok(()); + if let Err(SendError(_)) = self.historian.sender.send(event) { + return Err(AccountingError::SendError); } if let Some(x) = self.balances.get_mut(&from) { @@ -110,7 +116,7 @@ impl Accountant { self.balances.insert(to, data); } - self.historian.sender.send(event) + Ok(()) } pub fn transfer( @@ -118,7 +124,7 @@ impl Accountant { n: u64, keypair: &Ed25519KeyPair, to: PublicKey, - ) -> Result>> { + ) -> Result { use event::{get_pubkey, sign_transaction_data}; let from = get_pubkey(keypair); let sig = sign_transaction_data(&n, keypair, &to); @@ -190,7 +196,10 @@ mod tests { acc.wait_on_signature(&sig); let bob_pubkey = get_pubkey(&bob_keypair); - acc.transfer(10_001, &alice_keypair, bob_pubkey).unwrap(); + assert_eq!( + acc.transfer(10_001, &alice_keypair, bob_pubkey), + Err(AccountingError::InsufficientFunds) + ); sleep(Duration::from_millis(30)); let alice_pubkey = get_pubkey(&alice_keypair); From 32d677787ba3b59bb023701dcb85036c9c6eb4e9 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 10:35:35 -0700 Subject: [PATCH 05/13] Reduce transactions sent by demo We don't do retries yet, so keep tx count to something that won't trigger any packet loss. --- src/bin/client-demo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index ba0d4829421b9d..9c5f1ac42d70be 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -12,7 +12,7 @@ fn main() { let mut acc = AccountantStub::new(addr, socket); let alice_keypair = generate_keypair(); let alice_pubkey = get_pubkey(&alice_keypair); - let txs = 10_000; + let txs = 2_000; println!("Depositing {} units in Alice's account...", txs); let sig = acc.deposit(txs, &alice_keypair).unwrap(); acc.wait_on_signature(&sig).unwrap(); From 3e2d6d9e8bba44501e31e7feb4f1bc8269a5b313 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 10:41:13 -0700 Subject: [PATCH 06/13] Generalize Transaction to express a Claim If a Transaction doesn't have an existing address, it's being used to create new funds. --- src/accountant.rs | 2 +- src/event.rs | 4 ++-- src/log.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 2120650d310a28..0c03ae17ee2150 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -92,7 +92,7 @@ impl Accountant { } let event = Event::Transaction { - from, + from: Some(from), to, data, sig, diff --git a/src/event.rs b/src/event.rs index 8597394c549d9a..2daf04b89d6c70 100644 --- a/src/event.rs +++ b/src/event.rs @@ -35,7 +35,7 @@ pub enum Event { sig: Signature, }, Transaction { - from: PublicKey, + from: Option, to: PublicKey, data: T, sig: Signature, @@ -106,7 +106,7 @@ pub fn verify_event(event: &Event) -> bool { } = *event { let sign_data = serialize(&(&data, &to)).unwrap(); - if !verify_signature(&from, &sign_data, &sig) { + if !verify_signature(&from.unwrap_or(to), &sign_data, &sig) { return false; } } diff --git a/src/log.rs b/src/log.rs index 56d930b523a727..2f24dc2b458d4f 100644 --- a/src/log.rs +++ b/src/log.rs @@ -268,7 +268,7 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let event0 = Event::Transaction { - from: get_pubkey(&keypair0), + from: Some(get_pubkey(&keypair0)), to: pubkey1, data, sig: sign_transaction_data(&data, &keypair0, &pubkey1), @@ -285,7 +285,7 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let event0 = Event::Transaction { - from: get_pubkey(&keypair0), + from: Some(get_pubkey(&keypair0)), to: pubkey1, data: hash(b"goodbye cruel world"), // <-- attack! sig: sign_transaction_data(&data, &keypair0, &pubkey1), @@ -303,7 +303,7 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let event0 = Event::Transaction { - from: get_pubkey(&keypair0), + from: Some(get_pubkey(&keypair0)), to: get_pubkey(&thief_keypair), // <-- attack! data: hash(b"goodbye cruel world"), sig: sign_transaction_data(&data, &keypair0, &pubkey1), From 8fe464cfa3f8713107e96684600555840da62ed8 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 10:47:21 -0700 Subject: [PATCH 07/13] Rename Claim's key field to match same field in Transaction --- src/accountant.rs | 15 +++++---------- src/bin/demo.rs | 2 +- src/event.rs | 6 +++--- src/historian.rs | 6 +++--- src/log.rs | 8 ++++---- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 0c03ae17ee2150..f99e986fb8bce3 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -48,13 +48,8 @@ impl Accountant { entries } - pub fn deposit_signed( - self: &mut Self, - key: PublicKey, - data: u64, - sig: Signature, - ) -> Result<()> { - let event = Event::Claim { key, data, sig }; + pub fn deposit_signed(self: &mut Self, to: PublicKey, data: u64, sig: Signature) -> Result<()> { + let event = Event::Claim { to, data, sig }; if !self.historian.verify_event(&event) { return Err(AccountingError::InvalidEvent); } @@ -62,12 +57,12 @@ impl Accountant { return Err(AccountingError::SendError); } - if self.balances.contains_key(&key) { - if let Some(x) = self.balances.get_mut(&key) { + if self.balances.contains_key(&to) { + if let Some(x) = self.balances.get_mut(&to) { *x += data; } } else { - self.balances.insert(key, data); + self.balances.insert(to, data); } Ok(()) diff --git a/src/bin/demo.rs b/src/bin/demo.rs index c7ce456951b71d..cb09ee52ac81dc 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -12,7 +12,7 @@ fn create_log(hist: &Historian) -> Result<(), SendError; pub enum Event { Tick, Claim { - key: PublicKey, + to: PublicKey, data: T, sig: Signature, }, @@ -92,9 +92,9 @@ pub fn get_signature(event: &Event) -> Option { pub fn verify_event(event: &Event) -> bool { use bincode::serialize; - if let Event::Claim { key, ref data, sig } = *event { + if let Event::Claim { to, ref data, sig } = *event { let mut claim_data = serialize(&data).unwrap(); - if !verify_signature(&key, &claim_data, &sig) { + if !verify_signature(&to, &claim_data, &sig) { return false; } } diff --git a/src/historian.rs b/src/historian.rs index c01947c1dc4d11..bfbca2c8b96382 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -216,7 +216,7 @@ mod tests { let keypair = generate_keypair(); let sig = sign_serialized(&hash(b"hello, world"), &keypair); let event0 = Event::Claim { - key: get_pubkey(&keypair), + to: get_pubkey(&keypair), data: hash(b"goodbye cruel world"), sig, }; @@ -228,10 +228,10 @@ mod tests { #[test] fn test_duplicate_event_signature() { let keypair = generate_keypair(); - let key = get_pubkey(&keypair); + let to = get_pubkey(&keypair); let data = &hash(b"hello, world"); let sig = sign_serialized(data, &keypair); - let event0 = Event::Claim { key, data, sig }; + let event0 = Event::Claim { to, data, sig }; let mut sigs = HashSet::new(); assert!(verify_event_and_reserve_signature(&mut sigs, &event0)); assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); diff --git a/src/log.rs b/src/log.rs index 2f24dc2b458d4f..77d0584b1cc64a 100644 --- a/src/log.rs +++ b/src/log.rs @@ -212,13 +212,13 @@ mod tests { // First, verify Claim events let keypair = generate_keypair(); let event0 = Event::Claim { - key: get_pubkey(&keypair), + to: get_pubkey(&keypair), data: zero, sig: sign_serialized(&zero, &keypair), }; let event1 = Event::Claim { - key: get_pubkey(&keypair), + to: get_pubkey(&keypair), data: one, sig: sign_serialized(&one, &keypair), }; @@ -239,7 +239,7 @@ mod tests { let keypair = generate_keypair(); let data = hash(b"hello, world"); let event0 = Event::Claim { - key: get_pubkey(&keypair), + to: get_pubkey(&keypair), data, sig: sign_serialized(&data, &keypair), }; @@ -252,7 +252,7 @@ mod tests { fn test_wrong_data_claim_attack() { let keypair = generate_keypair(); let event0 = Event::Claim { - key: get_pubkey(&keypair), + to: get_pubkey(&keypair), data: hash(b"goodbye cruel world"), sig: sign_serialized(&hash(b"hello, world"), &keypair), }; From a0410c46775d84d7826701a20a7ba85359bdd8e1 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 10:58:43 -0700 Subject: [PATCH 08/13] Pipe all Claim constructors through a function --- src/accountant.rs | 2 +- src/bin/demo.rs | 6 +----- src/event.rs | 6 ++++++ src/historian.rs | 8 ++------ src/log.rs | 33 ++++++++++----------------------- 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index f99e986fb8bce3..62be909963b5ed 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -49,7 +49,7 @@ impl Accountant { } pub fn deposit_signed(self: &mut Self, to: PublicKey, data: u64, sig: Signature) -> Result<()> { - let event = Event::Claim { to, data, sig }; + let event = Event::new_claim(to, data, sig); if !self.historian.verify_event(&event) { return Err(AccountingError::InvalidEvent); } diff --git a/src/bin/demo.rs b/src/bin/demo.rs index cb09ee52ac81dc..c0eaef2225fdd4 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -11,11 +11,7 @@ fn create_log(hist: &Historian) -> Result<(), SendError { }, } +impl Event { + pub fn new_claim(to: PublicKey, data: T, sig: Signature) -> Self { + Event::Claim { to, data, sig } + } +} + /// Return a new ED25519 keypair pub fn generate_keypair() -> Ed25519KeyPair { use ring::{rand, signature}; diff --git a/src/historian.rs b/src/historian.rs index bfbca2c8b96382..573e927b57b615 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -215,11 +215,7 @@ mod tests { fn test_bad_event_signature() { let keypair = generate_keypair(); let sig = sign_serialized(&hash(b"hello, world"), &keypair); - let event0 = Event::Claim { - to: get_pubkey(&keypair), - data: hash(b"goodbye cruel world"), - sig, - }; + let event0 = Event::new_claim(get_pubkey(&keypair), hash(b"goodbye cruel world"), sig); let mut sigs = HashSet::new(); assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); assert!(!sigs.contains(&sig)); @@ -231,7 +227,7 @@ mod tests { let to = get_pubkey(&keypair); let data = &hash(b"hello, world"); let sig = sign_serialized(data, &keypair); - let event0 = Event::Claim { to, data, sig }; + let event0 = Event::new_claim(to, data, sig); let mut sigs = HashSet::new(); assert!(verify_event_and_reserve_signature(&mut sigs, &event0)); assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); diff --git a/src/log.rs b/src/log.rs index 77d0584b1cc64a..300522800fa912 100644 --- a/src/log.rs +++ b/src/log.rs @@ -209,24 +209,15 @@ mod tests { let zero = Sha256Hash::default(); let one = hash(&zero); - // First, verify Claim events + // First, verify entries let keypair = generate_keypair(); - let event0 = Event::Claim { - to: get_pubkey(&keypair), - data: zero, - sig: sign_serialized(&zero, &keypair), - }; - - let event1 = Event::Claim { - to: get_pubkey(&keypair), - data: one, - sig: sign_serialized(&one, &keypair), - }; + let event0 = Event::new_claim(get_pubkey(&keypair), zero, sign_serialized(&zero, &keypair)); + let event1 = Event::new_claim(get_pubkey(&keypair), one, sign_serialized(&one, &keypair)); let events = vec![event0, event1]; let mut entries = create_entries(&zero, 0, events); assert!(verify_slice(&entries, &zero)); - // Next, swap two Claim events and ensure verification fails. + // Next, swap two events and ensure verification fails. let event0 = entries[0].event.clone(); let event1 = entries[1].event.clone(); entries[0].event = event1; @@ -238,11 +229,7 @@ mod tests { fn test_claim() { let keypair = generate_keypair(); let data = hash(b"hello, world"); - let event0 = Event::Claim { - to: get_pubkey(&keypair), - data, - sig: sign_serialized(&data, &keypair), - }; + let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_serialized(&data, &keypair)); let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(verify_slice(&entries, &zero)); @@ -251,11 +238,11 @@ mod tests { #[test] fn test_wrong_data_claim_attack() { let keypair = generate_keypair(); - let event0 = Event::Claim { - to: get_pubkey(&keypair), - data: hash(b"goodbye cruel world"), - sig: sign_serialized(&hash(b"hello, world"), &keypair), - }; + let event0 = Event::new_claim( + get_pubkey(&keypair), + hash(b"goodbye cruel world"), + sign_serialized(&hash(b"hello, world"), &keypair), + ); let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(!verify_slice(&entries, &zero)); From 0d0fee1ca18a8e66c2a9d02cb175776ecbf88ffd Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 11:46:19 -0700 Subject: [PATCH 09/13] Sign Claim's 'to' field Otherwise, the accountant will treat deposits of the same amount as duplicates. --- src/accountant.rs | 4 ++-- src/accountant_stub.rs | 4 ++-- src/bin/demo.rs | 4 ++-- src/event.rs | 39 ++++++++++++++++++++++++++++++++++++--- src/historian.rs | 4 ++-- src/log.rs | 8 ++++---- 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 62be909963b5ed..2fa5edb0484a3a 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -69,9 +69,9 @@ impl Accountant { } pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> Result { - use event::{get_pubkey, sign_serialized}; + use event::{get_pubkey, sign_claim_data}; let key = get_pubkey(keypair); - let sig = sign_serialized(&n, keypair); + let sig = sign_claim_data(&n, keypair); self.deposit_signed(key, n, sig).map(|_| sig) } diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index 57b9e6e36c8ed4..b43ffd1eaa4dda 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -34,9 +34,9 @@ impl AccountantStub { } pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> io::Result { - use event::{get_pubkey, sign_serialized}; + use event::{get_pubkey, sign_claim_data}; let key = get_pubkey(keypair); - let sig = sign_serialized(&n, keypair); + let sig = sign_claim_data(&n, keypair); self.deposit_signed(key, n, sig).map(|_| sig) } diff --git a/src/bin/demo.rs b/src/bin/demo.rs index c0eaef2225fdd4..4d9e12000cad13 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -2,7 +2,7 @@ extern crate silk; use silk::historian::Historian; use silk::log::{verify_slice, Entry, Sha256Hash}; -use silk::event::{generate_keypair, get_pubkey, sign_serialized, Event}; +use silk::event::{generate_keypair, get_pubkey, sign_claim_data, Event}; use std::thread::sleep; use std::time::Duration; use std::sync::mpsc::SendError; @@ -11,7 +11,7 @@ fn create_log(hist: &Historian) -> Result<(), SendError { impl Event { pub fn new_claim(to: PublicKey, data: T, sig: Signature) -> Self { + // TODO: Change this to a Transaction Event::Claim { to, data, sig } + //Event::Transaction { + // from: None, + // to, + // data, + // sig, + //} } } @@ -63,7 +70,7 @@ pub fn get_pubkey(keypair: &Ed25519KeyPair) -> PublicKey { } /// Return a signature for the given data using the private key from the given keypair. -pub fn sign_serialized(data: &T, keypair: &Ed25519KeyPair) -> Signature { +fn sign_serialized(data: &T, keypair: &Ed25519KeyPair) -> Signature { use bincode::serialize; let serialized = serialize(data).unwrap(); GenericArray::clone_from_slice(keypair.sign(&serialized).as_ref()) @@ -78,6 +85,11 @@ pub fn sign_transaction_data( sign_serialized(&(data, to), keypair) } +/// Return a signature for the given data using the private key from the given keypair. +pub fn sign_claim_data(data: &T, keypair: &Ed25519KeyPair) -> Signature { + sign_transaction_data(data, keypair, &get_pubkey(keypair)) +} + /// Verify a signed message with the given public key. pub fn verify_signature(peer_public_key_bytes: &[u8], msg_bytes: &[u8], sig_bytes: &[u8]) -> bool { use untrusted; @@ -99,8 +111,8 @@ pub fn get_signature(event: &Event) -> Option { pub fn verify_event(event: &Event) -> bool { use bincode::serialize; if let Event::Claim { to, ref data, sig } = *event { - let mut claim_data = serialize(&data).unwrap(); - if !verify_signature(&to, &claim_data, &sig) { + let sign_data = serialize(&(&data, &to)).unwrap(); + if !verify_signature(&to, &sign_data, &sig) { return false; } } @@ -118,3 +130,24 @@ pub fn verify_event(event: &Event) -> bool { } true } + +#[cfg(test)] +mod tests { + use super::*; + use bincode::{deserialize, serialize}; + use log::{hash, Sha256Hash}; + + #[test] + fn test_serialize_claim() { + //let claim0 = Event::new_claim(Default::default(), 0u8, Default::default()); + let claim0 = Event::Transaction { + from: None, + to: Default::default(), + data: hash(b"hi"), + sig: Default::default(), + }; + let buf = serialize(&claim0).unwrap(); + let claim1: Event = deserialize(&buf).unwrap(); + assert_eq!(claim1, claim0); + } +} diff --git a/src/historian.rs b/src/historian.rs index 573e927b57b615..515a5746e98cfb 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -214,7 +214,7 @@ mod tests { #[test] fn test_bad_event_signature() { let keypair = generate_keypair(); - let sig = sign_serialized(&hash(b"hello, world"), &keypair); + let sig = sign_claim_data(&hash(b"hello, world"), &keypair); let event0 = Event::new_claim(get_pubkey(&keypair), hash(b"goodbye cruel world"), sig); let mut sigs = HashSet::new(); assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); @@ -226,7 +226,7 @@ mod tests { let keypair = generate_keypair(); let to = get_pubkey(&keypair); let data = &hash(b"hello, world"); - let sig = sign_serialized(data, &keypair); + let sig = sign_claim_data(data, &keypair); let event0 = Event::new_claim(to, data, sig); let mut sigs = HashSet::new(); assert!(verify_event_and_reserve_signature(&mut sigs, &event0)); diff --git a/src/log.rs b/src/log.rs index 300522800fa912..c1a4a7ee4f7906 100644 --- a/src/log.rs +++ b/src/log.rs @@ -211,8 +211,8 @@ mod tests { // First, verify entries let keypair = generate_keypair(); - let event0 = Event::new_claim(get_pubkey(&keypair), zero, sign_serialized(&zero, &keypair)); - let event1 = Event::new_claim(get_pubkey(&keypair), one, sign_serialized(&one, &keypair)); + let event0 = Event::new_claim(get_pubkey(&keypair), zero, sign_claim_data(&zero, &keypair)); + let event1 = Event::new_claim(get_pubkey(&keypair), one, sign_claim_data(&one, &keypair)); let events = vec![event0, event1]; let mut entries = create_entries(&zero, 0, events); assert!(verify_slice(&entries, &zero)); @@ -229,7 +229,7 @@ mod tests { fn test_claim() { let keypair = generate_keypair(); let data = hash(b"hello, world"); - let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_serialized(&data, &keypair)); + let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair)); let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(verify_slice(&entries, &zero)); @@ -241,7 +241,7 @@ mod tests { let event0 = Event::new_claim( get_pubkey(&keypair), hash(b"goodbye cruel world"), - sign_serialized(&hash(b"hello, world"), &keypair), + sign_claim_data(&hash(b"hello, world"), &keypair), ); let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); From 9f12cd0c09d2f7e0d5c13665d539d10a916a7664 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 11:48:56 -0700 Subject: [PATCH 10/13] Purge the Claim event type It's now represented as a Transaction from an unknown party. --- src/accountant.rs | 1 - src/event.rs | 26 ++++++-------------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 2fa5edb0484a3a..69966c49de1c37 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -137,7 +137,6 @@ impl Accountant { let mut found = false; while !found { found = entries.iter().any(|e| match e.event { - Event::Claim { sig, .. } => sig == *wait_sig, Event::Transaction { sig, .. } => sig == *wait_sig, _ => false, }); diff --git a/src/event.rs b/src/event.rs index 094c333095aead..d452e9e6b58a4b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -29,11 +29,6 @@ pub type Signature = GenericArray; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Event { Tick, - Claim { - to: PublicKey, - data: T, - sig: Signature, - }, Transaction { from: Option, to: PublicKey, @@ -44,14 +39,12 @@ pub enum Event { impl Event { pub fn new_claim(to: PublicKey, data: T, sig: Signature) -> Self { - // TODO: Change this to a Transaction - Event::Claim { to, data, sig } - //Event::Transaction { - // from: None, - // to, - // data, - // sig, - //} + Event::Transaction { + from: None, + to, + data, + sig, + } } } @@ -103,19 +96,12 @@ pub fn verify_signature(peer_public_key_bytes: &[u8], msg_bytes: &[u8], sig_byte pub fn get_signature(event: &Event) -> Option { match *event { Event::Tick => None, - Event::Claim { sig, .. } => Some(sig), Event::Transaction { sig, .. } => Some(sig), } } pub fn verify_event(event: &Event) -> bool { use bincode::serialize; - if let Event::Claim { to, ref data, sig } = *event { - let sign_data = serialize(&(&data, &to)).unwrap(); - if !verify_signature(&to, &sign_data, &sig) { - return false; - } - } if let Event::Transaction { from, to, From c998fbe2ae4fc433d254e939e7fb53f28c2d248a Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 11:56:40 -0700 Subject: [PATCH 11/13] Sign the owner's public key Without this, the accountant will reject transfers from different entities if they are for the same amount and to the same entity. --- src/event.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/event.rs b/src/event.rs index d452e9e6b58a4b..3d32aaf338b4c2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -75,12 +75,15 @@ pub fn sign_transaction_data( keypair: &Ed25519KeyPair, to: &PublicKey, ) -> Signature { - sign_serialized(&(data, to), keypair) + let from = &Some(get_pubkey(keypair)); + sign_serialized(&(from, to, data), keypair) } /// Return a signature for the given data using the private key from the given keypair. pub fn sign_claim_data(data: &T, keypair: &Ed25519KeyPair) -> Signature { - sign_transaction_data(data, keypair, &get_pubkey(keypair)) + let to = get_pubkey(keypair); + let from: Option = None; + sign_serialized(&(&from, &to, data), keypair) } /// Verify a signed message with the given public key. @@ -109,7 +112,7 @@ pub fn verify_event(event: &Event) -> bool { sig, } = *event { - let sign_data = serialize(&(&data, &to)).unwrap(); + let sign_data = serialize(&(&from, &to, &data)).unwrap(); if !verify_signature(&from.unwrap_or(to), &sign_data, &sig) { return false; } From be518b569b0226001eac9157fad4d14d61d61902 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 12:03:59 -0700 Subject: [PATCH 12/13] Remove cyclic dependency between event and log --- src/event.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/event.rs b/src/event.rs index 3d32aaf338b4c2..3342bcd2d5ede4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -124,19 +124,12 @@ pub fn verify_event(event: &Event) -> bool { mod tests { use super::*; use bincode::{deserialize, serialize}; - use log::{hash, Sha256Hash}; #[test] fn test_serialize_claim() { - //let claim0 = Event::new_claim(Default::default(), 0u8, Default::default()); - let claim0 = Event::Transaction { - from: None, - to: Default::default(), - data: hash(b"hi"), - sig: Default::default(), - }; + let claim0 = Event::new_claim(Default::default(), 0u8, Default::default()); let buf = serialize(&claim0).unwrap(); - let claim1: Event = deserialize(&buf).unwrap(); + let claim1: Event = deserialize(&buf).unwrap(); assert_eq!(claim1, claim0); } } From a148454376b0c1387ab305a64b6fc743b03f4c2e Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 2 Mar 2018 12:07:05 -0700 Subject: [PATCH 13/13] Update readme --- README.md | 14 ++++++++------ src/bin/demo.rs | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 57a0f0e5c09270..62b451a7a96e94 100644 --- a/README.md +++ b/README.md @@ -37,15 +37,18 @@ 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, Event, Sha256Hash}; +use silk::log::{verify_slice, Entry, Sha256Hash}; +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 data = Sha256Hash::default(); - hist.sender.send(Event::Claim { data })?; + let keypair = generate_keypair(); + let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair)); + hist.sender.send(event0)?; sleep(Duration::from_millis(10)); Ok(()) } @@ -55,11 +58,10 @@ fn main() { let hist = Historian::new(&seed, Some(10)); create_log(&hist).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); } - // Proof-of-History: Verify the historian learned about the events // in the same order they appear in the vector. assert!(verify_slice(&entries, &seed)); @@ -70,7 +72,7 @@ Running the program should produce a log similar to: ```rust Entry { num_hashes: 0, end_hash: [0, ...], event: Tick } -Entry { num_hashes: 2, end_hash: [67, ...], event: Claim { data: [37, ...] } } +Entry { num_hashes: 2, end_hash: [67, ...], event: Transaction { data: [37, ...] } } Entry { num_hashes: 3, end_hash: [123, ...], event: Tick } ``` diff --git a/src/bin/demo.rs b/src/bin/demo.rs index 4d9e12000cad13..c4b2121692f511 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -26,5 +26,7 @@ fn main() { for entry in &entries { println!("{:?}", entry); } + // Proof-of-History: Verify the historian learned about the events + // in the same order they appear in the vector. assert!(verify_slice(&entries, &seed)); }