diff --git a/Cargo.toml b/Cargo.toml index 99637e0cbf..a51cf84e43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ authors = [ license = "Apache-2.0" [[bin]] -name = "silk-demo" -path = "src/bin/demo.rs" +name = "silk-historian-demo" +path = "src/bin/historian-demo.rs" [[bin]] name = "silk-client-demo" @@ -24,12 +24,16 @@ name = "silk-testnode" path = "src/bin/testnode.rs" [[bin]] -name = "silk-genesis-block" -path = "src/bin/genesis-block.rs" +name = "silk-genesis" +path = "src/bin/genesis.rs" [[bin]] -name = "silk-genesis-file-demo" -path = "src/bin/genesis-file-demo.rs" +name = "silk-genesis-demo" +path = "src/bin/genesis-demo.rs" + +[[bin]] +name = "silk-mint" +path = "src/bin/mint.rs" [badges] codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" } diff --git a/README.md b/README.md index ab821b60ce..5b6fe128fb 100644 --- a/README.md +++ b/README.md @@ -32,27 +32,27 @@ First, build the demo executables in release mode (optimized for performance): ``` The testnode server is initialized with a transaction log from stdin and -generates a log on stdout. To create the input log, we'll need to create -a *genesis* configuration file and then generate a log from it. It's done -in two steps here because the demo-genesis.json file contains a private -key that will be used later in this demo. +generates new log entries on stdout. To create the input log, we'll need +to create *the mint* and use it to generate a *genesis log*. It's done in +two steps because the mint.json file contains a private key that will be +used later in this demo. ```bash - $ ./silk-genesis-file-demo > demo-genesis.json - $ cat demo-genesis.json | ./silk-genesis-block > demo-genesis.log + $ echo 500 | ./silk-mint > mint.json + $ cat mint.json | ./silk-genesis > genesis.log ``` Now you can start the server: ```bash - $ cat demo-genesis.log | ./silk-testnode > demo-entries0.log + $ cat genesis.log | ./silk-testnode > transactions0.log ``` Then, in a separate shell, let's execute some transactions. Note we pass in the JSON configuration file here, not the genesis log. ```bash - $ cat demo-genesis.json | ./silk-client-demo + $ cat mint.json | ./silk-client-demo ``` Now kill the server with Ctrl-C, and take a look at the transaction log. You should @@ -68,7 +68,7 @@ Now restart the server from where we left off. Pass it both the genesis log, and the transaction log. ```bash - $ cat demo-genesis.log demo-entries0.log | ./silk-testnode > demo-entries1.log + $ cat genesis.log transactions0.log | ./silk-testnode > transactions1.log ``` Lastly, run the client demo again, and verify that all funds were spent in the diff --git a/src/accountant.rs b/src/accountant.rs index 72bf12b8cb..c84cb70125 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -7,7 +7,7 @@ use entry::Entry; use event::Event; use transaction::Transaction; use signature::{KeyPair, PublicKey, Signature}; -use genesis::Genesis; +use mint::Mint; use historian::{reserve_signature, Historian}; use std::sync::mpsc::SendError; use std::collections::HashMap; @@ -62,8 +62,8 @@ impl Accountant { acc } - pub fn new(gen: &Genesis, ms_per_tick: Option) -> Self { - Self::new_from_entries(gen.create_entries(), ms_per_tick) + pub fn new(mint: &Mint, ms_per_tick: Option) -> Self { + Self::new_from_entries(mint.create_entries(), ms_per_tick) } pub fn sync(self: &mut Self) -> Hash { @@ -146,19 +146,18 @@ impl Accountant { #[cfg(test)] mod tests { use super::*; - use signature::{generate_keypair, get_pubkey}; + use signature::KeyPairUtil; use logger::ExitReason; #[test] fn test_accountant() { - let alice = Genesis::new(10_000); - let bob_pubkey = get_pubkey(&generate_keypair()); + let alice = Mint::new(10_000); + let bob_pubkey = KeyPair::new().pubkey(); let mut acc = Accountant::new(&alice, Some(2)); - acc.transfer(1_000, &alice.get_keypair(), bob_pubkey) - .unwrap(); + acc.transfer(1_000, &alice.keypair(), bob_pubkey).unwrap(); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_000); - acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap(); + acc.transfer(500, &alice.keypair(), bob_pubkey).unwrap(); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); drop(acc.historian.sender); @@ -170,17 +169,16 @@ mod tests { #[test] fn test_invalid_transfer() { - let alice = Genesis::new(11_000); + let alice = Mint::new(11_000); let mut acc = Accountant::new(&alice, Some(2)); - let bob_pubkey = get_pubkey(&generate_keypair()); - acc.transfer(1_000, &alice.get_keypair(), bob_pubkey) - .unwrap(); + let bob_pubkey = KeyPair::new().pubkey(); + acc.transfer(1_000, &alice.keypair(), bob_pubkey).unwrap(); assert_eq!( - acc.transfer(10_001, &alice.get_keypair(), bob_pubkey), + acc.transfer(10_001, &alice.keypair(), bob_pubkey), Err(AccountingError::InsufficientFunds) ); - let alice_pubkey = get_pubkey(&alice.get_keypair()); + let alice_pubkey = alice.keypair().pubkey(); assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), 10_000); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_000); @@ -193,10 +191,10 @@ mod tests { #[test] fn test_transfer_to_newb() { - let alice = Genesis::new(10_000); + let alice = Mint::new(10_000); let mut acc = Accountant::new(&alice, Some(2)); - let alice_keypair = alice.get_keypair(); - let bob_pubkey = get_pubkey(&generate_keypair()); + let alice_keypair = alice.keypair(); + let bob_pubkey = KeyPair::new().pubkey(); acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 500); diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index f3097022ac..bc842474f5 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -115,23 +115,23 @@ mod tests { use accountant_skel::AccountantSkel; use std::thread::{sleep, spawn}; use std::time::Duration; - use genesis::Genesis; - use signature::{generate_keypair, get_pubkey}; + use mint::Mint; + use signature::{KeyPair, KeyPairUtil}; #[test] fn test_accountant_stub() { let addr = "127.0.0.1:9000"; let send_addr = "127.0.0.1:9001"; - let alice = Genesis::new(10_000); + let alice = Mint::new(10_000); let acc = Accountant::new(&alice, None); - let bob_pubkey = get_pubkey(&generate_keypair()); + let bob_pubkey = KeyPair::new().pubkey(); spawn(move || AccountantSkel::new(acc).serve(addr).unwrap()); sleep(Duration::from_millis(30)); let socket = UdpSocket::bind(send_addr).unwrap(); let mut acc = AccountantStub::new(addr, socket); let last_id = acc.get_last_id().unwrap(); - let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey, &last_id) + let sig = acc.transfer(500, &alice.keypair(), bob_pubkey, &last_id) .unwrap(); acc.wait_on_signature(&sig).unwrap(); assert_eq!(acc.get_balance(&bob_pubkey).unwrap().unwrap(), 500); diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index 3005a1fa1c..7a3f4b4fd2 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -2,9 +2,9 @@ extern crate serde_json; extern crate silk; use silk::accountant_stub::AccountantStub; -use silk::signature::{generate_keypair, get_pubkey}; +use silk::signature::{KeyPair, KeyPairUtil}; use silk::transaction::Transaction; -use silk::genesis::Genesis; +use silk::mint::Mint; use std::time::Instant; use std::net::UdpSocket; use std::io::stdin; @@ -13,23 +13,23 @@ fn main() { let addr = "127.0.0.1:8000"; let send_addr = "127.0.0.1:8001"; - let gen: Genesis = serde_json::from_reader(stdin()).unwrap(); - let alice_keypair = gen.get_keypair(); - let alice_pubkey = gen.get_pubkey(); + let mint: Mint = serde_json::from_reader(stdin()).unwrap(); + let mint_keypair = mint.keypair(); + let mint_pubkey = mint.pubkey(); let socket = UdpSocket::bind(send_addr).unwrap(); let mut acc = AccountantStub::new(addr, socket); let last_id = acc.get_last_id().unwrap(); - let txs = acc.get_balance(&alice_pubkey).unwrap().unwrap(); - println!("Alice's Initial Balance {}", txs); + let txs = acc.get_balance(&mint_pubkey).unwrap().unwrap(); + println!("Mint's Initial Balance {}", txs); println!("Signing transactions..."); let now = Instant::now(); let transactions: Vec<_> = (0..txs) .map(|_| { - let rando_pubkey = get_pubkey(&generate_keypair()); - Transaction::new(&alice_keypair, rando_pubkey, 1, last_id) + let rando_pubkey = KeyPair::new().pubkey(); + Transaction::new(&mint_keypair, rando_pubkey, 1, last_id) }) .collect(); let duration = now.elapsed(); @@ -71,7 +71,7 @@ fn main() { let ns = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64; let tps = (txs * 1_000_000_000) as f64 / ns as f64; println!("Done. {} tps!", tps); - let val = acc.get_balance(&alice_pubkey).unwrap().unwrap(); - println!("Alice's Final Balance {}", val); + let val = acc.get_balance(&mint_pubkey).unwrap().unwrap(); + println!("Mint's Final Balance {}", val); assert_eq!(val, 0); } diff --git a/src/bin/genesis-block.rs b/src/bin/genesis-block.rs deleted file mode 100644 index 44cd5d9948..0000000000 --- a/src/bin/genesis-block.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! A command-line executable for generating the chain's genesis block. - -extern crate ring; -extern crate serde_json; -extern crate silk; - -use silk::genesis::Genesis; -use silk::log::verify_slice; -use std::io::stdin; - -fn main() { - let gen: Genesis = serde_json::from_reader(stdin()).unwrap(); - let entries = gen.create_entries(); - verify_slice(&entries, &entries[0].id); - for x in entries { - println!("{}", serde_json::to_string(&x).unwrap()); - } -} diff --git a/src/bin/genesis-file-demo.rs b/src/bin/genesis-demo.rs similarity index 53% rename from src/bin/genesis-file-demo.rs rename to src/bin/genesis-demo.rs index c56986c48f..ad3772d53c 100644 --- a/src/bin/genesis-file-demo.rs +++ b/src/bin/genesis-demo.rs @@ -1,30 +1,30 @@ extern crate serde_json; extern crate silk; -use silk::genesis::Genesis; +use silk::mint::Mint; use silk::event::Event; use silk::transaction::Transaction; use silk::log::create_entries; -use silk::signature::{generate_keypair, get_pubkey, KeyPair, PublicKey}; +use silk::signature::{KeyPair, KeyPairUtil, PublicKey}; use silk::hash::Hash; +use std::io::stdin; fn transfer(from: &KeyPair, (to, tokens): (PublicKey, i64), last_id: Hash) -> Event { Event::Transaction(Transaction::new(&from, to, tokens, last_id)) } fn main() { - let alice = (get_pubkey(&generate_keypair()), 200); - let bob = (get_pubkey(&generate_keypair()), 100); + let alice = (KeyPair::new().pubkey(), 200); + let bob = (KeyPair::new().pubkey(), 100); - let gen = Genesis::new(500); - let from = gen.get_keypair(); - let seed = gen.get_seed(); - let mut events = gen.create_events(); + let mint: Mint = serde_json::from_reader(stdin()).unwrap(); + let from = mint.keypair(); + let seed = mint.seed(); + let mut events = mint.create_events(); events.push(transfer(&from, alice, seed)); events.push(transfer(&from, bob, seed)); - let entries = create_entries(&seed, events); - for entry in entries { + for entry in create_entries(&seed, events) { println!("{}", serde_json::to_string(&entry).unwrap()); } } diff --git a/src/bin/genesis.rs b/src/bin/genesis.rs new file mode 100644 index 0000000000..8a241cb40c --- /dev/null +++ b/src/bin/genesis.rs @@ -0,0 +1,14 @@ +//! A command-line executable for generating the chain's genesis block. + +extern crate serde_json; +extern crate silk; + +use silk::mint::Mint; +use std::io::stdin; + +fn main() { + let mint: Mint = serde_json::from_reader(stdin()).unwrap(); + for x in mint.create_entries() { + println!("{}", serde_json::to_string(&x).unwrap()); + } +} diff --git a/src/bin/demo.rs b/src/bin/historian-demo.rs similarity index 85% rename from src/bin/demo.rs rename to src/bin/historian-demo.rs index 59cd7e9164..a421a1348f 100644 --- a/src/bin/demo.rs +++ b/src/bin/historian-demo.rs @@ -4,7 +4,7 @@ use silk::historian::Historian; use silk::hash::Hash; use silk::entry::Entry; use silk::log::verify_slice; -use silk::signature::{generate_keypair, get_pubkey}; +use silk::signature::{KeyPair, KeyPairUtil}; use silk::transaction::Transaction; use silk::event::Event; use std::thread::sleep; @@ -13,8 +13,8 @@ use std::sync::mpsc::SendError; fn create_log(hist: &Historian, seed: &Hash) -> Result<(), SendError> { sleep(Duration::from_millis(15)); - let keypair = generate_keypair(); - let tr = Transaction::new(&keypair, get_pubkey(&keypair), 42, *seed); + let keypair = KeyPair::new(); + let tr = Transaction::new(&keypair, keypair.pubkey(), 42, *seed); let event0 = Event::Transaction(tr); hist.sender.send(event0)?; sleep(Duration::from_millis(10)); diff --git a/src/bin/mint.rs b/src/bin/mint.rs new file mode 100644 index 0000000000..e1527dbc92 --- /dev/null +++ b/src/bin/mint.rs @@ -0,0 +1,15 @@ +extern crate serde_json; +extern crate silk; + +use silk::mint::Mint; +use std::io; + +fn main() { + let mut input_text = String::new(); + io::stdin().read_line(&mut input_text).unwrap(); + let trimmed = input_text.trim(); + let tokens = trimmed.parse::().unwrap(); + + let mint = Mint::new(tokens); + println!("{}", serde_json::to_string(&mint).unwrap()); +} diff --git a/src/lib.rs b/src/lib.rs index e452859c99..01cbc93aea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub mod transaction; pub mod event; pub mod entry; pub mod log; -pub mod genesis; +pub mod mint; pub mod logger; pub mod historian; pub mod accountant; diff --git a/src/log.rs b/src/log.rs index de3284b434..b01450cb50 100644 --- a/src/log.rs +++ b/src/log.rs @@ -48,7 +48,7 @@ pub fn next_ticks(start_hash: &Hash, num_hashes: u64, len: usize) -> Vec #[cfg(test)] mod tests { use super::*; - use signature::{generate_keypair, get_pubkey}; + use signature::{KeyPair, KeyPairUtil}; use transaction::Transaction; use hash::hash; @@ -71,9 +71,9 @@ mod tests { let zero = Hash::default(); // First, verify entries - let keypair = generate_keypair(); - let tr0 = Transaction::new(&keypair, get_pubkey(&keypair), 0, zero); - let tr1 = Transaction::new(&keypair, get_pubkey(&keypair), 1, zero); + let keypair = KeyPair::new(); + let tr0 = Transaction::new(&keypair, keypair.pubkey(), 0, zero); + let tr1 = Transaction::new(&keypair, keypair.pubkey(), 1, zero); let events = vec![Event::Transaction(tr0), Event::Transaction(tr1)]; let mut entries = create_entries(&zero, events); assert!(verify_slice(&entries, &zero)); diff --git a/src/genesis.rs b/src/mint.rs similarity index 57% rename from src/genesis.rs rename to src/mint.rs index 5c410b2ad7..f357737771 100644 --- a/src/genesis.rs +++ b/src/mint.rs @@ -2,51 +2,45 @@ use event::Event; use transaction::Transaction; -use signature::{get_pubkey, PublicKey}; +use signature::{KeyPair, KeyPairUtil, PublicKey}; use entry::Entry; use log::create_entries; use hash::{hash, Hash}; use ring::rand::SystemRandom; -use ring::signature::Ed25519KeyPair; use untrusted::Input; #[derive(Serialize, Deserialize, Debug)] -pub struct Genesis { +pub struct Mint { pub pkcs8: Vec, pub tokens: i64, } -impl Genesis { +impl Mint { pub fn new(tokens: i64) -> Self { let rnd = SystemRandom::new(); - let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap().to_vec(); - Genesis { pkcs8, tokens } + let pkcs8 = KeyPair::generate_pkcs8(&rnd).unwrap().to_vec(); + Mint { pkcs8, tokens } } - pub fn get_seed(&self) -> Hash { + pub fn seed(&self) -> Hash { hash(&self.pkcs8) } - pub fn get_keypair(&self) -> Ed25519KeyPair { - Ed25519KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap() + pub fn keypair(&self) -> KeyPair { + KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap() } - pub fn get_pubkey(&self) -> PublicKey { - get_pubkey(&self.get_keypair()) + pub fn pubkey(&self) -> PublicKey { + self.keypair().pubkey() } pub fn create_events(&self) -> Vec { - let tr = Transaction::new( - &self.get_keypair(), - self.get_pubkey(), - self.tokens, - self.get_seed(), - ); + let tr = Transaction::new(&self.keypair(), self.pubkey(), self.tokens, self.seed()); vec![Event::Tick, Event::Transaction(tr)] } pub fn create_entries(&self) -> Vec { - create_entries(&self.get_seed(), self.create_events()) + create_entries(&self.seed(), self.create_events()) } } @@ -57,7 +51,7 @@ mod tests { #[test] fn test_create_events() { - let mut events = Genesis::new(100).create_events().into_iter(); + let mut events = Mint::new(100).create_events().into_iter(); assert_eq!(events.next().unwrap(), Event::Tick); if let Event::Transaction(tr) = events.next().unwrap() { assert_eq!(tr.from, tr.to); @@ -69,7 +63,7 @@ mod tests { #[test] fn test_verify_entries() { - let entries = Genesis::new(100).create_entries(); + let entries = Mint::new(100).create_entries(); assert!(verify_slice(&entries, &entries[0].id)); } } diff --git a/src/signature.rs b/src/signature.rs index 06b705dde8..9fd7b74e2e 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -10,22 +10,34 @@ pub type KeyPair = Ed25519KeyPair; pub type PublicKey = GenericArray; pub type Signature = GenericArray; -/// Return a new ED25519 keypair -pub fn generate_keypair() -> KeyPair { - let rng = rand::SystemRandom::new(); - let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); - signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap() +pub trait KeyPairUtil { + fn new() -> Self; + fn pubkey(&self) -> PublicKey; } -/// Return the public key for the given keypair -pub fn get_pubkey(keypair: &KeyPair) -> PublicKey { - GenericArray::clone_from_slice(keypair.public_key_bytes()) +impl KeyPairUtil for Ed25519KeyPair { + /// Return a new ED25519 keypair + fn new() -> Self { + 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 + fn pubkey(&self) -> PublicKey { + GenericArray::clone_from_slice(self.public_key_bytes()) + } } -/// 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 { - 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 trait SignatureUtil { + fn verify(&self, peer_public_key_bytes: &[u8], msg_bytes: &[u8]) -> bool; +} + +impl SignatureUtil for GenericArray { + fn verify(&self, peer_public_key_bytes: &[u8], msg_bytes: &[u8]) -> bool { + let peer_public_key = untrusted::Input::from(peer_public_key_bytes); + let msg = untrusted::Input::from(msg_bytes); + let sig = untrusted::Input::from(self); + signature::verify(&signature::ED25519, peer_public_key, msg, sig).is_ok() + } } diff --git a/src/transaction.rs b/src/transaction.rs index ff2bc5f8df..dc3d400065 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,6 +1,6 @@ //! The `transaction` crate provides functionality for creating log transactions. -use signature::{get_pubkey, verify_signature, KeyPair, PublicKey, Signature}; +use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil}; use serde::Serialize; use bincode::serialize; use hash::Hash; @@ -17,7 +17,7 @@ pub struct Transaction { impl Transaction { pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { let mut tr = Transaction { - from: get_pubkey(&from_keypair), + from: from_keypair.pubkey(), to, asset, last_id, @@ -37,7 +37,7 @@ impl Transaction { } pub fn verify(&self) -> bool { - verify_signature(&self.from, &self.get_sign_data(), &self.sig) + self.sig.verify(&self.from, &self.get_sign_data()) } } @@ -45,24 +45,23 @@ impl Transaction { mod tests { use super::*; use bincode::{deserialize, serialize}; - use signature::*; use hash::hash; #[test] fn test_claim() { - let keypair = generate_keypair(); + let keypair = KeyPair::new(); let asset = hash(b"hello, world"); let zero = Hash::default(); - let tr0 = Transaction::new(&keypair, get_pubkey(&keypair), asset, zero); + let tr0 = Transaction::new(&keypair, keypair.pubkey(), asset, zero); assert!(tr0.verify()); } #[test] fn test_transfer() { let zero = Hash::default(); - let keypair0 = generate_keypair(); - let keypair1 = generate_keypair(); - let pubkey1 = get_pubkey(&keypair1); + let keypair0 = KeyPair::new(); + let keypair1 = KeyPair::new(); + let pubkey1 = keypair1.pubkey(); let asset = hash(b"hello, world"); let tr0 = Transaction::new(&keypair0, pubkey1, asset, zero); assert!(tr0.verify()); @@ -85,8 +84,8 @@ mod tests { #[test] fn test_bad_event_signature() { let zero = Hash::default(); - let keypair = generate_keypair(); - let pubkey = get_pubkey(&keypair); + let keypair = KeyPair::new(); + let pubkey = keypair.pubkey(); let mut tr = Transaction::new(&keypair, pubkey, hash(b"hello, world"), zero); tr.sign(&keypair); tr.asset = hash(b"goodbye cruel world"); // <-- attack! @@ -95,14 +94,14 @@ mod tests { #[test] fn test_hijack_attack() { - let keypair0 = generate_keypair(); - let keypair1 = generate_keypair(); - let thief_keypair = generate_keypair(); - let pubkey1 = get_pubkey(&keypair1); + let keypair0 = KeyPair::new(); + let keypair1 = KeyPair::new(); + let thief_keypair = KeyPair::new(); + let pubkey1 = keypair1.pubkey(); let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, pubkey1, hash(b"hello, world"), zero); tr.sign(&keypair0); - tr.to = get_pubkey(&thief_keypair); // <-- attack! + tr.to = thief_keypair.pubkey(); // <-- attack! assert!(!tr.verify()); } }