Get compiling for secp256k1 changes

This commit is contained in:
Andrew Poelstra 2015-04-13 22:40:32 -05:00
parent 17e27ec09f
commit b21acd440e
9 changed files with 154 additions and 125 deletions

View File

@ -9,7 +9,7 @@ name = "bitcoin"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies.secp256k1] [dependencies.secp256k1]
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git" git = "https://github.com/apoelstra/rust-secp256k1.git"
[dependencies.eventual] [dependencies.eventual]
git = "https://github.com/carllerche/eventual" git = "https://github.com/carllerche/eventual"

View File

@ -957,9 +957,10 @@ impl AbstractStackElem {
/// Whether an element could possibly be a pubkey /// Whether an element could possibly be a pubkey
pub fn may_be_pubkey(&self) -> bool { 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() <= 33 && self.len_hi() >= 33) ||
(self.len_lo() <= 65 && self.len_hi() >= 65)) && (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 /// Whether an element could possibly be less than another
@ -1497,11 +1498,11 @@ pub fn read_uint(data: &[u8], size: usize) -> Result<usize, Error> {
/// Check a signature -- returns an error that is currently just translated /// Check a signature -- returns an error that is currently just translated
/// into a 0/1 to push onto the script stack /// into a 0/1 to push onto the script stack
fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>, fn check_signature(secp: &Secp256k1, sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
tx: &Transaction, input_index: usize) -> Result<(), Error> { tx: &Transaction, input_index: usize) -> Result<(), Error> {
// Check public key // Check public key
let pubkey = PublicKey::from_slice(pk_slice); let pubkey = PublicKey::from_slice(secp, pk_slice);
if pubkey.is_err() { if pubkey.is_err() {
return Err(Error::BadPublicKey); return Err(Error::BadPublicKey);
} }
@ -1586,7 +1587,7 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
// We can unwrap -- only failure mode is on length, which is fixed to 32 // We can unwrap -- only failure mode is on length, which is fixed to 32
let msg = secp256k1::Message::from_slice(&signature_hash[..]).unwrap(); 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. // Macro to translate English stack instructions into Rust code.
@ -1727,7 +1728,7 @@ impl Script {
pub fn len(&self) -> usize { self.0.len() } pub fn len(&self) -> usize { self.0.len() }
/// Trace a script /// Trace a script
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>, pub fn trace<'a>(&'a self, secp: &Secp256k1, stack: &mut Vec<MaybeOwned<'a>>,
input_context: Option<(&Transaction, usize)>) input_context: Option<(&Transaction, usize)>)
-> ScriptTrace { -> ScriptTrace {
let mut trace = ScriptTrace { let mut trace = ScriptTrace {
@ -1737,7 +1738,7 @@ impl Script {
error: None error: None
}; };
match self.evaluate(stack, input_context, Some(&mut trace.iterations)) { match self.evaluate(secp, stack, input_context, Some(&mut trace.iterations)) {
Ok(_) => {}, Ok(_) => {},
Err(e) => { trace.error = Some(e.clone()); } Err(e) => { trace.error = Some(e.clone()); }
} }
@ -1745,7 +1746,7 @@ impl Script {
} }
/// Evaluate the script, modifying the stack in place /// Evaluate the script, modifying the stack in place
pub fn evaluate<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>, pub fn evaluate<'a>(&'a self, secp: &Secp256k1, stack: &mut Vec<MaybeOwned<'a>>,
input_context: Option<(&Transaction, usize)>, input_context: Option<(&Transaction, usize)>,
mut trace: Option<&mut Vec<TraceIteration>>) mut trace: Option<&mut Vec<TraceIteration>>)
-> Result<(), Error> { -> Result<(), Error> {
@ -1986,7 +1987,7 @@ impl Script {
// Otherwise unwrap it // Otherwise unwrap it
let (tx, input_index) = input_context.unwrap(); 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)), Ok(()) => stack.push(MaybeOwned::Borrowed(SCRIPT_TRUE)),
_ => stack.push(MaybeOwned::Borrowed(SCRIPT_FALSE)), _ => stack.push(MaybeOwned::Borrowed(SCRIPT_FALSE)),
} }
@ -2047,7 +2048,7 @@ impl Script {
// Try to validate the signature with the given key // Try to validate the signature with the given key
(Some(k), Some(s)) => { (Some(k), Some(s)) => {
// Move to the next signature if it is valid for the current key // 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(); sig = sig_iter.next();
} }
// Move to the next key in any case // Move to the next key in any case
@ -2552,6 +2553,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Script {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use secp256k1::Secp256k1;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use super::{Error, Script, Builder, build_scriptint, read_scriptint, read_scriptbool}; use super::{Error, Script, Builder, build_scriptint, read_scriptint, read_scriptbool};
@ -2562,6 +2564,7 @@ mod test {
use blockdata::transaction::Transaction; use blockdata::transaction::Transaction;
fn test_tx(tx_hex: &'static str, output_hex: Vec<&'static str>) { 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_hex = tx_hex.from_hex().unwrap();
let tx: Transaction = deserialize(&tx_hex).ok().expect("transaction"); let tx: Transaction = deserialize(&tx_hex).ok().expect("transaction");
@ -2575,8 +2578,8 @@ mod test {
for (n, script) in script_pk.iter().enumerate() { for (n, script) in script_pk.iter().enumerate() {
let mut stack = vec![]; let mut stack = vec![];
assert_eq!(tx.input[n].script_sig.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(&mut stack, Some((&tx, n)), None), Ok(())); assert_eq!(script.evaluate(&s, &mut stack, Some((&tx, n)), None), Ok(()));
assert!(stack.len() >= 1); assert!(stack.len() >= 1);
assert_eq!(read_scriptbool(&stack.pop().unwrap()[..]), true); assert_eq!(read_scriptbool(&stack.pop().unwrap()[..]), true);
} }
@ -2636,30 +2639,34 @@ mod test {
#[test] #[test]
fn script_eval_simple() { fn script_eval_simple() {
let s = Secp256k1::new();
let mut script = Builder::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); 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] #[test]
fn script_eval_checksig_without_tx() { fn script_eval_checksig_without_tx() {
let s = Secp256k1::new();
let hex_pk = "1976a914e729dea4a3a81108e16376d1cc329c91db58999488ac".from_hex().unwrap(); let hex_pk = "1976a914e729dea4a3a81108e16376d1cc329c91db58999488ac".from_hex().unwrap();
let script_pk: Script = deserialize(&hex_pk).ok().expect("scriptpk"); let script_pk: Script = deserialize(&hex_pk).ok().expect("scriptpk");
// Should be able to check that the sig is there and pk correct // Should be able to check that the sig is there and pk correct
// before needing a transaction // before needing a transaction
assert_eq!(script_pk.evaluate(&mut vec![], None, None), Err(Error::PopEmptyStack)); assert_eq!(script_pk.evaluate(&s, &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![Owned(vec![]), Owned(vec![])], None, None),
Err(Error::EqualVerifyFailed("e729dea4a3a81108e16376d1cc329c91db589994".to_string(), Err(Error::EqualVerifyFailed("e729dea4a3a81108e16376d1cc329c91db589994".to_string(),
"b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".to_string()))); "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".to_string())));
// But if the signature is there, we need a tx to check it // 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(&s, &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![0]), Owned("026d5d4cfef5f3d97d2263941b4d8e7aaa82910bf8e6f7c6cf1d8f0d755b9d2d1a".from_hex().unwrap())], None, None), Err(Error::NoTransaction));
} }
#[test] #[test]
fn script_eval_pubkeyhash() { fn script_eval_pubkeyhash() {
let s = Secp256k1::new();
// nb these are both prefixed with their length in 1 byte // nb these are both prefixed with their length in 1 byte
let tx_hex = "010000000125d6681b797691aebba34b9d8e50f769ab1e8807e78405ae505c218cf8e1e9e1a20100006a47304402204c2dd8a9b6f8d425fcd8ee9a20ac73b619906a6367eac6cb93e70375225ec0160220356878eff111ff3663d7e6bf08947f94443845e0dcc54961664d922f7660b80c0121029fa8e8d8e3fd61183ab52f98d65500fd028a5d0a899c6bcd4ecaf1eda9eac284ffffffff0110270000000000001976a914299567077f41bc20059dc21a1eb1ef5a6a43b9c088ac00000000".from_hex().unwrap(); let tx_hex = "010000000125d6681b797691aebba34b9d8e50f769ab1e8807e78405ae505c218cf8e1e9e1a20100006a47304402204c2dd8a9b6f8d425fcd8ee9a20ac73b619906a6367eac6cb93e70375225ec0160220356878eff111ff3663d7e6bf08947f94443845e0dcc54961664d922f7660b80c0121029fa8e8d8e3fd61183ab52f98d65500fd028a5d0a899c6bcd4ecaf1eda9eac284ffffffff0110270000000000001976a914299567077f41bc20059dc21a1eb1ef5a6a43b9c088ac00000000".from_hex().unwrap();
@ -2669,8 +2676,8 @@ mod test {
let script_pk: Script = deserialize(&output_hex).ok().expect("scriptpk"); let script_pk: Script = deserialize(&output_hex).ok().expect("scriptpk");
let mut stack = vec![]; let mut stack = vec![];
assert_eq!(tx.input[0].script_sig.evaluate(&mut stack, None, None), Ok(())); assert_eq!(tx.input[0].script_sig.evaluate(&s, &mut stack, None, None), Ok(()));
assert_eq!(script_pk.evaluate(&mut stack, Some((&tx, 0)), None), Ok(())); assert_eq!(script_pk.evaluate(&s, &mut stack, Some((&tx, 0)), None), Ok(()));
assert_eq!(stack.len(), 1); assert_eq!(stack.len(), 1);
assert_eq!(read_scriptbool(&stack.pop().unwrap()[..]), true); assert_eq!(read_scriptbool(&stack.pop().unwrap()[..]), true);
} }

View File

@ -25,6 +25,7 @@
use std::default::Default; use std::default::Default;
use serde; use serde;
use secp256k1::Secp256k1;
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
use blockdata::script::{self, Script, ScriptTrace, read_scriptbool}; use blockdata::script::{self, Script, ScriptTrace, read_scriptbool};
@ -153,6 +154,7 @@ pub struct TransactionTrace {
impl TxIn { impl TxIn {
/// Check an input's script for validity /// Check an input's script for validity
pub fn validate(&self, pub fn validate(&self,
secp: &Secp256k1,
utxoset: &UtxoSet, utxoset: &UtxoSet,
txn: &Transaction, txn: &Transaction,
index: usize) -> Result<(), Error> { index: usize) -> Result<(), Error> {
@ -162,7 +164,7 @@ impl TxIn {
let (mut p2sh_stack, mut p2sh_script) = (vec![], Script::new()); let (mut p2sh_stack, mut p2sh_script) = (vec![], Script::new());
let mut stack = vec![]; 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(_) => {} Ok(_) => {}
Err(e) => { return Err(Error::InputScriptFailure(e)); } Err(e) => { return Err(Error::InputScriptFailure(e)); }
} }
@ -174,7 +176,7 @@ impl TxIn {
None => unreachable!() 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(_) => {} Ok(_) => {}
Err(e) => { return Err(Error::OutputScriptFailure(e)); } Err(e) => { return Err(Error::OutputScriptFailure(e)); }
} }
@ -187,7 +189,7 @@ impl TxIn {
None => { return Err(Error::ScriptReturnedEmptyStack); } None => { return Err(Error::ScriptReturnedEmptyStack); }
} }
if txo.script_pubkey.is_p2sh() { 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(_) => {} Ok(_) => {}
Err(e) => { return Err(Error::P2shScriptFailure(e)); } Err(e) => { return Err(Error::P2shScriptFailure(e)); }
} }
@ -209,15 +211,15 @@ impl TxIn {
impl Transaction { impl Transaction {
/// Check a transaction for validity /// 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() { for (n, input) in self.input.iter().enumerate() {
try!(input.validate(utxoset, self, n)); try!(input.validate(secp, utxoset, self, n));
} }
Ok(()) Ok(())
} }
/// Produce a trace of a transaction's execution /// 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(), let mut ret = TransactionTrace { txid: self.bitcoin_hash(),
inputs: Vec::with_capacity(self.input.len()) }; inputs: Vec::with_capacity(self.input.len()) };
for (n, input) in self.input.iter().enumerate() { 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 p2sh_stack, mut p2sh_script) = (vec![], Script::new());
let mut stack = Vec::with_capacity(6); 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()); let err = trace.sig_trace.error.as_ref().map(|e| e.clone());
err.map(|e| trace.error = Some(Error::InputScriptFailure(e))); err.map(|e| trace.error = Some(Error::InputScriptFailure(e)));
@ -255,7 +257,7 @@ impl Transaction {
}; };
} }
if trace.error.is_none() { 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()); let err = trace.pubkey_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone());
err.map(|e| trace.error = Some(Error::OutputScriptFailure(e))); err.map(|e| trace.error = Some(Error::OutputScriptFailure(e)));
match stack.pop() { match stack.pop() {
@ -267,7 +269,7 @@ impl Transaction {
None => { trace.error = Some(Error::ScriptReturnedEmptyStack); } None => { trace.error = Some(Error::ScriptReturnedEmptyStack); }
} }
if trace.error.is_none() && txo.script_pubkey.is_p2sh() { 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()); let err = trace.p2sh_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone());
err.map(|e| trace.error = Some(Error::P2shScriptFailure(e))); err.map(|e| trace.error = Some(Error::P2shScriptFailure(e)));
match p2sh_stack.pop() { match p2sh_stack.pop() {

View File

@ -23,6 +23,7 @@ use std::collections::HashMap;
use std::collections::hash_map::Iter; use std::collections::hash_map::Iter;
use std::default::Default; use std::default::Default;
use std::mem; use std::mem;
use secp256k1::Secp256k1;
use eventual; use eventual;
use eventual::Async; use eventual::Async;
use num_cpus; use num_cpus;
@ -137,7 +138,6 @@ impl UtxoSet {
let new_node = { let new_node = {
let mut new_node = Vec::with_capacity(tx.output.len()); let mut new_node = Vec::with_capacity(tx.output.len());
for txo in tx.output.iter() { 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() { if txo.script_pubkey.is_provably_unspendable() {
new_node.push(None); new_node.push(None);
self.n_utxos -= 1; self.n_utxos -= 1;
@ -201,8 +201,9 @@ impl UtxoSet {
} }
/// Apply the transactions contained in a block /// Apply the transactions contained in a block
pub fn update(&mut self, block: &Block, blockheight: usize, validation: ValidationLevel) pub fn update(&mut self, secp: &Secp256k1, block: &Block,
-> Result<(), Error> { blockheight: usize, validation: ValidationLevel)
-> Result<(), Error> {
// Make sure we are extending the UTXO set in order // Make sure we are extending the UTXO set in order
if validation >= ValidationLevel::Chain && if validation >= ValidationLevel::Chain &&
self.last_hash != block.header.prev_blockhash { self.last_hash != block.header.prev_blockhash {
@ -270,9 +271,10 @@ impl UtxoSet {
// function or else risk use-after-free in the async threads. // function or else risk use-after-free in the async threads.
let static_txes = unsafe { &*(&block.txdata as *const Vec<Transaction>) }; let static_txes = unsafe { &*(&block.txdata as *const Vec<Transaction>) };
let static_self = unsafe { &*(self as *const UtxoSet) }; let static_self = unsafe { &*(self as *const UtxoSet) };
let static_secp = unsafe { &*(secp as *const Secp256k1) };
future_vec.push(eventual::Future::spawn(move || { future_vec.push(eventual::Future::spawn(move || {
for tx in static_txes[start..end].iter() { for tx in static_txes[start..end].iter() {
match tx.validate(static_self) { match tx.validate(static_secp, static_self) {
Ok(_) => {}, Ok(_) => {},
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); } Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); }
} }
@ -345,7 +347,7 @@ impl UtxoSet {
if skipped_genesis { if skipped_genesis {
let mut extract_vec = vec![]; let mut extract_vec = vec![];
mem::swap(&mut extract_vec, (&mut self.spent_txos[self.spent_idx as usize])); 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 // Remove the tx's utxo list and patch the txo into place
let new_node = match self.table.remove(&txid) { let new_node = match self.table.remove(&txid) {
Some(mut node) => { Some(mut node) => {
@ -414,6 +416,7 @@ impl UtxoSet {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use secp256k1::Secp256k1;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use super::{UtxoSet, ValidationLevel}; use super::{UtxoSet, ValidationLevel};
@ -424,12 +427,13 @@ mod tests {
#[test] #[test]
fn utxoset_serialize_test() { fn utxoset_serialize_test() {
let s = Secp256k1::new();
let mut empty_set = UtxoSet::new(Bitcoin, 100); let mut empty_set = UtxoSet::new(Bitcoin, 100);
let new_block: Block = deserialize(&"010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap()).unwrap(); 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 // 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); assert_eq!(empty_set.n_utxos(), 0);
// Add the block manually so that we'll have some UTXOs for the rest of the test // Add the block manually so that we'll have some UTXOs for the rest of the test
for tx in new_block.txdata.iter() { 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 // Check again that we can't add the block, and that this doesn't mess up the
// existing UTXOs // 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); assert_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() { for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash(); let hash = tx.bitcoin_hash();

View File

@ -197,7 +197,7 @@ impl<K, V> PatriciaTree<K, V>
let tmp = node; // hack to appease borrowck let tmp = node; // hack to appease borrowck
idx += tmp.skip_len as usize + 1; idx += tmp.skip_len as usize + 1;
let subtree = if key.bit(idx - 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 // Recurse, adding a new node if necessary
if subtree.is_none() { if subtree.is_none() {
*subtree = Some(Box::new(PatriciaTree { *subtree = Some(Box::new(PatriciaTree {
@ -205,7 +205,7 @@ impl<K, V> PatriciaTree<K, V>
child_l: None, child_l: None,
child_r: None, child_r: None,
skip_prefix: key.bit_slice(idx, key_len), 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<U> here, so &mut ** gets a &mut U // subtree.get_mut_ref is a &mut Box<U> here, so &mut ** gets a &mut U

View File

@ -101,7 +101,7 @@ macro_rules! construct_uint {
let mut carry = [0u64; $n_words]; let mut carry = [0u64; $n_words];
let mut b_carry = false; let mut b_carry = false;
for i in 0..$n_words { 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] { if i < $n_words - 1 && ret[i] < me[i] {
carry[i + 1] = 1; carry[i + 1] = 1;
b_carry = true; b_carry = true;
@ -295,14 +295,12 @@ macro_rules! construct_uint {
let mut ret = [0u64; $n_words]; let mut ret = [0u64; $n_words];
let word_shift = shift / 64; let word_shift = shift / 64;
let bit_shift = shift % 64; let bit_shift = shift % 64;
for i in 0..$n_words { for i in word_shift..$n_words {
// Shift // 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 // Carry
if bit_shift > 0 && i - word_shift - 1 < $n_words { if bit_shift > 0 && i < $n_words - 1 {
ret[i - word_shift - 1] += original[i] << (64 - bit_shift); ret[i - word_shift] += original[i + 1] << (64 - bit_shift);
} }
} }
$name(ret) $name(ret)

View File

@ -157,6 +157,7 @@ impl ::std::fmt::Debug for Address {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::Rng;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
@ -180,9 +181,15 @@ mod tests {
#[bench] #[bench]
pub fn generate_address(bh: &mut Bencher) { 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( || { bh.iter( || {
let (sk, pk) = s.generate_keypair(true); let (sk, pk) = s.generate_keypair(&mut r, true).unwrap();
black_box(sk); black_box(sk);
black_box(pk); black_box(pk);
let addr = Address::from_key(Bitcoin, &pk); let addr = Address::from_key(Bitcoin, &pk);
@ -192,27 +199,20 @@ mod tests {
#[bench] #[bench]
pub fn generate_uncompressed_address(bh: &mut Bencher) { 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( || { bh.iter( || {
let (sk, pk) = s.generate_keypair(false); let (sk, pk) = s.generate_keypair(&mut r, false).unwrap();
black_box(sk); black_box(sk);
black_box(pk); black_box(pk);
let addr = Address::from_key(Bitcoin, &pk); let addr = Address::from_key(Bitcoin, &pk);
black_box(addr); 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);
});
}
} }

View File

@ -28,7 +28,7 @@ use crypto::ripemd160::Ripemd160;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use crypto::sha2::Sha512; use crypto::sha2::Sha512;
use secp256k1::key::{PublicKey, SecretKey}; use secp256k1::key::{PublicKey, SecretKey};
use secp256k1; use secp256k1::{self, Secp256k1};
use network::constants::Network; use network::constants::Network;
use util::base58; use util::base58;
@ -130,7 +130,7 @@ pub enum Error {
impl ExtendedPrivKey { impl ExtendedPrivKey {
/// Construct a new master key from a seed value /// Construct a new master key from a seed value
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> { pub fn new_master(secp: &Secp256k1, network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
let mut result = [0; 64]; let mut result = [0; 64];
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed"); let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
hmac.input(seed); hmac.input(seed);
@ -141,23 +141,23 @@ impl ExtendedPrivKey {
depth: 0, depth: 0,
parent_fingerprint: Default::default(), parent_fingerprint: Default::default(),
child_number: ChildNumber::Normal(0), 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..]) chain_code: ChainCode::from_slice(&result[32..])
}) })
} }
/// Creates a privkey from a path /// Creates a privkey from a path
pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber]) pub fn from_path(secp: &Secp256k1, master: &ExtendedPrivKey, path: &[ChildNumber])
-> Result<ExtendedPrivKey, Error> { -> Result<ExtendedPrivKey, Error> {
let mut sk = *master; let mut sk = *master;
for &num in path.iter() { for &num in path.iter() {
sk = try!(sk.ckd_priv(num)); sk = try!(sk.ckd_priv(secp, num));
} }
Ok(sk) Ok(sk)
} }
/// Private->Private child key derivation /// Private->Private child key derivation
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> { pub fn ckd_priv(&self, secp: &Secp256k1, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
let mut result = [0; 64]; let mut result = [0; 64];
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]); let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
let mut be_n = [0; 32]; let mut be_n = [0; 32];
@ -165,9 +165,7 @@ impl ExtendedPrivKey {
ChildNumber::Normal(n) => { ChildNumber::Normal(n) => {
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) } if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
// Non-hardened key: compute public data and use that // Non-hardened key: compute public data and use that
secp256k1::init(); hmac.input(&PublicKey::from_secret_key(secp, &self.secret_key, true)[..]);
// Note the unwrap: this is fine, we checked the SK when we created it
hmac.input(&PublicKey::from_secret_key(&self.secret_key, true)[..]);
BigEndian::write_u32(&mut be_n, n); BigEndian::write_u32(&mut be_n, n);
} }
ChildNumber::Hardened(n) => { ChildNumber::Hardened(n) => {
@ -180,13 +178,13 @@ impl ExtendedPrivKey {
} }
hmac.input(&be_n); hmac.input(&be_n);
hmac.raw_result(&mut result); hmac.raw_result(&mut result);
let mut sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)); let mut sk = try!(SecretKey::from_slice(secp, &result[..32]).map_err(Error::Ecdsa));
try!(sk.add_assign(&self.secret_key).map_err(Error::Ecdsa)); try!(sk.add_assign(secp, &self.secret_key).map_err(Error::Ecdsa));
Ok(ExtendedPrivKey { Ok(ExtendedPrivKey {
network: self.network, network: self.network,
depth: self.depth + 1, depth: self.depth + 1,
parent_fingerprint: self.fingerprint(), parent_fingerprint: self.fingerprint(secp),
child_number: i, child_number: i,
secret_key: sk, secret_key: sk,
chain_code: ChainCode::from_slice(&result[32..]) chain_code: ChainCode::from_slice(&result[32..])
@ -194,11 +192,11 @@ impl ExtendedPrivKey {
} }
/// Returns the HASH160 of the chaincode /// 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 sha2_res = [0; 32];
let mut ripemd_res = [0; 20]; let mut ripemd_res = [0; 20];
// Compute extended public key // Compute extended public key
let pk = ExtendedPubKey::from_private(self); let pk = ExtendedPubKey::from_private(secp, self);
// Do SHA256 of just the ECDSA pubkey // Do SHA256 of just the ECDSA pubkey
let mut sha2 = Sha256::new(); let mut sha2 = Sha256::new();
sha2.input(&pk.public_key[..]); sha2.input(&pk.public_key[..]);
@ -212,27 +210,26 @@ impl ExtendedPrivKey {
} }
/// Returns the first four bytes of the identifier /// Returns the first four bytes of the identifier
pub fn fingerprint(&self) -> Fingerprint { pub fn fingerprint(&self, secp: &Secp256k1) -> Fingerprint {
Fingerprint::from_slice(&self.identifier()[0..4]) Fingerprint::from_slice(&self.identifier(secp)[0..4])
} }
} }
impl ExtendedPubKey { impl ExtendedPubKey {
/// Derives a public key from a private key /// Derives a public key from a private key
pub fn from_private(sk: &ExtendedPrivKey) -> ExtendedPubKey { pub fn from_private(secp: &Secp256k1, sk: &ExtendedPrivKey) -> ExtendedPubKey {
secp256k1::init();
ExtendedPubKey { ExtendedPubKey {
network: sk.network, network: sk.network,
depth: sk.depth, depth: sk.depth,
parent_fingerprint: sk.parent_fingerprint, parent_fingerprint: sk.parent_fingerprint,
child_number: sk.child_number, 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 chain_code: sk.chain_code
} }
} }
/// Public->Public child key derivation /// Public->Public child key derivation
pub fn ckd_pub(&self, i: ChildNumber) -> Result<ExtendedPubKey, Error> { pub fn ckd_pub(&self, secp: &Secp256k1, i: ChildNumber) -> Result<ExtendedPubKey, Error> {
match i { match i {
ChildNumber::Hardened(n) => { ChildNumber::Hardened(n) => {
if n >= (1 << 31) { if n >= (1 << 31) {
@ -251,9 +248,9 @@ impl ExtendedPubKey {
let mut result = [0; 64]; let mut result = [0; 64];
hmac.raw_result(&mut result); 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(); 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 { Ok(ExtendedPubKey {
network: self.network, network: self.network,
@ -317,6 +314,8 @@ impl ToBase58 for ExtendedPrivKey {
impl FromBase58 for ExtendedPrivKey { impl FromBase58 for ExtendedPrivKey {
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> { fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> {
let s = Secp256k1::with_caps(secp256k1::ContextFlag::None);
if data.len() != 78 { if data.len() != 78 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
} }
@ -335,7 +334,7 @@ impl FromBase58 for ExtendedPrivKey {
parent_fingerprint: Fingerprint::from_slice(&data[5..9]), parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
child_number: child_number, child_number: child_number,
chain_code: ChainCode::from_slice(&data[13..45]), 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| &data[46..78]).map_err(|e|
base58::Error::Other(e.to_string()))) base58::Error::Other(e.to_string())))
}) })
@ -370,6 +369,8 @@ impl ToBase58 for ExtendedPubKey {
impl FromBase58 for ExtendedPubKey { impl FromBase58 for ExtendedPubKey {
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> { fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> {
let s = Secp256k1::with_caps(secp256k1::ContextFlag::None);
if data.len() != 78 { if data.len() != 78 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
} }
@ -388,7 +389,7 @@ impl FromBase58 for ExtendedPubKey {
parent_fingerprint: Fingerprint::from_slice(&data[5..9]), parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
child_number: child_number, child_number: child_number,
chain_code: ChainCode::from_slice(&data[13..45]), 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| &data[45..78]).map_err(|e|
base58::Error::Other(e.to_string()))) base58::Error::Other(e.to_string())))
}) })
@ -397,6 +398,7 @@ impl FromBase58 for ExtendedPubKey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use secp256k1::Secp256k1;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
@ -406,25 +408,26 @@ mod tests {
use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey}; use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
use super::ChildNumber::{Hardened, Normal}; use super::ChildNumber::{Hardened, Normal};
fn test_path(network: Network, fn test_path(secp: &Secp256k1,
network: Network,
seed: &[u8], seed: &[u8],
path: &[ChildNumber], path: &[ChildNumber],
expected_sk: &str, expected_sk: &str,
expected_pk: &str) { expected_pk: &str) {
let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap(); let mut sk = ExtendedPrivKey::new_master(secp, network, seed).unwrap();
let mut pk = ExtendedPubKey::from_private(&sk); let mut pk = ExtendedPubKey::from_private(secp, &sk);
// Derive keys, checking hardened and non-hardened derivation // Derive keys, checking hardened and non-hardened derivation
for &num in path.iter() { for &num in path.iter() {
sk = sk.ckd_priv(num).unwrap(); sk = sk.ckd_priv(secp, num).unwrap();
match num { match num {
Normal(_) => { Normal(_) => {
let pk2 = pk.ckd_pub(num).unwrap(); let pk2 = pk.ckd_pub(secp, num).unwrap();
pk = ExtendedPubKey::from_private(&sk); pk = ExtendedPubKey::from_private(secp, &sk);
assert_eq!(pk, pk2); assert_eq!(pk, pk2);
} }
Hardened(_) => { Hardened(_) => {
pk = ExtendedPubKey::from_private(&sk); pk = ExtendedPubKey::from_private(secp, &sk);
} }
} }
} }
@ -441,69 +444,71 @@ mod tests {
#[test] #[test]
fn test_vector_1() { fn test_vector_1() {
let secp = Secp256k1::new();
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
// m // m
test_path(Bitcoin, &seed, &[], test_path(&secp, Bitcoin, &seed, &[],
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"); "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
// m/0h // m/0h
test_path(Bitcoin, &seed, &[Hardened(0)], test_path(&secp, Bitcoin, &seed, &[Hardened(0)],
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"); "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
// m/0h/1 // m/0h/1
test_path(Bitcoin, &seed, &[Hardened(0), Normal(1)], test_path(&secp, Bitcoin, &seed, &[Hardened(0), Normal(1)],
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"); "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
// m/0h/1/2h // 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", "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"); "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
// m/0h/1/2h/2 // 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", "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"); "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
// m/0h/1/2h/2/1000000000 // 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", "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"); "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
} }
#[test] #[test]
fn test_vector_2() { fn test_vector_2() {
let secp = Secp256k1::new();
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap(); let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
// m // m
test_path(Bitcoin, &seed, &[], test_path(&secp, Bitcoin, &seed, &[],
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"); "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
// m/0 // m/0
test_path(Bitcoin, &seed, &[Normal(0)], test_path(&secp, Bitcoin, &seed, &[Normal(0)],
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"); "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
// m/0/2147483647h // m/0/2147483647h
test_path(Bitcoin, &seed, &[Normal(0), Hardened(2147483647)], test_path(&secp, Bitcoin, &seed, &[Normal(0), Hardened(2147483647)],
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"); "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
// m/0/2147483647h/1 // 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", "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"); "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
// m/0/2147483647h/1/2147483646h // 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", "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"); "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
// m/0/2147483647h/1/2147483646h/2 // 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", "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"); "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
} }
@ -520,35 +525,38 @@ mod tests {
#[bench] #[bench]
pub fn generate_sequential_normal_children(bh: &mut Bencher) { pub fn generate_sequential_normal_children(bh: &mut Bencher) {
let secp = Secp256k1::new();
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); 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; let mut i = 0;
bh.iter( || { bh.iter( || {
black_box(msk.ckd_priv(Normal(i)).unwrap()); black_box(msk.ckd_priv(&secp, Normal(i)).unwrap());
i += 1; i += 1;
}) })
} }
#[bench] #[bench]
pub fn generate_sequential_hardened_children(bh: &mut Bencher) { pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
let secp = Secp256k1::new();
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); 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; let mut i = 0;
bh.iter( || { bh.iter( || {
black_box(msk.ckd_priv(Hardened(i)).unwrap()); black_box(msk.ckd_priv(&secp, Hardened(i)).unwrap());
i += 1; i += 1;
}) })
} }
#[bench] #[bench]
pub fn generate_sequential_public_children(bh: &mut Bencher) { pub fn generate_sequential_public_children(bh: &mut Bencher) {
let secp = Secp256k1::new();
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); let msk = ExtendedPrivKey::new_master(&secp, Bitcoin, &seed).unwrap();
let mpk = ExtendedPubKey::from_private(&msk); let mpk = ExtendedPubKey::from_private(&secp, &msk);
let mut i = 0; let mut i = 0;
bh.iter( || { bh.iter( || {
black_box(mpk.ckd_pub(Normal(i)).unwrap()); black_box(mpk.ckd_pub(&secp, Normal(i)).unwrap());
i += 1; i += 1;
}) })
} }
@ -557,13 +565,14 @@ mod tests {
pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) { pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) {
use wallet::address::Address; use wallet::address::Address;
let secp = Secp256k1::new();
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); let msk = ExtendedPrivKey::new_master(&secp, Bitcoin, &seed).unwrap();
let mpk = ExtendedPubKey::from_private(&msk); let mpk = ExtendedPubKey::from_private(&secp, &msk);
let mut i = 0; let mut i = 0;
bh.iter( || { 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)); black_box(Address::from_key(Bitcoin, &epk.public_key));
i += 1; i += 1;
}) })

View File

@ -20,6 +20,7 @@ use std::collections::HashMap;
use std::default::Default; use std::default::Default;
use serde::{Serialize, Deserialize, Serializer, Deserializer}; use serde::{Serialize, Deserialize, Serializer, Deserializer};
use secp256k1::Secp256k1;
use secp256k1::key::PublicKey; use secp256k1::key::PublicKey;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
@ -80,6 +81,7 @@ impl Default for Account {
/// A wallet /// A wallet
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Wallet { pub struct Wallet {
secp: Secp256k1,
master: ExtendedPrivKey, master: ExtendedPrivKey,
accounts: HashMap<String, Account>, accounts: HashMap<String, Account>,
index: Option<AddressIndex> index: Option<AddressIndex>
@ -97,6 +99,7 @@ impl Deserialize for Wallet {
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error> fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
where D: Deserializer { where D: Deserializer {
Ok(Wallet { Ok(Wallet {
secp: Secp256k1::new(),
master: try!(Deserialize::deserialize(d)), master: try!(Deserialize::deserialize(d)),
accounts: try!(Deserialize::deserialize(d)), accounts: try!(Deserialize::deserialize(d)),
index: None index: None
@ -110,9 +113,11 @@ impl Wallet {
pub fn from_seed(network: Network, seed: &[u8]) -> Result<Wallet, bip32::Error> { pub fn from_seed(network: Network, seed: &[u8]) -> Result<Wallet, bip32::Error> {
let mut accounts = HashMap::new(); let mut accounts = HashMap::new();
accounts.insert(String::new(), Default::default()); accounts.insert(String::new(), Default::default());
let secp = Secp256k1::new();
Ok(Wallet { Ok(Wallet {
master: try!(ExtendedPrivKey::new_master(network, seed)), master: try!(ExtendedPrivKey::new_master(&secp, network, seed)),
secp: Secp256k1::new(),
accounts: accounts, accounts: accounts,
index: None index: None
}) })
@ -174,25 +179,27 @@ impl Wallet {
let (mut i, master) = match chain { let (mut i, master) = match chain {
AccountChain::Internal => (account.internal_next, AccountChain::Internal => (account.internal_next,
try!(ExtendedPrivKey::from_path( try!(ExtendedPrivKey::from_path(
&self.secp,
&self.master, &self.master,
&account.internal_path).map_err(Error::Bip32Error))), &account.internal_path).map_err(Error::Bip32Error))),
AccountChain::External => (account.external_next, AccountChain::External => (account.external_next,
try!(ExtendedPrivKey::from_path( try!(ExtendedPrivKey::from_path(
&self.secp,
&self.master, &self.master,
&account.external_path).map_err(Error::Bip32Error))), &account.external_path).map_err(Error::Bip32Error))),
}; };
// Scan for next admissible address // 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( let mut address = Address::from_key(
master.network, 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) { while !index.admissible_address(&address) {
i += 1; 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( address = Address::from_key(
master.network, master.network,
&PublicKey::from_secret_key(&sk.secret_key, true)); &PublicKey::from_secret_key(&self.secp, &sk.secret_key, true));
} }
match chain { match chain {
@ -245,11 +252,12 @@ impl Wallet {
// Sum internal balance // Sum internal balance
let master = try!(ExtendedPrivKey::from_path( let master = try!(ExtendedPrivKey::from_path(
&self.secp,
&self.master, &self.master,
&account.internal_path).map_err(Error::Bip32Error)); &account.internal_path).map_err(Error::Bip32Error));
for &cnum in account.internal_used.iter() { for &cnum in account.internal_used.iter() {
let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error)); let sk = try!(master.ckd_priv(&self.secp, cnum).map_err(Error::Bip32Error));
let pk = ExtendedPubKey::from_private(&sk); let pk = ExtendedPubKey::from_private(&self.secp, &sk);
let addr = Address::from_key(pk.network, &pk.public_key); let addr = Address::from_key(pk.network, &pk.public_key);
for out in index.find_by_script(&addr.script_pubkey()).iter() { for out in index.find_by_script(&addr.script_pubkey()).iter() {
ret += out.txo.value; ret += out.txo.value;
@ -257,11 +265,12 @@ impl Wallet {
} }
// Sum external balance // Sum external balance
let master = try!(ExtendedPrivKey::from_path( let master = try!(ExtendedPrivKey::from_path(
&self.secp,
&self.master, &self.master,
&account.external_path).map_err(Error::Bip32Error)); &account.external_path).map_err(Error::Bip32Error));
for &cnum in account.external_used.iter() { for &cnum in account.external_used.iter() {
let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error)); let sk = try!(master.ckd_priv(&self.secp, cnum).map_err(Error::Bip32Error));
let pk = ExtendedPubKey::from_private(&sk); let pk = ExtendedPubKey::from_private(&self.secp, &sk);
let addr = Address::from_key(pk.network, &pk.public_key); let addr = Address::from_key(pk.network, &pk.public_key);
for out in index.find_by_script(&addr.script_pubkey()).iter() { for out in index.find_by_script(&addr.script_pubkey()).iter() {
ret += out.txo.value; ret += out.txo.value;