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"
[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"

View File

@ -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<usize, Error> {
/// 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<u8>,
fn check_signature(secp: &Secp256k1, sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
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<u8>,
// 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<MaybeOwned<'a>>,
pub fn trace<'a>(&'a self, secp: &Secp256k1, stack: &mut Vec<MaybeOwned<'a>>,
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<MaybeOwned<'a>>,
pub fn evaluate<'a>(&'a self, secp: &Secp256k1, stack: &mut Vec<MaybeOwned<'a>>,
input_context: Option<(&Transaction, usize)>,
mut trace: Option<&mut Vec<TraceIteration>>)
-> 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<D: SimpleDecoder> ConsensusDecodable<D> 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);
}

View File

@ -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() {

View File

@ -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,7 +201,8 @@ impl UtxoSet {
}
/// 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,
blockheight: usize, validation: ValidationLevel)
-> Result<(), Error> {
// Make sure we are extending the UTXO set in order
if validation >= ValidationLevel::Chain &&
@ -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<Transaction>) };
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();

View File

@ -205,7 +205,7 @@ impl<K, V> PatriciaTree<K, V>
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<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 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;
}
// 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)

View File

@ -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);
});
}
}

View File

@ -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<ExtendedPrivKey, Error> {
pub fn new_master(secp: &Secp256k1, network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
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])
pub fn from_path(secp: &Secp256k1, master: &ExtendedPrivKey, path: &[ChildNumber])
-> Result<ExtendedPrivKey, Error> {
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<ExtendedPrivKey, Error> {
pub fn ckd_priv(&self, secp: &Secp256k1, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
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<ExtendedPubKey, Error> {
pub fn ckd_pub(&self, secp: &Secp256k1, i: ChildNumber) -> Result<ExtendedPubKey, Error> {
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<u8>) -> Result<ExtendedPrivKey, base58::Error> {
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<u8>) -> Result<ExtendedPubKey, base58::Error> {
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;
})

View File

@ -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<String, Account>,
index: Option<AddressIndex>
@ -97,6 +99,7 @@ impl Deserialize for Wallet {
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
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<Wallet, bip32::Error> {
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;