Get compiling for secp256k1 changes
This commit is contained in:
parent
17e27ec09f
commit
b21acd440e
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
})
|
})
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue