diff --git a/Cargo.toml b/Cargo.toml index d2fe92ca..6453b870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ name = "bitcoin" path = "src/lib.rs" [dependencies.secp256k1] -git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git" +git = "https://github.com/apoelstra/rust-secp256k1.git" [dependencies.eventual] git = "https://github.com/carllerche/eventual" diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index b2dfb203..fef0e35f 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -957,9 +957,10 @@ impl AbstractStackElem { /// Whether an element could possibly be a pubkey pub fn may_be_pubkey(&self) -> bool { + let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); ((self.len_lo() <= 33 && self.len_hi() >= 33) || (self.len_lo() <= 65 && self.len_hi() >= 65)) && - (self.raw_value().is_none() || PublicKey::from_slice(self.raw_value().unwrap()).is_ok()) + (self.raw_value().is_none() || PublicKey::from_slice(&s, self.raw_value().unwrap()).is_ok()) } /// Whether an element could possibly be less than another @@ -1497,11 +1498,11 @@ pub fn read_uint(data: &[u8], size: usize) -> Result { /// Check a signature -- returns an error that is currently just translated /// into a 0/1 to push onto the script stack -fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec, +fn check_signature(secp: &Secp256k1, sig_slice: &[u8], pk_slice: &[u8], script: Vec, tx: &Transaction, input_index: usize) -> Result<(), Error> { // Check public key - let pubkey = PublicKey::from_slice(pk_slice); + let pubkey = PublicKey::from_slice(secp, pk_slice); if pubkey.is_err() { return Err(Error::BadPublicKey); } @@ -1586,7 +1587,7 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec, // We can unwrap -- only failure mode is on length, which is fixed to 32 let msg = secp256k1::Message::from_slice(&signature_hash[..]).unwrap(); - Secp256k1::verify_raw(&msg, sig_slice, &pubkey).map_err(Error::Ecdsa) + Secp256k1::verify_raw(secp, &msg, sig_slice, &pubkey).map_err(Error::Ecdsa) } // Macro to translate English stack instructions into Rust code. @@ -1727,7 +1728,7 @@ impl Script { pub fn len(&self) -> usize { self.0.len() } /// Trace a script - pub fn trace<'a>(&'a self, stack: &mut Vec>, + pub fn trace<'a>(&'a self, secp: &Secp256k1, stack: &mut Vec>, input_context: Option<(&Transaction, usize)>) -> ScriptTrace { let mut trace = ScriptTrace { @@ -1737,7 +1738,7 @@ impl Script { error: None }; - match self.evaluate(stack, input_context, Some(&mut trace.iterations)) { + match self.evaluate(secp, stack, input_context, Some(&mut trace.iterations)) { Ok(_) => {}, Err(e) => { trace.error = Some(e.clone()); } } @@ -1745,7 +1746,7 @@ impl Script { } /// Evaluate the script, modifying the stack in place - pub fn evaluate<'a>(&'a self, stack: &mut Vec>, + pub fn evaluate<'a>(&'a self, secp: &Secp256k1, stack: &mut Vec>, input_context: Option<(&Transaction, usize)>, mut trace: Option<&mut Vec>) -> Result<(), Error> { @@ -1986,7 +1987,7 @@ impl Script { // Otherwise unwrap it let (tx, input_index) = input_context.unwrap(); - match check_signature( sig_slice, pk_slice, script, tx, input_index) { + match check_signature(secp, sig_slice, pk_slice, script, tx, input_index) { Ok(()) => stack.push(MaybeOwned::Borrowed(SCRIPT_TRUE)), _ => stack.push(MaybeOwned::Borrowed(SCRIPT_FALSE)), } @@ -2047,7 +2048,7 @@ impl Script { // Try to validate the signature with the given key (Some(k), Some(s)) => { // Move to the next signature if it is valid for the current key - if check_signature(&s[..], &k[..], script.clone(), tx, input_index).is_ok() { + if check_signature(secp, &s[..], &k[..], script.clone(), tx, input_index).is_ok() { sig = sig_iter.next(); } // Move to the next key in any case @@ -2552,6 +2553,7 @@ impl ConsensusDecodable for Script { #[cfg(test)] mod test { + use secp256k1::Secp256k1; use serialize::hex::FromHex; use super::{Error, Script, Builder, build_scriptint, read_scriptint, read_scriptbool}; @@ -2562,6 +2564,7 @@ mod test { use blockdata::transaction::Transaction; fn test_tx(tx_hex: &'static str, output_hex: Vec<&'static str>) { + let s = Secp256k1::new(); let tx_hex = tx_hex.from_hex().unwrap(); let tx: Transaction = deserialize(&tx_hex).ok().expect("transaction"); @@ -2575,8 +2578,8 @@ mod test { for (n, script) in script_pk.iter().enumerate() { let mut stack = vec![]; - assert_eq!(tx.input[n].script_sig.evaluate(&mut stack, Some((&tx, n)), None), Ok(())); - assert_eq!(script.evaluate(&mut stack, Some((&tx, n)), None), Ok(())); + assert_eq!(tx.input[n].script_sig.evaluate(&s, &mut stack, Some((&tx, n)), None), Ok(())); + assert_eq!(script.evaluate(&s, &mut stack, Some((&tx, n)), None), Ok(())); assert!(stack.len() >= 1); assert_eq!(read_scriptbool(&stack.pop().unwrap()[..]), true); } @@ -2636,30 +2639,34 @@ mod test { #[test] fn script_eval_simple() { + let s = Secp256k1::new(); let mut script = Builder::new(); - assert!(script.clone().into_script().evaluate(&mut vec![], None, None).is_ok()); + assert!(script.clone().into_script().evaluate(&s, &mut vec![], None, None).is_ok()); script.push_opcode(opcodes::All::OP_RETURN); - assert!(script.clone().into_script().evaluate(&mut vec![], None, None).is_err()); + assert!(script.clone().into_script().evaluate(&s, &mut vec![], None, None).is_err()); } #[test] fn script_eval_checksig_without_tx() { + let s = Secp256k1::new(); let hex_pk = "1976a914e729dea4a3a81108e16376d1cc329c91db58999488ac".from_hex().unwrap(); let script_pk: Script = deserialize(&hex_pk).ok().expect("scriptpk"); // Should be able to check that the sig is there and pk correct // before needing a transaction - assert_eq!(script_pk.evaluate(&mut vec![], None, None), Err(Error::PopEmptyStack)); - assert_eq!(script_pk.evaluate(&mut vec![Owned(vec![]), Owned(vec![])], None, None), + assert_eq!(script_pk.evaluate(&s, &mut vec![], None, None), Err(Error::PopEmptyStack)); + assert_eq!(script_pk.evaluate(&s, &mut vec![Owned(vec![]), Owned(vec![])], None, None), Err(Error::EqualVerifyFailed("e729dea4a3a81108e16376d1cc329c91db589994".to_string(), "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".to_string()))); // But if the signature is there, we need a tx to check it - assert_eq!(script_pk.evaluate(&mut vec![Owned(vec![]), Owned("026d5d4cfef5f3d97d2263941b4d8e7aaa82910bf8e6f7c6cf1d8f0d755b9d2d1a".from_hex().unwrap())], None, None), Err(Error::NoTransaction)); - assert_eq!(script_pk.evaluate(&mut vec![Owned(vec![0]), Owned("026d5d4cfef5f3d97d2263941b4d8e7aaa82910bf8e6f7c6cf1d8f0d755b9d2d1a".from_hex().unwrap())], None, None), Err(Error::NoTransaction)); + assert_eq!(script_pk.evaluate(&s, &mut vec![Owned(vec![]), Owned("026d5d4cfef5f3d97d2263941b4d8e7aaa82910bf8e6f7c6cf1d8f0d755b9d2d1a".from_hex().unwrap())], None, None), Err(Error::NoTransaction)); + assert_eq!(script_pk.evaluate(&s, &mut vec![Owned(vec![0]), Owned("026d5d4cfef5f3d97d2263941b4d8e7aaa82910bf8e6f7c6cf1d8f0d755b9d2d1a".from_hex().unwrap())], None, None), Err(Error::NoTransaction)); } #[test] fn script_eval_pubkeyhash() { + let s = Secp256k1::new(); + // nb these are both prefixed with their length in 1 byte let tx_hex = "010000000125d6681b797691aebba34b9d8e50f769ab1e8807e78405ae505c218cf8e1e9e1a20100006a47304402204c2dd8a9b6f8d425fcd8ee9a20ac73b619906a6367eac6cb93e70375225ec0160220356878eff111ff3663d7e6bf08947f94443845e0dcc54961664d922f7660b80c0121029fa8e8d8e3fd61183ab52f98d65500fd028a5d0a899c6bcd4ecaf1eda9eac284ffffffff0110270000000000001976a914299567077f41bc20059dc21a1eb1ef5a6a43b9c088ac00000000".from_hex().unwrap(); @@ -2669,8 +2676,8 @@ mod test { let script_pk: Script = deserialize(&output_hex).ok().expect("scriptpk"); let mut stack = vec![]; - assert_eq!(tx.input[0].script_sig.evaluate(&mut stack, None, None), Ok(())); - assert_eq!(script_pk.evaluate(&mut stack, Some((&tx, 0)), None), Ok(())); + assert_eq!(tx.input[0].script_sig.evaluate(&s, &mut stack, None, None), Ok(())); + assert_eq!(script_pk.evaluate(&s, &mut stack, Some((&tx, 0)), None), Ok(())); assert_eq!(stack.len(), 1); assert_eq!(read_scriptbool(&stack.pop().unwrap()[..]), true); } diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index a47de7fc..131e0fc3 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -25,6 +25,7 @@ use std::default::Default; use serde; +use secp256k1::Secp256k1; use util::hash::Sha256dHash; use blockdata::script::{self, Script, ScriptTrace, read_scriptbool}; @@ -153,6 +154,7 @@ pub struct TransactionTrace { impl TxIn { /// Check an input's script for validity pub fn validate(&self, + secp: &Secp256k1, utxoset: &UtxoSet, txn: &Transaction, index: usize) -> Result<(), Error> { @@ -162,7 +164,7 @@ impl TxIn { let (mut p2sh_stack, mut p2sh_script) = (vec![], Script::new()); let mut stack = vec![]; - match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) { + match self.script_sig.evaluate(secp, &mut stack, Some((txn, index)), None) { Ok(_) => {} Err(e) => { return Err(Error::InputScriptFailure(e)); } } @@ -174,7 +176,7 @@ impl TxIn { None => unreachable!() }; } - match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) { + match txo.script_pubkey.evaluate(secp, &mut stack, Some((txn, index)), None) { Ok(_) => {} Err(e) => { return Err(Error::OutputScriptFailure(e)); } } @@ -187,7 +189,7 @@ impl TxIn { None => { return Err(Error::ScriptReturnedEmptyStack); } } if txo.script_pubkey.is_p2sh() { - match p2sh_script.evaluate(&mut p2sh_stack, Some((txn, index)), None) { + match p2sh_script.evaluate(secp, &mut p2sh_stack, Some((txn, index)), None) { Ok(_) => {} Err(e) => { return Err(Error::P2shScriptFailure(e)); } } @@ -209,15 +211,15 @@ impl TxIn { impl Transaction { /// Check a transaction for validity - pub fn validate(&self, utxoset: &UtxoSet) -> Result<(), Error> { + pub fn validate(&self, secp: &Secp256k1, utxoset: &UtxoSet) -> Result<(), Error> { for (n, input) in self.input.iter().enumerate() { - try!(input.validate(utxoset, self, n)); + try!(input.validate(secp, utxoset, self, n)); } Ok(()) } /// Produce a trace of a transaction's execution - pub fn trace(&self, utxoset: &UtxoSet) -> TransactionTrace { + pub fn trace(&self, secp: &Secp256k1, utxoset: &UtxoSet) -> TransactionTrace { let mut ret = TransactionTrace { txid: self.bitcoin_hash(), inputs: Vec::with_capacity(self.input.len()) }; for (n, input) in self.input.iter().enumerate() { @@ -242,7 +244,7 @@ impl Transaction { let (mut p2sh_stack, mut p2sh_script) = (vec![], Script::new()); let mut stack = Vec::with_capacity(6); - trace.sig_trace = input.script_sig.trace(&mut stack, Some((self, n))); + trace.sig_trace = input.script_sig.trace(secp, &mut stack, Some((self, n))); let err = trace.sig_trace.error.as_ref().map(|e| e.clone()); err.map(|e| trace.error = Some(Error::InputScriptFailure(e))); @@ -255,7 +257,7 @@ impl Transaction { }; } if trace.error.is_none() { - trace.pubkey_trace = Some(txo.script_pubkey.trace(&mut stack, Some((self, n)))); + trace.pubkey_trace = Some(txo.script_pubkey.trace(secp, &mut stack, Some((self, n)))); let err = trace.pubkey_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone()); err.map(|e| trace.error = Some(Error::OutputScriptFailure(e))); match stack.pop() { @@ -267,7 +269,7 @@ impl Transaction { None => { trace.error = Some(Error::ScriptReturnedEmptyStack); } } if trace.error.is_none() && txo.script_pubkey.is_p2sh() { - trace.p2sh_trace = Some(p2sh_script.trace(&mut p2sh_stack, Some((self, n)))); + trace.p2sh_trace = Some(p2sh_script.trace(secp, &mut p2sh_stack, Some((self, n)))); let err = trace.p2sh_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone()); err.map(|e| trace.error = Some(Error::P2shScriptFailure(e))); match p2sh_stack.pop() { diff --git a/src/blockdata/utxoset.rs b/src/blockdata/utxoset.rs index 95867f89..3e46c57c 100644 --- a/src/blockdata/utxoset.rs +++ b/src/blockdata/utxoset.rs @@ -23,6 +23,7 @@ use std::collections::HashMap; use std::collections::hash_map::Iter; use std::default::Default; use std::mem; +use secp256k1::Secp256k1; use eventual; use eventual::Async; use num_cpus; @@ -137,7 +138,6 @@ impl UtxoSet { let new_node = { let mut new_node = Vec::with_capacity(tx.output.len()); for txo in tx.output.iter() { - // Unsafe since we are not uninitializing the old data in the vector if txo.script_pubkey.is_provably_unspendable() { new_node.push(None); self.n_utxos -= 1; @@ -201,8 +201,9 @@ impl UtxoSet { } /// Apply the transactions contained in a block - pub fn update(&mut self, block: &Block, blockheight: usize, validation: ValidationLevel) - -> Result<(), Error> { + pub fn update(&mut self, secp: &Secp256k1, block: &Block, + blockheight: usize, validation: ValidationLevel) + -> Result<(), Error> { // Make sure we are extending the UTXO set in order if validation >= ValidationLevel::Chain && self.last_hash != block.header.prev_blockhash { @@ -270,9 +271,10 @@ impl UtxoSet { // function or else risk use-after-free in the async threads. let static_txes = unsafe { &*(&block.txdata as *const Vec) }; let static_self = unsafe { &*(self as *const UtxoSet) }; + let static_secp = unsafe { &*(secp as *const Secp256k1) }; future_vec.push(eventual::Future::spawn(move || { for tx in static_txes[start..end].iter() { - match tx.validate(static_self) { + match tx.validate(static_secp, static_self) { Ok(_) => {}, Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); } } @@ -345,7 +347,7 @@ impl UtxoSet { if skipped_genesis { let mut extract_vec = vec![]; mem::swap(&mut extract_vec, (&mut self.spent_txos[self.spent_idx as usize])); - for ((txid, n), (height, txo)) in extract_vec.into_iter() { + for ((txid, n), (height, txo)) in extract_vec { // Remove the tx's utxo list and patch the txo into place let new_node = match self.table.remove(&txid) { Some(mut node) => { @@ -414,6 +416,7 @@ impl UtxoSet { #[cfg(test)] mod tests { + use secp256k1::Secp256k1; use serialize::hex::FromHex; use super::{UtxoSet, ValidationLevel}; @@ -424,12 +427,13 @@ mod tests { #[test] fn utxoset_serialize_test() { + let s = Secp256k1::new(); let mut empty_set = UtxoSet::new(Bitcoin, 100); let new_block: Block = deserialize(&"010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap()).unwrap(); // Make sure we can't add the block directly, since we are missing the inputs - assert!(empty_set.update(&new_block, 1, ValidationLevel::Inputs).is_err()); + assert!(empty_set.update(&s, &new_block, 1, ValidationLevel::Inputs).is_err()); assert_eq!(empty_set.n_utxos(), 0); // Add the block manually so that we'll have some UTXOs for the rest of the test for tx in new_block.txdata.iter() { @@ -449,7 +453,7 @@ mod tests { // Check again that we can't add the block, and that this doesn't mess up the // existing UTXOs - assert!(empty_set.update(&new_block, 2, ValidationLevel::Inputs).is_err()); + assert!(empty_set.update(&s, &new_block, 2, ValidationLevel::Inputs).is_err()); assert_eq!(empty_set.n_utxos(), 2); for tx in new_block.txdata.iter() { let hash = tx.bitcoin_hash(); diff --git a/src/util/patricia_tree.rs b/src/util/patricia_tree.rs index e84098b2..243b2e0b 100644 --- a/src/util/patricia_tree.rs +++ b/src/util/patricia_tree.rs @@ -197,7 +197,7 @@ impl PatriciaTree let tmp = node; // hack to appease borrowck idx += tmp.skip_len as usize + 1; let subtree = if key.bit(idx - 1) - { &mut tmp.child_r } else { &mut tmp.child_l }; + { &mut tmp.child_r } else { &mut tmp.child_l }; // Recurse, adding a new node if necessary if subtree.is_none() { *subtree = Some(Box::new(PatriciaTree { @@ -205,7 +205,7 @@ impl PatriciaTree child_l: None, child_r: None, skip_prefix: key.bit_slice(idx, key_len), - skip_len: key_len as u8 - idx as u8 + skip_len: (key_len - idx) as u8 })); } // subtree.get_mut_ref is a &mut Box here, so &mut ** gets a &mut U diff --git a/src/util/uint.rs b/src/util/uint.rs index cb2e52bc..2a34104d 100644 --- a/src/util/uint.rs +++ b/src/util/uint.rs @@ -101,7 +101,7 @@ macro_rules! construct_uint { let mut carry = [0u64; $n_words]; let mut b_carry = false; for i in 0..$n_words { - ret[i] = me[i] + you[i]; + ret[i] = me[i].wrapping_add(you[i]); if i < $n_words - 1 && ret[i] < me[i] { carry[i + 1] = 1; b_carry = true; @@ -295,14 +295,12 @@ macro_rules! construct_uint { let mut ret = [0u64; $n_words]; let word_shift = shift / 64; let bit_shift = shift % 64; - for i in 0..$n_words { + for i in word_shift..$n_words { // Shift - if bit_shift < 64 && i - word_shift < $n_words { - ret[i - word_shift] += original[i] >> bit_shift; - } + ret[i - word_shift] += original[i] >> bit_shift; // Carry - if bit_shift > 0 && i - word_shift - 1 < $n_words { - ret[i - word_shift - 1] += original[i] << (64 - bit_shift); + if bit_shift > 0 && i < $n_words - 1 { + ret[i - word_shift] += original[i + 1] << (64 - bit_shift); } } $name(ret) diff --git a/src/wallet/address.rs b/src/wallet/address.rs index ca95847e..91b03d49 100644 --- a/src/wallet/address.rs +++ b/src/wallet/address.rs @@ -157,6 +157,7 @@ impl ::std::fmt::Debug for Address { #[cfg(test)] mod tests { + use rand::Rng; use serialize::hex::FromHex; use test::{Bencher, black_box}; @@ -180,9 +181,15 @@ mod tests { #[bench] pub fn generate_address(bh: &mut Bencher) { - let mut s = Secp256k1::new().unwrap(); + struct CounterRng(u32); + impl Rng for CounterRng { + fn next_u32(&mut self) -> u32 { self.0 += 1; self.0 } + } + + let s = Secp256k1::new(); + let mut r = CounterRng(0); bh.iter( || { - let (sk, pk) = s.generate_keypair(true); + let (sk, pk) = s.generate_keypair(&mut r, true).unwrap(); black_box(sk); black_box(pk); let addr = Address::from_key(Bitcoin, &pk); @@ -192,27 +199,20 @@ mod tests { #[bench] pub fn generate_uncompressed_address(bh: &mut Bencher) { - let mut s = Secp256k1::new().unwrap(); + struct CounterRng(u32); + impl Rng for CounterRng { + fn next_u32(&mut self) -> u32 { self.0 += 1; self.0 } + } + + let s = Secp256k1::new(); + let mut r = CounterRng(0); bh.iter( || { - let (sk, pk) = s.generate_keypair(false); + let (sk, pk) = s.generate_keypair(&mut r, false).unwrap(); black_box(sk); black_box(pk); let addr = Address::from_key(Bitcoin, &pk); black_box(addr); }); } - - #[bench] - pub fn generate_sequential_address(bh: &mut Bencher) { - let mut s = Secp256k1::new().unwrap(); - let (sk, _) = s.generate_keypair(true); - let mut iter = sk.sequence(true); - bh.iter( || { - let (sk, pk) = iter.next().unwrap(); - black_box(sk); - let addr = Address::from_key(Bitcoin, &pk); - black_box(addr); - }); - } } diff --git a/src/wallet/bip32.rs b/src/wallet/bip32.rs index 1bfab8c8..3c9ae3e7 100644 --- a/src/wallet/bip32.rs +++ b/src/wallet/bip32.rs @@ -28,7 +28,7 @@ use crypto::ripemd160::Ripemd160; use crypto::sha2::Sha256; use crypto::sha2::Sha512; use secp256k1::key::{PublicKey, SecretKey}; -use secp256k1; +use secp256k1::{self, Secp256k1}; use network::constants::Network; use util::base58; @@ -130,7 +130,7 @@ pub enum Error { impl ExtendedPrivKey { /// Construct a new master key from a seed value - pub fn new_master(network: Network, seed: &[u8]) -> Result { + pub fn new_master(secp: &Secp256k1, network: Network, seed: &[u8]) -> Result { let mut result = [0; 64]; let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed"); hmac.input(seed); @@ -141,23 +141,23 @@ impl ExtendedPrivKey { depth: 0, parent_fingerprint: Default::default(), child_number: ChildNumber::Normal(0), - secret_key: try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)), + secret_key: try!(SecretKey::from_slice(secp, &result[..32]).map_err(Error::Ecdsa)), chain_code: ChainCode::from_slice(&result[32..]) }) } /// Creates a privkey from a path - pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber]) - -> Result { + pub fn from_path(secp: &Secp256k1, master: &ExtendedPrivKey, path: &[ChildNumber]) + -> Result { let mut sk = *master; for &num in path.iter() { - sk = try!(sk.ckd_priv(num)); + sk = try!(sk.ckd_priv(secp, num)); } Ok(sk) } /// Private->Private child key derivation - pub fn ckd_priv(&self, i: ChildNumber) -> Result { + pub fn ckd_priv(&self, secp: &Secp256k1, i: ChildNumber) -> Result { let mut result = [0; 64]; let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]); let mut be_n = [0; 32]; @@ -165,9 +165,7 @@ impl ExtendedPrivKey { ChildNumber::Normal(n) => { if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) } // Non-hardened key: compute public data and use that - secp256k1::init(); - // Note the unwrap: this is fine, we checked the SK when we created it - hmac.input(&PublicKey::from_secret_key(&self.secret_key, true)[..]); + hmac.input(&PublicKey::from_secret_key(secp, &self.secret_key, true)[..]); BigEndian::write_u32(&mut be_n, n); } ChildNumber::Hardened(n) => { @@ -180,13 +178,13 @@ impl ExtendedPrivKey { } hmac.input(&be_n); hmac.raw_result(&mut result); - let mut sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)); - try!(sk.add_assign(&self.secret_key).map_err(Error::Ecdsa)); + let mut sk = try!(SecretKey::from_slice(secp, &result[..32]).map_err(Error::Ecdsa)); + try!(sk.add_assign(secp, &self.secret_key).map_err(Error::Ecdsa)); Ok(ExtendedPrivKey { network: self.network, depth: self.depth + 1, - parent_fingerprint: self.fingerprint(), + parent_fingerprint: self.fingerprint(secp), child_number: i, secret_key: sk, chain_code: ChainCode::from_slice(&result[32..]) @@ -194,11 +192,11 @@ impl ExtendedPrivKey { } /// Returns the HASH160 of the chaincode - pub fn identifier(&self) -> [u8; 20] { + pub fn identifier(&self, secp: &Secp256k1) -> [u8; 20] { let mut sha2_res = [0; 32]; let mut ripemd_res = [0; 20]; // Compute extended public key - let pk = ExtendedPubKey::from_private(self); + let pk = ExtendedPubKey::from_private(secp, self); // Do SHA256 of just the ECDSA pubkey let mut sha2 = Sha256::new(); sha2.input(&pk.public_key[..]); @@ -212,27 +210,26 @@ impl ExtendedPrivKey { } /// Returns the first four bytes of the identifier - pub fn fingerprint(&self) -> Fingerprint { - Fingerprint::from_slice(&self.identifier()[0..4]) + pub fn fingerprint(&self, secp: &Secp256k1) -> Fingerprint { + Fingerprint::from_slice(&self.identifier(secp)[0..4]) } } impl ExtendedPubKey { /// Derives a public key from a private key - pub fn from_private(sk: &ExtendedPrivKey) -> ExtendedPubKey { - secp256k1::init(); + pub fn from_private(secp: &Secp256k1, sk: &ExtendedPrivKey) -> ExtendedPubKey { ExtendedPubKey { network: sk.network, depth: sk.depth, parent_fingerprint: sk.parent_fingerprint, child_number: sk.child_number, - public_key: PublicKey::from_secret_key(&sk.secret_key, true), + public_key: PublicKey::from_secret_key(secp, &sk.secret_key, true), chain_code: sk.chain_code } } /// Public->Public child key derivation - pub fn ckd_pub(&self, i: ChildNumber) -> Result { + pub fn ckd_pub(&self, secp: &Secp256k1, i: ChildNumber) -> Result { match i { ChildNumber::Hardened(n) => { if n >= (1 << 31) { @@ -251,9 +248,9 @@ impl ExtendedPubKey { let mut result = [0; 64]; hmac.raw_result(&mut result); - let sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)); + let sk = try!(SecretKey::from_slice(secp, &result[..32]).map_err(Error::Ecdsa)); let mut pk = self.public_key.clone(); - try!(pk.add_exp_assign(&sk).map_err(Error::Ecdsa)); + try!(pk.add_exp_assign(secp, &sk).map_err(Error::Ecdsa)); Ok(ExtendedPubKey { network: self.network, @@ -317,6 +314,8 @@ impl ToBase58 for ExtendedPrivKey { impl FromBase58 for ExtendedPrivKey { fn from_base58_layout(data: Vec) -> Result { + let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); + if data.len() != 78 { return Err(base58::Error::InvalidLength(data.len())); } @@ -335,7 +334,7 @@ impl FromBase58 for ExtendedPrivKey { parent_fingerprint: Fingerprint::from_slice(&data[5..9]), child_number: child_number, chain_code: ChainCode::from_slice(&data[13..45]), - secret_key: try!(SecretKey::from_slice( + secret_key: try!(SecretKey::from_slice(&s, &data[46..78]).map_err(|e| base58::Error::Other(e.to_string()))) }) @@ -370,6 +369,8 @@ impl ToBase58 for ExtendedPubKey { impl FromBase58 for ExtendedPubKey { fn from_base58_layout(data: Vec) -> Result { + let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); + if data.len() != 78 { return Err(base58::Error::InvalidLength(data.len())); } @@ -388,7 +389,7 @@ impl FromBase58 for ExtendedPubKey { parent_fingerprint: Fingerprint::from_slice(&data[5..9]), child_number: child_number, chain_code: ChainCode::from_slice(&data[13..45]), - public_key: try!(PublicKey::from_slice( + public_key: try!(PublicKey::from_slice(&s, &data[45..78]).map_err(|e| base58::Error::Other(e.to_string()))) }) @@ -397,6 +398,7 @@ impl FromBase58 for ExtendedPubKey { #[cfg(test)] mod tests { + use secp256k1::Secp256k1; use serialize::hex::FromHex; use test::{Bencher, black_box}; @@ -406,25 +408,26 @@ mod tests { use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey}; use super::ChildNumber::{Hardened, Normal}; - fn test_path(network: Network, + fn test_path(secp: &Secp256k1, + network: Network, seed: &[u8], path: &[ChildNumber], expected_sk: &str, expected_pk: &str) { - let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap(); - let mut pk = ExtendedPubKey::from_private(&sk); + let mut sk = ExtendedPrivKey::new_master(secp, network, seed).unwrap(); + let mut pk = ExtendedPubKey::from_private(secp, &sk); // Derive keys, checking hardened and non-hardened derivation for &num in path.iter() { - sk = sk.ckd_priv(num).unwrap(); + sk = sk.ckd_priv(secp, num).unwrap(); match num { Normal(_) => { - let pk2 = pk.ckd_pub(num).unwrap(); - pk = ExtendedPubKey::from_private(&sk); + let pk2 = pk.ckd_pub(secp, num).unwrap(); + pk = ExtendedPubKey::from_private(secp, &sk); assert_eq!(pk, pk2); } Hardened(_) => { - pk = ExtendedPubKey::from_private(&sk); + pk = ExtendedPubKey::from_private(secp, &sk); } } } @@ -441,69 +444,71 @@ mod tests { #[test] fn test_vector_1() { + let secp = Secp256k1::new(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); // m - test_path(Bitcoin, &seed, &[], + test_path(&secp, Bitcoin, &seed, &[], "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"); // m/0h - test_path(Bitcoin, &seed, &[Hardened(0)], + test_path(&secp, Bitcoin, &seed, &[Hardened(0)], "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"); // m/0h/1 - test_path(Bitcoin, &seed, &[Hardened(0), Normal(1)], + test_path(&secp, Bitcoin, &seed, &[Hardened(0), Normal(1)], "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"); // m/0h/1/2h - test_path(Bitcoin, &seed, &[Hardened(0), Normal(1), Hardened(2)], + test_path(&secp, Bitcoin, &seed, &[Hardened(0), Normal(1), Hardened(2)], "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"); // m/0h/1/2h/2 - test_path(Bitcoin, &seed, &[Hardened(0), Normal(1), Hardened(2), Normal(2)], + test_path(&secp, Bitcoin, &seed, &[Hardened(0), Normal(1), Hardened(2), Normal(2)], "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"); // m/0h/1/2h/2/1000000000 - test_path(Bitcoin, &seed, &[Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)], + test_path(&secp, Bitcoin, &seed, &[Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)], "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"); } #[test] fn test_vector_2() { + let secp = Secp256k1::new(); let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap(); // m - test_path(Bitcoin, &seed, &[], + test_path(&secp, Bitcoin, &seed, &[], "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"); // m/0 - test_path(Bitcoin, &seed, &[Normal(0)], + test_path(&secp, Bitcoin, &seed, &[Normal(0)], "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"); // m/0/2147483647h - test_path(Bitcoin, &seed, &[Normal(0), Hardened(2147483647)], + test_path(&secp, Bitcoin, &seed, &[Normal(0), Hardened(2147483647)], "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"); // m/0/2147483647h/1 - test_path(Bitcoin, &seed, &[Normal(0), Hardened(2147483647), Normal(1)], + test_path(&secp, Bitcoin, &seed, &[Normal(0), Hardened(2147483647), Normal(1)], "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"); // m/0/2147483647h/1/2147483646h - test_path(Bitcoin, &seed, &[Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)], + test_path(&secp, Bitcoin, &seed, &[Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)], "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"); // m/0/2147483647h/1/2147483646h/2 - test_path(Bitcoin, &seed, &[Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)], + test_path(&secp, Bitcoin, &seed, &[Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)], "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"); } @@ -520,35 +525,38 @@ mod tests { #[bench] pub fn generate_sequential_normal_children(bh: &mut Bencher) { + let secp = Secp256k1::new(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); - let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); + let msk = ExtendedPrivKey::new_master(&secp, Bitcoin, &seed).unwrap(); let mut i = 0; bh.iter( || { - black_box(msk.ckd_priv(Normal(i)).unwrap()); + black_box(msk.ckd_priv(&secp, Normal(i)).unwrap()); i += 1; }) } #[bench] pub fn generate_sequential_hardened_children(bh: &mut Bencher) { + let secp = Secp256k1::new(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); - let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); + let msk = ExtendedPrivKey::new_master(&secp, Bitcoin, &seed).unwrap(); let mut i = 0; bh.iter( || { - black_box(msk.ckd_priv(Hardened(i)).unwrap()); + black_box(msk.ckd_priv(&secp, Hardened(i)).unwrap()); i += 1; }) } #[bench] pub fn generate_sequential_public_children(bh: &mut Bencher) { + let secp = Secp256k1::new(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); - let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); - let mpk = ExtendedPubKey::from_private(&msk); + let msk = ExtendedPrivKey::new_master(&secp, Bitcoin, &seed).unwrap(); + let mpk = ExtendedPubKey::from_private(&secp, &msk); let mut i = 0; bh.iter( || { - black_box(mpk.ckd_pub(Normal(i)).unwrap()); + black_box(mpk.ckd_pub(&secp, Normal(i)).unwrap()); i += 1; }) } @@ -557,13 +565,14 @@ mod tests { pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) { use wallet::address::Address; + let secp = Secp256k1::new(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); - let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); - let mpk = ExtendedPubKey::from_private(&msk); + let msk = ExtendedPrivKey::new_master(&secp, Bitcoin, &seed).unwrap(); + let mpk = ExtendedPubKey::from_private(&secp, &msk); let mut i = 0; bh.iter( || { - let epk = mpk.ckd_pub(Normal(i)).unwrap(); + let epk = mpk.ckd_pub(&secp, Normal(i)).unwrap(); black_box(Address::from_key(Bitcoin, &epk.public_key)); i += 1; }) diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index 36fb08ca..90ac2752 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -20,6 +20,7 @@ use std::collections::HashMap; use std::default::Default; use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use secp256k1::Secp256k1; use secp256k1::key::PublicKey; use byteorder::{ByteOrder, LittleEndian}; @@ -80,6 +81,7 @@ impl Default for Account { /// A wallet #[derive(Clone, PartialEq, Eq, Debug)] pub struct Wallet { + secp: Secp256k1, master: ExtendedPrivKey, accounts: HashMap, index: Option @@ -97,6 +99,7 @@ impl Deserialize for Wallet { fn deserialize(d: &mut D) -> Result where D: Deserializer { Ok(Wallet { + secp: Secp256k1::new(), master: try!(Deserialize::deserialize(d)), accounts: try!(Deserialize::deserialize(d)), index: None @@ -110,9 +113,11 @@ impl Wallet { pub fn from_seed(network: Network, seed: &[u8]) -> Result { let mut accounts = HashMap::new(); accounts.insert(String::new(), Default::default()); + let secp = Secp256k1::new(); Ok(Wallet { - master: try!(ExtendedPrivKey::new_master(network, seed)), + master: try!(ExtendedPrivKey::new_master(&secp, network, seed)), + secp: Secp256k1::new(), accounts: accounts, index: None }) @@ -174,25 +179,27 @@ impl Wallet { let (mut i, master) = match chain { AccountChain::Internal => (account.internal_next, try!(ExtendedPrivKey::from_path( + &self.secp, &self.master, &account.internal_path).map_err(Error::Bip32Error))), AccountChain::External => (account.external_next, try!(ExtendedPrivKey::from_path( + &self.secp, &self.master, &account.external_path).map_err(Error::Bip32Error))), }; // Scan for next admissible address - let mut sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error)); + let mut sk = try!(master.ckd_priv(&self.secp, Normal(i)).map_err(Error::Bip32Error)); let mut address = Address::from_key( master.network, - &PublicKey::from_secret_key(&sk.secret_key, true)); + &PublicKey::from_secret_key(&self.secp, &sk.secret_key, true)); while !index.admissible_address(&address) { i += 1; - sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error)); + sk = try!(master.ckd_priv(&self.secp, Normal(i)).map_err(Error::Bip32Error)); address = Address::from_key( master.network, - &PublicKey::from_secret_key(&sk.secret_key, true)); + &PublicKey::from_secret_key(&self.secp, &sk.secret_key, true)); } match chain { @@ -245,11 +252,12 @@ impl Wallet { // Sum internal balance let master = try!(ExtendedPrivKey::from_path( + &self.secp, &self.master, &account.internal_path).map_err(Error::Bip32Error)); for &cnum in account.internal_used.iter() { - let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error)); - let pk = ExtendedPubKey::from_private(&sk); + let sk = try!(master.ckd_priv(&self.secp, cnum).map_err(Error::Bip32Error)); + let pk = ExtendedPubKey::from_private(&self.secp, &sk); let addr = Address::from_key(pk.network, &pk.public_key); for out in index.find_by_script(&addr.script_pubkey()).iter() { ret += out.txo.value; @@ -257,11 +265,12 @@ impl Wallet { } // Sum external balance let master = try!(ExtendedPrivKey::from_path( + &self.secp, &self.master, &account.external_path).map_err(Error::Bip32Error)); for &cnum in account.external_used.iter() { - let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error)); - let pk = ExtendedPubKey::from_private(&sk); + let sk = try!(master.ckd_priv(&self.secp, cnum).map_err(Error::Bip32Error)); + let pk = ExtendedPubKey::from_private(&self.secp, &sk); let addr = Address::from_key(pk.network, &pk.public_key); for out in index.find_by_script(&addr.script_pubkey()).iter() { ret += out.txo.value;