diff --git a/Cargo.toml b/Cargo.toml index 1e914dc35d8ff6..9c5655e09cba03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,3 +56,4 @@ chrono = { version = "0.4.0", features = ["serde"] } log = "^0.4.1" matches = "^0.1.6" byteorder = "^1.2.1" +libc = "^0.2.1" diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000000..c7f06fd5e7c753 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +export RUST_LOG=packet=TRACE +#export RUST_BACKTRACE=1 + +all: htest + +htest:wfmt + #cargo test accountant_skel::tests::test_layout -- --nocapture 2>&1 | head -n 30 + #cargo test accountant_skel::tests::test_layout -- --nocapture + cargo test accountant_stub -- --nocapture 2>&1 | head -n 30 + +ci: test bench release clippy ipv6 + +build: + cargo build 2>&1 | head -n 30 + +loop: + while true; do fswatch -1 -r src; make; done + +test: + cargo test + +clippy: + cargo +nightly clippy --features="unstable" + +cov: + docker run -it --rm --security-opt seccomp=unconfined --volume "$$PWD:/volume" elmtai/docker-rust-kcov + +wfmt: + cargo fmt -- --write-mode=overwrite + +release: + cargo build --all-targets --release + +node: + cat genesis.log | cargo run --bin silk-testnode > transactions0.log + +bench: + cargo +nightly bench --features="unstable" -- --nocapture + +ipv6: + cargo test ipv6 --features="ipv6" -- --nocapture + +lib:libcuda_verify_ed25519.a +libcuda_verify_ed25519.a:dummy.c + cc -o dummy.o -c dummy.c + ar -cvq libcuda_verify_ed25519.a dummy.o diff --git a/build.rs b/build.rs new file mode 100644 index 00000000000000..10cc373cb53854 --- /dev/null +++ b/build.rs @@ -0,0 +1,8 @@ +fn main() { + println!("cargo:rustc-link-search=native=."); + println!("cargo:rustc-link-lib=static=cuda_verify_ed25519"); + println!("cargo:rustc-link-search=native=/usr/local/cuda/lib64"); + println!("cargo:rustc-link-lib=dylib=cudart"); + println!("cargo:rustc-link-lib=dylib=cuda"); + println!("cargo:rustc-link-lib=dylib=cudadevrt"); +} diff --git a/dummy.c b/dummy.c new file mode 100644 index 00000000000000..850df1eb729557 --- /dev/null +++ b/dummy.c @@ -0,0 +1,51 @@ +#include +#include +#include + +#define PACKET_SIZE 288 +#define PACKET_DATA_SIZE 256 +union Packet { + char data[PACKET_DATA_SIZE]; + char total[PACKET_SIZE]; +}; + +struct Elems { + union Packet *packet; + uint32_t len; +}; + +int ed25519_verify_many( + const struct Elems *vecs, + uint32_t num, + uint32_t message_size, + uint32_t public_key_offset, + uint32_t signature_offset, + uint32_t signed_message_offset, + uint32_t signed_message_len_offset, + uint8_t *out +) { + int i, p = 0; + assert(num > 0); + for(i = 0; i < num; ++i) { + int j; + assert(vecs[i].len > 0); + assert(message_size == PACKET_SIZE); + assert(signed_message_len_offset == PACKET_DATA_SIZE); + for(j = 0; j < vecs[i].len; ++j) { + uint32_t *len = (uint32_t*)&vecs[i].packet[j].total[signed_message_len_offset]; + assert(*len <= PACKET_DATA_SIZE); + p += 1; + if(public_key_offset > *len - 32) { + continue; + } + if(signature_offset > *len - 64) { + continue; + } + if(signed_message_offset > *len) { + continue; + } + out[p - 1] = 1; + } + } + return 0; +} diff --git a/src/accountant.rs b/src/accountant.rs index 9ed88571ce0b6b..7ab0f0772af53d 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -3,6 +3,8 @@ //! on behalf of the caller, and a private low-level API for when they have //! already been signed and verified. +extern crate libc; + use chrono::prelude::*; use event::Event; use hash::Hash; @@ -68,7 +70,7 @@ impl Accountant { /// Process a Transaction that has already been verified. pub fn process_verified_transaction(&mut self, tr: &Transaction) -> Result<()> { - if self.get_balance(&tr.from).unwrap_or(0) < tr.tokens { + if self.get_balance(&tr.data.from).unwrap_or(0) < tr.data.tokens { return Err(AccountingError::InsufficientFunds); } @@ -76,11 +78,11 @@ impl Accountant { return Err(AccountingError::InvalidTransferSignature); } - if let Some(x) = self.balances.get_mut(&tr.from) { - *x -= tr.tokens; + if let Some(x) = self.balances.get_mut(&tr.data.from) { + *x -= tr.data.tokens; } - let mut plan = tr.plan.clone(); + let mut plan = tr.data.plan.clone(); plan.apply_witness(&Witness::Timestamp(self.last_time)); if let Some(ref payment) = plan.final_payment() { diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs index dae08cd718c1f4..973a37e240cfcc 100644 --- a/src/accountant_skel.rs +++ b/src/accountant_skel.rs @@ -6,24 +6,26 @@ use accountant::Accountant; use bincode::{deserialize, serialize}; use entry::Entry; use event::Event; +use gpu; use hash::Hash; use historian::Historian; +use packet; +use packet::SharedPackets; use rayon::prelude::*; use recorder::Signal; use result::Result; use serde_json; use signature::PublicKey; +use std::collections::VecDeque; use std::io::Write; use std::net::{SocketAddr, UdpSocket}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::mpsc::{channel, SendError}; +use std::sync::mpsc::{channel, Receiver, SendError, Sender}; +use std::sync::{Arc, Mutex}; use std::thread::{spawn, JoinHandle}; use std::time::Duration; use streamer; -use packet; -use std::sync::{Arc, Mutex}; use transaction::Transaction; -use std::collections::VecDeque; pub struct AccountantSkel { acc: Accountant, @@ -44,14 +46,14 @@ impl Request { /// Verify the request is valid. pub fn verify(&self) -> bool { match *self { - Request::Transaction(ref tr) => tr.verify(), + Request::Transaction(ref tr) => tr.plan_verify(), _ => true, } } } /// Parallel verfication of a batch of requests. -fn filter_valid_requests(reqs: Vec<(Request, SocketAddr)>) -> Vec<(Request, SocketAddr)> { +pub fn filter_valid_requests(reqs: Vec<(Request, SocketAddr)>) -> Vec<(Request, SocketAddr)> { reqs.into_par_iter().filter({ |x| x.0.verify() }).collect() } @@ -83,14 +85,18 @@ impl AccountantSkel { } /// Process Request items sent by clients. - pub fn log_verified_request(&mut self, msg: Request) -> Option { + pub fn log_verified_request(&mut self, msg: Request, verify: u8) -> Option { match msg { + Request::Transaction(_) if verify == 0 => { + eprintln!("Transaction falid sigverify"); + None + } Request::Transaction(tr) => { if let Err(err) = self.acc.process_verified_transaction(&tr) { eprintln!("Transaction error: {:?}", err); } else if let Err(SendError(_)) = self.historian .sender - .send(Signal::Event(Event::Transaction(tr))) + .send(Signal::Event(Event::Transaction(tr.clone()))) { eprintln!("Channel send error"); } @@ -104,46 +110,99 @@ impl AccountantSkel { } } + fn verifier( + recvr: &streamer::PacketReceiver, + sendr: &Sender<(Vec, Vec>)>, + ) -> Result<()> { + let timer = Duration::new(1, 0); + let msgs = recvr.recv_timeout(timer)?; + //println!("got msgs"); + let mut v = Vec::new(); + v.push(msgs); + while let Ok(more) = recvr.try_recv() { + //println!("got more msgs"); + v.push(more); + } + //println!("verifying"); + let rvs = gpu::ecdsa_verify(&v); + //println!("verified!"); + let mut len = 0; + let mut sv = Vec::new(); + let mut sr = Vec::new(); + for (v, r) in v.iter().zip(rvs.iter()) { + if len + r.len() >= 256 { + println!("sending {}", len); + sendr.send((sv, sr))?; + sv = Vec::new(); + sr = Vec::new(); + len = 0; + } + sv.push(v.clone()); + sr.push(r.clone()); + len += r.len(); + assert!(len < 256); + } + if !sv.is_empty() { + sendr.send((sv, sr))?; + } + Ok(()) + } + + pub fn deserialize_packets(p: &packet::Packets) -> Vec> { + //deserealize in parallel + let mut r = vec![]; + for x in &p.packets { + let rsp_addr = x.meta.addr(); + let sz = x.meta.size; + if let Ok(req) = deserialize(&x.data[0..sz]) { + r.push(Some((req, rsp_addr))); + } else { + r.push(None); + } + } + r + } + fn process( obj: &Arc>>, - packet_receiver: &streamer::PacketReceiver, + verified_receiver: &Receiver<(Vec, Vec>)>, blob_sender: &streamer::BlobSender, packet_recycler: &packet::PacketRecycler, blob_recycler: &packet::BlobRecycler, ) -> Result<()> { let timer = Duration::new(1, 0); - let msgs = packet_receiver.recv_timeout(timer)?; - let msgs_ = msgs.clone(); - let mut rsps = VecDeque::new(); - { - let mut reqs = vec![]; - for packet in &msgs.read().unwrap().packets { - let rsp_addr = packet.meta.addr(); - let sz = packet.meta.size; - let req = deserialize(&packet.data[0..sz])?; - reqs.push((req, rsp_addr)); - } - let reqs = filter_valid_requests(reqs); - for (req, rsp_addr) in reqs { - if let Some(resp) = obj.lock().unwrap().log_verified_request(req) { - let blob = blob_recycler.allocate(); - { - let mut b = blob.write().unwrap(); - let v = serialize(&resp)?; - let len = v.len(); - b.data[..len].copy_from_slice(&v); - b.meta.size = len; - b.meta.set_addr(&rsp_addr); + let (mms, vvs) = verified_receiver.recv_timeout(timer)?; + for (msgs, vers) in mms.into_iter().zip(vvs.into_iter()) { + let msgs_ = msgs.clone(); + let mut rsps = VecDeque::new(); + { + let reqs = Self::deserialize_packets(&((*msgs).read().unwrap())); + for (data, v) in reqs.into_iter().zip(vers.into_iter()) { + if let Some((req, rsp_addr)) = data { + if !req.verify() { + continue; + } + if let Some(resp) = obj.lock().unwrap().log_verified_request(req, v) { + let blob = blob_recycler.allocate(); + { + let mut b = blob.write().unwrap(); + let v = serialize(&resp)?; + let len = v.len(); + b.data[..len].copy_from_slice(&v); + b.meta.size = len; + b.meta.set_addr(&rsp_addr); + } + rsps.push_back(blob); + } } - rsps.push_back(blob); } } + if !rsps.is_empty() { + //don't wake up the other side if there is nothing + blob_sender.send(rsps)?; + } + packet_recycler.recycle(msgs_); } - if !rsps.is_empty() { - //don't wake up the other side if there is nothing - blob_sender.send(rsps)?; - } - packet_recycler.recycle(msgs_); Ok(()) } @@ -168,11 +227,21 @@ impl AccountantSkel { let (blob_sender, blob_receiver) = channel(); let t_responder = streamer::responder(write, exit.clone(), blob_recycler.clone(), blob_receiver); + let (verified_sender, verified_receiver) = channel(); + + let exit_ = exit.clone(); + let t_verifier = spawn(move || loop { + let e = Self::verifier(&packet_receiver, &verified_sender); + if e.is_err() && exit_.load(Ordering::Relaxed) { + break; + } + }); + let skel = obj.clone(); let t_server = spawn(move || loop { let e = AccountantSkel::process( &skel, - &packet_receiver, + &verified_receiver, &blob_sender, &packet_recycler, &blob_recycler, @@ -181,6 +250,21 @@ impl AccountantSkel { break; } }); - Ok(vec![t_receiver, t_responder, t_server]) + Ok(vec![t_receiver, t_responder, t_server, t_verifier]) + } +} + +#[cfg(test)] +mod tests { + use accountant_skel::Request; + use bincode::serialize; + use gpu; + use transaction::{memfind, test_tx}; + #[test] + fn test_layout() { + let tr = test_tx(); + let tx = serialize(&tr).unwrap(); + let packet = serialize(&Request::Transaction(tr)).unwrap(); + assert_matches!(memfind(&packet, &tx), Some(gpu::TX_OFFSET)); } } diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index a91b4c2cd44f33..802953273f5f10 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -13,8 +13,8 @@ use std::thread::sleep; use std::time::{Duration, Instant}; fn main() { - let addr = "127.0.0.1:8000"; - let send_addr = "127.0.0.1:8001"; + let addr = "127.0.0.1:9000"; + let send_addr = "127.0.0.1:9001"; let mint: Mint = serde_json::from_reader(stdin()).unwrap(); let mint_keypair = mint.keypair(); diff --git a/src/bin/testnode.rs b/src/bin/testnode.rs index 40047a9ffd7f19..21b4fa5b4de19e 100644 --- a/src/bin/testnode.rs +++ b/src/bin/testnode.rs @@ -11,7 +11,7 @@ use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; fn main() { - let addr = "127.0.0.1:8000"; + let addr = "127.0.0.1:9000"; let stdin = io::stdin(); let mut entries = stdin .lock() @@ -27,7 +27,7 @@ fn main() { // transfer to oneself. let entry1: Entry = entries.next().unwrap(); let deposit = if let Event::Transaction(ref tr) = entry1.events[0] { - tr.plan.final_payment() + tr.data.plan.final_payment() } else { None }; diff --git a/src/event.rs b/src/event.rs index dbd2a934127487..6cb6ffdda7fe53 100644 --- a/src/event.rs +++ b/src/event.rs @@ -47,7 +47,7 @@ impl Event { /// spending plan is valid. pub fn verify(&self) -> bool { match *self { - Event::Transaction(ref tr) => tr.verify(), + Event::Transaction(ref tr) => tr.plan_verify(), Event::Signature { from, tx_sig, sig } => sig.verify(&from, &tx_sig), Event::Timestamp { from, dt, sig } => sig.verify(&from, &serialize(&dt).unwrap()), } diff --git a/src/gpu.rs b/src/gpu.rs new file mode 100644 index 00000000000000..4d0369c8b5ea78 --- /dev/null +++ b/src/gpu.rs @@ -0,0 +1,84 @@ +use packet::SharedPackets; +use packet::{Packet, PACKET_DATA_SIZE}; +use std::mem::size_of; + +pub const TX_OFFSET: usize = 4; +pub const SIGNED_DATA_OFFSET: usize = 72; +pub const SIG_OFFSET: usize = 8; +pub const PUB_KEY_OFFSET: usize = 80; + +#[repr(C)] +struct Elems { + elems: *const Packet, + num: u32, +} + +#[link(name = "cuda_verify_ed25519")] +extern "C" { + fn ed25519_verify_many( + vecs: *const Elems, + num: u32, //number of vecs + message_size: u32, //size of each element inside the elems field of the vec + public_key_offset: u32, + signature_offset: u32, + signed_message_offset: u32, + signed_message_len_offset: u32, + out: *mut u8, //combined length of all the items in vecs + ) -> u32; +} + +pub fn ecdsa_verify(batches: &Vec) -> Vec> { + let mut out = Vec::new(); + let mut elems = Vec::new(); + let mut locks = Vec::new(); + let mut rvs = Vec::new(); + for packets in batches { + locks.push(packets.read().unwrap()); + } + let mut num = 0; + for p in locks { + elems.push(Elems { + elems: p.packets.as_ptr(), + num: p.packets.len() as u32, + }); + let mut v = Vec::new(); + v.resize(p.packets.len(), 0); + rvs.push(v); + num += p.packets.len(); + } + out.resize(num, 0); + //println!("Starting verify num packets: {}", num); + //println!("elem len: {}", elems.len() as u32); + //println!("packet sizeof: {}", size_of::() as u32); + //println!("pub key: {}", (TX_OFFSET + PUB_KEY_OFFSET) as u32); + //println!("sig offset: {}", (TX_OFFSET + SIG_OFFSET) as u32); + //println!("sign data: {}", (TX_OFFSET + SIGNED_DATA_OFFSET) as u32); + //println!("len offset: {}", PACKET_DATA_SIZE as u32); + unsafe { + let res = ed25519_verify_many( + elems.as_ptr(), + elems.len() as u32, + size_of::() as u32, + (TX_OFFSET + PUB_KEY_OFFSET) as u32, + (TX_OFFSET + SIG_OFFSET) as u32, + (TX_OFFSET + SIGNED_DATA_OFFSET) as u32, + PACKET_DATA_SIZE as u32, + out.as_mut_ptr(), + ); + if res != 0 { + // println!("RETURN!!!: {}", res); + } + } + //println!("done verify"); + let mut num = 0; + for vs in rvs.iter_mut() { + for mut v in vs.iter_mut() { + *v = out[num]; + if *v != 0 { + // println!("VERIFIED PACKET!!!!!"); + } + num += 1; + } + } + rvs +} diff --git a/src/lib.rs b/src/lib.rs index a44a14be2b56ec..4e32974ca355aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,13 +4,14 @@ pub mod accountant_skel; pub mod accountant_stub; pub mod entry; pub mod event; +pub mod gpu; pub mod hash; +pub mod historian; pub mod ledger; pub mod mint; +pub mod packet; pub mod plan; pub mod recorder; -pub mod historian; -pub mod packet; pub mod result; pub mod signature; pub mod streamer; @@ -19,6 +20,7 @@ extern crate bincode; extern crate byteorder; extern crate chrono; extern crate generic_array; +extern crate libc; #[macro_use] extern crate log; extern crate rayon; diff --git a/src/mint.rs b/src/mint.rs index 5b869c3152fd31..1510b210dab133 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -68,8 +68,8 @@ mod tests { fn test_create_events() { let mut events = Mint::new(100).create_events().into_iter(); if let Event::Transaction(tr) = events.next().unwrap() { - if let Plan::Pay(payment) = tr.plan { - assert_eq!(tr.from, payment.to); + if let Plan::Pay(payment) = tr.data.plan { + assert_eq!(tr.data.from, payment.to); } } assert_eq!(events.next(), None); diff --git a/src/packet.rs b/src/packet.rs index e6c91592d0b2f4..e75f0a820b0123 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,10 +1,10 @@ -use std::sync::{Arc, Mutex, RwLock}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use result::{Error, Result}; +use std::collections::VecDeque; use std::fmt; use std::io; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket}; -use std::collections::VecDeque; -use result::{Error, Result}; +use std::sync::{Arc, Mutex, RwLock}; pub type SharedPackets = Arc>; pub type SharedBlob = Arc>; @@ -13,10 +13,11 @@ pub type BlobRecycler = Recycler; const NUM_PACKETS: usize = 1024 * 8; const BLOB_SIZE: usize = 64 * 1024; -pub const PACKET_SIZE: usize = 256; -pub const NUM_BLOBS: usize = (NUM_PACKETS * PACKET_SIZE) / BLOB_SIZE; +pub const PACKET_DATA_SIZE: usize = 256; +pub const NUM_BLOBS: usize = (NUM_PACKETS * PACKET_DATA_SIZE) / BLOB_SIZE; #[derive(Clone, Default)] +#[repr(C)] pub struct Meta { pub size: usize, pub addr: [u16; 8], @@ -25,8 +26,9 @@ pub struct Meta { } #[derive(Clone)] +#[repr(C)] pub struct Packet { - pub data: [u8; PACKET_SIZE], + pub data: [u8; PACKET_DATA_SIZE], pub meta: Meta, } @@ -44,7 +46,7 @@ impl fmt::Debug for Packet { impl Default for Packet { fn default() -> Packet { Packet { - data: [0u8; PACKET_SIZE], + data: [0u8; PACKET_DATA_SIZE], meta: Meta::default(), } } @@ -279,11 +281,11 @@ impl Blob { #[cfg(test)] mod test { - use std::net::UdpSocket; - use std::io::Write; - use std::io; - use std::collections::VecDeque; use packet::{Blob, BlobRecycler, Packet, PacketRecycler, Packets}; + use std::collections::VecDeque; + use std::io; + use std::io::Write; + use std::net::UdpSocket; #[test] pub fn packet_recycler_test() { let r = PacketRecycler::default(); diff --git a/src/plan.rs b/src/plan.rs index d1bc40745e5a95..72aa41f1c8e9e1 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -35,6 +35,7 @@ pub struct Payment { pub to: PublicKey, } +#[repr(C)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Plan { Pay(Payment), diff --git a/src/streamer.rs b/src/streamer.rs index 87581f32df793c..1725c5064da879 100644 --- a/src/streamer.rs +++ b/src/streamer.rs @@ -1,12 +1,12 @@ +use packet::{Blob, BlobRecycler, PacketRecycler, SharedBlob, SharedPackets, NUM_BLOBS}; +use result::Result; +use std::collections::VecDeque; +use std::net::UdpSocket; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc; -use std::time::Duration; -use std::net::UdpSocket; use std::thread::{spawn, JoinHandle}; -use std::collections::VecDeque; -use result::Result; -use packet::{Blob, BlobRecycler, PacketRecycler, SharedBlob, SharedPackets, NUM_BLOBS}; +use std::time::Duration; pub type PacketReceiver = mpsc::Receiver; pub type PacketSender = mpsc::Sender; @@ -141,16 +141,16 @@ pub fn window( mod bench { extern crate test; use self::test::Bencher; + use packet::{Packet, PacketRecycler, PACKET_DATA_SIZE}; use result::Result; use std::net::{SocketAddr, UdpSocket}; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::mpsc::channel; use std::sync::{Arc, Mutex}; use std::thread::sleep; use std::thread::{spawn, JoinHandle}; use std::time::Duration; use std::time::SystemTime; - use std::sync::mpsc::channel; - use std::sync::atomic::{AtomicBool, Ordering}; - use packet::{Packet, PacketRecycler, PACKET_SIZE}; use streamer::{receiver, PacketReceiver}; fn producer( @@ -163,7 +163,7 @@ mod bench { let msgs_ = msgs.clone(); msgs.write().unwrap().packets.resize(10, Packet::default()); for w in msgs.write().unwrap().packets.iter_mut() { - w.meta.size = PACKET_SIZE; + w.meta.size = PACKET_DATA_SIZE; w.meta.set_addr(&addr); } spawn(move || loop { @@ -241,15 +241,15 @@ mod bench { #[cfg(test)] mod test { + use packet::{Blob, BlobRecycler, Packet, PacketRecycler, Packets, PACKET_DATA_SIZE}; + use std::collections::VecDeque; + use std::io; + use std::io::Write; use std::net::UdpSocket; + use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::channel; - use std::io::Write; - use std::io; - use std::collections::VecDeque; use std::time::Duration; - use std::sync::Arc; - use packet::{Blob, BlobRecycler, Packet, PacketRecycler, Packets, PACKET_SIZE}; use streamer::{receiver, responder, window, BlobReceiver, PacketReceiver}; fn get_msgs(r: PacketReceiver, num: &mut usize) { @@ -288,7 +288,7 @@ mod test { let b_ = b.clone(); let mut w = b.write().unwrap(); w.data[0] = i as u8; - w.meta.size = PACKET_SIZE; + w.meta.size = PACKET_DATA_SIZE; w.meta.set_addr(&addr); msgs.push_back(b_); } @@ -338,7 +338,7 @@ mod test { let mut w = b.write().unwrap(); w.set_index(i).unwrap(); assert_eq!(i, w.get_index().unwrap()); - w.meta.size = PACKET_SIZE; + w.meta.size = PACKET_DATA_SIZE; w.meta.set_addr(&addr); msgs.push_back(b_); } diff --git a/src/transaction.rs b/src/transaction.rs index be43b88373bb00..e50fb15e728979 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -8,12 +8,17 @@ use rayon::prelude::*; use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil}; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub struct Transaction { +pub struct Signed { pub from: PublicKey, - pub plan: Plan, pub tokens: i64, pub last_id: Hash, + pub plan: Plan, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct Transaction { pub sig: Signature, + pub data: Signed, } impl Transaction { @@ -22,11 +27,13 @@ impl Transaction { let from = from_keypair.pubkey(); let plan = Plan::Pay(Payment { tokens, to }); let mut tr = Transaction { - from, - plan, - tokens, - last_id, sig: Signature::default(), + data: Signed { + from, + plan, + tokens, + last_id, + }, }; tr.sign(from_keypair); tr @@ -46,10 +53,12 @@ impl Transaction { (Condition::Signature(from), Payment { tokens, to: from }), ); let mut tr = Transaction { - from, - plan, - tokens, - last_id, + data: Signed { + from, + plan, + tokens, + last_id, + }, sig: Signature::default(), }; tr.sign(from_keypair); @@ -57,7 +66,7 @@ impl Transaction { } fn get_sign_data(&self) -> Vec { - serialize(&(&self.plan, &self.tokens, &self.last_id)).unwrap() + serialize(&(&self.data)).unwrap() } /// Sign this transaction. @@ -66,20 +75,44 @@ impl Transaction { self.sig = Signature::clone_from_slice(keypair.sign(&sign_data).as_ref()); } - /// Verify this transaction's signature and its spending plan. - pub fn verify(&self) -> bool { - self.sig.verify(&self.from, &self.get_sign_data()) && self.plan.verify(self.tokens) + pub fn sig_verify(&self) -> bool { + self.sig.verify(&self.data.from, &self.get_sign_data()) + } + pub fn plan_verify(&self) -> bool { + self.data.plan.verify(self.data.tokens) } } +#[cfg(test)] +pub fn test_tx() -> Transaction { + let keypair1 = KeyPair::new(); + let pubkey1 = keypair1.pubkey(); + let zero = Hash::default(); + let mut tr = Transaction::new(&keypair1, pubkey1, 42, zero); + tr.sign(&keypair1); + return tr; +} + +#[cfg(test)] +pub fn memfind(a: &[A], b: &[A]) -> Option { + assert!(a.len() >= b.len()); + let end = a.len() - b.len() + 1; + for i in 0..end { + if a[i..i + b.len()] == b[..] { + return Some(i); + } + } + None +} + /// Verify a batch of signatures. pub fn verify_signatures(transactions: &[Transaction]) -> bool { - transactions.par_iter().all(|tr| tr.verify()) + transactions.par_iter().all(|tr| tr.sig_verify()) } /// Verify a batch of spending plans. pub fn verify_plans(transactions: &[Transaction]) -> bool { - transactions.par_iter().all(|tr| tr.plan.verify(tr.tokens)) + transactions.par_iter().all(|tr| tr.plan_verify()) } /// Verify a batch of transactions. @@ -91,13 +124,14 @@ pub fn verify_transactions(transactions: &[Transaction]) -> bool { mod tests { use super::*; use bincode::{deserialize, serialize}; + use gpu; #[test] fn test_claim() { let keypair = KeyPair::new(); let zero = Hash::default(); let tr0 = Transaction::new(&keypair, keypair.pubkey(), 42, zero); - assert!(tr0.verify()); + assert!(tr0.plan_verify()); } #[test] @@ -107,7 +141,7 @@ mod tests { let keypair1 = KeyPair::new(); let pubkey1 = keypair1.pubkey(); let tr0 = Transaction::new(&keypair0, pubkey1, 42, zero); - assert!(tr0.verify()); + assert!(tr0.plan_verify()); } #[test] @@ -117,10 +151,12 @@ mod tests { to: Default::default(), }); let claim0 = Transaction { - from: Default::default(), - plan, - tokens: 0, - last_id: Default::default(), + data: Signed { + from: Default::default(), + plan, + tokens: 0, + last_id: Default::default(), + }, sig: Default::default(), }; let buf = serialize(&claim0).unwrap(); @@ -135,8 +171,8 @@ mod tests { let pubkey = keypair.pubkey(); let mut tr = Transaction::new(&keypair, pubkey, 42, zero); tr.sign(&keypair); - tr.tokens = 1_000_000; // <-- attack! - assert!(!tr.verify()); + tr.data.tokens = 1_000_000; // <-- attack! + assert!(!tr.plan_verify()); } #[test] @@ -148,10 +184,20 @@ mod tests { let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero); tr.sign(&keypair0); - if let Plan::Pay(ref mut payment) = tr.plan { + if let Plan::Pay(ref mut payment) = tr.data.plan { payment.to = thief_keypair.pubkey(); // <-- attack! }; - assert!(!tr.verify()); + assert!(tr.plan_verify()); + assert!(!tr.sig_verify()); + } + #[test] + fn test_layout() { + let tr = test_tx(); + let sign_data = tr.get_sign_data(); + let tx = serialize(&tr).unwrap(); + assert_matches!(memfind(&tx, &sign_data), Some(gpu::SIGNED_DATA_OFFSET)); + assert_matches!(memfind(&tx, &tr.sig), Some(gpu::SIG_OFFSET)); + assert_matches!(memfind(&tx, &tr.data.from), Some(gpu::PUB_KEY_OFFSET)); } #[test] @@ -160,16 +206,16 @@ mod tests { let keypair1 = KeyPair::new(); let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero); - if let Plan::Pay(ref mut payment) = tr.plan { + if let Plan::Pay(ref mut payment) = tr.data.plan { payment.tokens = 2; // <-- attack! } - assert!(!tr.verify()); + assert!(!tr.plan_verify()); // Also, ensure all branchs of the plan spend all tokens - if let Plan::Pay(ref mut payment) = tr.plan { + if let Plan::Pay(ref mut payment) = tr.data.plan { payment.tokens = 0; // <-- whoops! } - assert!(!tr.verify()); + assert!(!tr.plan_verify()); } #[test]