diff --git a/Cargo.toml b/Cargo.toml index 9523e3f6e..83ac0e595 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "silk" description = "A silky smooth implementation of the Loom architecture" -version = "0.2.3" +version = "0.3.0" documentation = "https://docs.rs/silk" homepage = "http://loomprotocol.com/" repository = "https://github.com/loomprotocol/silk" @@ -15,6 +15,14 @@ license = "Apache-2.0" name = "silk-demo" path = "src/bin/demo.rs" +[[bin]] +name = "silk-client-demo" +path = "src/bin/client-demo.rs" + +[[bin]] +name = "silk-testnode" +path = "src/bin/testnode.rs" + [badges] codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" } diff --git a/src/accountant.rs b/src/accountant.rs index 857cc8ad2..2892c3973 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::{Event, PublicKey, Sha256Hash, Signature}; use historian::Historian; use ring::signature::Ed25519KeyPair; use std::sync::mpsc::{RecvError, SendError}; @@ -24,8 +24,8 @@ impl Accountant { } } - pub fn process_event(self: &mut Self, event: Event) { - match event { + 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) { @@ -52,12 +52,31 @@ impl Accountant { } pub fn sync(self: &mut Self) { + let mut entries = vec![]; while let Ok(entry) = self.historian.receiver.try_recv() { - assert!(verify_entry(&entry, &self.end_hash)); - self.end_hash = entry.end_hash; - - self.process_event(entry.event); + 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); + } + } + + 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( @@ -65,8 +84,30 @@ impl Accountant { 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 { + // TODO: Replace the SendError result with a custom one. + println!("Error: Insufficient funds"); + return Ok(()); + } + let event = Event::Transaction { + from, + to, + data, + sig, + }; self.historian.sender.send(event) } @@ -74,17 +115,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 +135,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 +148,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 +171,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 +187,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 +214,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..18419896c --- /dev/null +++ b/src/accountant_skel.rs @@ -0,0 +1,75 @@ +use std::io; +use accountant::Accountant; +use log::{PublicKey, Signature}; +//use serde::Serialize; + +pub struct AccountantSkel { + pub obj: Accountant, +} + +#[derive(Serialize, Deserialize, Debug)] +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, Debug)] +pub enum Response { + Balance { key: PublicKey, val: u64 }, +} + +impl AccountantSkel { + pub fn new(obj: Accountant) -> Self { + AccountantSkel { obj } + } + + pub fn process_request(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![0u8; 1024]; + loop { + //println!("skel: Waiting for incoming connections..."); + let (mut stream, _from_addr) = listener.accept()?; + let _sz = stream.read(&mut buf)?; + + // TODO: Return a descriptive error message if deserialization fails. + let req = deserialize(&buf).expect("deserialize request"); + + if let Some(resp) = self.process_request(req) { + stream.write(&serialize(&resp).expect("serialize response"))?; + } + } + } +} diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs new file mode 100644 index 000000000..5c5749b1b --- /dev/null +++ b/src/accountant_stub.rs @@ -0,0 +1,116 @@ +//! The `accountant` is a client of the `historian`. It uses the historian's +//! event log to record transactions. Its users can deposit funds and +//! transfer funds to other users. + +use std::net::TcpStream; +use std::io; +use std::io::{Read, Write}; +use bincode::{deserialize, serialize}; +use log::{PublicKey, Signature}; +use ring::signature::Ed25519KeyPair; +use accountant_skel::{Request, Response}; + +pub struct AccountantStub { + pub addr: String, +} + +impl AccountantStub { + pub fn new(addr: &str) -> Self { + AccountantStub { + addr: addr.to_string(), + } + } + + pub fn deposit_signed( + self: &mut Self, + key: PublicKey, + val: u64, + sig: Signature, + ) -> io::Result { + let req = Request::Deposit { key, val, sig }; + let data = serialize(&req).unwrap(); + let mut stream = TcpStream::connect(&self.addr)?; + stream.write(&data) + } + + pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> io::Result { + 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, + val: u64, + sig: Signature, + ) -> io::Result { + let req = Request::Transfer { from, to, val, sig }; + let data = serialize(&req).unwrap(); + let mut stream = TcpStream::connect(&self.addr)?; + stream.write(&data) + } + + pub fn transfer( + self: &mut Self, + n: u64, + keypair: &Ed25519KeyPair, + to: PublicKey, + ) -> io::Result { + use log::{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) + } + + pub fn get_balance(self: &mut Self, pubkey: &PublicKey) -> io::Result { + let mut stream = TcpStream::connect(&self.addr)?; + let req = Request::GetBalance { key: *pubkey }; + let data = serialize(&req).expect("serialize GetBalance"); + stream.write(&data)?; + let mut buf = vec![0u8; 1024]; + stream.read(&mut buf)?; + let resp = deserialize(&buf).expect("deserialize balance"); + let Response::Balance { key, val } = resp; + assert_eq!(key, *pubkey); + Ok(val) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use accountant::Accountant; + use accountant_skel::AccountantSkel; + use std::thread::{sleep, spawn}; + use std::time::Duration; + use log::{generate_keypair, get_pubkey, Sha256Hash}; + + #[test] + fn test_accountant_stub() { + let addr = "127.0.0.1:8000"; + spawn(move || { + let zero = Sha256Hash::default(); + let acc = Accountant::new(&zero, None); + let mut skel = AccountantSkel::new(acc); + skel.serve(addr).unwrap(); + }); + + sleep(Duration::from_millis(30)); + + let mut acc = AccountantStub::new(addr); + 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 = get_pubkey(&bob_keypair); + acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); + + sleep(Duration::from_millis(300)); + assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); + } +} diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs new file mode 100644 index 000000000..e2ea5e56b --- /dev/null +++ b/src/bin/client-demo.rs @@ -0,0 +1,45 @@ +extern crate silk; + +fn main() { + use silk::accountant_stub::AccountantStub; + use std::thread::sleep; + use std::time::Duration; + use silk::log::{generate_keypair, get_pubkey}; + + let addr = "127.0.0.1:8000"; + let mut acc = AccountantStub::new(addr); + let alice_keypair = generate_keypair(); + let bob_keypair = generate_keypair(); + let txs = 10_000; + println!("Depositing {} units in Alice's account...", txs); + acc.deposit(txs, &alice_keypair).unwrap(); + //acc.deposit(1_000, &bob_keypair).unwrap(); + println!("Done."); + + sleep(Duration::from_millis(30)); + let alice_pubkey = get_pubkey(&alice_keypair); + let bob_pubkey = get_pubkey(&bob_keypair); + println!("Transferring 1 unit {} times...", txs); + for _ in 0..txs { + acc.transfer(1, &alice_keypair, bob_pubkey).unwrap(); + } + println!("Done."); + + sleep(Duration::from_millis(20)); + let mut alice_val = acc.get_balance(&alice_pubkey).unwrap(); + while alice_val > 0 { + println!("Checking on Alice's Balance {}", alice_val); + sleep(Duration::from_millis(20)); + alice_val = acc.get_balance(&alice_pubkey).unwrap(); + } + println!("Done. Checking balances."); + println!( + "Alice's Final Balance {}", + acc.get_balance(&alice_pubkey).unwrap() + ); + + println!( + "Bob's Final Balance {}", + acc.get_balance(&bob_pubkey).unwrap() + ); +} diff --git a/src/bin/testnode.rs b/src/bin/testnode.rs new file mode 100644 index 000000000..4231eda35 --- /dev/null +++ b/src/bin/testnode.rs @@ -0,0 +1,14 @@ +extern crate silk; + +use silk::accountant_skel::AccountantSkel; +use silk::accountant::Accountant; +use silk::log::Sha256Hash; + +fn main() { + let addr = "127.0.0.1:8000"; + let zero = Sha256Hash::default(); + let acc = Accountant::new(&zero, Some(1000)); + let mut skel = AccountantSkel::new(acc); + println!("Listening on {}", addr); + skel.serve(addr).unwrap(); +} diff --git a/src/historian.rs b/src/historian.rs index 0daba33fb..99e2ff600 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -6,13 +6,14 @@ //! The resulting stream of entries represents ordered events in time. use std::thread::JoinHandle; -use std::sync::mpsc::{Receiver, Sender}; +use std::sync::mpsc::{Receiver, SyncSender}; use std::time::{Duration, SystemTime}; use log::{hash, hash_event, verify_event, Entry, Event, Sha256Hash}; use serde::Serialize; +use std::fmt::Debug; pub struct Historian { - pub sender: Sender>, + pub sender: SyncSender>, pub receiver: Receiver>, pub thread_hdl: JoinHandle<(Entry, ExitReason)>, } @@ -22,8 +23,8 @@ pub enum ExitReason { RecvDisconnected, SendDisconnected, } -fn log_event( - sender: &Sender>, +fn log_event( + sender: &SyncSender>, num_hashes: &mut u64, end_hash: &mut Sha256Hash, event: Event, @@ -41,9 +42,9 @@ fn log_event( Ok(()) } -fn log_events( +fn log_events( receiver: &Receiver>, - sender: &Sender>, + sender: &SyncSender>, num_hashes: &mut u64, end_hash: &mut Sha256Hash, epoch: SystemTime, @@ -82,11 +83,11 @@ fn log_events( /// A background thread that will continue tagging received Event messages and /// sending back Entry messages until either the receiver or sender channel is closed. -pub fn create_logger( +pub fn create_logger( start_hash: Sha256Hash, ms_per_tick: Option, receiver: Receiver>, - sender: Sender>, + sender: SyncSender>, ) -> JoinHandle<(Entry, ExitReason)> { use std::thread; thread::spawn(move || { @@ -112,11 +113,11 @@ pub fn create_logger( }) } -impl Historian { +impl Historian { pub fn new(start_hash: &Sha256Hash, ms_per_tick: Option) -> Self { - use std::sync::mpsc::channel; - let (sender, event_receiver) = channel(); - let (entry_sender, receiver) = channel(); + use std::sync::mpsc::sync_channel; + let (sender, event_receiver) = sync_channel(4000); + let (entry_sender, receiver) = sync_channel(4000); let thread_hdl = create_logger(*start_hash, ms_per_tick, event_receiver, entry_sender); Historian { sender, @@ -192,11 +193,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..6deb92941 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ pub mod log; pub mod historian; pub mod accountant; +pub mod accountant_skel; +pub mod accountant_stub; extern crate bincode; extern crate generic_array; extern crate rayon; diff --git a/src/log.rs b/src/log.rs index 00df286cc..523a12afa 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; } @@ -228,6 +219,14 @@ pub fn verify_slice(events: &[Entry], start_hash: &Sha256Hash) -> bo event_pairs.all(|(x0, x1)| verify_entry(&x1, &x0.end_hash)) } +/// Verifies the hashes and counts of a slice of events are all consistent. +pub fn verify_slice_u64(events: &[Entry], start_hash: &Sha256Hash) -> bool { + use rayon::prelude::*; + let genesis = [Entry::new_tick(Default::default(), start_hash)]; + let event_pairs = genesis.par_iter().chain(events).zip(events); + event_pairs.all(|(x0, x1)| verify_entry(&x1, &x0.end_hash)) +} + /// Verifies the hashes and events serially. Exists only for reference. pub fn verify_slice_seq(events: &[Entry], start_hash: &Sha256Hash) -> bool { let genesis = [Entry::new_tick(0, start_hash)]; @@ -338,7 +337,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 +351,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 +365,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 +382,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 +399,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));