diff --git a/src/accountant.rs b/src/accountant.rs index 6cb11734ce..c3a5100fdc 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -2,7 +2,7 @@ //! event log to record transactions. Its users can deposit funds and //! transfer funds to other users. -use log::{hash, Entry, Sha256Hash}; +use log::Sha256Hash; use event::{get_pubkey, sign_transaction_data, verify_event, Event, PublicKey, Signature}; use genesis::Genesis; use historian::{reserve_signature, Historian}; @@ -23,16 +23,18 @@ pub type Result = result::Result; pub struct Accountant { pub historian: Historian, pub balances: HashMap, + pub first_id: Sha256Hash, pub last_id: Sha256Hash, } impl Accountant { pub fn new(gen: &Genesis, ms_per_tick: Option) -> Self { - let start_hash = hash(&gen.pkcs8); + let start_hash = gen.get_seed(); let hist = Historian::::new(&start_hash, ms_per_tick); let mut acc = Accountant { historian: hist, balances: HashMap::new(), + first_id: start_hash, last_id: start_hash, }; for (i, event) in gen.create_events().iter().enumerate() { @@ -41,17 +43,11 @@ impl Accountant { acc } - pub fn sync(self: &mut Self) -> Vec> { - let mut entries = vec![]; + pub fn sync(self: &mut Self) -> Sha256Hash { while let Ok(entry) = self.historian.receiver.try_recv() { - entries.push(entry); + self.last_id = entry.id; } - - if let Some(last_entry) = entries.last() { - self.last_id = last_entry.id; - } - - entries + self.last_id } fn is_deposit(allow_deposits: bool, from: &PublicKey, to: &PublicKey) -> bool { @@ -112,11 +108,13 @@ impl Accountant { to: PublicKey, ) -> Result { let from = get_pubkey(keypair); - let sig = sign_transaction_data(&n, keypair, &to); + let last_id = self.last_id; + let sig = sign_transaction_data(&n, keypair, &to, &last_id); let event = Event::Transaction { from, to, data: n, + last_id, sig, }; self.process_event(event).map(|_| sig) diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs index 32296cd09f..1dd95c8dc6 100644 --- a/src/accountant_skel.rs +++ b/src/accountant_skel.rs @@ -6,7 +6,7 @@ use std::net::UdpSocket; use bincode::{deserialize, serialize}; pub struct AccountantSkel { - pub obj: Accountant, + pub acc: Accountant, } #[derive(Serialize, Deserialize, Debug)] @@ -15,47 +15,67 @@ pub enum Request { from: PublicKey, to: PublicKey, val: u64, + last_id: Sha256Hash, sig: Signature, }, GetBalance { key: PublicKey, }, GetEntries { - last_id: Option, + last_id: Sha256Hash, + }, + GetId { + is_last: bool, }, } #[derive(Serialize, Deserialize, Debug)] pub enum Response { Balance { key: PublicKey, val: u64 }, - Confirmed { sig: Signature }, Entries { entries: Vec> }, + Id { id: Sha256Hash, is_last: bool }, } impl AccountantSkel { - pub fn new(obj: Accountant) -> Self { - AccountantSkel { obj } + pub fn new(acc: Accountant) -> Self { + AccountantSkel { acc } } pub fn process_request(self: &mut Self, msg: Request) -> Option { match msg { - Request::Transfer { from, to, val, sig } => { + Request::Transfer { + from, + to, + val, + last_id, + sig, + } => { let event = Event::Transaction { from, to, data: val, + last_id, sig, }; - if let Err(err) = self.obj.process_event(event) { + if let Err(err) = self.acc.process_event(event) { println!("Transfer error: {:?}", err); } None } Request::GetBalance { key } => { - let val = self.obj.get_balance(&key).unwrap(); + let val = self.acc.get_balance(&key).unwrap(); Some(Response::Balance { key, val }) } Request::GetEntries { .. } => Some(Response::Entries { entries: vec![] }), + Request::GetId { is_last } => Some(Response::Id { + id: if is_last { + self.acc.sync(); + self.acc.last_id + } else { + self.acc.first_id + }, + is_last, + }), } } diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index cbb5b336b3..15615275c7 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -30,9 +30,16 @@ impl AccountantStub { from: PublicKey, to: PublicKey, val: u64, + last_id: Sha256Hash, sig: Signature, ) -> io::Result { - let req = Request::Transfer { from, to, val, sig }; + let req = Request::Transfer { + from, + to, + val, + last_id, + sig, + }; let data = serialize(&req).unwrap(); self.socket.send_to(&data, &self.addr) } @@ -42,10 +49,12 @@ impl AccountantStub { n: u64, keypair: &Ed25519KeyPair, to: PublicKey, + last_id: &Sha256Hash, ) -> io::Result { let from = get_pubkey(keypair); - let sig = sign_transaction_data(&n, keypair, &to); - self.transfer_signed(from, to, n, sig).map(|_| sig) + let sig = sign_transaction_data(&n, keypair, &to, last_id); + self.transfer_signed(from, to, n, *last_id, sig) + .map(|_| sig) } pub fn get_balance(&self, pubkey: &PublicKey) -> io::Result { @@ -62,10 +71,34 @@ impl AccountantStub { Ok(0) } + 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)?; + let mut buf = vec![0u8; 1024]; + self.socket.recv_from(&mut buf)?; + let resp = deserialize(&buf).expect("deserialize Id"); + if let Response::Id { id, .. } = resp { + return Ok(id); + } + Ok(Default::default()) + } + + pub fn get_last_id(&self) -> io::Result { + self.get_id(true) + } + pub fn wait_on_signature(&mut self, wait_sig: &Signature) -> io::Result<()> { - let req = Request::GetEntries { - last_id: self.last_id, + let last_id = match self.last_id { + None => { + let first_id = self.get_id(false)?; + self.last_id = Some(first_id); + first_id + } + Some(last_id) => last_id, }; + + let req = Request::GetEntries { last_id }; let data = serialize(&req).unwrap(); self.socket.send_to(&data, &self.addr).map(|_| ())?; @@ -110,7 +143,9 @@ mod tests { let socket = UdpSocket::bind(send_addr).unwrap(); let mut acc = AccountantStub::new(addr, socket); - let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap(); + let last_id = acc.get_last_id().unwrap(); + let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey, &last_id) + .unwrap(); acc.wait_on_signature(&sig).unwrap(); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); } diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index 3b93ad7780..e255419807 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -29,6 +29,7 @@ fn main() { let socket = UdpSocket::bind(send_addr).unwrap(); let mut acc = AccountantStub::new(addr, socket); + let last_id = acc.get_last_id().unwrap(); let alice_pubkey = get_pubkey(&alice_keypair); let one = 1; println!("Signing transactions..."); @@ -37,7 +38,7 @@ fn main() { .map(|_| { let rando_keypair = generate_keypair(); let rando_pubkey = get_pubkey(&rando_keypair); - let sig = sign_transaction_data(&one, &alice_keypair, &rando_pubkey); + let sig = sign_transaction_data(&one, &alice_keypair, &rando_pubkey, &last_id); (rando_pubkey, sig) }) .collect(); @@ -58,6 +59,7 @@ fn main() { from: alice_pubkey, to: k, data: one, + last_id, sig: s, }; assert!(verify_event(&e)); @@ -76,7 +78,8 @@ fn main() { let now = Instant::now(); let mut sig = Default::default(); for (k, s) in sigs { - acc.transfer_signed(alice_pubkey, k, one, s).unwrap(); + acc.transfer_signed(alice_pubkey, k, one, last_id, s) + .unwrap(); sig = s; } println!("Waiting for last transaction to be confirmed...",); diff --git a/src/bin/demo.rs b/src/bin/demo.rs index c4b2121692..4f6967e418 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -7,11 +7,19 @@ 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, + seed: &Sha256Hash, +) -> Result<(), SendError>> { sleep(Duration::from_millis(15)); let data = Sha256Hash::default(); let keypair = generate_keypair(); - let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair)); + let event0 = Event::new_claim( + get_pubkey(&keypair), + data, + *seed, + sign_claim_data(&data, &keypair, seed), + ); hist.sender.send(event0)?; sleep(Duration::from_millis(10)); Ok(()) @@ -20,7 +28,7 @@ fn create_log(hist: &Historian) -> Result<(), SendError> = hist.receiver.iter().collect(); for entry in &entries { diff --git a/src/event.rs b/src/event.rs index 7c263e437a..1b1fd2f386 100644 --- a/src/event.rs +++ b/src/event.rs @@ -20,6 +20,7 @@ use ring::{rand, signature}; use untrusted; use serde::Serialize; use bincode::serialize; +use log::Sha256Hash; pub type PublicKey = GenericArray; pub type Signature = GenericArray; @@ -36,16 +37,18 @@ pub enum Event { from: PublicKey, to: PublicKey, data: T, + last_id: Sha256Hash, sig: Signature, }, } impl Event { - pub fn new_claim(to: PublicKey, data: T, sig: Signature) -> Self { + pub fn new_claim(to: PublicKey, data: T, last_id: Sha256Hash, sig: Signature) -> Self { Event::Transaction { from: to, to, data, + last_id, sig, } } @@ -74,14 +77,19 @@ pub fn sign_transaction_data( data: &T, keypair: &Ed25519KeyPair, to: &PublicKey, + last_id: &Sha256Hash, ) -> Signature { let from = &get_pubkey(keypair); - sign_serialized(&(from, to, data), keypair) + sign_serialized(&(from, to, data, last_id), 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)) +pub fn sign_claim_data( + data: &T, + keypair: &Ed25519KeyPair, + last_id: &Sha256Hash, +) -> Signature { + sign_transaction_data(data, keypair, &get_pubkey(keypair), last_id) } /// Verify a signed message with the given public key. @@ -104,10 +112,11 @@ pub fn verify_event(event: &Event) -> bool { from, to, ref data, + last_id, sig, } = *event { - let sign_data = serialize(&(&from, &to, &data)).unwrap(); + let sign_data = serialize(&(&from, &to, &data, &last_id)).unwrap(); if !verify_signature(&from, &sign_data, &sig) { return false; } @@ -122,7 +131,12 @@ mod tests { #[test] fn test_serialize_claim() { - let claim0 = Event::new_claim(Default::default(), 0u8, Default::default()); + let claim0 = Event::new_claim( + Default::default(), + 0u8, + Default::default(), + Default::default(), + ); let buf = serialize(&claim0).unwrap(); let claim1: Event = deserialize(&buf).unwrap(); assert_eq!(claim1, claim0); diff --git a/src/genesis.rs b/src/genesis.rs index 7982411796..8d09dc646d 100644 --- a/src/genesis.rs +++ b/src/genesis.rs @@ -1,6 +1,7 @@ //! A library for generating the chain's genesis block. use event::{generate_keypair, get_pubkey, sign_transaction_data, Event, PublicKey}; +use log::{hash, Sha256Hash}; use ring::rand::SystemRandom; use ring::signature::Ed25519KeyPair; use untrusted::Input; @@ -38,6 +39,10 @@ impl Genesis { } } + pub fn get_seed(&self) -> Sha256Hash { + hash(&self.pkcs8) + } + pub fn get_keypair(&self) -> Ed25519KeyPair { Ed25519KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap() } @@ -47,12 +52,14 @@ impl Genesis { } pub fn create_transaction(&self, data: u64, to: &PublicKey) -> Event { + let last_id = self.get_seed(); let from = self.get_pubkey(); - let sig = sign_transaction_data(&data, &self.get_keypair(), to); + let sig = sign_transaction_data(&data, &self.get_keypair(), to, &last_id); Event::Transaction { from, to: *to, data, + last_id, sig, } } diff --git a/src/historian.rs b/src/historian.rs index 7f87e0f943..6db553b476 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -114,8 +114,9 @@ mod tests { let keypair = generate_keypair(); let to = get_pubkey(&keypair); let data = b"hello, world"; - let sig = sign_claim_data(&data, &keypair); - let event0 = Event::new_claim(to, &data, sig); + let zero = Sha256Hash::default(); + let sig = sign_claim_data(&data, &keypair, &zero); + let event0 = Event::new_claim(to, &data, zero, sig); let mut sigs = HashSet::new(); assert!(reserve_signature(&mut sigs, &event0)); assert!(!reserve_signature(&mut sigs, &event0)); diff --git a/src/log.rs b/src/log.rs index 8762d289d0..3a254fd159 100644 --- a/src/log.rs +++ b/src/log.rs @@ -217,8 +217,18 @@ mod tests { // First, verify entries let keypair = generate_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 event0 = Event::new_claim( + get_pubkey(&keypair), + zero, + zero, + sign_claim_data(&zero, &keypair, &zero), + ); + let event1 = Event::new_claim( + get_pubkey(&keypair), + one, + zero, + sign_claim_data(&one, &keypair, &zero), + ); let events = vec![event0, event1]; let mut entries = create_entries(&zero, events); assert!(verify_slice(&entries, &zero)); @@ -235,8 +245,13 @@ 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_claim_data(&data, &keypair)); let zero = Sha256Hash::default(); + let event0 = Event::new_claim( + get_pubkey(&keypair), + data, + zero, + sign_claim_data(&data, &keypair, &zero), + ); let entries = create_entries(&zero, vec![event0]); assert!(verify_slice(&entries, &zero)); } @@ -244,18 +259,20 @@ mod tests { #[test] fn test_wrong_data_claim_attack() { let keypair = generate_keypair(); + let zero = Sha256Hash::default(); let event0 = Event::new_claim( get_pubkey(&keypair), hash(b"goodbye cruel world"), - sign_claim_data(&hash(b"hello, world"), &keypair), + zero, + sign_claim_data(&hash(b"hello, world"), &keypair, &zero), ); - let zero = Sha256Hash::default(); let entries = create_entries(&zero, vec![event0]); assert!(!verify_slice(&entries, &zero)); } #[test] fn test_transfer() { + let zero = Sha256Hash::default(); let keypair0 = generate_keypair(); let keypair1 = generate_keypair(); let pubkey1 = get_pubkey(&keypair1); @@ -264,9 +281,9 @@ mod tests { from: get_pubkey(&keypair0), to: pubkey1, data, - sig: sign_transaction_data(&data, &keypair0, &pubkey1), + last_id: zero, + sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero), }; - let zero = Sha256Hash::default(); let entries = create_entries(&zero, vec![event0]); assert!(verify_slice(&entries, &zero)); } @@ -277,13 +294,14 @@ mod tests { let keypair1 = generate_keypair(); let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); + let zero = Sha256Hash::default(); let event0 = Event::Transaction { from: get_pubkey(&keypair0), to: pubkey1, data: hash(b"goodbye cruel world"), // <-- attack! - sig: sign_transaction_data(&data, &keypair0, &pubkey1), + last_id: zero, + sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero), }; - let zero = Sha256Hash::default(); let entries = create_entries(&zero, vec![event0]); assert!(!verify_slice(&entries, &zero)); } @@ -295,13 +313,14 @@ mod tests { let thief_keypair = generate_keypair(); let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); + let zero = Sha256Hash::default(); let event0 = Event::Transaction { from: get_pubkey(&keypair0), to: get_pubkey(&thief_keypair), // <-- attack! data: hash(b"goodbye cruel world"), - sig: sign_transaction_data(&data, &keypair0, &pubkey1), + last_id: zero, + sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero), }; - let zero = Sha256Hash::default(); let entries = create_entries(&zero, vec![event0]); assert!(!verify_slice(&entries, &zero)); } diff --git a/src/logger.rs b/src/logger.rs index a4def4a8d5..46218d93a1 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -83,16 +83,22 @@ mod tests { #[test] fn test_bad_event_signature() { + let zero = Sha256Hash::default(); let keypair = generate_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 sig = sign_claim_data(&hash(b"hello, world"), &keypair, &zero); + let event0 = Event::new_claim( + get_pubkey(&keypair), + hash(b"goodbye cruel world"), + zero, + sig, + ); assert!(!verify_event(&event0)); } fn run_genesis(gen: Genesis) -> Vec> { let (sender, event_receiver) = sync_channel(100); let (entry_sender, receiver) = sync_channel(100); - let mut logger = Logger::new(event_receiver, entry_sender, hash(&gen.pkcs8)); + let mut logger = Logger::new(event_receiver, entry_sender, gen.get_seed()); for tx in gen.create_events() { sender.send(tx).unwrap(); }