From 604ccf755277f4467a3c82bef1e2ecc1c22a13ee Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Wed, 28 Feb 2018 10:07:54 -0700 Subject: [PATCH] Add network interface for accountant --- Cargo.toml | 4 ++ src/accountant.rs | 65 ++++++++++++++++------- src/accountant_skel.rs | 72 ++++++++++++++++++++++++++ src/bin/client-demo.rs | 84 ++++++++++++++++++++++++++++++ src/historian.rs | 10 ++-- src/lib.rs | 1 + src/log.rs | 115 +++++++++++++++++++---------------------- 7 files changed, 265 insertions(+), 86 deletions(-) create mode 100644 src/accountant_skel.rs create mode 100644 src/bin/client-demo.rs diff --git a/Cargo.toml b/Cargo.toml index 9523e3f6e..c5bd74829 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ license = "Apache-2.0" name = "silk-demo" path = "src/bin/demo.rs" +[[bin]] +name = "silk-client-demo" +path = "src/bin/client-demo.rs" + [badges] codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" } diff --git a/src/accountant.rs b/src/accountant.rs index 857cc8ad2..0e850adbc 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::{verify_entry, Event, PublicKey, Sha256Hash}; +use log::{verify_entry, Event, PublicKey, Sha256Hash, Signature}; use historian::Historian; use ring::signature::Ed25519KeyPair; use std::sync::mpsc::{RecvError, SendError}; @@ -60,13 +60,43 @@ impl Accountant { } } + pub fn deposit_signed( + self: &Self, + key: PublicKey, + data: u64, + sig: Signature, + ) -> Result<(), SendError>> { + let event = Event::Claim { key, data, sig }; + self.historian.sender.send(event) + } + pub fn deposit( self: &Self, n: u64, keypair: &Ed25519KeyPair, ) -> Result<(), SendError>> { - use log::sign_hash; - let event = sign_hash(n, &keypair); + use log::{get_pubkey, sign_serialized}; + let key = get_pubkey(keypair); + let sig = sign_serialized(&n, keypair); + self.deposit_signed(key, n, sig) + } + + pub fn transfer_signed( + self: &mut Self, + from: PublicKey, + to: PublicKey, + data: u64, + sig: Signature, + ) -> Result<(), SendError>> { + if self.get_balance(&from).unwrap() < data { + return Ok(()); + } + let event = Event::Transaction { + from, + to, + data, + sig, + }; self.historian.sender.send(event) } @@ -74,17 +104,13 @@ impl Accountant { self: &mut Self, n: u64, keypair: &Ed25519KeyPair, - pubkey: PublicKey, + to: PublicKey, ) -> Result<(), SendError>> { - use log::transfer_hash; - use generic_array::GenericArray; + use log::{get_pubkey, sign_transaction_data}; - let sender_pubkey = GenericArray::clone_from_slice(keypair.public_key_bytes()); - if self.get_balance(&sender_pubkey).unwrap() >= n { - let event = transfer_hash(n, keypair, pubkey); - return self.historian.sender.send(event); - } - Ok(()) + let from = get_pubkey(keypair); + let sig = sign_transaction_data(&n, keypair, &to); + self.transfer_signed(from, to, n, sig) } pub fn get_balance(self: &mut Self, pubkey: &PublicKey) -> Result { @@ -98,9 +124,8 @@ mod tests { use super::*; use std::thread::sleep; use std::time::Duration; - use log::generate_keypair; + use log::{generate_keypair, get_pubkey}; use historian::ExitReason; - use generic_array::GenericArray; #[test] fn test_accountant() { @@ -112,7 +137,7 @@ mod tests { acc.deposit(1_000, &bob_keypair).unwrap(); sleep(Duration::from_millis(30)); - let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes()); + let bob_pubkey = get_pubkey(&bob_keypair); acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); sleep(Duration::from_millis(30)); @@ -135,11 +160,11 @@ mod tests { acc.deposit(1_000, &bob_keypair).unwrap(); sleep(Duration::from_millis(30)); - let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes()); + let bob_pubkey = get_pubkey(&bob_keypair); acc.transfer(10_001, &alice_keypair, bob_pubkey).unwrap(); sleep(Duration::from_millis(30)); - let alice_pubkey = GenericArray::clone_from_slice(alice_keypair.public_key_bytes()); + let alice_pubkey = get_pubkey(&alice_keypair); assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), 10_000); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_000); @@ -151,14 +176,14 @@ mod tests { } #[test] - fn test_mulitple_claims() { + fn test_multiple_claims() { let zero = Sha256Hash::default(); let mut acc = Accountant::new(&zero, Some(2)); let keypair = generate_keypair(); acc.deposit(1, &keypair).unwrap(); acc.deposit(2, &keypair).unwrap(); - let pubkey = GenericArray::clone_from_slice(keypair.public_key_bytes()); + let pubkey = get_pubkey(&keypair); sleep(Duration::from_millis(30)); assert_eq!(acc.get_balance(&pubkey).unwrap(), 3); @@ -178,7 +203,7 @@ mod tests { acc.deposit(10_000, &alice_keypair).unwrap(); sleep(Duration::from_millis(30)); - let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes()); + let bob_pubkey = get_pubkey(&bob_keypair); acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); sleep(Duration::from_millis(30)); diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs new file mode 100644 index 000000000..60cb36508 --- /dev/null +++ b/src/accountant_skel.rs @@ -0,0 +1,72 @@ +use std::io; +use accountant::Accountant; +use log::{PublicKey, Signature}; +//use serde::Serialize; + +pub struct AccountantSkel { + pub obj: Accountant, +} + +#[derive(Serialize, Deserialize)] +pub enum Request { + Deposit { + key: PublicKey, + val: u64, + sig: Signature, + }, + Transfer { + from: PublicKey, + to: PublicKey, + val: u64, + sig: Signature, + }, + GetBalance { + key: PublicKey, + }, +} + +#[derive(Serialize, Deserialize)] +pub enum Response { + Balance { key: PublicKey, val: u64 }, +} + +impl AccountantSkel { + pub fn process_message(self: &mut Self, msg: Request) -> Option { + match msg { + Request::Deposit { key, val, sig } => { + let _ = self.obj.deposit_signed(key, val, sig); + None + } + Request::Transfer { from, to, val, sig } => { + let _ = self.obj.transfer_signed(from, to, val, sig); + None + } + Request::GetBalance { key } => { + let val = self.obj.get_balance(&key).unwrap(); + Some(Response::Balance { key, val }) + } + } + } + + /// TCP Server that forwards messages to Accountant methods. + pub fn serve(self: &mut Self, addr: &str) -> io::Result<()> { + use std::net::TcpListener; + use std::io::{Read, Write}; + use bincode::{deserialize, serialize}; + let listener = TcpListener::bind(addr)?; + let mut buf = vec![]; + loop { + let (mut stream, addr) = listener.accept()?; + println!("connection received from {}", addr); + + // TODO: Guard against large message DoS attack. + stream.read_to_end(&mut buf)?; + + // TODO: Return a descriptive error message if deserialization fails. + let msg = deserialize(&buf).unwrap(); + if let Some(resp) = self.process_message(msg) { + stream.write(&serialize(&resp).unwrap())?; + } + } + } +} diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs new file mode 100644 index 000000000..003caf6e2 --- /dev/null +++ b/src/bin/client-demo.rs @@ -0,0 +1,84 @@ +extern crate generic_array; +extern crate silk; + +//use log::{Event, PublicKey, Sha256Hash}; +//use std::net::TcpStream; +//use ring::signature::Ed25519KeyPair; +// +//pub struct AccountantStub { +// pub stream: TcpStream, +//} +// +//impl AccountantStub { +// pub fn new(addr: ()) -> Self { +// let mut stream = TcpStream::connect(addr).unwrap(); +// AccountantStub { +// stream: TcpString, +// } +// } +// +// pub fn deposit( +// self: &Self, +// n: u64, +// keypair: &Ed25519KeyPair, +// ) -> Result<(), SendError>> { +// use log::sign_hash; +// let event = sign_hash(n, &keypair); +// self.stream.send(&serialize(event)) +// } +// +// pub fn transfer( +// self: &mut Self, +// n: u64, +// keypair: &Ed25519KeyPair, +// pubkey: PublicKey, +// ) -> io::Result<()> { +// use log::transfer_hash; +// use generic_array::GenericArray; +// let event = transfer_hash(n, &keypair); +// self.stream.send(&serialize(event)) +// } +// +// pub fn get_balance( +// self: &mut Self, +// pubkey: PublicKey, +// ) -> io::Result<()> { +// let event = GetBalance { key: pubkey }; +// self.stream.send(&serialize(event)); +// msg = deserialize(self.sender.recv()); +// if let AccountantMsg::Balance { val } = msg { +// Ok(val) +// } else { +// Err() +// } +// } +//} + +use silk::accountant::Accountant; +use std::thread::sleep; +use std::time::Duration; +use silk::log::{generate_keypair, Sha256Hash}; +use silk::historian::ExitReason; +use generic_array::GenericArray; + +fn main() { + let zero = Sha256Hash::default(); + let mut acc = Accountant::new(&zero, Some(2)); + let alice_keypair = generate_keypair(); + let bob_keypair = generate_keypair(); + acc.deposit(10_000, &alice_keypair).unwrap(); + acc.deposit(1_000, &bob_keypair).unwrap(); + + sleep(Duration::from_millis(30)); + let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes()); + acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); + + sleep(Duration::from_millis(30)); + assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); + + drop(acc.historian.sender); + assert_eq!( + acc.historian.thread_hdl.join().unwrap().1, + ExitReason::RecvDisconnected + ); +} diff --git a/src/historian.rs b/src/historian.rs index 0daba33fb..80df3ba6f 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -192,11 +192,11 @@ mod tests { let zero = Sha256Hash::default(); let hist = Historian::new(&zero, None); let keypair = generate_keypair(); - let mut event0 = sign_hash(hash(b"hello, world"), &keypair); - if let Event::Claim { key, sig, .. } = event0 { - let data = hash(b"goodbye cruel world"); - event0 = Event::Claim { key, data, sig }; - } + let event0 = Event::Claim { + key: get_pubkey(&keypair), + data: hash(b"goodbye cruel world"), + sig: sign_serialized(&hash(b"hello, world"), &keypair), + }; hist.sender.send(event0).unwrap(); drop(hist.sender); assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index 5aef0ab23..c30052ad9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod log; pub mod historian; pub mod accountant; +pub mod accountant_skel; extern crate bincode; extern crate generic_array; extern crate rayon; diff --git a/src/log.rs b/src/log.rs index 00df286cc..a0e6b739c 100644 --- a/src/log.rs +++ b/src/log.rs @@ -65,7 +65,7 @@ impl Entry { } } -// Return a new ED25519 keypair +/// Return a new ED25519 keypair pub fn generate_keypair() -> Ed25519KeyPair { use ring::{rand, signature}; use untrusted; @@ -74,33 +74,25 @@ pub fn generate_keypair() -> Ed25519KeyPair { signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap() } -/// Return a Claim Event for the given hash and key-pair. -pub fn sign_hash(data: T, keypair: &Ed25519KeyPair) -> Event { - use bincode::serialize; - let sig = keypair.sign(&serialize(&data).unwrap()); - let peer_public_key_bytes = keypair.public_key_bytes(); - let sig_bytes = sig.as_ref(); - Event::Claim { - key: GenericArray::clone_from_slice(peer_public_key_bytes), - data, - sig: GenericArray::clone_from_slice(sig_bytes), - } +/// 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 Transaction Event that indicates a transfer in ownership of the given hash. -pub fn transfer_hash(data: T, keypair: &Ed25519KeyPair, to: PublicKey) -> Event { +/// 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 from_public_key_bytes = keypair.public_key_bytes(); - let mut sign_data = serialize(&data).unwrap(); - sign_data.extend_from_slice(&to); - let sig = keypair.sign(&sign_data); - let sig_bytes = sig.as_ref(); - Event::Transaction { - from: GenericArray::clone_from_slice(from_public_key_bytes), - to, - data, - sig: GenericArray::clone_from_slice(sig_bytes), - } + 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. @@ -202,8 +194,7 @@ pub fn verify_event(event: &Event) -> bool { sig, } = *event { - let mut sign_data = serialize(&data).unwrap(); - sign_data.extend_from_slice(&to); + let sign_data = serialize(&(&data, &to)).unwrap(); if !verify_signature(&from, &sign_data, &sig) { return false; } @@ -338,7 +329,12 @@ mod tests { #[test] fn test_claim() { let keypair = generate_keypair(); - let event0 = sign_hash(hash(b"hello, world"), &keypair); + let data = hash(b"hello, world"); + let event0 = Event::Claim { + key: get_pubkey(&keypair), + data, + sig: sign_serialized(&data, &keypair), + }; let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(verify_slice(&entries, &zero)); @@ -347,11 +343,11 @@ mod tests { #[test] fn test_wrong_data_claim_attack() { let keypair = generate_keypair(); - let mut event0 = sign_hash(hash(b"hello, world"), &keypair); - if let Event::Claim { key, sig, .. } = event0 { - let data = hash(b"goodbye cruel world"); - event0 = Event::Claim { key, data, sig }; - } + let event0 = Event::Claim { + key: get_pubkey(&keypair), + data: hash(b"goodbye cruel world"), + sig: sign_serialized(&hash(b"hello, world"), &keypair), + }; let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(!verify_slice(&entries, &zero)); @@ -361,8 +357,14 @@ mod tests { fn test_transfer() { let keypair0 = generate_keypair(); let keypair1 = generate_keypair(); - let pubkey1 = GenericArray::clone_from_slice(keypair1.public_key_bytes()); - let event0 = transfer_hash(hash(b"hello, world"), &keypair0, pubkey1); + let pubkey1 = get_pubkey(&keypair1); + let data = hash(b"hello, world"); + let event0 = Event::Transaction { + from: get_pubkey(&keypair0), + to: pubkey1, + data, + sig: sign_transaction_data(&data, &keypair0, &pubkey1), + }; let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(verify_slice(&entries, &zero)); @@ -372,17 +374,14 @@ mod tests { fn test_wrong_data_transfer_attack() { let keypair0 = generate_keypair(); let keypair1 = generate_keypair(); - let pubkey1 = GenericArray::clone_from_slice(keypair1.public_key_bytes()); - let mut event0 = transfer_hash(hash(b"hello, world"), &keypair0, pubkey1); - if let Event::Transaction { from, to, sig, .. } = event0 { - let data = hash(b"goodbye cruel world"); - event0 = Event::Transaction { - from, - to, - data, - sig, - }; - } + let pubkey1 = get_pubkey(&keypair1); + let data = hash(b"hello, world"); + let event0 = Event::Transaction { + from: get_pubkey(&keypair0), + to: pubkey1, + data: hash(b"goodbye cruel world"), // <-- attack! + sig: sign_transaction_data(&data, &keypair0, &pubkey1), + }; let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(!verify_slice(&entries, &zero)); @@ -392,21 +391,15 @@ mod tests { fn test_transfer_hijack_attack() { let keypair0 = generate_keypair(); let keypair1 = generate_keypair(); - let pubkey1 = GenericArray::clone_from_slice(keypair1.public_key_bytes()); - let mut event0 = transfer_hash(hash(b"hello, world"), &keypair0, pubkey1); - if let Event::Transaction { - from, data, sig, .. - } = event0 - { - let theif_keypair = generate_keypair(); - let to = GenericArray::clone_from_slice(theif_keypair.public_key_bytes()); - event0 = Event::Transaction { - from, - to, - data, - sig, - }; - } + let thief_keypair = generate_keypair(); + let pubkey1 = get_pubkey(&keypair1); + let data = hash(b"hello, world"); + 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), + }; let zero = Sha256Hash::default(); let entries = create_entries(&zero, 0, vec![event0]); assert!(!verify_slice(&entries, &zero));