diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index a90f320..fddab33 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -1365,7 +1365,7 @@ pub fn read_uint<'a, I:Iterator<&'a u8>>(mut iter: I, size: uint) /// Check a signature -- returns an error that is currently just translated /// into a 0/1 to push onto the script stack -fn check_signature(secp: &Secp256k1, sig_slice: &[u8], pk_slice: &[u8], script: Vec, +fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec, tx: &Transaction, input_index: uint) -> Result<(), ScriptError> { // Check public key @@ -1451,7 +1451,7 @@ fn check_signature(secp: &Secp256k1, sig_slice: &[u8], pk_slice: &[u8], script: serialize(&Sha256dHash::from_data(data_to_sign.as_slice())).unwrap() }; - secp.verify(signature_hash.as_slice(), sig_slice, &pubkey).map_err(|e| EcdsaError(e)) + Secp256k1::verify(signature_hash.as_slice(), sig_slice, &pubkey).map_err(|e| EcdsaError(e)) } // Macro to translate English stack instructions into Rust code. @@ -1678,7 +1678,6 @@ impl Script { mut trace: Option<&mut Vec>) -> Result<(), ScriptError> { let &Script(ref raw) = self; - let secp = Secp256k1::new(); let mut codeseparator_index = 0u; let mut exec_stack = vec![]; @@ -1916,7 +1915,7 @@ impl Script { // Otherwise unwrap it let (tx, input_index) = input_context.unwrap(); - match check_signature(&secp, sig_slice, pk_slice, script, tx, input_index) { + match check_signature( sig_slice, pk_slice, script, tx, input_index) { Ok(()) => stack.push(Slice(SCRIPT_TRUE)), _ => stack.push(Slice(SCRIPT_FALSE)), } @@ -1977,7 +1976,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(&secp, s.as_slice(), k.as_slice(), + if check_signature(s.as_slice(), k.as_slice(), script.clone(), tx, input_index).is_ok() { sig = sig_iter.next(); } diff --git a/src/lib.rs b/src/lib.rs index b4e82bd..f7d2fd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ extern crate rand; extern crate rustrt; extern crate serialize; extern crate sync; +extern crate test; extern crate time; extern crate secp256k1 = "bitcoin-secp256k1-rs"; diff --git a/src/util/hash.rs b/src/util/hash.rs index 3af6ace..ca1c385 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -25,7 +25,8 @@ use std::hash; use serialize::json::{mod, ToJson}; use crypto::digest::Digest; -use crypto::sha2; +use crypto::sha2::Sha256; +use crypto::ripemd160::Ripemd160; use network::encodable::{ConsensusDecodable, ConsensusEncodable}; use network::serialize::{RawEncoder, BitcoinHash, SimpleDecoder}; @@ -130,6 +131,19 @@ impl Default for DumbHasher { fn default() -> DumbHasher { DumbHasher } } +impl Ripemd160Hash { + /// Create a hash by hashing some data + pub fn from_data(data: &[u8]) -> Ripemd160Hash { + let mut ret = [0, ..20]; + let mut rmd = Ripemd160::new(); + rmd.input(data); + rmd.result(ret.as_mut_slice()); + Ripemd160Hash(ret) + } +} + +// This doesn't make much sense to me, but is implicit behaviour +// in the C++ reference client impl Default for Sha256dHash { #[inline] fn default() -> Sha256dHash { Sha256dHash([0u8, ..32]) } @@ -139,7 +153,7 @@ impl Sha256dHash { /// Create a hash by hashing some data pub fn from_data(data: &[u8]) -> Sha256dHash { let Sha256dHash(mut ret): Sha256dHash = Default::default(); - let mut sha2 = sha2::Sha256::new(); + let mut sha2 = Sha256::new(); sha2.input(data); sha2.result(ret.as_mut_slice()); sha2.reset(); diff --git a/src/wallet/address.rs b/src/wallet/address.rs index 19cf429..25ec5ae 100644 --- a/src/wallet/address.rs +++ b/src/wallet/address.rs @@ -16,6 +16,12 @@ //! Support for ordinary base58 Bitcoin addresses //! +use secp256k1::key::PublicKey; +use crypto::digest::Digest; +use crypto::sha2::Sha256; + +use blockdata::script::Script; +use blockdata::opcodes::all; use network::constants::{Network, Bitcoin, BitcoinTestnet}; use util::hash::Ripemd160Hash; use util::base58::{Base58Error, @@ -29,6 +35,31 @@ pub struct Address { hash: Ripemd160Hash } +impl Address { + /// Creates an address from a public key + pub fn from_key(network: Network, pk: &PublicKey) -> Address { + let mut sha = Sha256::new(); + let mut out = [0, ..32]; + sha.input(pk.as_slice()); + sha.result(out.as_mut_slice()); + Address { + network: network, + hash: Ripemd160Hash::from_data(out) + } + } + + /// Generates a script pubkey spending to this address + pub fn script_pubkey(&self) -> Script { + let mut script = Script::new(); + script.push_opcode(all::OP_DUP); + script.push_opcode(all::OP_HASH160); + script.push_slice(self.hash.as_slice()); + script.push_opcode(all::OP_EQUALVERIFY); + script.push_opcode(all::OP_CHECKSIG); + script + } +} + impl ToBase58 for Address { fn base58_layout(&self) -> Vec { let mut ret = vec![ @@ -68,6 +99,9 @@ impl ::std::fmt::Show for Address { #[cfg(test)] mod tests { use serialize::hex::FromHex; + use test::{Bencher, black_box}; + + use secp256k1::Secp256k1; use network::constants::Bitcoin; use util::hash::Ripemd160Hash; @@ -84,5 +118,42 @@ mod tests { assert_eq!(addr.to_base58check().as_slice(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr)); } + + #[bench] + pub fn generate_address(bh: &mut Bencher) { + let mut s = Secp256k1::new().unwrap(); + bh.iter( || { + let (sk, pk) = s.generate_keypair(true); + black_box(sk); + black_box(pk); + let addr = Address::from_key(Bitcoin, &pk); + black_box(addr); + }); + } + + #[bench] + pub fn generate_uncompressed_address(bh: &mut Bencher) { + let mut s = Secp256k1::new().unwrap(); + bh.iter( || { + let (sk, pk) = s.generate_keypair(false); + black_box(sk); + black_box(pk); + let addr = Address::from_key(Bitcoin, &pk); + black_box(addr); + }); + } + + #[bench] + pub fn generate_sequential_address(bh: &mut Bencher) { + let mut s = Secp256k1::new().unwrap(); + let (sk, _) = s.generate_keypair(true); + let mut iter = sk.sequence(true); + bh.iter( || { + let (sk, pk) = iter.next().unwrap(); + black_box(sk); + let addr = Address::from_key(Bitcoin, &pk); + black_box(addr); + }); + } } diff --git a/src/wallet/bip32.rs b/src/wallet/bip32.rs index affd5ac..7503dab 100644 --- a/src/wallet/bip32.rs +++ b/src/wallet/bip32.rs @@ -353,6 +353,7 @@ impl FromBase58 for ExtendedPubKey { #[cfg(test)] mod tests { use serialize::hex::FromHex; + use test::{Bencher, black_box}; use network::constants::{Network, Bitcoin}; use util::base58::{FromBase58, ToBase58}; @@ -460,5 +461,56 @@ mod tests { "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"); } + + #[bench] + pub fn generate_sequential_normal_children(bh: &mut Bencher) { + let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); + let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap(); + let mut i = 0; + bh.iter( || { + black_box(msk.ckd_priv(Normal(i))); + i += 1; + }) + } + + #[bench] + pub fn generate_sequential_hardened_children(bh: &mut Bencher) { + let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); + let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap(); + let mut i = 0; + bh.iter( || { + black_box(msk.ckd_priv(Hardened(i))); + i += 1; + }) + } + + #[bench] + pub fn generate_sequential_public_children(bh: &mut Bencher) { + let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); + let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap(); + let mpk = ExtendedPubKey::from_private(&msk); + + let mut i = 0; + bh.iter( || { + black_box(mpk.ckd_pub(Normal(i))); + i += 1; + }) + } + + #[bench] + pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) { + use wallet::address::Address; + + let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); + let msk = ExtendedPrivKey::new_master(Bitcoin, seed.as_slice()).unwrap(); + let mpk = ExtendedPubKey::from_private(&msk); + + let mut i = 0; + bh.iter( || { + let epk = mpk.ckd_pub(Normal(i)).unwrap(); + black_box(Address::from_key(Bitcoin, &epk.public_key)); + i += 1; + }) + } }