Checkpoint commit

Work is stalled on some other library work (to give better lifetime
requirements on `eventual::Future` and avoid some unsafety), so
committing here.

There are only three errors left in this round :)

Also all the indenting is done, so there should be no more massive
rewrite commits. Depending how invasive the lifetime-error fixes
are, I may even be able to do sanely sized commits from here on.
This commit is contained in:
Andrew Poelstra 2015-04-07 17:51:57 -05:00
parent 200e0fe8e3
commit 08a20f8764
24 changed files with 4174 additions and 4202 deletions

View File

@ -11,6 +11,9 @@ path = "src/lib.rs"
[dependencies.secp256k1] [dependencies.secp256k1]
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git" git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git"
[dependencies.eventual]
git = "https://github.com/carllerche/eventual"
[dependencies] [dependencies]
byteorder = "*" byteorder = "*"
num_cpus = "*" num_cpus = "*"

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -34,101 +34,101 @@ use blockdata::transaction::Transaction;
/// the actual transactions /// the actual transactions
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct BlockHeader { pub struct BlockHeader {
/// The protocol version. Should always be 1. /// The protocol version. Should always be 1.
pub version: u32, pub version: u32,
/// Reference to the previous block in the chain /// Reference to the previous block in the chain
pub prev_blockhash: Sha256dHash, pub prev_blockhash: Sha256dHash,
/// The root hash of the merkle tree of transactions in the block /// The root hash of the merkle tree of transactions in the block
pub merkle_root: Sha256dHash, pub merkle_root: Sha256dHash,
/// The timestamp of the block, as claimed by the mainer /// The timestamp of the block, as claimed by the mainer
pub time: u32, pub time: u32,
/// The target value below which the blockhash must lie, encoded as a /// The target value below which the blockhash must lie, encoded as a
/// a float (with well-defined rounding, of course) /// a float (with well-defined rounding, of course)
pub bits: u32, pub bits: u32,
/// The nonce, selected to obtain a low enough blockhash /// The nonce, selected to obtain a low enough blockhash
pub nonce: u32, pub nonce: u32,
} }
/// A Bitcoin block, which is a collection of transactions with an attached /// A Bitcoin block, which is a collection of transactions with an attached
/// proof of work. /// proof of work.
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct Block { pub struct Block {
/// The block header /// The block header
pub header: BlockHeader, pub header: BlockHeader,
/// List of transactions contained in the block /// List of transactions contained in the block
pub txdata: Vec<Transaction> pub txdata: Vec<Transaction>
} }
/// A block header with txcount attached, which is given in the `headers` /// A block header with txcount attached, which is given in the `headers`
/// network message. /// network message.
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct LoneBlockHeader { pub struct LoneBlockHeader {
/// The actual block header /// The actual block header
pub header: BlockHeader, pub header: BlockHeader,
/// The number of transactions in the block. This will always be zero /// The number of transactions in the block. This will always be zero
/// when the LoneBlockHeader is returned as part ef a `headers` message. /// when the LoneBlockHeader is returned as part ef a `headers` message.
pub tx_count: VarInt pub tx_count: VarInt
} }
impl BlockHeader { impl BlockHeader {
/// Computes the target [0, T] that a blockhash must land in to be valid /// Computes the target [0, T] that a blockhash must land in to be valid
pub fn target(&self) -> Uint256 { pub fn target(&self) -> Uint256 {
// This is a floating-point "compact" encoding originally used by // This is a floating-point "compact" encoding originally used by
// OpenSSL, which satoshi put into consensus code, so we're stuck // OpenSSL, which satoshi put into consensus code, so we're stuck
// with it. The exponent needs to have 3 subtracted from it, hence // with it. The exponent needs to have 3 subtracted from it, hence
// this goofy decoding code: // this goofy decoding code:
let (mant, expt) = { let (mant, expt) = {
let unshifted_expt = self.bits >> 24; let unshifted_expt = self.bits >> 24;
if unshifted_expt <= 3 { if unshifted_expt <= 3 {
((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as usize), 0) ((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as usize), 0)
} else { } else {
(self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3)) (self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3))
} }
}; };
// The mantissa is signed but may not be negative // The mantissa is signed but may not be negative
if mant > 0x7FFFFF { if mant > 0x7FFFFF {
Zero::zero() Zero::zero()
} else { } else {
from_u64::<Uint256>(mant as u64).unwrap() << (expt as usize) from_u64::<Uint256>(mant as u64).unwrap() << (expt as usize)
}
} }
}
/// Performs an SPV validation of a block, which confirms that the proof-of-work /// Performs an SPV validation of a block, which confirms that the proof-of-work
/// is correct, but does not verify that the transactions are valid or encoded /// is correct, but does not verify that the transactions are valid or encoded
/// correctly. /// correctly.
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> { pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> {
let ref target = self.target(); let ref target = self.target();
if target != required_target { if target != required_target {
return Err(SpvBadTarget); return Err(SpvBadTarget);
}
let ref hash = self.bitcoin_hash().into_le();
if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
} }
let ref hash = self.bitcoin_hash().into_le();
if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
}
/// Returns the total work of the block /// Returns the total work of the block
pub fn work(&self) -> Uint256 { pub fn work(&self) -> Uint256 {
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind) // 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
let mut ret = !self.target(); let mut ret = !self.target();
let mut ret1 = self.target(); let mut ret1 = self.target();
ret1.increment(); ret1.increment();
ret = ret.div(&ret1); ret = ret / ret1;
ret.increment(); ret.increment();
ret ret
} }
} }
impl BitcoinHash for BlockHeader { impl BitcoinHash for BlockHeader {
fn bitcoin_hash(&self) -> Sha256dHash { fn bitcoin_hash(&self) -> Sha256dHash {
use network::serialize::serialize; use network::serialize::serialize;
Sha256dHash::from_data(serialize(self).unwrap().as_slice()) Sha256dHash::from_data(&serialize(self).unwrap())
} }
} }
impl BitcoinHash for Block { impl BitcoinHash for Block {
fn bitcoin_hash(&self) -> Sha256dHash { fn bitcoin_hash(&self) -> Sha256dHash {
self.header.bitcoin_hash() self.header.bitcoin_hash()
} }
} }
impl_consensus_encoding!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce); impl_consensus_encoding!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce);
@ -137,36 +137,36 @@ impl_consensus_encoding!(LoneBlockHeader, header, tx_count);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io; use std::io;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use blockdata::block::Block; use blockdata::block::Block;
use network::serialize::{deserialize, serialize}; use network::serialize::{deserialize, serialize};
#[test] #[test]
fn block_test() { fn block_test() {
let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap(); let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap();
let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap(); let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap();
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap(); let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap(); let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
let decode: io::Result<Block> = deserialize(some_block.clone()); let decode: io::Result<Block> = deserialize(some_block.clone());
let bad_decode: io::Result<Block> = deserialize(cutoff_block); let bad_decode: io::Result<Block> = deserialize(cutoff_block);
assert!(decode.is_ok()); assert!(decode.is_ok());
assert!(bad_decode.is_err()); assert!(bad_decode.is_err());
let real_decode = decode.unwrap(); let real_decode = decode.unwrap();
assert_eq!(real_decode.header.version, 1); assert_eq!(real_decode.header.version, 1);
assert_eq!(serialize(&real_decode.header.prev_blockhash), Ok(prevhash)); assert_eq!(serialize(&real_decode.header.prev_blockhash), Ok(prevhash));
// [test] TODO: actually compute the merkle root // [test] TODO: actually compute the merkle root
assert_eq!(serialize(&real_decode.header.merkle_root), Ok(merkle)); assert_eq!(serialize(&real_decode.header.merkle_root), Ok(merkle));
assert_eq!(real_decode.header.time, 1231965655); assert_eq!(real_decode.header.time, 1231965655);
assert_eq!(real_decode.header.bits, 486604799); assert_eq!(real_decode.header.bits, 486604799);
assert_eq!(real_decode.header.nonce, 2067413810); assert_eq!(real_decode.header.nonce, 2067413810);
// [test] TODO: check the transaction data // [test] TODO: check the transaction data
assert_eq!(serialize(&real_decode), Ok(some_block)); assert_eq!(serialize(&real_decode), Ok(some_block));
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -23,7 +23,7 @@ use std::default::Default;
use std::num::from_u64; use std::num::from_u64;
use blockdata::opcodes; use blockdata::opcodes;
use blockdata::script::Script; use blockdata::script::ScriptBuilder;
use blockdata::transaction::{Transaction, TxOut, TxIn}; use blockdata::transaction::{Transaction, TxOut, TxIn};
use blockdata::block::{Block, BlockHeader}; use blockdata::block::{Block, BlockHeader};
use network::constants::Network; use network::constants::Network;
@ -39,144 +39,144 @@ pub static DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600;
/// In Bitcoind this is insanely described as ~((u256)0 >> 32) /// In Bitcoind this is insanely described as ~((u256)0 >> 32)
pub fn max_target(_: Network) -> Uint256 { pub fn max_target(_: Network) -> Uint256 {
from_u64::<Uint256>(0xFFFF).unwrap() << 208 from_u64::<Uint256>(0xFFFF).unwrap() << 208
} }
/// The maximum value allowed in an output (useful for sanity checking, /// The maximum value allowed in an output (useful for sanity checking,
/// since keeping everything below this value should prevent overflows /// since keeping everything below this value should prevent overflows
/// if you are doing anything remotely sane with monetary values). /// if you are doing anything remotely sane with monetary values).
pub fn max_money(_: Network) -> u64 { pub fn max_money(_: Network) -> u64 {
21_000_000 * COIN_VALUE 21_000_000 * COIN_VALUE
} }
/// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block /// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block
fn bitcoin_genesis_tx() -> Transaction { fn bitcoin_genesis_tx() -> Transaction {
// Base // Base
let mut ret = Transaction { let mut ret = Transaction {
version: 1, version: 1,
lock_time: 0, lock_time: 0,
input: vec![], input: vec![],
output: vec![] output: vec![]
}; };
// Inputs // Inputs
let mut in_script = Script::new(); let mut in_script = ScriptBuilder::new();
in_script.push_scriptint(486604799); in_script.push_scriptint(486604799);
in_script.push_scriptint(4); in_script.push_scriptint(4);
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes()); in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
ret.input.push(TxIn { ret.input.push(TxIn {
prev_hash: Default::default(), prev_hash: Default::default(),
prev_index: 0xFFFFFFFF, prev_index: 0xFFFFFFFF,
script_sig: in_script, script_sig: in_script.into_script(),
sequence: MAX_SEQUENCE sequence: MAX_SEQUENCE
}); });
// Outputs // Outputs
let mut out_script = Script::new(); let mut out_script = ScriptBuilder::new();
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice()); out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
out_script.push_opcode(opcodes::All::OP_CHECKSIG); out_script.push_opcode(opcodes::All::OP_CHECKSIG);
ret.output.push(TxOut { ret.output.push(TxOut {
value: 50 * COIN_VALUE, value: 50 * COIN_VALUE,
script_pubkey: out_script script_pubkey: out_script.into_script()
}); });
// end // end
ret ret
} }
/// Constructs and returns the genesis block /// Constructs and returns the genesis block
pub fn genesis_block(network: Network) -> Block { pub fn genesis_block(network: Network) -> Block {
match network { match network {
Network::Bitcoin => { Network::Bitcoin => {
let txdata = vec![bitcoin_genesis_tx()]; let txdata = vec![bitcoin_genesis_tx()];
Block { Block {
header: BlockHeader { header: BlockHeader {
version: 1, version: 1,
prev_blockhash: Default::default(), prev_blockhash: Default::default(),
merkle_root: txdata.merkle_root(), merkle_root: txdata.merkle_root(),
time: 1231006505, time: 1231006505,
bits: 0x1d00ffff, bits: 0x1d00ffff,
nonce: 2083236893 nonce: 2083236893
}, },
txdata: txdata txdata: txdata
} }
}
Network::Testnet => {
let txdata = vec![bitcoin_genesis_tx()];
Block {
header: BlockHeader {
version: 1,
prev_blockhash: Default::default(),
merkle_root: txdata.merkle_root(),
time: 1296688602,
bits: 0x1d00ffff,
nonce: 414098458
},
txdata: txdata
}
}
} }
Network::Testnet => {
let txdata = vec![bitcoin_genesis_tx()];
Block {
header: BlockHeader {
version: 1,
prev_blockhash: Default::default(),
merkle_root: txdata.merkle_root(),
time: 1296688602,
bits: 0x1d00ffff,
nonce: 414098458
},
txdata: txdata
}
}
}
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::default::Default; use std::default::Default;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use network::constants::Network; use network::constants::Network;
use network::serialize::{BitcoinHash, serialize}; use network::serialize::{BitcoinHash, serialize};
use blockdata::constants::{genesis_block, bitcoin_genesis_tx}; use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE}; use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
#[test] #[test]
fn bitcoin_genesis_first_transaction() { fn bitcoin_genesis_first_transaction() {
let gen = bitcoin_genesis_tx(); let gen = bitcoin_genesis_tx();
assert_eq!(gen.version, 1); assert_eq!(gen.version, 1);
assert_eq!(gen.input.len(), 1); assert_eq!(gen.input.len(), 1);
assert_eq!(gen.input[0].prev_hash, Default::default()); assert_eq!(gen.input[0].prev_hash, Default::default());
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF); assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
assert_eq!(serialize(&gen.input[0].script_sig), assert_eq!(serialize(&gen.input[0].script_sig),
Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap())); Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE); assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
assert_eq!(gen.output.len(), 1); assert_eq!(gen.output.len(), 1);
assert_eq!(serialize(&gen.output[0].script_pubkey), assert_eq!(serialize(&gen.output[0].script_pubkey),
Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap())); Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
assert_eq!(gen.output[0].value, 50 * COIN_VALUE); assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
assert_eq!(gen.lock_time, 0); assert_eq!(gen.lock_time, 0);
assert_eq!(gen.bitcoin_hash().be_hex_string(), assert_eq!(gen.bitcoin_hash().be_hex_string(),
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string()); "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
} }
#[test] #[test]
fn bitcoin_genesis_full_block() { fn bitcoin_genesis_full_block() {
let gen = genesis_block(network::Bitcoin); let gen = genesis_block(network::Bitcoin);
assert_eq!(gen.header.version, 1); assert_eq!(gen.header.version, 1);
assert_eq!(gen.header.prev_blockhash, Default::default()); assert_eq!(gen.header.prev_blockhash, Default::default());
assert_eq!(gen.header.merkle_root.be_hex_string(), assert_eq!(gen.header.merkle_root.be_hex_string(),
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string()); "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
assert_eq!(gen.header.time, 1231006505); assert_eq!(gen.header.time, 1231006505);
assert_eq!(gen.header.bits, 0x1d00ffff); assert_eq!(gen.header.bits, 0x1d00ffff);
assert_eq!(gen.header.nonce, 2083236893); assert_eq!(gen.header.nonce, 2083236893);
assert_eq!(gen.header.bitcoin_hash().be_hex_string(), assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string()); "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
} }
#[test] #[test]
fn testnet_genesis_full_block() { fn testnet_genesis_full_block() {
let gen = genesis_block(network::Testnet); let gen = genesis_block(network::Testnet);
assert_eq!(gen.header.version, 1); assert_eq!(gen.header.version, 1);
assert_eq!(gen.header.prev_blockhash, Default::default()); assert_eq!(gen.header.prev_blockhash, Default::default());
assert_eq!(gen.header.merkle_root.be_hex_string(), assert_eq!(gen.header.merkle_root.be_hex_string(),
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string()); "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
assert_eq!(gen.header.time, 1296688602); assert_eq!(gen.header.time, 1296688602);
assert_eq!(gen.header.bits, 0x1d00ffff); assert_eq!(gen.header.bits, 0x1d00ffff);
assert_eq!(gen.header.nonce, 414098458); assert_eq!(gen.header.nonce, 414098458);
assert_eq!(gen.header.bitcoin_hash().be_hex_string(), assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string()); "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
} }
} }

View File

@ -592,10 +592,10 @@ impl All {
// 16 opcodes // 16 opcodes
} else if All::OP_PUSHNUM_1 as u8 <= *self as u8 && } else if All::OP_PUSHNUM_1 as u8 <= *self as u8 &&
*self as u8 <= All::OP_PUSHNUM_16 as u8 { *self as u8 <= All::OP_PUSHNUM_16 as u8 {
Class::PushNum(1 + *self as isize - All::OP_PUSHNUM_1 as isize) Class::PushNum(1 + *self as i32 - All::OP_PUSHNUM_1 as i32)
// 76 opcodes // 76 opcodes
} else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 { } else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 {
Class::PushBytes(*self as usize) Class::PushBytes(*self as u32)
// 60 opcodes // 60 opcodes
} else { } else {
Class::Ordinary(unsafe { transmute(*self) }) Class::Ordinary(unsafe { transmute(*self) })
@ -636,9 +636,9 @@ pub static OP_TRUE: All = All::OP_PUSHNUM_1;
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum Class { pub enum Class {
/// Pushes the given number onto the stack /// Pushes the given number onto the stack
PushNum(isize), PushNum(i32),
/// Pushes the given number of bytes onto the stack /// Pushes the given number of bytes onto the stack
PushBytes(usize), PushBytes(u32),
/// Fails the script if executed /// Fails the script if executed
ReturnOp, ReturnOp,
/// Fails the script even if not executed /// Fails the script even if not executed

View File

@ -35,7 +35,7 @@ use crypto::digest::Digest;
use crypto::ripemd160::Ripemd160; use crypto::ripemd160::Ripemd160;
use crypto::sha1::Sha1; use crypto::sha1::Sha1;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use secp256k1::Secp256k1; use secp256k1::{self, Secp256k1};
use secp256k1::key::PublicKey; use secp256k1::key::PublicKey;
use serde; use serde;
@ -56,13 +56,16 @@ impl Clone for Script {
} }
} }
#[derive(PartialEq, Eq, Debug, Clone, Display)]
/// An object which can be used to construct a script piece by piece
pub struct ScriptBuilder(Vec<u8>);
impl hash::Hash for Script { impl hash::Hash for Script {
#[inline] #[inline]
fn hash<H>(&self, state: &mut H) fn hash<H>(&self, state: &mut H)
where H: hash::Hasher where H: hash::Hasher
{ {
let &Script(ref raw) = self; (&self.0[..]).hash(state);
(&raw[..]).hash(state);
} }
#[inline] #[inline]
@ -70,8 +73,7 @@ impl hash::Hash for Script {
where H: hash::Hasher where H: hash::Hasher
{ {
for s in data.iter() { for s in data.iter() {
let &Script(ref raw) = s; (&s.0[..]).hash(state);
(&raw[..]).hash(state);
} }
} }
} }
@ -95,7 +97,7 @@ pub enum Error {
/// OP_CHECKSIG was called with a bad signature /// OP_CHECKSIG was called with a bad signature
BadSignature, BadSignature,
/// An ECDSA error /// An ECDSA error
Ecdsa(::secp256k1::Error), Ecdsa(secp256k1::Error),
/// An OP_ELSE happened while not in an OP_IF tree /// An OP_ELSE happened while not in an OP_IF tree
ElseWithoutIf, ElseWithoutIf,
/// An OP_ENDIF happened while not in an OP_IF tree /// An OP_ENDIF happened while not in an OP_IF tree
@ -1558,7 +1560,7 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
for _ in 0..input_index { for _ in 0..input_index {
new_outs.push(Default::default()) new_outs.push(Default::default())
} }
new_outs.push(tx_copy.output.swap_remove(input_index).unwrap()); new_outs.push(tx_copy.output.swap_remove(input_index));
tx_copy.output = new_outs; tx_copy.output = new_outs;
} else { } else {
sighash_single_bug = true; sighash_single_bug = true;
@ -1581,7 +1583,10 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
serialize(&Sha256dHash::from_data(&data_to_sign[..])).unwrap() serialize(&Sha256dHash::from_data(&data_to_sign[..])).unwrap()
}; };
Secp256k1::verify_raw(&signature_hash[..], sig_slice, &pubkey).map_err(Error::Ecdsa) // 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)
} }
// Macro to translate English stack instructions into Rust code. // Macro to translate English stack instructions into Rust code.
@ -1719,70 +1724,7 @@ impl Script {
pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) } pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) }
/// The length in bytes of the script /// The length in bytes of the script
pub fn len(&self) -> usize { pub fn len(&self) -> usize { self.0.len() }
let &Script(ref raw) = self;
raw.len()
}
/// Adds instructions to push an integer onto the stack. Integers are
/// encoded as little-endian signed-magnitude numbers, but there are
/// dedicated opcodes to push some small integers.
pub fn push_int(&mut self, data: i64) {
// We can special-case -1, 1-16
if data == -1 || (data >= 1 && data <=16) {
let &Script(ref mut raw) = self;
raw.push(data as u8 + opcodes::All::OP_TRUE as u8);
return;
}
// We can also special-case zero
if data == 0 {
let &Script(ref mut raw) = self;
raw.push(opcodes::All::OP_FALSE as u8);
return;
}
// Otherwise encode it as data
self.push_scriptint(data);
}
/// Adds instructions to push an integer onto the stack, using the explicit
/// encoding regardless of the availability of dedicated opcodes.
pub fn push_scriptint(&mut self, data: i64) {
self.push_slice(&build_scriptint(data));
}
/// Adds instructions to push some arbitrary data onto the stack
pub fn push_slice(&mut self, data: &[u8]) {
let &Script(ref mut raw) = self;
// Start with a PUSH opcode
match data.len() {
n if n < opcodes::Ordinary::OP_PUSHDATA1 as usize => { raw.push(n as u8); },
n if n < 0x100 => {
raw.push(opcodes::Ordinary::OP_PUSHDATA1 as u8);
raw.push(n as u8);
},
n if n < 0x10000 => {
raw.push(opcodes::Ordinary::OP_PUSHDATA2 as u8);
raw.push((n % 0x100) as u8);
raw.push((n / 0x100) as u8);
},
n if n < 0x100000000 => {
raw.push(opcodes::Ordinary::OP_PUSHDATA4 as u8);
raw.push((n % 0x100) as u8);
raw.push(((n / 0x100) % 0x100) as u8);
raw.push(((n / 0x10000) % 0x100) as u8);
raw.push((n / 0x1000000) as u8);
}
_ => panic!("tried to put a 4bn+ sized object into a script!")
}
// Then push the acraw
raw.extend(data.iter().map(|n| *n));
}
/// Adds an individual opcode to the script
pub fn push_opcode(&mut self, data: opcodes::All) {
let &Script(ref mut raw) = self;
raw.push(data as u8);
}
/// Trace a script /// Trace a script
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>, pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
@ -1807,21 +1749,19 @@ impl Script {
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> {
let &Script(ref raw) = self;
let mut codeseparator_index = 0; let mut codeseparator_index = 0;
let mut exec_stack = vec![]; let mut exec_stack = vec![];
let mut alt_stack = vec![]; let mut alt_stack = vec![];
let mut index = 0; let mut index = 0;
let mut op_count = 0; let mut op_count = 0;
while index < raw.len() { while index < self.0.len() {
let executing = exec_stack.iter().all(|e| *e); let executing = exec_stack.iter().all(|e| *e);
let byte = unsafe { *raw.get(index) }; let byte = self.0[index];
// Write out the trace, except the stack which we don't know yet // Write out the trace, except the stack which we don't know yet
match trace { match trace {
Some(ref mut t) => { Some(ref mut t) => {
let opcode = opcodes::All::Opcode::from_u8(byte); let opcode = opcodes::All::from_u8(byte);
t.push(TraceIteration { t.push(TraceIteration {
index: index, index: index,
opcode: opcode, opcode: opcode,
@ -1837,7 +1777,7 @@ impl Script {
op_count += 1; op_count += 1;
index += 1; index += 1;
// The definitions of all these categories are in opcodes.rs // The definitions of all these categories are in opcodes.rs
match (executing, opcodes::All::Opcode::from_u8(byte).classify()) { match (executing, opcodes::All::from_u8(byte).classify()) {
// Illegal operations mean failure regardless of execution state // Illegal operations mean failure regardless of execution state
(_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode), (_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode),
// Push number // Push number
@ -1846,29 +1786,30 @@ impl Script {
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn), (true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
// Data-reading statements still need to read, even when not executing // Data-reading statements still need to read, even when not executing
(_, opcodes::Class::PushBytes(n)) => { (_, opcodes::Class::PushBytes(n)) => {
if raw.len() < index + n { return Err(Error::EarlyEndOfScript); } let n = n as usize;
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index, index + n))); } if self.0.len() < index + n { return Err(Error::EarlyEndOfScript); }
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index..index + n])); }
index += n; index += n;
} }
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => { (_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => {
if raw.len() < index + 1 { return Err(Error::EarlyEndOfScript); } if self.0.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
let n = try!(read_uint(&raw[index..], 1)); let n = try!(read_uint(&self.0[index..], 1));
if raw.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); } if self.0.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 1, index + n + 1))); } if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 1..index + n + 1])); }
index += 1 + n; index += 1 + n;
} }
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => { (_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => {
if raw.len() < index + 2 { return Err(Error::EarlyEndOfScript); } if self.0.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
let n = try!(read_uint(&raw[index..], 2)); let n = try!(read_uint(&self.0[index..], 2));
if raw.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); } if self.0.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 2, index + n + 2))); } if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 2..index + n + 2])); }
index += 2 + n; index += 2 + n;
} }
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => { (_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => {
if raw.len() < index + 4 { return Err(Error::EarlyEndOfScript); } if self.0.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
let n = try!(read_uint(&raw[index..], 4)); let n = try!(read_uint(&self.0[index..], 4));
if raw.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); } if self.0.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 4, index + n + 4))); } if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 4..index + n + 4])); }
index += 4 + n; index += 4 + n;
} }
// If-statements take effect when not executing // If-statements take effect when not executing
@ -1942,7 +1883,7 @@ impl Script {
opcodes::Ordinary::OP_OVER => stack_opcode!(stack(2): copy 2), opcodes::Ordinary::OP_OVER => stack_opcode!(stack(2): copy 2),
opcodes::Ordinary::OP_PICK => { opcodes::Ordinary::OP_PICK => {
let n = match stack.pop() { let n = match stack.pop() {
Some(data) => try!(read_scriptint(&data)), Some(data) => try!(read_scriptint(&data[..])),
None => { return Err(Error::PopEmptyStack); } None => { return Err(Error::PopEmptyStack); }
}; };
if n < 0 { return Err(Error::NegativePick); } if n < 0 { return Err(Error::NegativePick); }
@ -1951,7 +1892,7 @@ impl Script {
} }
opcodes::Ordinary::OP_ROLL => { opcodes::Ordinary::OP_ROLL => {
let n = match stack.pop() { let n = match stack.pop() {
Some(data) => try!(read_scriptint(&data)), Some(data) => try!(read_scriptint(&data[..])),
None => { return Err(Error::PopEmptyStack); } None => { return Err(Error::PopEmptyStack); }
}; };
if n < 0 { return Err(Error::NegativeRoll); } if n < 0 { return Err(Error::NegativeRoll); }
@ -2033,11 +1974,12 @@ impl Script {
// Compute the section of script that needs to be hashed: everything // Compute the section of script that needs to be hashed: everything
// from the last CODESEPARATOR, except the signature itself. // from the last CODESEPARATOR, except the signature itself.
let mut script = (&raw[codeseparator_index..]).to_vec(); let mut script = (&self.0[codeseparator_index..]).to_vec();
let mut remove = Script::new(); let mut remove = ScriptBuilder::new();
remove.push_slice(sig_slice); remove.push_slice(sig_slice);
script_find_and_remove(&mut script, &remove); script_find_and_remove(&mut script, &remove[..]);
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]); // Also all of the OP_CODESEPARATORS, even the unevaluated ones
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
// This is as far as we can go without a transaction, so fail here // This is as far as we can go without a transaction, so fail here
if input_context.is_none() { return Err(Error::NoTransaction); } if input_context.is_none() { return Err(Error::NoTransaction); }
@ -2053,7 +1995,7 @@ impl Script {
opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => { opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => {
// Read all the keys // Read all the keys
if stack.len() < 1 { return Err(Error::PopEmptyStack); } if stack.len() < 1 { return Err(Error::PopEmptyStack); }
let n_keys = try!(read_scriptint(&stack.pop().unwrap())); let n_keys = try!(read_scriptint(&stack.pop().unwrap()[..]));
if n_keys < 0 || n_keys > 20 { if n_keys < 0 || n_keys > 20 {
return Err(Error::MultisigBadKeyCount(n_keys as isize)); return Err(Error::MultisigBadKeyCount(n_keys as isize));
} }
@ -2066,7 +2008,7 @@ impl Script {
// Read all the signatures // Read all the signatures
if stack.len() < 1 { return Err(Error::PopEmptyStack); } if stack.len() < 1 { return Err(Error::PopEmptyStack); }
let n_sigs = try!(read_scriptint(&stack.pop().unwrap())); let n_sigs = try!(read_scriptint(&stack.pop().unwrap()[..]));
if n_sigs < 0 || n_sigs > n_keys { if n_sigs < 0 || n_sigs > n_keys {
return Err(Error::MultisigBadSigCount(n_sigs as isize)); return Err(Error::MultisigBadSigCount(n_sigs as isize));
} }
@ -2082,12 +2024,12 @@ impl Script {
// Compute the section of script that needs to be hashed: everything // Compute the section of script that needs to be hashed: everything
// from the last CODESEPARATOR, except the signatures themselves. // from the last CODESEPARATOR, except the signatures themselves.
let mut script = (&raw[codeseparator_index..]).to_vec(); let mut script = (&self.0[codeseparator_index..]).to_vec();
for sig in sigs.iter() { for sig in sigs.iter() {
let mut remove = Script::new(); let mut remove = ScriptBuilder::new();
remove.push_slice(&sig); remove.push_slice(&sig[..]);
script_find_and_remove(&mut script, &remove); script_find_and_remove(&mut script, &remove[..]);
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]); script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
} }
// This is as far as we can go without a transaction, so fail here // This is as far as we can go without a transaction, so fail here
@ -2105,7 +2047,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(&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
@ -2142,12 +2084,11 @@ impl Script {
/// Checks whether a script pubkey is a p2sh output /// Checks whether a script pubkey is a p2sh output
#[inline] #[inline]
pub fn is_p2sh(&self) -> bool { pub fn is_p2sh(&self) -> bool {
let &Script(ref raw) = self;
unsafe { unsafe {
raw.len() == 23 && self.0.len() == 23 &&
*raw.get(0) == opcodes::All::OP_HASH160 as u8 && self.0[0] == opcodes::All::OP_HASH160 as u8 &&
*raw.get(1) == opcodes::All::OP_PUSHBYTES_20 as u8 && self.0[1] == opcodes::All::OP_PUSHBYTES_20 as u8 &&
*raw.get(22) == opcodes::All::OP_EQUAL as u8 self.0[22] == opcodes::All::OP_EQUAL as u8
} }
} }
@ -2186,9 +2127,10 @@ impl Script {
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn), (true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
// Data-reading statements still need to read, even when not executing // Data-reading statements still need to read, even when not executing
(_, opcodes::Class::PushBytes(n)) => { (_, opcodes::Class::PushBytes(n)) => {
let n = n as usize;
if script.len() < index + n { return Err(Error::EarlyEndOfScript); } if script.len() < index + n { return Err(Error::EarlyEndOfScript); }
if executing { if executing {
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index, index + n))); stack.push_alloc(AbstractStackElem::new_raw(&script[index..index + n]));
} }
index += n; index += n;
} }
@ -2200,7 +2142,7 @@ impl Script {
}; };
if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); } if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
if executing { if executing {
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 1, index + n + 1))); stack.push_alloc(AbstractStackElem::new_raw(&script[index + 1..index + n + 1]));
} }
index += 1 + n; index += 1 + n;
} }
@ -2212,7 +2154,7 @@ impl Script {
}; };
if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); } if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
if executing { if executing {
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 2, index + n + 2))); stack.push_alloc(AbstractStackElem::new_raw(&script[index + 2..index + n + 2]));
} }
index += 2 + n; index += 2 + n;
} }
@ -2223,7 +2165,7 @@ impl Script {
}; };
if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); } if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
if executing { if executing {
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 4, index + n + 4))); stack.push_alloc(AbstractStackElem::new_raw(&script[index + 4..index + n + 4]));
} }
index += 4 + n; index += 4 + n;
} }
@ -2495,8 +2437,7 @@ impl Script {
} }
} }
let &Script(ref raw) = self; recurse(&self.0, AbstractStack::new(), vec![], 1)
recurse(&raw, AbstractStack::new(), vec![], 1)
} }
} }
@ -2504,62 +2445,93 @@ impl Default for Script {
fn default() -> Script { Script(vec![].into_boxed_slice()) } fn default() -> Script { Script(vec![].into_boxed_slice()) }
} }
impl_index_newtype!(Script, u8);
impl ops::Index<usize> for Script { impl ScriptBuilder {
type Output = u8; /// Creates a new empty script
#[inline] pub fn new() -> ScriptBuilder { ScriptBuilder(vec![]) }
fn index(&self, index: usize) -> &u8 {
let &Script(ref raw) = self; /// Creates a new script from an existing vector
&raw[index] pub fn from_vec(v: Vec<u8>) -> ScriptBuilder { ScriptBuilder(v) }
/// The length in bytes of the script
pub fn len(&self) -> usize { self.0.len() }
/// Adds instructions to push an integer onto the stack. Integers are
/// encoded as little-endian signed-magnitude numbers, but there are
/// dedicated opcodes to push some small integers.
pub fn push_int(&mut self, data: i64) {
// We can special-case -1, 1-16
if data == -1 || (data >= 1 && data <=16) {
self.0.push(data as u8 + opcodes::OP_TRUE as u8);
}
// We can also special-case zero
else if data == 0 {
self.0.push(opcodes::OP_FALSE as u8);
}
// Otherwise encode it as data
else { self.push_scriptint(data); }
}
/// Adds instructions to push an integer onto the stack, using the explicit
/// encoding regardless of the availability of dedicated opcodes.
pub fn push_scriptint(&mut self, data: i64) {
self.push_slice(&build_scriptint(data));
}
/// Adds instructions to push some arbitrary data onto the stack
pub fn push_slice(&mut self, data: &[u8]) {
// Start with a PUSH opcode
match data.len() {
n if n < opcodes::Ordinary::OP_PUSHDATA1 as usize => { self.0.push(n as u8); },
n if n < 0x100 => {
self.0.push(opcodes::Ordinary::OP_PUSHDATA1 as u8);
self.0.push(n as u8);
},
n if n < 0x10000 => {
self.0.push(opcodes::Ordinary::OP_PUSHDATA2 as u8);
self.0.push((n % 0x100) as u8);
self.0.push((n / 0x100) as u8);
},
n if n < 0x100000000 => {
self.0.push(opcodes::Ordinary::OP_PUSHDATA4 as u8);
self.0.push((n % 0x100) as u8);
self.0.push(((n / 0x100) % 0x100) as u8);
self.0.push(((n / 0x10000) % 0x100) as u8);
self.0.push((n / 0x1000000) as u8);
}
_ => panic!("tried to put a 4bn+ sized object into a script!")
}
// Then push the acraw
self.0.extend(data.iter().map(|n| *n));
}
pub fn push_opcode(&mut self, data: opcodes::All) {
self.0.push(data as u8);
}
pub fn into_script(self) -> Script {
Script(self.0.into_boxed_slice())
} }
} }
impl ops::Index<ops::Range<usize>> for Script { /// Adds an individual opcode to the script
type Output = [u8]; impl Default for ScriptBuilder {
#[inline] fn default() -> ScriptBuilder { ScriptBuilder(vec![]) }
fn index(&self, index: ops::Range<usize>) -> &[u8] {
let &Script(ref raw) = self;
&raw[index]
}
} }
impl ops::Index<ops::RangeTo<usize>> for Script { impl_index_newtype!(ScriptBuilder, u8);
type Output = [u8];
#[inline]
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
let &Script(ref raw) = self;
&raw[index]
}
}
impl ops::Index<ops::RangeFrom<usize>> for Script {
type Output = [u8];
#[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
let &Script(ref raw) = self;
&raw[index]
}
}
impl ops::Index<ops::RangeFull> for Script {
type Output = [u8];
#[inline]
fn index(&self, _: ops::RangeFull) -> &[u8] {
let &Script(ref raw) = self;
&raw[..]
}
}
// User-facing serialization // User-facing serialization
impl serde::Serialize for Script { impl serde::Serialize for Script {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer, where S: serde::Serializer,
{ {
let &Script(ref raw) = self; for dat in self.0.iter() {
for dat in raw.iter() { try!(serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap()));
serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap()); try!(serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap()));
serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap());
} }
Ok(())
} }
} }
@ -2567,8 +2539,7 @@ impl serde::Serialize for Script {
impl<S: SimpleEncoder> ConsensusEncodable<S> for Script { impl<S: SimpleEncoder> ConsensusEncodable<S> for Script {
#[inline] #[inline]
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
let &Script(ref data) = self; self.0.consensus_encode(s)
data.consensus_encode(s)
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -32,271 +32,272 @@ use blockdata::utxoset::UtxoSet;
use network::encodable::ConsensusEncodable; use network::encodable::ConsensusEncodable;
use network::serialize::BitcoinHash; use network::serialize::BitcoinHash;
use network::constants::Network; use network::constants::Network;
use wallet::address::Address; use wallet::address::{Address, ToAddress};
/// A transaction input, which defines old coins to be consumed /// A transaction input, which defines old coins to be consumed
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct TxIn { pub struct TxIn {
/// The hash of the transaction whose output is being used an an input /// The hash of the transaction whose output is being used an an input
pub prev_hash: Sha256dHash, pub prev_hash: Sha256dHash,
/// The index of the output in the previous transaction, which may have several /// The index of the output in the previous transaction, which may have several
pub prev_index: u32, pub prev_index: u32,
/// The script which pushes values on the stack which will cause /// The script which pushes values on the stack which will cause
/// the referenced output's script to accept /// the referenced output's script to accept
pub script_sig: Script, pub script_sig: Script,
/// The sequence number, which suggests to miners which of two /// The sequence number, which suggests to miners which of two
/// conflicting transactions should be preferred, or 0xFFFFFFFF /// conflicting transactions should be preferred, or 0xFFFFFFFF
/// to ignore this feature. This is generally never used since /// to ignore this feature. This is generally never used since
/// the miner behaviour cannot be enforced. /// the miner behaviour cannot be enforced.
pub sequence: u32, pub sequence: u32,
} }
/// A transaction output, which defines new coins to be created from old ones. /// A transaction output, which defines new coins to be created from old ones.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct TxOut { pub struct TxOut {
/// The value of the output, in satoshis /// The value of the output, in satoshis
pub value: u64, pub value: u64,
/// The script which must satisfy for the output to be spent /// The script which must satisfy for the output to be spent
pub script_pubkey: Script pub script_pubkey: Script
} }
// This is used as a "null txout" in consensus signing code // This is used as a "null txout" in consensus signing code
impl Default for TxOut { impl Default for TxOut {
fn default() -> TxOut { fn default() -> TxOut {
TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() } TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() }
} }
} }
/// A classification for script pubkeys /// A classification for script pubkeys
pub enum ScriptPubkeyTemplate { pub enum ScriptPubkeyTemplate {
/// A pay-to-address output /// A pay-to-address output
PayToPubkeyHash(Address), PayToPubkeyHash(Address),
/// Another kind of output /// Another kind of output
Unknown Unknown
} }
impl TxOut { impl TxOut {
/// Determines the template that this output adheres to, if any /// Determines the template that this output adheres to, if any
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate { pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
if self.script_pubkey.len() == 25 && if self.script_pubkey.len() == 25 &&
self.script_pubkey.slice_to(3) == &[0x76, 0xa9, 0x14] && &self.script_pubkey[0..3] == &[0x76, 0xa9, 0x14] &&
self.script_pubkey.slice_from(23) == &[0x88, 0xac] { &self.script_pubkey[23..] == &[0x88, 0xac] {
ScriptPubkeyTemplate::PayToPubkeyHash(self.script_pubkey.slice(3, 23).to_address(network)) ScriptPubkeyTemplate::PayToPubkeyHash((&self.script_pubkey[3..23]).to_address(network))
} else { } else {
ScriptPubkeyTemplate::Unknown ScriptPubkeyTemplate::Unknown
}
} }
}
} }
/// A Bitcoin transaction, which describes an authenticated movement of coins /// A Bitcoin transaction, which describes an authenticated movement of coins
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Transaction { pub struct Transaction {
/// The protocol version, should always be 1. /// The protocol version, should always be 1.
pub version: u32, pub version: u32,
/// Block number before which this transaction is valid, or 0 for /// Block number before which this transaction is valid, or 0 for
/// valid immediately. /// valid immediately.
pub lock_time: u32, pub lock_time: u32,
/// List of inputs /// List of inputs
pub input: Vec<TxIn>, pub input: Vec<TxIn>,
/// List of outputs /// List of outputs
pub output: Vec<TxOut> pub output: Vec<TxOut>
} }
/// A transaction error /// A transaction error
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub enum Error { pub enum Error {
/// Concatenated script failed in the input half (script error) /// Concatenated script failed in the input half (script error)
InputScriptFailure(script::Error), InputScriptFailure(script::Error),
/// Concatenated script failed in the output half (script error) /// Concatenated script failed in the output half (script error)
OutputScriptFailure(script::Error), OutputScriptFailure(script::Error),
/// P2SH serialized script failed (script error) /// P2SH serialized script failed (script error)
P2shScriptFailure(script::Error), P2shScriptFailure(script::Error),
/// P2SH serialized script ended with false at the top of the stack /// P2SH serialized script ended with false at the top of the stack
P2shScriptReturnedFalse, P2shScriptReturnedFalse,
/// P2SH serialized script ended with nothing in the stack /// P2SH serialized script ended with nothing in the stack
P2shScriptReturnedEmptyStack, P2shScriptReturnedEmptyStack,
/// Script ended with false at the top of the stack /// Script ended with false at the top of the stack
ScriptReturnedFalse, ScriptReturnedFalse,
/// Script ended with nothing in the stack /// Script ended with nothing in the stack
ScriptReturnedEmptyStack, ScriptReturnedEmptyStack,
/// Script ended with nothing in the stack (input txid, input vout) /// Script ended with nothing in the stack (input txid, input vout)
InputNotFound(Sha256dHash, u32), InputNotFound(Sha256dHash, u32),
} }
display_from_debug!(Error);
impl serde::Serialize for Error { impl serde::Serialize for Error {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer, where S: serde::Serializer,
{ {
serializer.visit_str(&self.to_string()) serializer.visit_str(&self.to_string())
} }
} }
/// A trace of a transaction input's script execution /// A trace of a transaction input's script execution
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct InputTrace { pub struct InputTrace {
input_txid: Sha256dHash, input_txid: Sha256dHash,
input_vout: usize, input_vout: usize,
sig_trace: ScriptTrace, sig_trace: ScriptTrace,
pubkey_trace: Option<ScriptTrace>, pubkey_trace: Option<ScriptTrace>,
p2sh_trace: Option<ScriptTrace>, p2sh_trace: Option<ScriptTrace>,
error: Option<Error> error: Option<Error>
} }
/// A trace of a transaction's execution /// A trace of a transaction's execution
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct TransactionTrace { pub struct TransactionTrace {
txid: Sha256dHash, txid: Sha256dHash,
inputs: Vec<InputTrace> inputs: Vec<InputTrace>
} }
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,
utxoset: &UtxoSet, utxoset: &UtxoSet,
txn: &Transaction, txn: &Transaction,
index: usize) -> Result<(), Error> { index: usize) -> Result<(), Error> {
let txo = utxoset.get_utxo(self.prev_hash, self.prev_index); let txo = utxoset.get_utxo(self.prev_hash, self.prev_index);
match txo { match txo {
Some((_, txo)) => { Some((_, txo)) => {
let mut p2sh_stack = Vec::new(); let mut p2sh_stack = Vec::new();
let mut p2sh_script = Script::new(); let mut p2sh_script = Script::new();
let mut stack = Vec::with_capacity(6); let mut stack = Vec::with_capacity(6);
match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) { match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) {
Ok(_) => {} Ok(_) => {}
Err(e) => { return Err(Error::InputScriptFailure(e)); } Err(e) => { return Err(Error::InputScriptFailure(e)); }
} }
if txo.script_pubkey.is_p2sh() && stack.len() > 0 { if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
p2sh_stack = stack.clone(); p2sh_stack = stack.clone();
p2sh_script = match p2sh_stack.pop() { p2sh_script = match p2sh_stack.pop() {
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v), Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()), Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
None => unreachable!() None => unreachable!()
}; };
} }
match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) { match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) {
Ok(_) => {} Ok(_) => {}
Err(e) => { return Err(Error::OutputScriptFailure(e)); } Err(e) => { return Err(Error::OutputScriptFailure(e)); }
} }
match stack.pop() { match stack.pop() {
Some(v) => { Some(v) => {
if !read_scriptbool(&v[..]) { if !read_scriptbool(&v[..]) {
return Err(Error::ScriptReturnedFalse); return Err(Error::ScriptReturnedFalse);
}
}
None => { return Err(Error::ScriptReturnedEmptyStack); }
}
if txo.script_pubkey.is_p2sh() {
match p2sh_script.evaluate(&mut p2sh_stack, Some((txn, index)), None) {
Ok(_) => {}
Err(e) => { return Err(Error::P2shScriptFailure(e)); }
}
match p2sh_stack.pop() {
Some(v) => {
if !read_scriptbool(&v[..]) {
return Err(Error::P2shScriptReturnedFalse);
}
}
None => { return Err(Error::P2shScriptReturnedEmptyStack); }
}
}
} }
} None => { return Err(Error::InputNotFound(self.prev_hash, self.prev_index)); }
None => { return Err(Error::ScriptReturnedEmptyStack); }
} }
if txo.script_pubkey.is_p2sh() { Ok(())
match p2sh_script.evaluate(&mut p2sh_stack, Some((txn, index)), None) {
Ok(_) => {}
Err(e) => { return Err(Error::P2shScriptFailure(e)); }
}
match p2sh_stack.pop() {
Some(v) => {
if !read_scriptbool(&v[..]) {
return Err(Error::P2shScriptReturnedFalse);
}
}
None => { return Err(Error::P2shScriptReturnedEmptyStack); }
}
}
}
None => { return Err(Error::InputNotFound(self.prev_hash, self.prev_index)); }
} }
Ok(())
}
} }
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, 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(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, 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() {
// Setup trace // Setup trace
let mut trace = InputTrace { let mut trace = InputTrace {
input_txid: input.prev_hash, input_txid: input.prev_hash,
input_vout: input.prev_index as usize, input_vout: input.prev_index as usize,
sig_trace: ScriptTrace { sig_trace: ScriptTrace {
script: Script::new(), script: Script::new(),
initial_stack: vec![], initial_stack: vec![],
iterations: vec![], iterations: vec![],
error: None error: None
}, },
pubkey_trace: None, pubkey_trace: None,
p2sh_trace: None, p2sh_trace: None,
error: None error: None
};
// Run through the input
let txo = utxoset.get_utxo(input.prev_hash, input.prev_index);
match txo {
Some((_, txo)) => {
let mut p2sh_stack = Vec::new();
let mut p2sh_script = Script::new();
let mut stack = Vec::with_capacity(6);
trace.sig_trace = input.script_sig.trace(&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)));
if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
p2sh_stack = stack.clone();
p2sh_script = match p2sh_stack.pop() {
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
None => unreachable!()
}; };
} // Run through the input
if trace.error.is_none() { let txo = utxoset.get_utxo(input.prev_hash, input.prev_index);
trace.pubkey_trace = Some(txo.script_pubkey.trace(&mut stack, Some((self, n)))); match txo {
let err = trace.pubkey_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone()); Some((_, txo)) => {
err.map(|e| trace.error = Some(Error::OutputScriptFailure(e))); let mut p2sh_stack = Vec::new();
match stack.pop() { let mut p2sh_script = Script::new();
Some(v) => {
if !read_scriptbool(&v[..]) { let mut stack = Vec::with_capacity(6);
trace.error = Some(Error::ScriptReturnedFalse); trace.sig_trace = input.script_sig.trace(&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)));
if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
p2sh_stack = stack.clone();
p2sh_script = match p2sh_stack.pop() {
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
None => unreachable!()
};
}
if trace.error.is_none() {
trace.pubkey_trace = Some(txo.script_pubkey.trace(&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() {
Some(v) => {
if !read_scriptbool(&v[..]) {
trace.error = Some(Error::ScriptReturnedFalse);
}
}
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))));
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() {
Some(v) => {
if !read_scriptbool(&v[..]) {
trace.error = Some(Error::P2shScriptReturnedFalse);
}
}
None => { trace.error = Some(Error::P2shScriptReturnedEmptyStack); }
}
}
}
} }
} None => {
None => { trace.error = Some(Error::ScriptReturnedEmptyStack); } trace.error = Some(Error::InputNotFound(input.prev_hash, input.prev_index));
}
if trace.error.is_none() && txo.script_pubkey.is_p2sh() {
trace.p2sh_trace = Some(p2sh_script.trace(&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() {
Some(v) => {
if !read_scriptbool(&v[..]) {
trace.error = Some(Error::P2shScriptReturnedFalse);
}
} }
None => { trace.error = Some(Error::P2shScriptReturnedEmptyStack); }
}
} }
} ret.inputs.push(trace);
} }
None => { ret
trace.error = Some(Error::InputNotFound(input.prev_hash, input.prev_index));
}
}
ret.inputs.push(trace);
} }
ret
}
} }
impl BitcoinHash for Transaction { impl BitcoinHash for Transaction {
fn bitcoin_hash(&self) -> Sha256dHash { fn bitcoin_hash(&self) -> Sha256dHash {
use network::serialize::serialize; use network::serialize::serialize;
Sha256dHash::from_data(&serialize(self).unwrap()) Sha256dHash::from_data(&serialize(self).unwrap())
} }
} }
impl_consensus_encoding!(TxIn, prev_hash, prev_index, script_sig, sequence); impl_consensus_encoding!(TxIn, prev_hash, prev_index, script_sig, sequence);
@ -305,40 +306,40 @@ impl_consensus_encoding!(Transaction, version, input, output, lock_time);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Transaction, TxIn}; use super::{Transaction, TxIn};
use std::io; use std::io;
use network::serialize::BitcoinHash; use network::serialize::BitcoinHash;
use network::serialize::deserialize; use network::serialize::deserialize;
use util::misc::hex_bytes; use util::misc::hex_bytes;
#[test] #[test]
fn test_txin() { fn test_txin() {
let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap()); let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
assert!(txin.is_ok()); assert!(txin.is_ok());
} }
#[test] #[test]
fn test_transaction() { fn test_transaction() {
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap(); let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
let tx: io::Result<Transaction> = deserialize(hex_tx); let tx: io::Result<Transaction> = deserialize(hex_tx);
assert!(tx.is_ok()); assert!(tx.is_ok());
let realtx = tx.unwrap(); let realtx = tx.unwrap();
// All these tests aren't really needed because if they fail, the hash check at the end // All these tests aren't really needed because if they fail, the hash check at the end
// will also fail. But these will show you where the failure is so I'll leave them in. // will also fail. But these will show you where the failure is so I'll leave them in.
assert_eq!(realtx.version, 1); assert_eq!(realtx.version, 1);
assert_eq!(realtx.input.len(), 1); assert_eq!(realtx.input.len(), 1);
// In particular this one is easy to get backward -- in bitcoin hashes are encoded // In particular this one is easy to get backward -- in bitcoin hashes are encoded
// as little-endian 256-bit numbers rather than as data strings. // as little-endian 256-bit numbers rather than as data strings.
assert_eq!(realtx.input[0].prev_hash.be_hex_string(), assert_eq!(realtx.input[0].prev_hash.be_hex_string(),
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string()); "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
assert_eq!(realtx.input[0].prev_index, 1); assert_eq!(realtx.input[0].prev_index, 1);
assert_eq!(realtx.output.len(), 1); assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, 0); assert_eq!(realtx.lock_time, 0);
assert_eq!(realtx.bitcoin_hash().be_hex_string(), assert_eq!(realtx.bitcoin_hash().be_hex_string(),
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -21,11 +21,11 @@
use std::cmp; use std::cmp;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash::map::Iter; use std::collections::hash::map::Iter;
use std::hash::SipHasher;
use std::default::Default; use std::default::Default;
use std::mem; use std::mem;
use eventual;
use eventual::Async;
use num_cpus; use num_cpus;
use std::sync::Future;
use blockdata::transaction::{self, Transaction, TxOut}; use blockdata::transaction::{self, Transaction, TxOut};
use blockdata::constants::genesis_block; use blockdata::constants::genesis_block;
@ -37,475 +37,469 @@ use util::hash::Sha256dHash;
/// The amount of validation to do when updating the UTXO set /// The amount of validation to do when updating the UTXO set
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub enum ValidationLevel { pub enum ValidationLevel {
/// Blindly update the UTXO set (NOT recommended) /// Blindly update the UTXO set (NOT recommended)
Nothing, Nothing,
/// Check that the blocks are at least in the right order /// Check that the blocks are at least in the right order
Chain, Chain,
/// Check that any inputs are actually txouts in the set /// Check that any inputs are actually txouts in the set
Inputs, Inputs,
/// Execute the scripts and ensure they pass /// Execute the scripts and ensure they pass
Script Script
} }
/// An error returned from a UTXO set operation /// An error returned from a UTXO set operation
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub enum Error { pub enum Error {
/// prevhash of the new block is not the hash of the old block (expected, actual) /// prevhash of the new block is not the hash of the old block (expected, actual)
BadPrevHash(Sha256dHash, Sha256dHash), BadPrevHash(Sha256dHash, Sha256dHash),
/// A TXID was duplicated /// A TXID was duplicated
DuplicatedTxid(Sha256dHash), DuplicatedTxid(Sha256dHash),
/// A tx was invalid (txid, error) /// A tx was invalid (txid, error)
InvalidTx(Sha256dHash, transaction::Error), InvalidTx(Sha256dHash, transaction::Error),
} }
struct UtxoNode { struct UtxoNode {
/// Blockheight at which this UTXO appeared in the blockchain /// Blockheight at which this UTXO appeared in the blockchain
height: u32, height: u32,
/// Vector of outputs; None indicates a nonexistent or already spent output /// Vector of outputs; None indicates a nonexistent or already spent output
outputs: Box<[Option<TxOut>]> outputs: Box<[Option<TxOut>]>
} }
impl_consensus_encoding!(UtxoNode, height, outputs); impl_consensus_encoding!(UtxoNode, height, outputs);
/// An iterator over UTXOs /// An iterator over UTXOs
pub struct UtxoIterator<'a> { pub struct UtxoIterator<'a> {
tx_iter: Iter<'a, Sha256dHash, UtxoNode>, tx_iter: Iter<'a, Sha256dHash, UtxoNode>,
current_key: Sha256dHash, current_key: Sha256dHash,
current: Option<&'a UtxoNode>, current: Option<&'a UtxoNode>,
tx_index: u32 tx_index: u32
} }
impl<'a> Iterator for UtxoIterator<'a> { impl<'a> Iterator for UtxoIterator<'a> {
type Item = (Sha256dHash, u32, &'a TxOut, u32); type Item = (Sha256dHash, u32, &'a TxOut, u32);
fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> { fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> {
while self.current.is_some() { while let Some(current) = self.current {
let current = &self.current.unwrap().outputs; while self.tx_index < current.outputs.len() as u32 {
while self.tx_index < current.len() as u32 { self.tx_index += 1;
self.tx_index += 1; if let Some(ref cur) = current.outputs[self.tx_index as usize - 1] {
if unsafe { current.get(self.tx_index as usize - 1) }.is_some() { return Some((self.current_key, self.tx_index,
return Some((self.current_key, cur, current.height));
self.tx_index, }
unsafe { current.get(self.tx_index as usize - 1) }.as_ref().unwrap(), }
self.current.unwrap().height)); match self.tx_iter.next() {
Some((&x, y)) => {
self.tx_index = 0;
self.current_key = x;
self.current = Some(y);
}
None => { self.current = None; }
}
} }
} return None;
match self.tx_iter.next() {
Some((&x, y)) => {
self.tx_index = 0;
self.current_key = x;
self.current = Some(y);
}
None => { self.current = None; }
}
} }
return None;
}
} }
/// The UTXO set /// The UTXO set
pub struct UtxoSet { pub struct UtxoSet {
table: HashMap<Sha256dHash, UtxoNode, SipHasher>, table: HashMap<Sha256dHash, UtxoNode>,
last_hash: Sha256dHash, last_hash: Sha256dHash,
// A circular buffer of deleted utxos, grouped by block // A circular buffer of deleted utxos, grouped by block
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>, spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
// The last index into the above buffer that was assigned to // The last index into the above buffer that was assigned to
spent_idx: u64, spent_idx: u64,
n_utxos: u64, n_utxos: u64,
n_pruned: u64 n_pruned: u64
} }
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, n_pruned, spent_txos, spent_idx, table); impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, n_pruned, spent_txos, spent_idx, table);
impl UtxoSet { impl UtxoSet {
/// Constructs a new UTXO set /// Constructs a new UTXO set
pub fn new(network: Network, rewind_limit: usize) -> UtxoSet { pub fn new(network: Network, rewind_limit: usize) -> UtxoSet {
// There is in fact a transaction in the genesis block, but the Bitcoin // There is in fact a transaction in the genesis block, but the Bitcoin
// reference client does not add its sole output to the UTXO set. We // reference client does not add its sole output to the UTXO set. We
// must follow suit, otherwise we will accept a transaction spending it // must follow suit, otherwise we will accept a transaction spending it
// while the reference client won't, causing us to fork off the network. // while the reference client won't, causing us to fork off the network.
UtxoSet { UtxoSet {
table: HashMap::with_hasher(SipHasher::new()), table: HashMap::new(),
last_hash: genesis_block(network).header.bitcoin_hash(), last_hash: genesis_block(network).header.bitcoin_hash(),
spent_txos: Vec::from_elem(rewind_limit, vec![]), spent_txos: vec![vec![]; rewind_limit],
spent_idx: 0, spent_idx: 0,
n_utxos: 0, n_utxos: 0,
n_pruned: 0 n_pruned: 0
}
}
/// Add all the UTXOs of a transaction to the set
fn add_utxos(&mut self, tx: &Transaction, height: u32) -> Option<UtxoNode> {
let txid = tx.bitcoin_hash();
// Locate node if it's already there
let new_node = unsafe {
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;
self.n_pruned += 1;
} else {
new_node.push(Some(txo.clone()));
} }
}
UtxoNode { outputs: new_node.into_boxed_slice(), height: height }
};
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
// and the other comments in this file referring to it)
let ret = self.table.swap(txid, new_node.into_boxed_slice());
if ret.is_none() {
self.n_utxos += tx.output.len() as u64;
} }
ret
}
/// Remove a UTXO from the set and return it /// Add all the UTXOs of a transaction to the set
fn take_utxo(&mut self, txid: Sha256dHash, vout: u32) -> Option<(u32, TxOut)> { fn add_utxos(&mut self, tx: &Transaction, height: u32) -> Option<UtxoNode> {
// This whole function has awkward scoping thx to lexical borrow scoping :( let txid = tx.bitcoin_hash();
let (height, ret, should_delete) = { // Locate node if it's already there
// Locate the UTXO, failing if not found let new_node = unsafe {
let node = match self.table.find_mut(&txid) { let mut new_node = Vec::with_capacity(tx.output.len());
Some(node) => node, for txo in tx.output.iter() {
None => return None // 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;
self.n_pruned += 1;
} else {
new_node.push(Some(txo.clone()));
}
}
UtxoNode { outputs: new_node.into_boxed_slice(), height: height }
};
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
// and the other comments in this file referring to it)
let ret = self.table.insert(txid, new_node);
if ret.is_none() {
self.n_utxos += tx.output.len() as u64;
}
ret
}
let ret = { /// Remove a UTXO from the set and return it
fn take_utxo(&mut self, txid: Sha256dHash, vout: u32) -> Option<(u32, TxOut)> {
// This whole function has awkward scoping thx to lexical borrow scoping :(
let (height, ret, should_delete) = {
// Locate the UTXO, failing if not found
let node = match self.table.get_mut(&txid) {
Some(node) => node,
None => return None
};
let ret = {
// Check that this specific output is there
if vout as usize >= node.outputs.len() { return None; }
let replace = &mut node.outputs[vout as usize];
replace.take()
};
let should_delete = node.outputs.iter().filter(|slot| slot.is_some()).count() == 0;
(node.height, ret, should_delete)
};
// Delete the whole node if it is no longer being used
if should_delete {
self.table.remove(&txid);
}
self.n_utxos -= if ret.is_some() { 1 } else { 0 };
ret.map(|o| (height, o))
}
/// Get a reference to a UTXO in the set
pub fn get_utxo<'a>(&'a self, txid: Sha256dHash, vout: u32) -> Option<(usize, &'a TxOut)> {
// Locate the UTXO, failing if not found
let node = match self.table.get(&txid) {
Some(node) => node,
None => return None
};
// Check that this specific output is there // Check that this specific output is there
if vout as usize >= node.outputs.len() { return None; } if vout as usize >= node.outputs.len() { return None; }
let replace = unsafe { node.outputs.get_mut(vout as usize) }; let replace = node.outputs[vout as usize];
replace.take() Some((node.height as usize, replace.as_ref().unwrap()))
};
let should_delete = node.outputs.iter().filter(|slot| slot.is_some()).count() == 0;
(node.height, ret, should_delete)
};
// Delete the whole node if it is no longer being used
if should_delete {
self.table.remove(&txid);
} }
self.n_utxos -= if ret.is_some() { 1 } else { 0 }; /// Apply the transactions contained in a block
ret.map(|o| (height, o)) pub fn update(&mut self, block: &Block, blockheight: usize, validation: ValidationLevel)
} -> Result<(), Error> {
// Make sure we are extending the UTXO set in order
/// Get a reference to a UTXO in the set if validation >= ValidationLevel::Chain &&
pub fn get_utxo<'a>(&'a self, txid: Sha256dHash, vout: u32) -> Option<(usize, &'a TxOut)> { self.last_hash != block.header.prev_blockhash {
// Locate the UTXO, failing if not found return Err(Error::BadPrevHash(self.last_hash, block.header.prev_blockhash));
let node = match self.table.find(&txid) {
Some(node) => node,
None => return None
};
// Check that this specific output is there
if vout as usize >= node.outputs.len() { return None; }
let replace = unsafe { node.outputs.get(vout as usize) };
Some((node.height as usize, replace.as_ref().unwrap()))
}
/// Apply the transactions contained in a block
pub fn update(&mut self, block: &Block, blockheight: usize, validation: ValidationLevel)
-> Result<(), Error> {
// Make sure we are extending the UTXO set in order
if validation >= ValidationLevel::Chain &&
self.last_hash != block.header.prev_blockhash {
return Err(Error::BadPrevHash(self.last_hash, block.header.prev_blockhash));
}
// Set the next hash immediately so that if anything goes wrong,
// we can rewind from the point that we're at.
self.last_hash = block.header.bitcoin_hash();
let spent_idx = self.spent_idx as usize;
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
self.spent_txos.get_mut(spent_idx).clear();
// Add all the utxos so that we can have chained transactions within the
// same block. (Note that Bitcoin requires chained transactions to be in
// the correct order, which we do not check, so we are minorly too permissive.
// TODO this is a consensus bug.)
for tx in block.txdata.iter() {
let txid = tx.bitcoin_hash();
// Add outputs -- add_utxos returns the original transaction if this is a dupe.
// Note that this can only happen with coinbases, and in this case the block
// is invalid, -except- for two historic blocks which appeared in the
// blockchain before the dupes were noticed.
// See bitcoind commit `ab91bf39` and BIP30.
match self.add_utxos(tx, blockheight as u32) {
Some(mut replace) => {
let blockhash = block.header.bitcoin_hash().be_hex_string();
if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
// For these specific blocks, overwrite the old UTXOs.
// (Actually add_utxos() already did this, so we do nothing.)
} else {
// Otherwise put the replaced txouts into the `deleted` cache
// so that rewind will put them back.
self.spent_txos.get_mut(spent_idx).reserve_additional(replace.outputs.len());
for (n, input) in replace.outputs.iter_mut().enumerate() {
match input.take() {
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(((txid, n as u32), (replace.height, txo))); }
None => {}
}
}
// Otherwise fail the block
self.rewind(block);
return Err(Error::DuplicatedTxid(txid));
}
} }
// Didn't replace anything? Good.
None => {}
}
}
// If we are validating scripts, do all that now in parallel // Set the next hash immediately so that if anything goes wrong,
if validation >= ValidationLevel::Script { // we can rewind from the point that we're at.
let mut future_vec = Vec::with_capacity(block.txdata.len() - 1); self.last_hash = block.header.bitcoin_hash();
// skip the genesis since we don't validate this script. (TODO this might let spent_idx = self.spent_idx as usize;
// be a consensus bug since we don't even check that the opcodes make sense.) self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
let n_threads = cmp::min(block.txdata.len() - 1, num_cpus::get()); (&mut self.spent_txos[spent_idx]).clear();
for j in 0..n_threads {
let n_elems = block.txdata.len() - 1;
let start = 1 + j * n_elems / n_threads;
let end = cmp::min(n_elems, 1 + (j + 1) * n_elems / n_threads);
let s = self as *mut _ as *const UtxoSet; // Add all the utxos so that we can have chained transactions within the
let txes = &block.txdata as *const _; // same block. (Note that Bitcoin requires chained transactions to be in
future_vec.push(Future::spawn(move || { // the correct order, which we do not check, so we are minorly too permissive.
let txes = unsafe {&*txes}; // TODO this is a consensus bug.)
for tx in txes.slice(start, end).iter() { for tx in block.txdata.iter() {
match tx.validate(unsafe {&*s}) { let txid = tx.bitcoin_hash();
Ok(_) => {}, // Add outputs -- add_utxos returns the original transaction if this is a dupe.
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); } // Note that this can only happen with coinbases, and in this case the block
} // is invalid, -except- for two historic blocks which appeared in the
} // blockchain before the dupes were noticed.
Ok(()) // See bitcoind commit `ab91bf39` and BIP30.
})); match self.add_utxos(tx, blockheight as u32) {
} Some(mut replace) => {
// Return the last error since we need to finish every future before let blockhash = block.header.bitcoin_hash().be_hex_string();
// leaving this function, and given that, it's easier to return the last. if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
let mut last_error = Ok(()); blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
for res in future_vec.iter_mut().map(|f| f.get()) { // For these specific blocks, overwrite the old UTXOs.
if res.is_err() { // (Actually add_utxos() already did this, so we do nothing.)
last_error = res; } else {
} // Otherwise put the replaced txouts into the `deleted` cache
} // so that rewind will put them back.
if last_error.is_err() { (&mut self.spent_txos[spent_idx]).reserve(replace.outputs.len());
return last_error; for (n, input) in replace.outputs.iter_mut().enumerate() {
} match input.take() {
} Some(txo) => { (&mut self.spent_txos[spent_idx]).push(((txid, n as u32), (replace.height, txo))); }
None => {}
for tx in block.txdata.iter().skip(1) { }
let txid = tx.bitcoin_hash(); }
// Put the removed utxos into the stxo cache, in case we need to rewind // Otherwise fail the block
self.spent_txos.get_mut(spent_idx).reserve_additional(tx.input.len()); self.rewind(block);
for (n, input) in tx.input.iter().enumerate() { return Err(Error::DuplicatedTxid(txid));
let taken = self.take_utxo(input.prev_hash, input.prev_index);
match taken {
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(((txid, n as u32), txo)); }
None => {
if validation >= ValidationLevel::Inputs {
self.rewind(block);
return Err(Error::InvalidTx(txid,
transaction::Error::InputNotFound(input.prev_hash, input.prev_index)));
}
}
}
}
}
// If we made it here, success!
Ok(())
}
/// Unapply the transactions contained in a block
pub fn rewind(&mut self, block: &Block) -> bool {
// Make sure we are rewinding the latest block
if self.last_hash != block.header.bitcoin_hash() {
return false;
}
// We deliberately do no error checking here, since we may be rewinding
// from halfway through the new block addition, in which case many of
// the utxos we try to remove may be missing; the ones we try to add,
// we stored ourselves when we removed them, so they won't be unaddable
// for any reason.
// Plus we don't care too much about efficiency, not many blocks should
// get rewound.
// Delete added txouts
let mut skipped_genesis = false;
for tx in block.txdata.iter() {
let txhash = tx.bitcoin_hash();
for n in 0..tx.output.len() {
// Just bomb out the whole transaction
// TODO: this does not conform to BIP30: if a duplicate txid occurs,
// the block will be (rightly) rejected, causing it to be
// unwound. But when we get here, we can't see the duplicate,
// so we wind up deleting the old txid! This is very bad, and
// if it occurs, an affected user will have to recreate his
// whole UTXO index to get the original txid back.
self.take_utxo(txhash, n as u32);
}
// Read deleted txouts
if skipped_genesis {
let mut extract_vec = vec![];
mem::swap(&mut extract_vec, self.spent_txos.get_mut(self.spent_idx as usize));
for ((txid, n), (height, txo)) in extract_vec.into_iter() {
// Remove the tx's utxo list and patch the txo into place
let new_node =
match self.table.pop(&txid) {
Some(mut node) => {
node.outputs[n as usize] = Some(txo);
node
}
None => {
unsafe {
let mut thinvec = Vec::with_capacity(n + 1);
for _ in 0..n {
thinvec.push(None);
} }
thinvec.push(Some(txo));
UtxoNode { outputs: thinvec.into_boxed_slice(), height: height }
}
} }
}; // Didn't replace anything? Good.
// Ram it back into the tree None => {}
self.table.insert(txid, new_node); }
} }
}
skipped_genesis = true; // If we are validating scripts, do all that now in parallel
if validation >= ValidationLevel::Script {
let mut future_vec = Vec::with_capacity(block.txdata.len() - 1);
// skip the genesis since we don't validate this script. (TODO this might
// be a consensus bug since we don't even check that the opcodes make sense.)
let n_threads = cmp::min(block.txdata.len() - 1, num_cpus::get());
for j in 0..n_threads {
let n_elems = block.txdata.len() - 1;
let start = 1 + j * n_elems / n_threads;
let end = cmp::min(n_elems, 1 + (j + 1) * n_elems / n_threads);
// WARNING: we are asserting that these variables will outlive the Futures;
// this means that we need to await all Futures before leaving the
// 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) };
future_vec.push(eventual::Future::spawn(move || {
for tx in static_txes[start..end].iter() {
match tx.validate(static_self) {
Ok(_) => {},
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); }
}
}
Ok(())
}));
}
// Return the last error since we need to finish every future before
// leaving this function, and given that, it's easier to return the last.
let mut last_err = Ok(());
for res in future_vec.iter_mut().map(|f| f.await().unwrap()) {
if res.is_err() { last_err = res; }
}
if last_err.is_err() { return last_err; }
}
for tx in block.txdata.iter().skip(1) {
let txid = tx.bitcoin_hash();
// Put the removed utxos into the stxo cache, in case we need to rewind
(&self.spent_txos[spent_idx]).reserve(tx.input.len());
for (n, input) in tx.input.iter().enumerate() {
let taken = self.take_utxo(input.prev_hash, input.prev_index);
match taken {
Some(txo) => { (&mut self.spent_txos[spent_idx]).push(((txid, n as u32), txo)); }
None => {
if validation >= ValidationLevel::Inputs {
self.rewind(block);
return Err(Error::InvalidTx(txid,
transaction::Error::InputNotFound(input.prev_hash, input.prev_index)));
}
}
}
}
}
// If we made it here, success!
Ok(())
} }
// Decrement mod the spent txo cache size /// Unapply the transactions contained in a block
self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) % pub fn rewind(&mut self, block: &Block) -> bool {
self.spent_txos.len() as u64; // Make sure we are rewinding the latest block
self.last_hash = block.header.prev_blockhash; if self.last_hash != block.header.bitcoin_hash() {
return true; return false;
} }
/// Get the hash of the last block added to the utxo set // We deliberately do no error checking here, since we may be rewinding
pub fn last_hash(&self) -> Sha256dHash { // from halfway through the new block addition, in which case many of
self.last_hash // the utxos we try to remove may be missing; the ones we try to add,
} // we stored ourselves when we removed them, so they won't be unaddable
// for any reason.
// Plus we don't care too much about efficiency, not many blocks should
// get rewound.
/// Get the number of UTXOs in the set // Delete added txouts
pub fn n_utxos(&self) -> usize { let mut skipped_genesis = false;
self.n_utxos as usize for tx in block.txdata.iter() {
} let txhash = tx.bitcoin_hash();
for n in 0..tx.output.len() {
// Just bomb out the whole transaction
// TODO: this does not conform to BIP30: if a duplicate txid occurs,
// the block will be (rightly) rejected, causing it to be
// unwound. But when we get here, we can't see the duplicate,
// so we wind up deleting the old txid! This is very bad, and
// if it occurs, an affected user will have to recreate his
// whole UTXO index to get the original txid back.
self.take_utxo(txhash, n as u32);
}
/// Get the number of UTXOs ever pruned from the set (this is not updated // Read deleted txouts
/// during reorgs, so it may return a higher number than is realistic). if skipped_genesis {
pub fn n_pruned(&self) -> usize { let mut extract_vec = vec![];
self.n_pruned 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() {
// Remove the tx's utxo list and patch the txo into place
let new_node = match self.table.remove(&txid) {
Some(mut node) => {
node.outputs[n as usize] = Some(txo);
node
}
None => {
unsafe {
let mut thinvec = Vec::with_capacity(n as usize + 1);
for _ in 0..n {
thinvec.push(None);
}
thinvec.push(Some(txo));
UtxoNode { outputs: thinvec.into_boxed_slice(), height: height }
}
}
};
// Ram it back into the tree
self.table.insert(txid, new_node);
}
}
skipped_genesis = true;
}
/// Get an iterator over all UTXOs // Decrement mod the spent txo cache size
pub fn iter<'a>(&'a self) -> UtxoIterator<'a> { self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) %
let mut iter = self.table.iter(); self.spent_txos.len() as u64;
let first = iter.next(); self.last_hash = block.header.prev_blockhash;
match first { return true;
Some((&key, val)) => UtxoIterator { }
current_key: key,
current: Some(val), /// Get the hash of the last block added to the utxo set
tx_iter: iter, pub fn last_hash(&self) -> Sha256dHash {
tx_index: 0 self.last_hash
}, }
None => UtxoIterator {
current_key: Default::default(), /// Get the number of UTXOs in the set
current: None, pub fn n_utxos(&self) -> usize {
tx_iter: iter, self.n_utxos as usize
tx_index: 0 }
/// Get the number of UTXOs ever pruned from the set (this is not updated
/// during reorgs, so it may return a higher number than is realistic).
pub fn n_pruned(&self) -> usize {
self.n_pruned as usize
}
/// Get an iterator over all UTXOs
pub fn iter<'a>(&'a self) -> UtxoIterator<'a> {
let mut iter = self.table.iter();
let first = iter.next();
match first {
Some((&key, val)) => UtxoIterator {
current_key: key,
current: Some(val),
tx_iter: iter,
tx_index: 0
},
None => UtxoIterator {
current_key: Default::default(),
current: None,
tx_iter: iter,
tx_index: 0
}
} }
} }
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::prelude::*; use std::prelude::*;
use std::io; use std::io;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use super::{UtxoSet, ValidationLevel}; use super::{UtxoSet, ValidationLevel};
use blockdata::block::Block; use blockdata::block::Block;
use network::constants::Network::Bitcoin; use network::constants::Network::Bitcoin;
use network::serialize::{BitcoinHash, deserialize, serialize}; use network::serialize::{BitcoinHash, deserialize, serialize};
#[test] #[test]
fn utxoset_serialize_test() { fn utxoset_serialize_test() {
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(&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() {
empty_set.add_utxos(tx, 1); empty_set.add_utxos(tx, 1);
}
empty_set.last_hash = new_block.header.bitcoin_hash();
// Check that all the UTXOs were added
assert_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() {
let n = n as u32;
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
}
}
// 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_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() {
let n = n as u32;
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
}
}
// Serialize/deserialize the resulting UTXO set
let serial = serialize(&empty_set).unwrap();
let deserial: io::Result<UtxoSet> = deserialize(serial.clone());
assert!(deserial.is_ok());
// Check that all outputs are there
let mut read_set = deserial.unwrap();
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() {
let n = n as u32;
// Try taking non-existent UTXO
assert_eq!(read_set.take_utxo(hash, 100 + n), None);
// Check take of real UTXO
let ret = read_set.take_utxo(hash, n);
assert_eq!(ret, Some((1, out.clone())));
// Try double-take
assert_eq!(read_set.take_utxo(hash, n), None);
}
}
let deserial_again: io::Result<UtxoSet> = deserialize(serial);
let mut read_again = deserial_again.unwrap();
assert!(read_again.rewind(&new_block));
assert_eq!(read_again.n_utxos(), 0);
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for n in 0..tx.output.len() {
let n = n as u32;
let ret = read_again.take_utxo(hash, n);
assert_eq!(ret, None);
}
}
} }
empty_set.last_hash = new_block.header.bitcoin_hash();
// Check that all the UTXOs were added
assert_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() {
let n = n as u32;
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
}
}
// 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_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() {
let n = n as u32;
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
}
}
// Serialize/deserialize the resulting UTXO set
let serial = serialize(&empty_set).unwrap();
let deserial: io::Result<UtxoSet> = deserialize(serial.clone());
assert!(deserial.is_ok());
// Check that all outputs are there
let mut read_set = deserial.unwrap();
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() {
let n = n as u32;
// Try taking non-existent UTXO
assert_eq!(read_set.take_utxo(hash, 100 + n), None);
// Check take of real UTXO
let ret = read_set.take_utxo(hash, n);
assert_eq!(ret, Some((1, out.clone())));
// Try double-take
assert_eq!(read_set.take_utxo(hash, n), None);
}
}
let deserial_again: io::Result<UtxoSet> = deserialize(serial);
let mut read_again = deserial_again.unwrap();
assert!(read_again.rewind(&new_block));
assert_eq!(read_again.n_utxos(), 0);
for tx in new_block.txdata.iter() {
let hash = tx.bitcoin_hash();
for n in 0..tx.output.len() {
let n = n as u32;
let ret = read_again.take_utxo(hash, n);
assert_eq!(ret, None);
}
}
}
} }

View File

@ -99,45 +99,7 @@ macro_rules! impl_array_newtype {
} }
} }
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing { impl_index_newtype!($thing, $ty);
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
let &$thing(ref dat) = self;
&dat[index]
}
}
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
let &$thing(ref dat) = self;
&dat[index]
}
}
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
let &$thing(ref dat) = self;
&dat[index]
}
}
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
let &$thing(ref dat) = self;
&dat[..]
}
}
impl PartialEq for $thing { impl PartialEq for $thing {
#[inline] #[inline]
@ -234,6 +196,47 @@ macro_rules! impl_array_newtype_show {
} }
} }
macro_rules! impl_index_newtype {
($thing:ident, $ty:ty) => {
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
&self.0[index]
}
}
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
&self.0[index]
}
}
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
&self.0[index]
}
}
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
type Output = [$ty];
#[inline]
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
&self.0[..]
}
}
}
}
macro_rules! display_from_debug { macro_rules! display_from_debug {
($thing:ident) => { ($thing:ident) => {
impl ::std::fmt::Display for $thing { impl ::std::fmt::Display for $thing {

View File

@ -47,6 +47,7 @@
extern crate alloc; extern crate alloc;
extern crate byteorder; extern crate byteorder;
extern crate collections; extern crate collections;
extern crate eventual;
extern crate num_cpus; extern crate num_cpus;
extern crate rand; extern crate rand;
extern crate rustc_serialize as serialize; extern crate rustc_serialize as serialize;

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -18,128 +18,127 @@
#[macro_export] #[macro_export]
macro_rules! nu_select { macro_rules! nu_select {
($($name:pat = $rx:expr => $code:expr),+) => ({ ($($name:pat = $rx:expr => $code:expr),+) => ({
nu_select!{ $($name = $rx, recv => $code),+ } nu_select!{ $($name = $rx, recv => $code),+ }
}); });
($($name:pat = $rx:expr, $meth:ident => $code:expr),+) => ({ ($($name:pat = $rx:expr, $meth:ident => $code:expr),+) => ({
use rustrt::local::Local; use rustrt::local::Local;
use rustrt::task::Task; use rustrt::task::Task;
use sync::comm::Packet; use sync::comm::Packet;
// Is anything already ready to receive? Grab it without waiting. // Is anything already ready to receive? Grab it without waiting.
$( $(
if (&$rx as &Packet).can_recv() { if (&$rx as &Packet).can_recv() {
let $name = $rx.$meth(); let $name = $rx.$meth();
$code $code
}
)else+
else {
// Start selecting on as many as we need to before getting a bite.
// Keep count of how many, since we need to abort every selection
// that we started.
let mut started_count = 0;
// Restrict lifetime of borrows in `packets`
{
let packets = [ $( &$rx as &Packet, )+ ];
let task: Box<Task> = Local::take();
task.deschedule(packets.len(), |task| {
match packets[started_count].start_selection(task) {
Ok(()) => {
started_count += 1;
Ok(())
} }
Err(task) => Err(task) )else+
}
});
}
let mut i = 0;
let ret = $(
// Abort the receivers, stopping at the first ready one to get its data.
if { i += 1; i <= started_count } &&
// If start_selection() failed, abort_selection() will fail too,
// but it still counts as "data available".
($rx.abort_selection() || i == started_count) {
// React to the first
let $name = $rx.$meth();
$code
})else+
else { else {
fail!("we didn't find the ready receiver, but we should have had one"); // Start selecting on as many as we need to before getting a bite.
}; // Keep count of how many, since we need to abort every selection
// At this point, the first i receivers have been aborted. We need to abort the rest: // that we started.
$(if i > 0 { let mut started_count = 0;
i -= 1; // Restrict lifetime of borrows in `packets`
} else { {
$rx.abort_selection(); let packets = [ $( &$rx as &Packet, )+ ];
})+
let _ = i; // Shut up `i -= 1 but i is never read` warning let task: Box<Task> = Local::take();
// Return task.deschedule(packets.len(), |task| {
ret match packets[started_count].start_selection(task) {
} Ok(()) => {
}) started_count += 1;
Ok(())
}
Err(task) => Err(task)
}
});
}
let mut i = 0;
let ret = $(
// Abort the receivers, stopping at the first ready one to get its data.
if { i += 1; i <= started_count } &&
// If start_selection() failed, abort_selection() will fail too,
// but it still counts as "data available".
($rx.abort_selection() || i == started_count) {
// React to the first
let $name = $rx.$meth();
$code
})else+
else {
fail!("we didn't find the ready receiver, but we should have had one");
};
// At this point, the first i receivers have been aborted. We need to abort the rest:
$(if i > 0 {
i -= 1;
} else {
$rx.abort_selection();
})+
let _ = i; // Shut up `i -= 1 but i is never read` warning
// Return
ret
}
})
} }
#[macro_export] #[macro_export]
macro_rules! user_enum { macro_rules! user_enum {
($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => ( ($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => (
$(#[$attr])* $(#[$attr])*
pub enum $name { pub enum $name {
$(#[$doc] $elem),* $(#[$doc] $elem),*
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.pad(match *self {
$($elem => $txt),*
})
}
}
impl ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.pad(match *self {
$($elem => $txt),*
})
}
}
impl ::serde::Deserialize for $name {
#[inline]
fn deserialize<D>(d: &mut D) -> Result<$name, D::Error>
where D: ::serde::Deserializer
{
struct Visitor;
impl ::serde::de::Visitor for Visitor {
type Value = $name;
fn visit_string<E>(&mut self, v: String) -> Result<$name, E>
where E: ::serde::de::Error
{
self.visit_str(&v)
}
fn visit_str<E>(&mut self, s: &str) -> Result<$name, E>
where E: ::serde::de::Error
{
$( if s == $txt { Ok($name::$elem) } )else*
else { Err(::serde::de::Error::syntax_error()) }
}
} }
d.visit(Visitor) impl ::std::fmt::Debug for $name {
} fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
} f.pad(match *self {
$($elem => $txt),*
})
}
}
impl ::serde::Serialize for $name { impl ::std::fmt::Display for $name {
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error> fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
where S: ::serde::Serializer f.pad(match *self {
{ $($elem => $txt),*
s.visit_str(&self.to_string()) })
} }
} }
);
impl ::serde::Deserialize for $name {
#[inline]
fn deserialize<D>(d: &mut D) -> Result<$name, D::Error>
where D: ::serde::Deserializer
{
struct Visitor;
impl ::serde::de::Visitor for Visitor {
type Value = $name;
fn visit_string<E>(&mut self, v: String) -> Result<$name, E>
where E: ::serde::de::Error
{
self.visit_str(&v)
}
fn visit_str<E>(&mut self, s: &str) -> Result<$name, E>
where E: ::serde::de::Error
{
$( if s == $txt { Ok($name::$elem) } )else*
else { Err(::serde::de::Error::syntax_error()) }
}
}
d.visit(Visitor)
}
}
impl ::serde::Serialize for $name {
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
where S: ::serde::Serializer
{
s.visit_str(&self.to_string())
}
}
);
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -25,107 +25,107 @@ use network::encodable::{ConsensusDecodable, ConsensusEncodable};
/// A message which can be sent on the Bitcoin network /// A message which can be sent on the Bitcoin network
pub struct Address { pub struct Address {
/// Services provided by the peer whose address this is /// Services provided by the peer whose address this is
pub services: u64, pub services: u64,
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address /// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
pub address: [u16; 8], pub address: [u16; 8],
/// Network port /// Network port
pub port: u16 pub port: u16
} }
impl<S: SimpleEncoder> ConsensusEncodable<S> for Address { impl<S: SimpleEncoder> ConsensusEncodable<S> for Address {
#[inline] #[inline]
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
try!(self.services.consensus_encode(s)); try!(self.services.consensus_encode(s));
try!(self.address.consensus_encode(s)); try!(self.address.consensus_encode(s));
// Explicitly code the port since it needs to be big-endian // Explicitly code the port since it needs to be big-endian
try!(((self.port / 0x100) as u8).consensus_encode(s)); try!(((self.port / 0x100) as u8).consensus_encode(s));
try!(((self.port % 0x100) as u8).consensus_encode(s)); try!(((self.port % 0x100) as u8).consensus_encode(s));
Ok(()) Ok(())
} }
} }
impl<D: SimpleDecoder> ConsensusDecodable<D> for Address { impl<D: SimpleDecoder> ConsensusDecodable<D> for Address {
#[inline] #[inline]
fn consensus_decode(d: &mut D) -> Result<Address, D::Error> { fn consensus_decode(d: &mut D) -> Result<Address, D::Error> {
Ok(Address { Ok(Address {
services: try!(ConsensusDecodable::consensus_decode(d)), services: try!(ConsensusDecodable::consensus_decode(d)),
address: try!(ConsensusDecodable::consensus_decode(d)), address: try!(ConsensusDecodable::consensus_decode(d)),
// Explicitly code the port since it needs to be big-endian // Explicitly code the port since it needs to be big-endian
port: { port: {
let b1: u8 = try!(ConsensusDecodable::consensus_decode(d)); let b1: u8 = try!(ConsensusDecodable::consensus_decode(d));
let b2: u8 = try!(ConsensusDecodable::consensus_decode(d)); let b2: u8 = try!(ConsensusDecodable::consensus_decode(d));
(b1 as u16 * 0x100) + (b2 as u16) (b1 as u16 * 0x100) + (b2 as u16)
} }
}) })
} }
} }
impl fmt::Debug for Address { impl fmt::Debug for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO: render services and hex-ize address // TODO: render services and hex-ize address
write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}", write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}",
self.services, &self.address[..], self.port) self.services, &self.address[..], self.port)
} }
} }
impl Clone for Address { impl Clone for Address {
fn clone(&self) -> Address { fn clone(&self) -> Address {
unsafe { unsafe {
use std::intrinsics::copy_nonoverlapping; use std::intrinsics::copy_nonoverlapping;
use std::mem; use std::mem;
let mut ret = mem::uninitialized(); let mut ret = mem::uninitialized();
copy_nonoverlapping(self, copy_nonoverlapping(self,
&mut ret, &mut ret,
mem::size_of::<Address>()); mem::size_of::<Address>());
ret ret
}
} }
}
} }
impl PartialEq for Address { impl PartialEq for Address {
fn eq(&self, other: &Address) -> bool { fn eq(&self, other: &Address) -> bool {
self.services == other.services && self.services == other.services &&
&self.address[..] == &other.address[..] && &self.address[..] == &other.address[..] &&
self.port == other.port self.port == other.port
} }
} }
impl Eq for Address {} impl Eq for Address {}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::Address; use super::Address;
use std::io; use std::io;
use network::serialize::{deserialize, serialize}; use network::serialize::{deserialize, serialize};
#[test] #[test]
fn serialize_address_test() { fn serialize_address_test() {
assert_eq!(serialize(&Address { assert_eq!(serialize(&Address {
services: 1, services: 1,
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1], address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
port: 8333 port: 8333
}), }),
Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1, 0x20, 0x8d])); 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1, 0x20, 0x8d]));
} }
#[test] #[test]
fn deserialize_address_test() { fn deserialize_address_test() {
let mut addr: io::Result<Address> = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, let mut addr: io::Result<Address> = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
0, 1, 0x20, 0x8d]); 0, 1, 0x20, 0x8d]);
assert!(addr.is_ok()); assert!(addr.is_ok());
let full = addr.unwrap(); let full = addr.unwrap();
assert!(full.services == 1); assert!(full.services == 1);
assert!(full.address == [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]); assert!(full.address == [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
assert!(full.port == 8333); assert!(full.port == 8333);
addr = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, addr = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]); 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
assert!(addr.is_err()); assert!(addr.is_err());
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -26,12 +26,12 @@ use util::hash::Sha256dHash;
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
/// The type of an inventory object /// The type of an inventory object
pub enum InvType { pub enum InvType {
/// Error --- these inventories can be ignored /// Error --- these inventories can be ignored
Error, Error,
/// Transaction /// Transaction
Transaction, Transaction,
/// Block /// Block
Block Block
} }
// Some simple messages // Some simple messages
@ -39,133 +39,133 @@ pub enum InvType {
/// The `getblocks` message /// The `getblocks` message
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct GetBlocksMessage { pub struct GetBlocksMessage {
/// The protocol version /// The protocol version
pub version: u32, pub version: u32,
/// Locator hashes --- ordered newest to oldest. The remote peer will /// Locator hashes --- ordered newest to oldest. The remote peer will
/// reply with its longest known chain, starting from a locator hash /// reply with its longest known chain, starting from a locator hash
/// if possible and block 1 otherwise. /// if possible and block 1 otherwise.
pub locator_hashes: Vec<Sha256dHash>, pub locator_hashes: Vec<Sha256dHash>,
/// References the block to stop at, or zero to just fetch the maximum 500 blocks /// References the block to stop at, or zero to just fetch the maximum 500 blocks
pub stop_hash: Sha256dHash pub stop_hash: Sha256dHash
} }
/// The `getheaders` message /// The `getheaders` message
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct GetHeadersMessage { pub struct GetHeadersMessage {
/// The protocol version /// The protocol version
pub version: u32, pub version: u32,
/// Locator hashes --- ordered newest to oldest. The remote peer will /// Locator hashes --- ordered newest to oldest. The remote peer will
/// reply with its longest known chain, starting from a locator hash /// reply with its longest known chain, starting from a locator hash
/// if possible and block 1 otherwise. /// if possible and block 1 otherwise.
pub locator_hashes: Vec<Sha256dHash>, pub locator_hashes: Vec<Sha256dHash>,
/// References the header to stop at, or zero to just fetch the maximum 2000 headers /// References the header to stop at, or zero to just fetch the maximum 2000 headers
pub stop_hash: Sha256dHash pub stop_hash: Sha256dHash
} }
/// An inventory object --- a reference to a Bitcoin object /// An inventory object --- a reference to a Bitcoin object
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct Inventory { pub struct Inventory {
/// The type of object that is referenced /// The type of object that is referenced
pub inv_type: InvType, pub inv_type: InvType,
/// The object's hash /// The object's hash
pub hash: Sha256dHash pub hash: Sha256dHash
} }
impl GetBlocksMessage { impl GetBlocksMessage {
/// Construct a new `getblocks` message /// Construct a new `getblocks` message
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage { pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
GetBlocksMessage { GetBlocksMessage {
version: constants::PROTOCOL_VERSION, version: constants::PROTOCOL_VERSION,
locator_hashes: locator_hashes.clone(), locator_hashes: locator_hashes.clone(),
stop_hash: stop_hash stop_hash: stop_hash
}
} }
}
} }
impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash); impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
impl GetHeadersMessage { impl GetHeadersMessage {
/// Construct a new `getheaders` message /// Construct a new `getheaders` message
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage { pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
GetHeadersMessage { GetHeadersMessage {
version: constants::PROTOCOL_VERSION, version: constants::PROTOCOL_VERSION,
locator_hashes: locator_hashes, locator_hashes: locator_hashes,
stop_hash: stop_hash stop_hash: stop_hash
}
} }
}
} }
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash); impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory { impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory {
#[inline] #[inline]
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
try!(match self.inv_type { try!(match self.inv_type {
InvType::Error => 0u32, InvType::Error => 0u32,
InvType::Transaction => 1, InvType::Transaction => 1,
InvType::Block => 2 InvType::Block => 2
}.consensus_encode(s)); }.consensus_encode(s));
self.hash.consensus_encode(s) self.hash.consensus_encode(s)
} }
} }
impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory { impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
#[inline] #[inline]
fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> { fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d)); let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
Ok(Inventory { Ok(Inventory {
inv_type: match int_type { inv_type: match int_type {
0 => InvType::Error, 0 => InvType::Error,
1 => InvType::Transaction, 1 => InvType::Transaction,
2 => InvType::Block, 2 => InvType::Block,
// TODO do not fail here // TODO do not fail here
_ => { panic!("bad inventory type field") } _ => { panic!("bad inventory type field") }
}, },
hash: try!(ConsensusDecodable::consensus_decode(d)) hash: try!(ConsensusDecodable::consensus_decode(d))
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{GetHeadersMessage, GetBlocksMessage}; use super::{GetHeadersMessage, GetBlocksMessage};
use std::io; use std::io;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use network::serialize::{deserialize, serialize}; use network::serialize::{deserialize, serialize};
use std::default::Default; use std::default::Default;
#[test] #[test]
fn getblocks_message_test() { fn getblocks_message_test() {
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap(); let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone()); let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone());
assert!(decode.is_ok()); assert!(decode.is_ok());
let real_decode = decode.unwrap(); let real_decode = decode.unwrap();
assert_eq!(real_decode.version, 70002); assert_eq!(real_decode.version, 70002);
assert_eq!(real_decode.locator_hashes.len(), 1); assert_eq!(real_decode.locator_hashes.len(), 1);
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash)); assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
assert_eq!(real_decode.stop_hash, Default::default()); assert_eq!(real_decode.stop_hash, Default::default());
assert_eq!(serialize(&real_decode), Ok(from_sat)); assert_eq!(serialize(&real_decode), Ok(from_sat));
} }
#[test] #[test]
fn getheaders_message_test() { fn getheaders_message_test() {
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap(); let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone()); let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone());
assert!(decode.is_ok()); assert!(decode.is_ok());
let real_decode = decode.unwrap(); let real_decode = decode.unwrap();
assert_eq!(real_decode.version, 70002); assert_eq!(real_decode.version, 70002);
assert_eq!(real_decode.locator_hashes.len(), 1); assert_eq!(real_decode.locator_hashes.len(), 1);
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash)); assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
assert_eq!(real_decode.stop_hash, Default::default()); assert_eq!(real_decode.stop_hash, Default::default());
assert_eq!(serialize(&real_decode), Ok(from_sat)); assert_eq!(serialize(&real_decode), Ok(from_sat));
} }
} }

View File

@ -34,24 +34,24 @@ use util::{self, propagate_err};
/// Format an IP address in the 16-byte bitcoin protocol serialization /// Format an IP address in the 16-byte bitcoin protocol serialization
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] { fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
match *ipaddr { match *ipaddr {
ip::IpAddr::V4(ref addr) => &addr.to_ipv6_mapped(), ip::IpAddr::V4(ref addr) => &addr.to_ipv6_mapped(),
ip::IpAddr::V6(ref addr) => addr ip::IpAddr::V6(ref addr) => addr
}.segments() }.segments()
} }
/// A network socket along with information about the peer /// A network socket along with information about the peer
#[derive(Clone)] #[derive(Clone)]
pub struct Socket { pub struct Socket {
/// The underlying TCP socket /// The underlying TCP socket
socket: Arc<Mutex<Option<tcp::TcpStream>>>, socket: Arc<Mutex<Option<tcp::TcpStream>>>,
/// Services supported by us /// Services supported by us
pub services: u64, pub services: u64,
/// Our user agent /// Our user agent
pub user_agent: String, pub user_agent: String,
/// Nonce to identify our `version` messages /// Nonce to identify our `version` messages
pub version_nonce: u64, pub version_nonce: u64,
/// Network magic /// Network magic
pub magic: u32 pub magic: u32
} }

View File

@ -23,204 +23,204 @@ use util::hash::Sha256dHash;
/// An error that might occur during base58 decoding /// An error that might occur during base58 decoding
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error { pub enum Error {
/// Invalid character encountered /// Invalid character encountered
BadByte(u8), BadByte(u8),
/// Checksum was not correct (expected, actual) /// Checksum was not correct (expected, actual)
BadChecksum(u32, u32), BadChecksum(u32, u32),
/// The length (in bytes) of the object was not correct /// The length (in bytes) of the object was not correct
InvalidLength(usize), InvalidLength(usize),
/// Version byte(s) were not recognized /// Version byte(s) were not recognized
InvalidVersion(Vec<u8>), InvalidVersion(Vec<u8>),
/// Checked data was less than 4 bytes /// Checked data was less than 4 bytes
TooShort(usize), TooShort(usize),
/// Any other error /// Any other error
Other(String) Other(String)
} }
static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static BASE58_DIGITS: [Option<u8>; 128] = [ static BASE58_DIGITS: [Option<u8>; 128] = [
None, None, None, None, None, None, None, None, // 0-7 None, None, None, None, None, None, None, None, // 0-7
None, None, None, None, None, None, None, None, // 8-15 None, None, None, None, None, None, None, None, // 8-15
None, None, None, None, None, None, None, None, // 16-23 None, None, None, None, None, None, None, None, // 16-23
None, None, None, None, None, None, None, None, // 24-31 None, None, None, None, None, None, None, None, // 24-31
None, None, None, None, None, None, None, None, // 32-39 None, None, None, None, None, None, None, None, // 32-39
None, None, None, None, None, None, None, None, // 40-47 None, None, None, None, None, None, None, None, // 40-47
None, Some(0), Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), // 48-55 None, Some(0), Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), // 48-55
Some(7), Some(8), None, None, None, None, None, None, // 56-63 Some(7), Some(8), None, None, None, None, None, None, // 56-63
None, Some(9), Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), // 64-71 None, Some(9), Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), // 64-71
Some(16), None, Some(17), Some(18), Some(19), Some(20), Some(21), None, // 72-79 Some(16), None, Some(17), Some(18), Some(19), Some(20), Some(21), None, // 72-79
Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), // 80-87 Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), // 80-87
Some(30), Some(31), Some(32), None, None, None, None, None, // 88-95 Some(30), Some(31), Some(32), None, None, None, None, None, // 88-95
None, Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), // 96-103 None, Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), // 96-103
Some(40), Some(41), Some(42), Some(43), None, Some(44), Some(45), Some(46), // 104-111 Some(40), Some(41), Some(42), Some(43), None, Some(44), Some(45), Some(46), // 104-111
Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), // 112-119 Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), // 112-119
Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127 Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127
]; ];
/// Trait for objects which can be read as base58 /// Trait for objects which can be read as base58
pub trait FromBase58 { pub trait FromBase58: Sized {
/// Constructs an object flrom the byte-encoding (base 256) /// Constructs an object flrom the byte-encoding (base 256)
/// representation of its base58 format /// representation of its base58 format
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>; fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
/// Obtain an object from its base58 encoding /// Obtain an object from its base58 encoding
fn from_base58(data: &str) -> Result<Self, Error> { fn from_base58(data: &str) -> Result<Self, Error> {
// 11/15 is just over log_256(58) // 11/15 is just over log_256(58)
let mut scratch = Vec::from_elem(1 + data.len() * 11 / 15, 0u8); let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
// Build in base 256 // Build in base 256
for d58 in data.bytes() { for d58 in data.bytes() {
// Compute "X = X * 58 + next_digit" in base 256 // Compute "X = X * 58 + next_digit" in base 256
if d58 as usize > BASE58_DIGITS.len() { if d58 as usize > BASE58_DIGITS.len() {
return Err(Error::BadByte(d58)); return Err(Error::BadByte(d58));
} }
let mut carry = match BASE58_DIGITS[d58 as usize] { let mut carry = match BASE58_DIGITS[d58 as usize] {
Some(d58) => d58 as u32, Some(d58) => d58 as u32,
None => { return Err(Error::BadByte(d58)); } None => { return Err(Error::BadByte(d58)); }
}; };
for d256 in scratch.iter_mut().rev() { for d256 in scratch.iter_mut().rev() {
carry += *d256 as u32 * 58; carry += *d256 as u32 * 58;
*d256 = carry as u8; *d256 = carry as u8;
carry /= 256; carry /= 256;
} }
assert_eq!(carry, 0); assert_eq!(carry, 0);
}
// Copy leading zeroes directly
let mut ret: Vec<u8> = data.bytes().take_while(|&x| x == BASE58_CHARS[0])
.map(|_| 0)
.collect();
// Copy rest of string
ret.extend(scratch.into_iter().skip_while(|&x| x == 0));
FromBase58::from_base58_layout(ret)
} }
// Copy leading zeroes directly /// Obtain an object from its base58check encoding
let mut ret: Vec<u8> = data.bytes().take_while(|&x| x == BASE58_CHARS[0]) fn from_base58check(data: &str) -> Result<Self, Error> {
.map(|_| 0) let mut ret: Vec<u8> = try!(FromBase58::from_base58(data));
.collect(); if ret.len() < 4 {
// Copy rest of string return Err(Error::TooShort(ret.len()));
ret.extend(scratch.into_iter().skip_while(|&x| x == 0)); }
FromBase58::from_base58_layout(ret) let ck_start = ret.len() - 4;
} let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32();
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
if expected != actual {
return Err(Error::BadChecksum(expected, actual));
}
/// Obtain an object from its base58check encoding ret.truncate(ck_start);
fn from_base58check(data: &str) -> Result<Self, Error> { FromBase58::from_base58_layout(ret)
let mut ret: Vec<u8> = try!(FromBase58::from_base58(data));
if ret.len() < 4 {
return Err(Error::TooShort(ret.len()));
} }
let ck_start = ret.len() - 4;
let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32();
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
if expected != actual {
return Err(Error::BadChecksum(expected, actual));
}
ret.truncate(ck_start);
FromBase58::from_base58_layout(ret)
}
} }
/// Directly encode a slice as base58 /// Directly encode a slice as base58
pub fn base58_encode_slice(data: &[u8]) -> String { pub fn base58_encode_slice(data: &[u8]) -> String {
// 7/5 is just over log_58(256) // 7/5 is just over log_58(256)
let mut scratch = vec![0u8; 1 + data.len() * 7 / 5]; let mut scratch = vec![0u8; 1 + data.len() * 7 / 5];
// Build in base 58 // Build in base 58
for &d256 in data.base58_layout().iter() { for &d256 in data.base58_layout().iter() {
// Compute "X = X * 256 + next_digit" in base 58 // Compute "X = X * 256 + next_digit" in base 58
let mut carry = d256 as u32; let mut carry = d256 as u32;
for d58 in scratch.iter_mut().rev() { for d58 in scratch.iter_mut().rev() {
carry += (*d58 as u32) << 8; carry += (*d58 as u32) << 8;
*d58 = (carry % 58) as u8; *d58 = (carry % 58) as u8;
carry /= 58; carry /= 58;
}
assert_eq!(carry, 0);
} }
assert_eq!(carry, 0);
}
// Unsafely translate the bytes to a utf8 string // Unsafely translate the bytes to a utf8 string
unsafe { unsafe {
// Copy leading zeroes directly // Copy leading zeroes directly
let mut ret: Vec<u8> = str::from_utf8(data.iter().take_while(|&&x| x == 0) let mut ret: Vec<u8> = data.iter().take_while(|&&x| x == 0)
.map(|_| BASE58_CHARS[0]) .map(|_| BASE58_CHARS[0])
.collect()).unwrap(); .collect();
// Copy rest of string // Copy rest of string
ret.as_mut_vec().extend(scratch.into_iter().skip_while(|&x| x == 0) ret.extend(scratch.into_iter().skip_while(|&x| x == 0)
.map(|x| BASE58_CHARS[x as usize])); .map(|x| BASE58_CHARS[x as usize]));
ret String::from_utf8(ret).unwrap()
} }
} }
/// Trait for objects which can be written as base58 /// Trait for objects which can be written as base58
pub trait ToBase58 { pub trait ToBase58 {
/// The serialization to be converted into base58 /// The serialization to be converted into base58
fn base58_layout(&self) -> Vec<u8>; fn base58_layout(&self) -> Vec<u8>;
/// Obtain a string with the base58 encoding of the object /// Obtain a string with the base58 encoding of the object
fn to_base58(&self) -> String { fn to_base58(&self) -> String {
base58_encode_slice(&self.base58_layout()[..]) base58_encode_slice(&self.base58_layout()[..])
} }
/// Obtain a string with the base58check encoding of the object /// Obtain a string with the base58check encoding of the object
/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.) /// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.)
fn to_base58check(&self) -> String { fn to_base58check(&self) -> String {
let mut data = self.base58_layout(); let mut data = self.base58_layout();
let checksum = Sha256dHash::from_data(&data).into_le().low_u32(); let checksum = Sha256dHash::from_data(&data).into_le().low_u32();
data.write_u32::<LittleEndian>(checksum); data.write_u32::<LittleEndian>(checksum);
base58_encode_slice(&data) base58_encode_slice(&data)
} }
} }
// Trivial implementations for slices and vectors // Trivial implementations for slices and vectors
impl<'a> ToBase58 for &'a [u8] { impl<'a> ToBase58 for &'a [u8] {
fn base58_layout(&self) -> Vec<u8> { self.to_vec() } fn base58_layout(&self) -> Vec<u8> { self.to_vec() }
fn to_base58(&self) -> String { base58_encode_slice(*self) } fn to_base58(&self) -> String { base58_encode_slice(*self) }
} }
impl FromBase58 for Vec<u8> { impl FromBase58 for Vec<u8> {
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> { fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
Ok(data) Ok(data)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serialize::hex::FromHex; use serialize::hex::FromHex;
use super::ToBase58; use super::ToBase58;
use super::FromBase58; use super::FromBase58;
#[test] #[test]
fn test_base58_encode() { fn test_base58_encode() {
// Basics // Basics
assert_eq!([0].as_slice().to_base58().as_slice(), "1"); assert_eq!([0].as_slice().to_base58().as_slice(), "1");
assert_eq!([1].as_slice().to_base58().as_slice(), "2"); assert_eq!([1].as_slice().to_base58().as_slice(), "2");
assert_eq!([58].as_slice().to_base58().as_slice(), "21"); assert_eq!([58].as_slice().to_base58().as_slice(), "21");
assert_eq!([13, 36].as_slice().to_base58().as_slice(), "211"); assert_eq!([13, 36].as_slice().to_base58().as_slice(), "211");
// Leading zeroes // Leading zeroes
assert_eq!([0, 13, 36].as_slice().to_base58().as_slice(), "1211"); assert_eq!([0, 13, 36].as_slice().to_base58().as_slice(), "1211");
assert_eq!([0, 0, 0, 0, 13, 36].as_slice().to_base58().as_slice(), "1111211"); assert_eq!([0, 0, 0, 0, 13, 36].as_slice().to_base58().as_slice(), "1111211");
// Addresses // Addresses
assert_eq!("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check().as_slice(), assert_eq!("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check().as_slice(),
"1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"); "1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH");
} }
#[test] #[test]
fn test_base58_decode() { fn test_base58_decode() {
// Basics // Basics
assert_eq!(FromBase58::from_base58("1"), Ok(vec![0u8])); assert_eq!(FromBase58::from_base58("1"), Ok(vec![0u8]));
assert_eq!(FromBase58::from_base58("2"), Ok(vec![1u8])); assert_eq!(FromBase58::from_base58("2"), Ok(vec![1u8]));
assert_eq!(FromBase58::from_base58("21"), Ok(vec![58u8])); assert_eq!(FromBase58::from_base58("21"), Ok(vec![58u8]));
assert_eq!(FromBase58::from_base58("211"), Ok(vec![13u8, 36])); assert_eq!(FromBase58::from_base58("211"), Ok(vec![13u8, 36]));
// Leading zeroes // Leading zeroes
assert_eq!(FromBase58::from_base58("1211"), Ok(vec![0u8, 13, 36])); assert_eq!(FromBase58::from_base58("1211"), Ok(vec![0u8, 13, 36]));
assert_eq!(FromBase58::from_base58("111211"), Ok(vec![0u8, 0, 0, 13, 36])); assert_eq!(FromBase58::from_base58("111211"), Ok(vec![0u8, 0, 0, 13, 36]));
// Addresses // Addresses
assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"), assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"),
Ok("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap())) Ok("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap()))
} }
#[test] #[test]
fn test_base58_roundtrip() { fn test_base58_roundtrip() {
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"; let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
let v: Vec<u8> = FromBase58::from_base58check(s).unwrap(); let v: Vec<u8> = FromBase58::from_base58check(s).unwrap();
assert_eq!(v.to_base58check().as_slice(), s); assert_eq!(v.to_base58check().as_slice(), s);
assert_eq!(FromBase58::from_base58check(v.to_base58check().as_slice()), Ok(v)); assert_eq!(FromBase58::from_base58check(v.to_base58check().as_slice()), Ok(v));
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without // the public domain worldwide. This software is distributed without
@ -38,9 +38,9 @@ pub struct Sha256dHash([u8; 32]);
impl_array_newtype!(Sha256dHash, u8, 32); impl_array_newtype!(Sha256dHash, u8, 32);
impl ::std::fmt::Debug for Sha256dHash { impl ::std::fmt::Debug for Sha256dHash {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.be_hex_string().as_slice()) write!(f, "{}", self.be_hex_string().as_slice())
} }
} }
/// A RIPEMD-160 hash /// A RIPEMD-160 hash
@ -60,100 +60,100 @@ pub struct Hash48((u8, u8, u8, u8, u8, u8));
pub struct Hash64((u8, u8, u8, u8, u8, u8, u8, u8)); pub struct Hash64((u8, u8, u8, u8, u8, u8, u8, u8));
impl Ripemd160Hash { impl Ripemd160Hash {
/// Create a hash by hashing some data /// Create a hash by hashing some data
pub fn from_data(data: &[u8]) -> Ripemd160Hash { pub fn from_data(data: &[u8]) -> Ripemd160Hash {
let mut ret = [0; 20]; let mut ret = [0; 20];
let mut rmd = Ripemd160::new(); let mut rmd = Ripemd160::new();
rmd.input(data); rmd.input(data);
rmd.result(&mut ret); rmd.result(&mut ret);
Ripemd160Hash(ret) Ripemd160Hash(ret)
} }
} }
// This doesn't make much sense to me, but is implicit behaviour // This doesn't make much sense to me, but is implicit behaviour
// in the C++ reference client // in the C++ reference client
impl Default for Sha256dHash { impl Default for Sha256dHash {
#[inline] #[inline]
fn default() -> Sha256dHash { Sha256dHash([0u8; 32]) } fn default() -> Sha256dHash { Sha256dHash([0u8; 32]) }
} }
impl Sha256dHash { impl Sha256dHash {
/// Create a hash by hashing some data /// Create a hash by hashing some data
pub fn from_data(data: &[u8]) -> Sha256dHash { pub fn from_data(data: &[u8]) -> Sha256dHash {
let Sha256dHash(mut ret): Sha256dHash = Default::default(); let Sha256dHash(mut ret): Sha256dHash = Default::default();
let mut sha2 = Sha256::new(); let mut sha2 = Sha256::new();
sha2.input(data); sha2.input(data);
sha2.result(&mut ret); sha2.result(&mut ret);
sha2.reset(); sha2.reset();
sha2.input(&ret); sha2.input(&ret);
sha2.result(&mut ret); sha2.result(&mut ret);
Sha256dHash(ret) Sha256dHash(ret)
}
/// Converts a hash to a little-endian Uint256
#[inline]
pub fn into_le(self) -> Uint256 {
let Sha256dHash(data) = self;
let mut ret: [u64; 4] = unsafe { transmute(data) };
for x in (&mut ret).iter_mut() { *x = x.to_le(); }
Uint256(ret)
}
/// Converts a hash to a big-endian Uint256
#[inline]
pub fn into_be(self) -> Uint256 {
let Sha256dHash(mut data) = self;
data.reverse();
let mut ret: [u64; 4] = unsafe { transmute(data) };
for x in (&mut ret).iter_mut() { *x = x.to_be(); }
Uint256(ret)
}
/// Converts a hash to a Hash32 by truncation
#[inline]
pub fn into_hash32(self) -> Hash32 {
let Sha256dHash(data) = self;
unsafe { transmute([data[0], data[8], data[16], data[24]]) }
}
/// Converts a hash to a Hash48 by truncation
#[inline]
pub fn into_hash48(self) -> Hash48 {
let Sha256dHash(data) = self;
unsafe { transmute([data[0], data[6], data[12], data[18], data[24], data[30]]) }
}
/// Human-readable hex output
/// Converts a hash to a Hash64 by truncation
#[inline]
pub fn into_hash64(self) -> Hash64 {
let Sha256dHash(data) = self;
unsafe { transmute([data[0], data[4], data[8], data[12],
data[16], data[20], data[24], data[28]]) }
}
/// Human-readable hex output
pub fn le_hex_string(&self) -> String {
let &Sha256dHash(data) = self;
let mut ret = String::with_capacity(64);
for i in 0..32 {
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
} }
ret
}
/// Human-readable hex output /// Converts a hash to a little-endian Uint256
pub fn be_hex_string(&self) -> String { #[inline]
let &Sha256dHash(data) = self; pub fn into_le(self) -> Uint256 {
let mut ret = String::with_capacity(64); let Sha256dHash(data) = self;
for i in (0..32).rev() { let mut ret: [u64; 4] = unsafe { transmute(data) };
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap()); for x in (&mut ret).iter_mut() { *x = x.to_le(); }
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap()); Uint256(ret)
}
/// Converts a hash to a big-endian Uint256
#[inline]
pub fn into_be(self) -> Uint256 {
let Sha256dHash(mut data) = self;
data.reverse();
let mut ret: [u64; 4] = unsafe { transmute(data) };
for x in (&mut ret).iter_mut() { *x = x.to_be(); }
Uint256(ret)
}
/// Converts a hash to a Hash32 by truncation
#[inline]
pub fn into_hash32(self) -> Hash32 {
let Sha256dHash(data) = self;
unsafe { transmute([data[0], data[8], data[16], data[24]]) }
}
/// Converts a hash to a Hash48 by truncation
#[inline]
pub fn into_hash48(self) -> Hash48 {
let Sha256dHash(data) = self;
unsafe { transmute([data[0], data[6], data[12], data[18], data[24], data[30]]) }
}
/// Human-readable hex output
/// Converts a hash to a Hash64 by truncation
#[inline]
pub fn into_hash64(self) -> Hash64 {
let Sha256dHash(data) = self;
unsafe { transmute([data[0], data[4], data[8], data[12],
data[16], data[20], data[24], data[28]]) }
}
/// Human-readable hex output
pub fn le_hex_string(&self) -> String {
let &Sha256dHash(data) = self;
let mut ret = String::with_capacity(64);
for i in 0..32 {
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
}
ret
}
/// Human-readable hex output
pub fn be_hex_string(&self) -> String {
let &Sha256dHash(data) = self;
let mut ret = String::with_capacity(64);
for i in (0..32).rev() {
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
}
ret
} }
ret
}
} }
// Note that this outputs hashes as big endian hex numbers, so this should be // Note that this outputs hashes as big endian hex numbers, so this should be
@ -161,48 +161,48 @@ impl Sha256dHash {
// little-endian and should be done using the consensus `encodable::ConsensusEncodable` // little-endian and should be done using the consensus `encodable::ConsensusEncodable`
// interface. // interface.
impl serde::Serialize for Sha256dHash { impl serde::Serialize for Sha256dHash {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer, where S: serde::Serializer,
{ {
serializer.visit_str(&self.be_hex_string()) serializer.visit_str(&self.be_hex_string())
} }
} }
impl serde::Deserialize for Sha256dHash { impl serde::Deserialize for Sha256dHash {
#[inline] #[inline]
fn deserialize<D>(d: &mut D) -> Result<Sha256dHash, D::Error> fn deserialize<D>(d: &mut D) -> Result<Sha256dHash, D::Error>
where D: serde::Deserializer where D: serde::Deserializer
{ {
use serialize::hex::FromHex; use serialize::hex::FromHex;
struct Sha256dHashVisitor; struct Sha256dHashVisitor;
impl serde::de::Visitor for Sha256dHashVisitor { impl serde::de::Visitor for Sha256dHashVisitor {
type Value = Sha256dHash; type Value = Sha256dHash;
fn visit_string<E>(&mut self, v: String) -> Result<Sha256dHash, E> fn visit_string<E>(&mut self, v: String) -> Result<Sha256dHash, E>
where E: serde::de::Error where E: serde::de::Error
{ {
self.visit_str(&v) self.visit_str(&v)
} }
fn visit_str<E>(&mut self, hex_str: &str) -> Result<Sha256dHash, E> fn visit_str<E>(&mut self, hex_str: &str) -> Result<Sha256dHash, E>
where E: serde::de::Error where E: serde::de::Error
{ {
if hex_str.len() != 64 { if hex_str.len() != 64 {
return Err(serde::de::Error::syntax_error()); return Err(serde::de::Error::syntax_error());
}
let raw_str = try!(hex_str.from_hex()
.map_err(|_| serde::de::Error::syntax_error()));
let mut ret = [0u8; 32];
for i in 0..32 {
ret[i] = raw_str[31 - i];
}
Ok(Sha256dHash(ret))
}
} }
let raw_str = try!(hex_str.from_hex()
.map_err(|_| serde::de::Error::syntax_error())); d.visit(Sha256dHashVisitor)
let mut ret = [0u8; 32];
for i in 0..32 {
ret[i] = raw_str[31 - i];
}
Ok(Sha256dHash(ret))
}
} }
d.visit(Sha256dHashVisitor)
}
} }
// Consensus encoding (little-endian) // Consensus encoding (little-endian)
@ -212,108 +212,108 @@ impl_newtype_consensus_encoding!(Hash64);
impl_newtype_consensus_encoding!(Sha256dHash); impl_newtype_consensus_encoding!(Sha256dHash);
impl fmt::LowerHex for Sha256dHash { impl fmt::LowerHex for Sha256dHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &Sha256dHash(data) = self; let &Sha256dHash(data) = self;
let mut rv = [0; 64]; let mut rv = [0; 64];
for ch in data.iter().rev() { for ch in data.iter().rev() {
try!(write!(f, "{:02x}", ch)); try!(write!(f, "{:02x}", ch));
}
Ok(())
} }
Ok(())
}
} }
/// Any collection of objects for which a merkle root makes sense to calculate /// Any collection of objects for which a merkle root makes sense to calculate
pub trait MerkleRoot { pub trait MerkleRoot {
/// Construct a merkle tree from a collection, with elements ordered as /// Construct a merkle tree from a collection, with elements ordered as
/// they were in the original collection, and return the merkle root. /// they were in the original collection, and return the merkle root.
fn merkle_root(&self) -> Sha256dHash; fn merkle_root(&self) -> Sha256dHash;
} }
impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] { impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
fn merkle_root(&self) -> Sha256dHash { fn merkle_root(&self) -> Sha256dHash {
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash { fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
// Base case // Base case
if data.len() < 1 { if data.len() < 1 {
return Default::default(); return Default::default();
} }
if data.len() < 2 { if data.len() < 2 {
return data[0]; return data[0];
} }
// Recursion // Recursion
let mut next = vec![]; let mut next = vec![];
for idx in 0..((data.len() + 1) / 2) { for idx in 0..((data.len() + 1) / 2) {
let idx1 = 2 * idx; let idx1 = 2 * idx;
let idx2 = min(idx1 + 1, data.len() - 1); let idx2 = min(idx1 + 1, data.len() - 1);
let mut encoder = RawEncoder::new(Cursor::new(vec![])); let mut encoder = RawEncoder::new(Cursor::new(vec![]));
data[idx1].consensus_encode(&mut encoder).unwrap(); data[idx1].consensus_encode(&mut encoder).unwrap();
data[idx2].consensus_encode(&mut encoder).unwrap(); data[idx2].consensus_encode(&mut encoder).unwrap();
next.push(encoder.unwrap().into_inner().bitcoin_hash()); next.push(encoder.into_inner().into_inner().bitcoin_hash());
} }
merkle_root(next) merkle_root(next)
}
merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect())
} }
merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect())
}
} }
impl <T: BitcoinHash> MerkleRoot for Vec<T> { impl <T: BitcoinHash> MerkleRoot for Vec<T> {
fn merkle_root(&self) -> Sha256dHash { fn merkle_root(&self) -> Sha256dHash {
(&self[..]).merkle_root() (&self[..]).merkle_root()
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::prelude::*; use std::prelude::*;
use std::io::Cursor; use std::io::Cursor;
use std::str::from_utf8; use std::str::from_utf8;
use serialize::Encodable; use serialize::Encodable;
use serialize::json; use serialize::json;
use network::serialize::{serialize, deserialize}; use network::serialize::{serialize, deserialize};
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
#[test] #[test]
fn test_sha256d() { fn test_sha256d() {
// nb the 5df6... output is the one you get from sha256sum. this is the // nb the 5df6... output is the one you get from sha256sum. this is the
// "little-endian" hex string since it matches the in-memory representation // "little-endian" hex string since it matches the in-memory representation
// of a Uint256 (which is little-endian) after transmutation // of a Uint256 (which is little-endian) after transmutation
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(), assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string()); "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string());
assert_eq!(Sha256dHash::from_data(&[]).be_hex_string(), assert_eq!(Sha256dHash::from_data(&[]).be_hex_string(),
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string()); "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string());
}
#[test]
fn test_consenus_encode_roundtrip() {
let hash = Sha256dHash::from_data(&[]);
let serial = serialize(&hash).unwrap();
let deserial = deserialize(serial).unwrap();
assert_eq!(hash, deserial);
}
#[test]
fn test_hash_encode_decode() {
let hash = Sha256dHash::from_data(&[]);
let mut writer = Cursor::new(vec![]);
{
let mut encoder = json::Encoder::new(&mut writer);
assert!(hash.encode(&mut encoder).is_ok());
} }
let res = writer.unwrap();
assert_eq!(&res.as_slice(),
"\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes());
assert_eq!(json::decode(from_utf8(res.as_slice()).unwrap()), Ok(hash));
}
#[test] #[test]
fn test_sighash_single_vec() { fn test_consenus_encode_roundtrip() {
let one = Sha256dHash([1, 0, 0, 0, 0, 0, 0, 0, let hash = Sha256dHash::from_data(&[]);
0, 0, 0, 0, 0, 0, 0, 0, let serial = serialize(&hash).unwrap();
0, 0, 0, 0, 0, 0, 0, 0, let deserial = deserialize(serial).unwrap();
0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(hash, deserial);
assert_eq!(Some(one.into_le()), FromPrimitive::from_u64(1)); }
assert_eq!(Some(one.into_le().low_128()), FromPrimitive::from_u64(1));
} #[test]
fn test_hash_encode_decode() {
let hash = Sha256dHash::from_data(&[]);
let mut writer = Cursor::new(vec![]);
{
let mut encoder = json::Encoder::new(&mut writer);
assert!(hash.encode(&mut encoder).is_ok());
}
let res = writer.unwrap();
assert_eq!(&res.as_slice(),
"\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes());
assert_eq!(json::decode(from_utf8(res.as_slice()).unwrap()), Ok(hash));
}
#[test]
fn test_sighash_single_vec() {
let one = Sha256dHash([1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(Some(one.into_le()), FromPrimitive::from_u64(1));
assert_eq!(Some(one.into_le().low_128()), FromPrimitive::from_u64(1));
}
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -19,62 +19,62 @@
/// An iterator that returns pairs of elements /// An iterator that returns pairs of elements
pub struct Pair<I> pub struct Pair<I>
where I: Iterator where I: Iterator
{ {
iter: I, iter: I,
last_elem: Option<I::Item> last_elem: Option<I::Item>
} }
impl<I: Iterator> Iterator for Pair<I> { impl<I: Iterator> Iterator for Pair<I> {
type Item = (I::Item, I::Item); type Item = (I::Item, I::Item);
#[inline] #[inline]
fn next(&mut self) -> Option<(I::Item, I::Item)> { fn next(&mut self) -> Option<(I::Item, I::Item)> {
let elem1 = self.iter.next(); let elem1 = self.iter.next();
if elem1.is_none() { if elem1.is_none() {
None None
} else { } else {
let elem2 = self.iter.next(); let elem2 = self.iter.next();
if elem2.is_none() { if elem2.is_none() {
self.last_elem = elem1; self.last_elem = elem1;
None None
} else { } else {
Some((elem1.unwrap(), elem2.unwrap())) Some((elem1.unwrap(), elem2.unwrap()))
} }
}
} }
}
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
match self.iter.size_hint() { match self.iter.size_hint() {
(n, None) => (n/2, None), (n, None) => (n/2, None),
(n, Some(m)) => (n/2, Some(m/2)) (n, Some(m)) => (n/2, Some(m/2))
}
} }
}
} }
impl<I: Iterator> Pair<I> { impl<I: Iterator> Pair<I> {
/// Returns the last element of the iterator if there were an odd /// Returns the last element of the iterator if there were an odd
/// number of elements remaining before it was Pair-ified. /// number of elements remaining before it was Pair-ified.
#[inline] #[inline]
pub fn remainder(self) -> Option<I::Item> { pub fn remainder(self) -> Option<I::Item> {
self.last_elem self.last_elem
} }
} }
/// Returns an iterator that returns elements of the original iterator 2 at a time /// Returns an iterator that returns elements of the original iterator 2 at a time
pub trait Pairable { pub trait Pairable {
/// Returns an iterator that returns elements of the original iterator 2 at a time /// Returns an iterator that returns elements of the original iterator 2 at a time
fn pair(self) -> Pair<Self>; fn pair(self) -> Pair<Self>;
} }
impl<I: Iterator> Pairable for I { impl<I: Iterator> Pairable for I {
/// Creates an iterator that yields pairs ef elements from the underlying /// Creates an iterator that yields pairs ef elements from the underlying
/// iterator, yielding `None` when there are fewer than two elements to /// iterator, yielding `None` when there are fewer than two elements to
/// return. /// return.
#[inline] #[inline]
fn pair(self) -> Pair<I> { fn pair(self) -> Pair<I> {
Pair {iter: self, last_elem: None } Pair {iter: self, last_elem: None }
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -22,132 +22,132 @@ use util::iter::Pairable;
/// Convert a hexadecimal-encoded string to its corresponding bytes /// Convert a hexadecimal-encoded string to its corresponding bytes
pub fn hex_bytes(s: &str) -> Result<Vec<u8>, Error> { pub fn hex_bytes(s: &str) -> Result<Vec<u8>, Error> {
let mut v = vec![]; let mut v = vec![];
let mut iter = s.chars().pair(); let mut iter = s.chars().pair();
// Do the parsing // Do the parsing
try!(iter.fold(Ok(()), |e, (f, s)| try!(iter.fold(Ok(()), |e, (f, s)|
if e.is_err() { e } if e.is_err() { e }
else { else {
match (f.to_digit(16), s.to_digit(16)) { match (f.to_digit(16), s.to_digit(16)) {
(None, _) => Err(Error::Detail( (None, _) => Err(Error::Detail(
format!("expected hex, got {:}", f), format!("expected hex, got {:}", f),
Box::new(Error::ParseFailed) Box::new(Error::ParseFailed)
)),
(_, None) => Err(Error::Detail(
format!("expected hex, got {:}", s),
Box::new(Error::ParseFailed)
)),
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
}
}
));
// Check that there was no remainder
match iter.remainder() {
Some(_) => Err(Error::Detail(
format!("hexstring of odd length"),
Box::new(Error::ParseFailed)
)), )),
(_, None) => Err(Error::Detail( None => Ok(v)
format!("expected hex, got {:}", s),
Box::new(Error::ParseFailed)
)),
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
}
} }
));
// Check that there was no remainder
match iter.remainder() {
Some(_) => Err(Error::Detail(
format!("hexstring of odd length"),
Box::new(Error::ParseFailed)
)),
None => Ok(v)
}
} }
/// Dump an error message to the screen /// Dump an error message to the screen
/// TODO all uses of this should be replaced with some sort of logging infrastructure /// TODO all uses of this should be replaced with some sort of logging infrastructure
pub fn consume_err<T>(s: &str, res: Result<T, Error>) { pub fn consume_err<T>(s: &str, res: Result<T, Error>) {
match res { match res {
Ok(_) => {}, Ok(_) => {},
Err(e) => { println!("{}: {:?}", s, e); } Err(e) => { println!("{}: {:?}", s, e); }
}; };
} }
/// Search for `needle` in the vector `haystack` and remove every /// Search for `needle` in the vector `haystack` and remove every
/// instance of it, returning the number of instances removed. /// instance of it, returning the number of instances removed.
/// Loops through the vector opcode by opcode, skipping pushed data. /// Loops through the vector opcode by opcode, skipping pushed data.
pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize { pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
if needle.len() > haystack.len() { return 0; } if needle.len() > haystack.len() { return 0; }
if needle.len() == 0 { return 0; } if needle.len() == 0 { return 0; }
let mut top = haystack.len() - needle.len(); let mut top = haystack.len() - needle.len();
let mut n_deleted = 0; let mut n_deleted = 0;
let mut i = 0; let mut i = 0;
while i <= top { while i <= top {
if &haystack[i..(i + needle.len())] == needle { if &haystack[i..(i + needle.len())] == needle {
let v = &mut haystack; let v = &mut haystack;
for j in i..top { for j in i..top {
v.swap(j + needle.len(), j); v.swap(j + needle.len(), j);
} }
n_deleted += 1; n_deleted += 1;
// This is ugly but prevents infinite loop in case of overflow // This is ugly but prevents infinite loop in case of overflow
let overflow = top < needle.len(); let overflow = top < needle.len();
top -= needle.len(); top -= needle.len();
if overflow { break; } if overflow { break; }
} else { } else {
i += match opcodes::All::from_u8((*haystack)[i]).classify() { i += match opcodes::All::from_u8((*haystack)[i]).classify() {
opcodes::Class::PushBytes(n) => n + 1, opcodes::Class::PushBytes(n) => n as usize + 1,
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
_ => 1 _ => 1
}; };
}
} }
} haystack.truncate(top + needle.len());
haystack.truncate(top + needle.len()); n_deleted
n_deleted
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::prelude::*; use std::prelude::*;
use super::script_find_and_remove; use super::script_find_and_remove;
use super::hex_bytes; use super::hex_bytes;
#[test] #[test]
fn test_script_find_and_remove() { fn test_script_find_and_remove() {
let mut v = vec![101u8, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109]; let mut v = vec![101u8, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109];
assert_eq!(script_find_and_remove(&mut v, []), 0); assert_eq!(script_find_and_remove(&mut v, []), 0);
assert_eq!(script_find_and_remove(&mut v, [105, 105, 105]), 0); assert_eq!(script_find_and_remove(&mut v, [105, 105, 105]), 0);
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109]); assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109]);
assert_eq!(script_find_and_remove(&mut v, [105, 106, 107]), 1); assert_eq!(script_find_and_remove(&mut v, [105, 106, 107]), 1);
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 108, 109]); assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 108, 109]);
assert_eq!(script_find_and_remove(&mut v, [104, 108, 109]), 1); assert_eq!(script_find_and_remove(&mut v, [104, 108, 109]), 1);
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103]); assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103]);
assert_eq!(script_find_and_remove(&mut v, [101]), 1); assert_eq!(script_find_and_remove(&mut v, [101]), 1);
assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]); assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]);
assert_eq!(script_find_and_remove(&mut v, [102]), 3); assert_eq!(script_find_and_remove(&mut v, [102]), 3);
assert_eq!(v, vec![103, 104, 103, 104, 103]); assert_eq!(v, vec![103, 104, 103, 104, 103]);
assert_eq!(script_find_and_remove(&mut v, [103, 104]), 2); assert_eq!(script_find_and_remove(&mut v, [103, 104]), 2);
assert_eq!(v, vec![103]); assert_eq!(v, vec![103]);
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0); assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
assert_eq!(script_find_and_remove(&mut v, [105]), 0); assert_eq!(script_find_and_remove(&mut v, [105]), 0);
assert_eq!(script_find_and_remove(&mut v, [103]), 1); assert_eq!(script_find_and_remove(&mut v, [103]), 1);
assert_eq!(v, vec![]); assert_eq!(v, vec![]);
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0); assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
assert_eq!(script_find_and_remove(&mut v, [105]), 0); assert_eq!(script_find_and_remove(&mut v, [105]), 0);
} }
#[test] #[test]
fn test_script_codesep_remove() { fn test_script_codesep_remove() {
let mut s = vec![33u8, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 81]; let mut s = vec![33u8, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 81];
assert_eq!(script_find_and_remove(&mut s, [171]), 2); assert_eq!(script_find_and_remove(&mut s, [171]), 2);
assert_eq!(s, vec![33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 81]); assert_eq!(s, vec![33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 81]);
} }
#[test] #[test]
fn test_hex_bytes() { fn test_hex_bytes() {
assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]); assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]);
assert!(hex_bytes("abcde").is_err()); assert!(hex_bytes("abcde").is_err());
assert!(hex_bytes("aBcDeF").is_ok()); assert!(hex_bytes("aBcDeF").is_ok());
assert!(hex_bytes("aBcD4eFL").is_err()); assert!(hex_bytes("aBcD4eFL").is_err());
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// //
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
@ -25,483 +25,483 @@ use network::serialize::RawEncoder;
use util::BitArray; use util::BitArray;
macro_rules! construct_uint { macro_rules! construct_uint {
($name:ident, $n_words:expr) => ( ($name:ident, $n_words:expr) => (
/// Little-endian large integer type /// Little-endian large integer type
#[repr(C)] #[repr(C)]
pub struct $name(pub [u64; $n_words]); pub struct $name(pub [u64; $n_words]);
impl_array_newtype!($name, u64, $n_words); impl_array_newtype!($name, u64, $n_words);
impl $name { impl $name {
/// Conversion to u32 /// Conversion to u32
#[inline] #[inline]
pub fn low_u32(&self) -> u32 { pub fn low_u32(&self) -> u32 {
let &$name(ref arr) = self; let &$name(ref arr) = self;
arr[0] as u32 arr[0] as u32
} }
/// Return the least number of bits needed to represent the number /// Return the least number of bits needed to represent the number
#[inline] #[inline]
pub fn bits(&self) -> usize { pub fn bits(&self) -> usize {
let &$name(ref arr) = self; let &$name(ref arr) = self;
for i in 1..$n_words { for i in 1..$n_words {
if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; } if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; }
} }
0x40 - arr[0].leading_zeros() as usize 0x40 - arr[0].leading_zeros() as usize
} }
/// Multiplication by u32 /// Multiplication by u32
pub fn mul_u32(self, other: u32) -> $name { pub fn mul_u32(self, other: u32) -> $name {
let $name(ref arr) = self; let $name(ref arr) = self;
let mut carry = [0u64; $n_words]; let mut carry = [0u64; $n_words];
let mut ret = [0u64; $n_words]; let mut ret = [0u64; $n_words];
for i in 0..$n_words { for i in 0..$n_words {
let upper = other as u64 * (arr[i] >> 32); let upper = other as u64 * (arr[i] >> 32);
let lower = other as u64 * (arr[i] & 0xFFFFFFFF); let lower = other as u64 * (arr[i] & 0xFFFFFFFF);
if i < 3 { if i < 3 {
carry[i + 1] += upper >> 32; carry[i + 1] += upper >> 32;
} }
ret[i] = lower + (upper << 32); ret[i] = lower + (upper << 32);
} }
$name(ret) + $name(carry) $name(ret) + $name(carry)
} }
}
impl ::std::num::FromPrimitive for $name {
#[inline]
fn from_u64(init: u64) -> Option<$name> {
let mut ret = [0; $n_words];
ret[0] = init;
Some($name(ret))
}
#[inline]
fn from_i64(init: i64) -> Option<$name> {
::std::num::FromPrimitive::from_u64(init as u64)
}
}
impl ::std::num::Zero for $name {
fn zero() -> $name { $name([0; $n_words]) }
}
impl ::std::num::One for $name {
fn one() -> $name {
$name({ let mut ret = [0; $n_words]; ret[0] = 1; ret })
}
}
impl ::std::ops::Add<$name> for $name {
type Output = $name;
fn add(self, other: $name) -> $name {
let $name(ref me) = self;
let $name(ref you) = other;
let mut ret = [0u64; $n_words];
let mut carry = [0u64; $n_words];
let mut b_carry = false;
for i in 0..$n_words {
ret[i] = me[i] + you[i];
if i < $n_words - 1 && ret[i] < me[i] {
carry[i + 1] = 1;
b_carry = true;
}
}
if b_carry { $name(ret) + $name(carry) } else { $name(ret) }
}
}
impl ::std::ops::Sub<$name> for $name {
type Output = $name;
#[inline]
fn sub(self, other: $name) -> $name {
self + !other + One::one()
}
}
impl ::std::ops::Mul<$name> for $name {
type Output = $name;
fn mul(self, other: $name) -> $name {
let mut me = self;
// TODO: be more efficient about this
for i in 0..(2 * $n_words) {
me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
}
me
}
}
impl ::std::ops::Div<$name> for $name {
type Output = $name;
fn div(self, other: $name) -> $name {
let mut sub_copy = self;
let mut shift_copy = other;
let mut ret = [0u64; $n_words];
let my_bits = self.bits();
let your_bits = other.bits();
// Check for division by 0
assert!(your_bits != 0);
// Early return in case we are dividing by a larger number than us
if my_bits < your_bits {
return $name(ret);
} }
// Bitwise long division impl ::std::num::FromPrimitive for $name {
let mut shift = my_bits - your_bits; #[inline]
shift_copy = shift_copy << shift; fn from_u64(init: u64) -> Option<$name> {
loop { let mut ret = [0; $n_words];
if sub_copy >= shift_copy { ret[0] = init;
ret[shift / 64] |= 1 << (shift % 64); Some($name(ret))
sub_copy = sub_copy - shift_copy; }
}
shift_copy = shift_copy >> 1; #[inline]
if shift == 0 { break; } fn from_i64(init: i64) -> Option<$name> {
shift -= 1; ::std::num::FromPrimitive::from_u64(init as u64)
}
} }
$name(ret) impl ::std::num::Zero for $name {
} fn zero() -> $name { $name([0; $n_words]) }
}
impl BitArray for $name {
#[inline]
fn bit(&self, index: usize) -> bool {
let &$name(ref arr) = self;
arr[index / 64] & (1 << (index % 64)) != 0
}
#[inline]
fn bit_slice(&self, start: usize, end: usize) -> $name {
(*self >> start).mask(end - start)
}
#[inline]
fn mask(&self, n: usize) -> $name {
let &$name(ref arr) = self;
let mut ret = [0; $n_words];
for i in 0..$n_words {
if n >= 0x40 * (i + 1) {
ret[i] = arr[i];
} else {
ret[i] = arr[i] & ((1 << (n - 0x40 * i)) - 1);
break;
}
} }
$name(ret)
}
#[inline] impl ::std::num::One for $name {
fn trailing_zeros(&self) -> usize { fn one() -> $name {
let &$name(ref arr) = self; $name({ let mut ret = [0; $n_words]; ret[0] = 1; ret })
for i in 0..($n_words - 1) { }
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros() as usize; }
} }
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as usize
}
}
impl ::std::ops::BitAnd<$name> for $name { impl ::std::ops::Add<$name> for $name {
type Output = $name; type Output = $name;
#[inline] fn add(self, other: $name) -> $name {
fn bitand(self, other: $name) -> $name { let $name(ref me) = self;
let $name(ref arr1) = self; let $name(ref you) = other;
let $name(ref arr2) = other; let mut ret = [0u64; $n_words];
let mut ret = [0u64; $n_words]; let mut carry = [0u64; $n_words];
for i in 0..$n_words { let mut b_carry = false;
ret[i] = arr1[i] & arr2[i]; for i in 0..$n_words {
ret[i] = me[i] + you[i];
if i < $n_words - 1 && ret[i] < me[i] {
carry[i + 1] = 1;
b_carry = true;
}
}
if b_carry { $name(ret) + $name(carry) } else { $name(ret) }
}
} }
$name(ret)
}
}
impl ::std::ops::BitXor<$name> for $name { impl ::std::ops::Sub<$name> for $name {
type Output = $name; type Output = $name;
#[inline] #[inline]
fn bitxor(self, other: $name) -> $name { fn sub(self, other: $name) -> $name {
let $name(ref arr1) = self; self + !other + One::one()
let $name(ref arr2) = other; }
let mut ret = [0u64; $n_words];
for i in 0..$n_words {
ret[i] = arr1[i] ^ arr2[i];
} }
$name(ret)
}
}
impl ::std::ops::BitOr<$name> for $name { impl ::std::ops::Mul<$name> for $name {
type Output = $name; type Output = $name;
#[inline] fn mul(self, other: $name) -> $name {
fn bitor(self, other: $name) -> $name { let mut me = self;
let $name(ref arr1) = self; // TODO: be more efficient about this
let $name(ref arr2) = other; for i in 0..(2 * $n_words) {
let mut ret = [0u64; $n_words]; me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
for i in 0..$n_words { }
ret[i] = arr1[i] | arr2[i]; me
}
} }
$name(ret)
}
}
impl ::std::ops::Not for $name { impl ::std::ops::Div<$name> for $name {
type Output = $name; type Output = $name;
#[inline] fn div(self, other: $name) -> $name {
fn not(self) -> $name { let mut sub_copy = self;
let $name(ref arr) = self; let mut shift_copy = other;
let mut ret = [0u64; $n_words]; let mut ret = [0u64; $n_words];
for i in 0..$n_words {
ret[i] = !arr[i]; let my_bits = self.bits();
let your_bits = other.bits();
// Check for division by 0
assert!(your_bits != 0);
// Early return in case we are dividing by a larger number than us
if my_bits < your_bits {
return $name(ret);
}
// Bitwise long division
let mut shift = my_bits - your_bits;
shift_copy = shift_copy << shift;
loop {
if sub_copy >= shift_copy {
ret[shift / 64] |= 1 << (shift % 64);
sub_copy = sub_copy - shift_copy;
}
shift_copy = shift_copy >> 1;
if shift == 0 { break; }
shift -= 1;
}
$name(ret)
}
} }
$name(ret)
}
}
impl ::std::ops::Shl<usize> for $name { impl BitArray for $name {
type Output = $name; #[inline]
fn bit(&self, index: usize) -> bool {
let &$name(ref arr) = self;
arr[index / 64] & (1 << (index % 64)) != 0
}
fn shl(self, shift: usize) -> $name { #[inline]
let $name(ref original) = self; fn bit_slice(&self, start: usize, end: usize) -> $name {
let mut ret = [0u64; $n_words]; (*self >> start).mask(end - start)
let word_shift = shift / 64; }
let bit_shift = shift % 64;
for i in 0..$n_words { #[inline]
// Shift fn mask(&self, n: usize) -> $name {
if bit_shift < 64 && i + word_shift < $n_words { let &$name(ref arr) = self;
ret[i + word_shift] += original[i] << bit_shift; let mut ret = [0; $n_words];
} for i in 0..$n_words {
// Carry if n >= 0x40 * (i + 1) {
if bit_shift > 0 && i + word_shift + 1 < $n_words { ret[i] = arr[i];
ret[i + word_shift + 1] += original[i] >> (64 - bit_shift); } else {
} ret[i] = arr[i] & ((1 << (n - 0x40 * i)) - 1);
break;
}
}
$name(ret)
}
#[inline]
fn trailing_zeros(&self) -> usize {
let &$name(ref arr) = self;
for i in 0..($n_words - 1) {
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros() as usize; }
}
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as usize
}
} }
$name(ret)
}
}
impl ::std::ops::Shr<usize> for $name { impl ::std::ops::BitAnd<$name> for $name {
type Output = $name; type Output = $name;
#[allow(unsigned_negate)] #[inline]
fn shr(self, shift: usize) -> $name { fn bitand(self, other: $name) -> $name {
let $name(ref original) = self; let $name(ref arr1) = self;
let mut ret = [0u64; $n_words]; let $name(ref arr2) = other;
let word_shift = shift / 64; let mut ret = [0u64; $n_words];
let bit_shift = shift % 64; for i in 0..$n_words {
for i in 0..$n_words { ret[i] = arr1[i] & arr2[i];
// Shift }
if bit_shift < 64 && i - word_shift < $n_words { $name(ret)
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);
}
} }
$name(ret)
}
}
impl ::std::cmp::Ord for $name { impl ::std::ops::BitXor<$name> for $name {
fn cmp(&self, other: &$name) -> ::std::cmp::Ordering { type Output = $name;
let &$name(ref me) = self;
let &$name(ref you) = other; #[inline]
for i in 0..$n_words { fn bitxor(self, other: $name) -> $name {
if me[$n_words - 1 - i] < you[$n_words - 1 - i] { return ::std::cmp::Ordering::Less; } let $name(ref arr1) = self;
if me[$n_words - 1 - i] > you[$n_words - 1 - i] { return ::std::cmp::Ordering::Greater; } let $name(ref arr2) = other;
let mut ret = [0u64; $n_words];
for i in 0..$n_words {
ret[i] = arr1[i] ^ arr2[i];
}
$name(ret)
}
} }
return ::std::cmp::Ordering::Equal;
}
}
impl ::std::cmp::PartialOrd for $name { impl ::std::ops::BitOr<$name> for $name {
fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> { type Output = $name;
Some(self.cmp(other))
}
}
impl fmt::Debug for $name { #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn bitor(self, other: $name) -> $name {
let &$name(ref data) = self; let $name(ref arr1) = self;
try!(write!(f, "0x")); let $name(ref arr2) = other;
for ch in data.iter().rev() { let mut ret = [0u64; $n_words];
try!(write!(f, "{:02x}", ch)); for i in 0..$n_words {
ret[i] = arr1[i] | arr2[i];
}
$name(ret)
}
} }
Ok(())
}
}
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $name { impl ::std::ops::Not for $name {
#[inline] type Output = $name;
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
use network::encodable::ConsensusEncodable;
let &$name(ref data) = self;
for word in data.iter() { try!(word.consensus_encode(s)); }
Ok(())
}
}
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $name { #[inline]
fn consensus_decode(d: &mut D) -> Result<$name, D::Error> { fn not(self) -> $name {
use network::encodable::ConsensusDecodable; let $name(ref arr) = self;
let ret: [u64; $n_words] = try!(ConsensusDecodable::consensus_decode(d)); let mut ret = [0u64; $n_words];
Ok($name(ret)) for i in 0..$n_words {
} ret[i] = !arr[i];
} }
); $name(ret)
}
}
impl ::std::ops::Shl<usize> for $name {
type Output = $name;
fn shl(self, shift: usize) -> $name {
let $name(ref original) = self;
let mut ret = [0u64; $n_words];
let word_shift = shift / 64;
let bit_shift = shift % 64;
for i in 0..$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);
}
}
$name(ret)
}
}
impl ::std::ops::Shr<usize> for $name {
type Output = $name;
#[allow(unsigned_negate)]
fn shr(self, shift: usize) -> $name {
let $name(ref original) = self;
let mut ret = [0u64; $n_words];
let word_shift = shift / 64;
let bit_shift = shift % 64;
for i in 0..$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);
}
}
$name(ret)
}
}
impl ::std::cmp::Ord for $name {
fn cmp(&self, other: &$name) -> ::std::cmp::Ordering {
let &$name(ref me) = self;
let &$name(ref you) = other;
for i in 0..$n_words {
if me[$n_words - 1 - i] < you[$n_words - 1 - i] { return ::std::cmp::Ordering::Less; }
if me[$n_words - 1 - i] > you[$n_words - 1 - i] { return ::std::cmp::Ordering::Greater; }
}
return ::std::cmp::Ordering::Equal;
}
}
impl ::std::cmp::PartialOrd for $name {
fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &$name(ref data) = self;
try!(write!(f, "0x"));
for ch in data.iter().rev() {
try!(write!(f, "{:02x}", ch));
}
Ok(())
}
}
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $name {
#[inline]
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
use network::encodable::ConsensusEncodable;
let &$name(ref data) = self;
for word in data.iter() { try!(word.consensus_encode(s)); }
Ok(())
}
}
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $name {
fn consensus_decode(d: &mut D) -> Result<$name, D::Error> {
use network::encodable::ConsensusDecodable;
let ret: [u64; $n_words] = try!(ConsensusDecodable::consensus_decode(d));
Ok($name(ret))
}
}
);
} }
construct_uint!(Uint256, 4); construct_uint!(Uint256, 4);
construct_uint!(Uint128, 2); construct_uint!(Uint128, 2);
impl Uint256 { impl Uint256 {
/// Increment by 1 /// Increment by 1
#[inline] #[inline]
pub fn increment(&mut self) { pub fn increment(&mut self) {
let &mut Uint256(ref mut arr) = self; let &mut Uint256(ref mut arr) = self;
arr[0] += 1; arr[0] += 1;
if arr[0] == 0 { if arr[0] == 0 {
arr[1] += 1; arr[1] += 1;
if arr[1] == 0 { if arr[1] == 0 {
arr[2] += 1; arr[2] += 1;
if arr[2] == 0 { if arr[2] == 0 {
arr[3] += 1; arr[3] += 1;
}
}
} }
}
} }
}
/// Decay to a uint128 /// Decay to a uint128
#[inline] #[inline]
pub fn low_128(&self) -> Uint128 { pub fn low_128(&self) -> Uint128 {
let &Uint256(data) = self; let &Uint256(data) = self;
Uint128([data[0], data[1]]) Uint128([data[0], data[1]])
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io; use std::io;
use std::num::from_u64; use std::num::from_u64;
use network::serialize::{deserialize, serialize}; use network::serialize::{deserialize, serialize};
use util::uint::Uint256; use util::uint::Uint256;
use util::BitArray; use util::BitArray;
#[test] #[test]
pub fn uint256_bits_test() { pub fn uint256_bits_test() {
assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8); assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8);
assert_eq!(from_u64::<Uint256>(256).unwrap().bits(), 9); assert_eq!(from_u64::<Uint256>(256).unwrap().bits(), 9);
assert_eq!(from_u64::<Uint256>(300).unwrap().bits(), 9); assert_eq!(from_u64::<Uint256>(300).unwrap().bits(), 9);
assert_eq!(from_u64::<Uint256>(60000).unwrap().bits(), 16); assert_eq!(from_u64::<Uint256>(60000).unwrap().bits(), 16);
assert_eq!(from_u64::<Uint256>(70000).unwrap().bits(), 17); assert_eq!(from_u64::<Uint256>(70000).unwrap().bits(), 17);
// Try to read the following lines out loud quickly // Try to read the following lines out loud quickly
let mut shl: Uint256 = from_u64(70000).unwrap(); let mut shl: Uint256 = from_u64(70000).unwrap();
shl = shl << 100; shl = shl << 100;
assert_eq!(shl.bits(), 117); assert_eq!(shl.bits(), 117);
shl = shl << 100; shl = shl << 100;
assert_eq!(shl.bits(), 217); assert_eq!(shl.bits(), 217);
shl = shl << 100; shl = shl << 100;
assert_eq!(shl.bits(), 0); assert_eq!(shl.bits(), 0);
// Bit set check // Bit set check
assert!(!from_u64::<Uint256>(10).unwrap().bit(0)); assert!(!from_u64::<Uint256>(10).unwrap().bit(0));
assert!(from_u64::<Uint256>(10).unwrap().bit(1)); assert!(from_u64::<Uint256>(10).unwrap().bit(1));
assert!(!from_u64::<Uint256>(10).unwrap().bit(2)); assert!(!from_u64::<Uint256>(10).unwrap().bit(2));
assert!(from_u64::<Uint256>(10).unwrap().bit(3)); assert!(from_u64::<Uint256>(10).unwrap().bit(3));
assert!(!from_u64::<Uint256>(10).unwrap().bit(4)); assert!(!from_u64::<Uint256>(10).unwrap().bit(4));
} }
#[test] #[test]
pub fn uint256_comp_test() { pub fn uint256_comp_test() {
let small = Uint256([10u64, 0, 0, 0]); let small = Uint256([10u64, 0, 0, 0]);
let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]); let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]);
assert!(small < big); assert!(small < big);
assert!(big < bigger); assert!(big < bigger);
assert!(bigger < biggest); assert!(bigger < biggest);
assert!(bigger <= biggest); assert!(bigger <= biggest);
assert!(biggest <= biggest); assert!(biggest <= biggest);
assert!(bigger >= big); assert!(bigger >= big);
assert!(bigger >= small); assert!(bigger >= small);
assert!(small <= small); assert!(small <= small);
} }
#[test] #[test]
pub fn uint256_arithmetic_test() { pub fn uint256_arithmetic_test() {
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap(); let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
let copy = init; let copy = init;
let add = init + copy; let add = init + copy;
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0])); assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
// Bitshifts // Bitshifts
let shl = add << 88; let shl = add << 88;
assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0])); assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0]));
let shr = shl >> 40; let shr = shl >> 40;
assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0])); assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]));
// Increment // Increment
let mut incr = shr; let mut incr = shr;
incr.increment(); incr.increment();
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0])); assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
// Subtraction // Subtraction
let sub = incr - init; let sub = incr - init;
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0])); assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
// Multiplication // Multiplication
let mult = sub.mul_u32(300); let mult = sub.mul_u32(300);
assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0])); assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]));
// Division // Division
assert_eq!(from_u64::<Uint256>(105).unwrap() / assert_eq!(from_u64::<Uint256>(105).unwrap() /
from_u64::<Uint256>(5).unwrap(), from_u64::<Uint256>(5).unwrap(),
from_u64::<Uint256>(21).unwrap()); from_u64::<Uint256>(21).unwrap());
let div = mult / from_u64::<Uint256>(300).unwrap(); let div = mult / from_u64::<Uint256>(300).unwrap();
assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0])); assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
// TODO: bit inversion // TODO: bit inversion
} }
#[test] #[test]
pub fn uint256_bitslice_test() { pub fn uint256_bitslice_test() {
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap(); let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
let add = init + (init << 64); let add = init + (init << 64);
assert_eq!(add.bit_slice(64, 128), init); assert_eq!(add.bit_slice(64, 128), init);
assert_eq!(add.mask(64), init); assert_eq!(add.mask(64), init);
} }
#[test] #[test]
pub fn uint256_extreme_bitshift_test() { pub fn uint256_extreme_bitshift_test() {
// Shifting a u64 by 64 bits gives an undefined value, so make sure that // Shifting a u64 by 64 bits gives an undefined value, so make sure that
// we're doing the Right Thing here // we're doing the Right Thing here
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap(); let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0])); assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
let add = (init << 64) + init; let add = (init << 64) + init;
assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0])); assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0])); assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
} }
#[test] #[test]
pub fn uint256_serialize_test() { pub fn uint256_serialize_test() {
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]); let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
let serial1 = serialize(&start1).unwrap(); let serial1 = serialize(&start1).unwrap();
let serial2 = serialize(&start2).unwrap(); let serial2 = serialize(&start2).unwrap();
let end1: io::Result<Uint256> = deserialize(serial1); let end1: io::Result<Uint256> = deserialize(serial1);
let end2: io::Result<Uint256> = deserialize(serial2); let end2: io::Result<Uint256> = deserialize(serial2);
assert_eq!(end1, Ok(start1)); assert_eq!(end1, Ok(start1));
assert_eq!(end2, Ok(start2)); assert_eq!(end2, Ok(start2));
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without // the public domain worldwide. This software is distributed without
@ -21,7 +21,7 @@ use crypto::digest::Digest;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use std::ops; use std::ops;
use blockdata::script::Script; use blockdata::script::{Script, ScriptBuilder};
use blockdata::opcodes; use blockdata::opcodes;
use network::constants::Network; use network::constants::Network;
use util::hash::Ripemd160Hash; use util::hash::Ripemd160Hash;
@ -30,189 +30,189 @@ use util::base58::{self, FromBase58, ToBase58};
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
/// A Bitcoin address /// A Bitcoin address
pub struct Address { pub struct Address {
/// The network on which this address is usable /// The network on which this address is usable
pub network: Network, pub network: Network,
/// The pubkeyhash that this address encodes /// The pubkeyhash that this address encodes
pub hash: Ripemd160Hash pub hash: Ripemd160Hash
} }
impl Address { impl Address {
/// Creates an address from a public key /// Creates an address from a public key
#[inline] #[inline]
pub fn from_key(network: Network, pk: &PublicKey) -> Address { pub fn from_key(network: Network, pk: &PublicKey) -> Address {
let mut sha = Sha256::new(); let mut sha = Sha256::new();
let mut out = [0;32]; let mut out = [0;32];
sha.input(&pk[..]); sha.input(&pk[..]);
sha.result(&mut out); sha.result(&mut out);
Address { Address {
network: network, network: network,
hash: Ripemd160Hash::from_data(&out) hash: Ripemd160Hash::from_data(&out)
}
} }
}
/// Generates a script pubkey spending to this address /// Generates a script pubkey spending to this address
#[inline] #[inline]
pub fn script_pubkey(&self) -> Script { pub fn script_pubkey(&self) -> Script {
let mut script = Script::new(); let mut script = ScriptBuilder::new();
script.push_opcode(opcodes::All::OP_DUP); script.push_opcode(opcodes::All::OP_DUP);
script.push_opcode(opcodes::All::OP_HASH160); script.push_opcode(opcodes::All::OP_HASH160);
script.push_slice(&self.hash[..]); script.push_slice(&self.hash[..]);
script.push_opcode(opcodes::All::OP_EQUALVERIFY); script.push_opcode(opcodes::All::OP_EQUALVERIFY);
script.push_opcode(opcodes::All::OP_CHECKSIG); script.push_opcode(opcodes::All::OP_CHECKSIG);
script script.into_script()
} }
} }
impl ops::Index<usize> for Address { impl ops::Index<usize> for Address {
type Output = u8; type Output = u8;
#[inline] #[inline]
fn index(&self, index: usize) -> &u8 { fn index(&self, index: usize) -> &u8 {
&self.hash[index] &self.hash[index]
} }
} }
impl ops::Index<ops::Range<usize>> for Address { impl ops::Index<ops::Range<usize>> for Address {
type Output = [u8]; type Output = [u8];
#[inline] #[inline]
fn index(&self, index: ops::Range<usize>) -> &[u8] { fn index(&self, index: ops::Range<usize>) -> &[u8] {
&self.hash[index] &self.hash[index]
} }
} }
impl ops::Index<ops::RangeTo<usize>> for Address { impl ops::Index<ops::RangeTo<usize>> for Address {
type Output = [u8]; type Output = [u8];
#[inline] #[inline]
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] { fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
&self.hash[index] &self.hash[index]
} }
} }
impl ops::Index<ops::RangeFrom<usize>> for Address { impl ops::Index<ops::RangeFrom<usize>> for Address {
type Output = [u8]; type Output = [u8];
#[inline] #[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] { fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
&self.hash[index] &self.hash[index]
} }
} }
impl ops::Index<ops::RangeFull> for Address { impl ops::Index<ops::RangeFull> for Address {
type Output = [u8]; type Output = [u8];
#[inline] #[inline]
fn index(&self, _: ops::RangeFull) -> &[u8] { fn index(&self, _: ops::RangeFull) -> &[u8] {
&self.hash[..] &self.hash[..]
} }
} }
/// Conversion from other types into an address /// Conversion from other types into an address
pub trait ToAddress { pub trait ToAddress {
/// Copies `self` into a new `Address` /// Copies `self` into a new `Address`
fn to_address(&self, network: Network) -> Address; fn to_address(&self, network: Network) -> Address;
} }
impl<'a> ToAddress for &'a [u8] { impl<'a> ToAddress for &'a [u8] {
#[inline] #[inline]
fn to_address(&self, network: Network) -> Address { fn to_address(&self, network: Network) -> Address {
Address { Address {
network: network, network: network,
hash: Ripemd160Hash::from_slice(*self) hash: Ripemd160Hash::from_slice(*self)
}
} }
}
} }
impl ToBase58 for Address { impl ToBase58 for Address {
fn base58_layout(&self) -> Vec<u8> { fn base58_layout(&self) -> Vec<u8> {
let mut ret = vec![ let mut ret = vec![
match self.network { match self.network {
Network::Bitcoin => 0, Network::Bitcoin => 0,
Network::Testnet => 111 Network::Testnet => 111
} }
]; ];
ret.push_all(&self.hash[..]); ret.push_all(&self.hash[..]);
ret ret
} }
} }
impl FromBase58 for Address { impl FromBase58 for Address {
fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> { fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> {
if data.len() != 21 { if data.len() != 21 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
} }
Ok(Address { Ok(Address {
network: match data[0] { network: match data[0] {
0 => Network::Bitcoin, 0 => Network::Bitcoin,
111 => Network::Testnet, 111 => Network::Testnet,
x => { return Err(base58::Error::InvalidVersion(vec![x])); } x => { return Err(base58::Error::InvalidVersion(vec![x])); }
}, },
hash: Ripemd160Hash::from_slice(&data[1..]) hash: Ripemd160Hash::from_slice(&data[1..])
}) })
} }
} }
impl ::std::fmt::Debug for Address { impl ::std::fmt::Debug for Address {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.to_base58check()) write!(f, "{}", self.to_base58check())
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serialize::hex::FromHex; use serialize::hex::FromHex;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use network::constants::Network::Bitcoin; use network::constants::Network::Bitcoin;
use util::hash::Ripemd160Hash; use util::hash::Ripemd160Hash;
use util::base58::{FromBase58, ToBase58}; use util::base58::{FromBase58, ToBase58};
use super::Address; use super::Address;
#[test] #[test]
fn test_address_58() { fn test_address_58() {
let addr = Address { let addr = Address {
network: Bitcoin, network: Bitcoin,
hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap()) hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap())
}; };
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr)); assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
} }
#[bench] #[bench]
pub fn generate_address(bh: &mut Bencher) { pub fn generate_address(bh: &mut Bencher) {
let mut s = Secp256k1::new().unwrap(); let mut s = Secp256k1::new().unwrap();
bh.iter( || { bh.iter( || {
let (sk, pk) = s.generate_keypair(true); let (sk, pk) = s.generate_keypair(true);
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] #[bench]
pub fn generate_uncompressed_address(bh: &mut Bencher) { pub fn generate_uncompressed_address(bh: &mut Bencher) {
let mut s = Secp256k1::new().unwrap(); let mut s = Secp256k1::new().unwrap();
bh.iter( || { bh.iter( || {
let (sk, pk) = s.generate_keypair(false); let (sk, pk) = s.generate_keypair(false);
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] #[bench]
pub fn generate_sequential_address(bh: &mut Bencher) { pub fn generate_sequential_address(bh: &mut Bencher) {
let mut s = Secp256k1::new().unwrap(); let mut s = Secp256k1::new().unwrap();
let (sk, _) = s.generate_keypair(true); let (sk, _) = s.generate_keypair(true);
let mut iter = sk.sequence(true); let mut iter = sk.sequence(true);
bh.iter( || { bh.iter( || {
let (sk, pk) = iter.next().unwrap(); let (sk, pk) = iter.next().unwrap();
black_box(sk); black_box(sk);
let addr = Address::from_key(Bitcoin, &pk); let addr = Address::from_key(Bitcoin, &pk);
black_box(addr); black_box(addr);
}); });
} }
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without // the public domain worldwide. This software is distributed without
@ -35,100 +35,100 @@ use util::hash::Sha256dHash;
/// The type of a wallet-spendable txout /// The type of a wallet-spendable txout
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum WalletTxOutType { pub enum WalletTxOutType {
/// Pay-to-address transaction redeemable using an ECDSA key /// Pay-to-address transaction redeemable using an ECDSA key
PayToAddress(SecretKey), PayToAddress(SecretKey),
/// Undetermined /// Undetermined
Unknown Unknown
} }
/// A txout that is spendable by the wallet /// A txout that is spendable by the wallet
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct WalletTxOut { pub struct WalletTxOut {
/// The TXID of the transaction this output is part of /// The TXID of the transaction this output is part of
pub txid: Sha256dHash, pub txid: Sha256dHash,
/// The index of the output in its transaction /// The index of the output in its transaction
pub vout: u32, pub vout: u32,
/// The blockheight at which this output appeared in the blockchain /// The blockheight at which this output appeared in the blockchain
pub height: u32, pub height: u32,
/// The actual output /// The actual output
pub txo: TxOut, pub txo: TxOut,
/// A classification of the output /// A classification of the output
pub kind: WalletTxOutType pub kind: WalletTxOutType
} }
/// An address index /// An address index
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct AddressIndex { pub struct AddressIndex {
tentative_index: HashMap<Script, Vec<WalletTxOut>>, tentative_index: HashMap<Script, Vec<WalletTxOut>>,
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>, index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>,
network: Network, network: Network,
k1: u64, k1: u64,
k2: u64 k2: u64
} }
impl AddressIndex { impl AddressIndex {
/// Creates a new address index from a wallet (which provides an authenticated /// Creates a new address index from a wallet (which provides an authenticated
/// hash function for prefix filtering) and UTXO set (which is what gets filtered). /// hash function for prefix filtering) and UTXO set (which is what gets filtered).
pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex { pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex {
let (k1, k2) = wallet.siphash_key(); let (k1, k2) = wallet.siphash_key();
let mut ret = AddressIndex { let mut ret = AddressIndex {
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256), tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
index: HashMap::new(), index: HashMap::new(),
network: wallet.network(), network: wallet.network(),
k1: k1, k1: k1,
k2: k2 k2: k2
};
for (key, idx, txo, height) in utxo_set.iter() {
if ret.admissible_txo(txo) {
let new = WalletTxOut {
txid: key,
vout: idx,
height: height,
txo: txo.clone(),
kind: WalletTxOutType::Unknown
}; };
let mut entry = ret.tentative_index.entry(txo.script_pubkey.clone()); for (key, idx, txo, height) in utxo_set.iter() {
if ret.admissible_txo(txo) {
let new = WalletTxOut {
txid: key,
vout: idx,
height: height,
txo: txo.clone(),
kind: WalletTxOutType::Unknown
};
let mut entry = ret.tentative_index.entry(txo.script_pubkey.clone());
let txos = entry.or_insert(vec![]);
txos.push(new);
}
}
ret
}
///
#[inline]
pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) {
let mut new = wtx.clone();
new.kind = kind;
let mut entry = self.index.entry((wtx.txid, wtx.vout));
let txos = entry.or_insert(vec![]); let txos = entry.or_insert(vec![]);
txos.push(new); txos.push(new);
}
} }
ret
}
/// /// A filtering function used for creating a small address index.
#[inline] #[inline]
pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) { pub fn admissible_address(&self, addr: &Address) -> bool {
let mut new = wtx.clone(); let mut hasher = SipHasher::new_with_keys(self.k1, self.k2);
new.kind = kind; (&addr[..]).hash(&mut hasher);
let mut entry = self.index.entry((wtx.txid, wtx.vout)); hasher.finish() & 0xFF == 0
let txos = entry.or_insert(vec![]);
txos.push(new);
}
/// A filtering function used for creating a small address index.
#[inline]
pub fn admissible_address(&self, addr: &Address) -> bool {
let mut hasher = SipHasher::new_with_keys(self.k1, self.k2);
(&addr[..]).hash(&mut hasher);
hasher.finish() & 0xFF == 0
}
/// A filtering function used for creating a small address index.
#[inline]
pub fn admissible_txo(&self, out: &TxOut) -> bool {
match out.classify(self.network) {
PayToPubkeyHash(addr) => self.admissible_address(&addr),
_ => false
} }
}
/// Lookup a txout by its scriptpubkey. Returns a slice because there /// A filtering function used for creating a small address index.
/// may be more than one for any given scriptpubkey. #[inline]
#[inline] pub fn admissible_txo(&self, out: &TxOut) -> bool {
pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] { match out.classify(self.network) {
self.tentative_index.get(pubkey).map(|v| &v[..]).unwrap_or(&[]) PayToPubkeyHash(addr) => self.admissible_address(&addr),
} _ => false
}
}
/// Lookup a txout by its scriptpubkey. Returns a slice because there
/// may be more than one for any given scriptpubkey.
#[inline]
pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] {
self.tentative_index.get(pubkey).map(|v| &v[..]).unwrap_or(&[])
}
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without // the public domain worldwide. This software is distributed without
@ -46,535 +46,535 @@ impl_array_newtype_show!(Fingerprint);
impl_array_newtype_encodable!(Fingerprint, u8, 4); impl_array_newtype_encodable!(Fingerprint, u8, 4);
impl Default for Fingerprint { impl Default for Fingerprint {
fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) } fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) }
} }
/// Extended private key /// Extended private key
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ExtendedPrivKey { pub struct ExtendedPrivKey {
/// The network this key is to be used on /// The network this key is to be used on
pub network: Network, pub network: Network,
/// How many derivations this key is from the master (which is 0) /// How many derivations this key is from the master (which is 0)
pub depth: u8, pub depth: u8,
/// Fingerprint of the parent key (0 for master) /// Fingerprint of the parent key (0 for master)
pub parent_fingerprint: Fingerprint, pub parent_fingerprint: Fingerprint,
/// Child number of the key used to derive from parent (0 for master) /// Child number of the key used to derive from parent (0 for master)
pub child_number: ChildNumber, pub child_number: ChildNumber,
/// Secret key /// Secret key
pub secret_key: SecretKey, pub secret_key: SecretKey,
/// Chain code /// Chain code
pub chain_code: ChainCode pub chain_code: ChainCode
} }
/// Extended public key /// Extended public key
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ExtendedPubKey { pub struct ExtendedPubKey {
/// The network this key is to be used on /// The network this key is to be used on
pub network: Network, pub network: Network,
/// How many derivations this key is from the master (which is 0) /// How many derivations this key is from the master (which is 0)
pub depth: u8, pub depth: u8,
/// Fingerprint of the parent key /// Fingerprint of the parent key
pub parent_fingerprint: Fingerprint, pub parent_fingerprint: Fingerprint,
/// Child number of the key used to derive from parent (0 for master) /// Child number of the key used to derive from parent (0 for master)
pub child_number: ChildNumber, pub child_number: ChildNumber,
/// Public key /// Public key
pub public_key: PublicKey, pub public_key: PublicKey,
/// Chain code /// Chain code
pub chain_code: ChainCode pub chain_code: ChainCode
} }
/// A child number for a derived key /// A child number for a derived key
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum ChildNumber { pub enum ChildNumber {
/// Hardened key index, within [0, 2^31 - 1] /// Hardened key index, within [0, 2^31 - 1]
Hardened(u32), Hardened(u32),
/// Non-hardened key, within [0, 2^31 - 1] /// Non-hardened key, within [0, 2^31 - 1]
Normal(u32), Normal(u32),
} }
impl Serialize for ChildNumber { impl Serialize for ChildNumber {
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
where S: Serializer { where S: Serializer {
match *self { match *self {
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s), ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
ChildNumber::Normal(n) => n.serialize(s) ChildNumber::Normal(n) => n.serialize(s)
}
} }
}
} }
impl Deserialize for ChildNumber { impl Deserialize for ChildNumber {
fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error> fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error>
where D: Deserializer { where D: Deserializer {
let n: u32 = try!(Deserialize::deserialize(d)); let n: u32 = try!(Deserialize::deserialize(d));
if n < (1 << 31) { if n < (1 << 31) {
Ok(ChildNumber::Normal(n)) Ok(ChildNumber::Normal(n))
} else { } else {
Ok(ChildNumber::Hardened(n - (1 << 31))) Ok(ChildNumber::Hardened(n - (1 << 31)))
}
} }
}
} }
/// A BIP32 error /// A BIP32 error
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum Error { pub enum Error {
/// A pk->pk derivation was attempted on a hardened key /// A pk->pk derivation was attempted on a hardened key
CannotDeriveFromHardenedKey, CannotDeriveFromHardenedKey,
/// A secp256k1 error occured /// A secp256k1 error occured
Ecdsa(secp256k1::Error), Ecdsa(secp256k1::Error),
/// A child number was provided that was out of range /// A child number was provided that was out of range
InvalidChildNumber(ChildNumber), InvalidChildNumber(ChildNumber),
/// Error creating a master seed --- for application use /// Error creating a master seed --- for application use
RngError(String) RngError(String)
} }
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(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);
hmac.raw_result(&mut result); hmac.raw_result(&mut result);
Ok(ExtendedPrivKey { Ok(ExtendedPrivKey {
network: network, network: network,
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(&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
pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber])
-> Result<ExtendedPrivKey, Error> {
let mut sk = *master;
for &num in path.iter() {
sk = try!(sk.ckd_priv(num));
} }
Ok(sk)
}
/// Private->Private child key derivation /// Creates a privkey from a path
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> { pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber])
let mut result = [0; 64]; -> Result<ExtendedPrivKey, Error> {
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]); let mut sk = *master;
let mut be_n = [0; 32]; for &num in path.iter() {
match i { sk = try!(sk.ckd_priv(num));
ChildNumber::Normal(n) => { }
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) } Ok(sk)
// 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)[..]);
write_u32_be(&mut be_n, n);
}
ChildNumber::Hardened(n) => {
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
// Hardened key: use only secret data to prevent public derivation
hmac.input(&[0u8]);
hmac.input(&self.secret_key[..]);
write_u32_be(&mut be_n, n + (1 << 31));
}
} }
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));
Ok(ExtendedPrivKey { /// Private->Private child key derivation
network: self.network, pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
depth: self.depth + 1, let mut result = [0; 64];
parent_fingerprint: self.fingerprint(), let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
child_number: i, let mut be_n = [0; 32];
secret_key: sk, match i {
chain_code: ChainCode::from_slice(&result[32..]) 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)[..]);
write_u32_be(&mut be_n, n);
}
ChildNumber::Hardened(n) => {
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
// Hardened key: use only secret data to prevent public derivation
hmac.input(&[0u8]);
hmac.input(&self.secret_key[..]);
write_u32_be(&mut be_n, n + (1 << 31));
}
}
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));
/// Returns the HASH160 of the chaincode Ok(ExtendedPrivKey {
pub fn identifier(&self) -> [u8; 20] { network: self.network,
let mut sha2_res = [0; 32]; depth: self.depth + 1,
let mut ripemd_res = [0; 20]; parent_fingerprint: self.fingerprint(),
// Compute extended public key child_number: i,
let pk = ExtendedPubKey::from_private(self); secret_key: sk,
// Do SHA256 of just the ECDSA pubkey chain_code: ChainCode::from_slice(&result[32..])
let mut sha2 = Sha256::new(); })
sha2.input(&pk.public_key[..]); }
sha2.result(&mut sha2_res);
// do RIPEMD160
let mut ripemd = Ripemd160::new();
ripemd.input(&sha2_res);
ripemd.result(&mut ripemd_res);
// Return
ripemd_res
}
/// Returns the first four bytes of the identifier /// Returns the HASH160 of the chaincode
pub fn fingerprint(&self) -> Fingerprint { pub fn identifier(&self) -> [u8; 20] {
Fingerprint::from_slice(&self.identifier()[0..4]) let mut sha2_res = [0; 32];
} let mut ripemd_res = [0; 20];
// Compute extended public key
let pk = ExtendedPubKey::from_private(self);
// Do SHA256 of just the ECDSA pubkey
let mut sha2 = Sha256::new();
sha2.input(&pk.public_key[..]);
sha2.result(&mut sha2_res);
// do RIPEMD160
let mut ripemd = Ripemd160::new();
ripemd.input(&sha2_res);
ripemd.result(&mut ripemd_res);
// Return
ripemd_res
}
/// Returns the first four bytes of the identifier
pub fn fingerprint(&self) -> Fingerprint {
Fingerprint::from_slice(&self.identifier()[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(sk: &ExtendedPrivKey) -> ExtendedPubKey {
secp256k1::init(); 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(&sk.secret_key, true),
chain_code: sk.chain_code chain_code: sk.chain_code
}
}
/// Public->Public child key derivation
pub fn ckd_pub(&self, i: ChildNumber) -> Result<ExtendedPubKey, Error> {
match i {
ChildNumber::Hardened(n) => {
if n >= (1 << 31) {
Err(Error::InvalidChildNumber(i))
} else {
Err(Error::CannotDeriveFromHardenedKey)
} }
}
ChildNumber::Normal(n) => {
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
hmac.input(&self.public_key[..]);
let mut be_n = [0; 32];
write_u32_be(&mut be_n, n);
hmac.input(&be_n);
let mut result = [0; 64];
hmac.raw_result(&mut result);
let sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
let mut pk = self.public_key.clone();
try!(pk.add_exp_assign(&sk).map_err(Error::Ecdsa));
Ok(ExtendedPubKey {
network: self.network,
depth: self.depth + 1,
parent_fingerprint: self.fingerprint(),
child_number: i,
public_key: pk,
chain_code: ChainCode::from_slice(&result[32..])
})
}
} }
}
/// Returns the HASH160 of the chaincode /// Public->Public child key derivation
pub fn identifier(&self) -> [u8; 20] { pub fn ckd_pub(&self, i: ChildNumber) -> Result<ExtendedPubKey, Error> {
let mut sha2_res = [0; 32]; match i {
let mut ripemd_res = [0; 20]; ChildNumber::Hardened(n) => {
// Do SHA256 of just the ECDSA pubkey if n >= (1 << 31) {
let mut sha2 = Sha256::new(); Err(Error::InvalidChildNumber(i))
sha2.input(&self.public_key[..]); } else {
sha2.result(&mut sha2_res); Err(Error::CannotDeriveFromHardenedKey)
// do RIPEMD160 }
let mut ripemd = Ripemd160::new(); }
ripemd.input(&sha2_res); ChildNumber::Normal(n) => {
ripemd.result(&mut ripemd_res); let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
// Return hmac.input(&self.public_key[..]);
ripemd_res let mut be_n = [0; 32];
} write_u32_be(&mut be_n, n);
hmac.input(&be_n);
/// Returns the first four bytes of the identifier let mut result = [0; 64];
pub fn fingerprint(&self) -> Fingerprint { hmac.raw_result(&mut result);
Fingerprint::from_slice(&self.identifier()[0..4])
} let sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
let mut pk = self.public_key.clone();
try!(pk.add_exp_assign(&sk).map_err(Error::Ecdsa));
Ok(ExtendedPubKey {
network: self.network,
depth: self.depth + 1,
parent_fingerprint: self.fingerprint(),
child_number: i,
public_key: pk,
chain_code: ChainCode::from_slice(&result[32..])
})
}
}
}
/// Returns the HASH160 of the chaincode
pub fn identifier(&self) -> [u8; 20] {
let mut sha2_res = [0; 32];
let mut ripemd_res = [0; 20];
// Do SHA256 of just the ECDSA pubkey
let mut sha2 = Sha256::new();
sha2.input(&self.public_key[..]);
sha2.result(&mut sha2_res);
// do RIPEMD160
let mut ripemd = Ripemd160::new();
ripemd.input(&sha2_res);
ripemd.result(&mut ripemd_res);
// Return
ripemd_res
}
/// Returns the first four bytes of the identifier
pub fn fingerprint(&self) -> Fingerprint {
Fingerprint::from_slice(&self.identifier()[0..4])
}
} }
impl ToBase58 for ExtendedPrivKey { impl ToBase58 for ExtendedPrivKey {
fn base58_layout(&self) -> Vec<u8> { fn base58_layout(&self) -> Vec<u8> {
let mut ret = Vec::with_capacity(78); let mut ret = Vec::with_capacity(78);
ret.push_all(match self.network { ret.push_all(match self.network {
Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4], Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4],
Network::Testnet => &[0x04, 0x35, 0x83, 0x94] Network::Testnet => &[0x04, 0x35, 0x83, 0x94]
}); });
ret.push(self.depth as u8); ret.push(self.depth as u8);
ret.push_all(&self.parent_fingerprint[..]); ret.push_all(&self.parent_fingerprint[..]);
let mut be_n = [0; 32]; let mut be_n = [0; 32];
match self.child_number { match self.child_number {
ChildNumber::Hardened(n) => { ChildNumber::Hardened(n) => {
write_u32_be(&mut be_n, n + (1 << 31)); write_u32_be(&mut be_n, n + (1 << 31));
} }
ChildNumber::Normal(n) => { ChildNumber::Normal(n) => {
write_u32_be(&mut be_n, n); write_u32_be(&mut be_n, n);
} }
}
ret.push_all(&be_n);
ret.push_all(&self.chain_code[..]);
ret.push(0);
ret.push_all(&self.secret_key[..]);
ret
} }
ret.push_all(&be_n);
ret.push_all(&self.chain_code[..]);
ret.push(0);
ret.push_all(&self.secret_key[..]);
ret
}
} }
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> {
if data.len() != 78 { if data.len() != 78 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
}
let cn_int = read_u32_be(&data[9..13]);
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
Ok(ExtendedPrivKey {
network: match &data[0..4] {
[0x04u8, 0x88, 0xAD, 0xE4] => Network::Bitcoin,
[0x04u8, 0x35, 0x83, 0x94] => Network::Testnet,
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
},
depth: data[4],
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(
&data[46..78]).map_err(|e|
base58::Error::Other(e.to_string())))
})
} }
let cn_int = read_u32_be(&data[9..13]);
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
Ok(ExtendedPrivKey {
network: match &data[0..4] {
[0x04u8, 0x88, 0xAD, 0xE4] => Network::Bitcoin,
[0x04u8, 0x35, 0x83, 0x94] => Network::Testnet,
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
},
depth: data[4],
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(
&data[46..78]).map_err(|e|
base58::Error::Other(e.to_string())))
})
}
} }
impl ToBase58 for ExtendedPubKey { impl ToBase58 for ExtendedPubKey {
fn base58_layout(&self) -> Vec<u8> { fn base58_layout(&self) -> Vec<u8> {
assert!(self.public_key.is_compressed()); assert!(self.public_key.is_compressed());
let mut ret = Vec::with_capacity(78); let mut ret = Vec::with_capacity(78);
ret.push_all(match self.network { ret.push_all(match self.network {
Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E], Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E],
Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF] Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF]
}); });
ret.push(self.depth as u8); ret.push(self.depth as u8);
ret.push_all(&self.parent_fingerprint[..]); ret.push_all(&self.parent_fingerprint[..]);
let mut be_n = [0; 32]; let mut be_n = [0; 32];
match self.child_number { match self.child_number {
ChildNumber::Hardened(n) => { ChildNumber::Hardened(n) => {
write_u32_be(&mut be_n, n + (1 << 31)); write_u32_be(&mut be_n, n + (1 << 31));
} }
ChildNumber::Normal(n) => { ChildNumber::Normal(n) => {
write_u32_be(&mut be_n, n); write_u32_be(&mut be_n, n);
} }
}
ret.push_all(&be_n);
ret.push_all(&self.chain_code[..]);
ret.push_all(&self.public_key[..]);
ret
} }
ret.push_all(&be_n);
ret.push_all(&self.chain_code[..]);
ret.push_all(&self.public_key[..]);
ret
}
} }
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> {
if data.len() != 78 { if data.len() != 78 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
}
let cn_int = read_u32_be(&data[9..13]);
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
Ok(ExtendedPubKey {
network: match &data[0..4] {
[0x04, 0x88, 0xB2, 0x1E] => Network::Bitcoin,
[0x04, 0x35, 0x87, 0xCF] => Network::Testnet,
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
},
depth: data[4],
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(
&data[45..78]).map_err(|e|
base58::Error::Other(e.to_string())))
})
} }
let cn_int = read_u32_be(&data[9..13]);
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
Ok(ExtendedPubKey {
network: match &data[0..4] {
[0x04, 0x88, 0xB2, 0x1E] => Network::Bitcoin,
[0x04, 0x35, 0x87, 0xCF] => Network::Testnet,
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
},
depth: data[4],
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(
&data[45..78]).map_err(|e|
base58::Error::Other(e.to_string())))
})
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serialize::hex::FromHex; use serialize::hex::FromHex;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
use network::constants::Network::{self, Bitcoin}; use network::constants::Network::{self, Bitcoin};
use util::base58::{FromBase58, ToBase58}; use util::base58::{FromBase58, ToBase58};
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(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(network, seed).unwrap();
let mut pk = ExtendedPubKey::from_private(&sk); let mut pk = ExtendedPubKey::from_private(&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(num).unwrap();
match num { match num {
Normal(_) => { Normal(_) => {
let pk2 = pk.ckd_pub(num).unwrap(); let pk2 = pk.ckd_pub(num).unwrap();
pk = ExtendedPubKey::from_private(&sk); pk = ExtendedPubKey::from_private(&sk);
assert_eq!(pk, pk2); assert_eq!(pk, pk2);
}
Hardened(_) => {
pk = ExtendedPubKey::from_private(&sk);
}
}
} }
Hardened(_) => {
pk = ExtendedPubKey::from_private(&sk); // Check result against expected base58
} assert_eq!(&sk.to_base58check()[..], expected_sk);
} assert_eq!(&pk.to_base58check()[..], expected_pk);
// Check decoded base58 against result
let decoded_sk = FromBase58::from_base58check(expected_sk);
let decoded_pk = FromBase58::from_base58check(expected_pk);
assert_eq!(Ok(sk), decoded_sk);
assert_eq!(Ok(pk), decoded_pk);
} }
// Check result against expected base58 #[test]
assert_eq!(&sk.to_base58check()[..], expected_sk); fn test_vector_1() {
assert_eq!(&pk.to_base58check()[..], expected_pk); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
// Check decoded base58 against result // m
let decoded_sk = FromBase58::from_base58check(expected_sk); test_path(Bitcoin, &seed, [],
let decoded_pk = FromBase58::from_base58check(expected_pk); "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
assert_eq!(Ok(sk), decoded_sk); "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
assert_eq!(Ok(pk), decoded_pk);
}
#[test] // m/0h
fn test_vector_1() { test_path(Bitcoin, &seed, [Hardened(0)],
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
// m "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
test_path(Bitcoin, &seed, [],
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
// m/0h // m/0h/1
test_path(Bitcoin, &seed, [Hardened(0)], test_path(Bitcoin, &seed, [Hardened(0), Normal(1)],
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"); "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
// m/0h/1 // m/0h/1/2h
test_path(Bitcoin, &seed, [Hardened(0), Normal(1)], test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)],
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"); "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
// m/0h/1/2h // m/0h/1/2h/2
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)], test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)],
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"); "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
// m/0h/1/2h/2 // m/0h/1/2h/2/1000000000
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)], test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"); "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
}
// m/0h/1/2h/2/1000000000 #[test]
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)], fn test_vector_2() {
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
}
#[test] // m
fn test_vector_2() { test_path(Bitcoin, &seed, [],
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap(); "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
// m // m/0
test_path(Bitcoin, &seed, [], test_path(Bitcoin, &seed, [Normal(0)],
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"); "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
// m/0 // m/0/2147483647h
test_path(Bitcoin, &seed, [Normal(0)], test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)],
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"); "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
// m/0/2147483647h // m/0/2147483647h/1
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)], test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)],
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"); "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
// m/0/2147483647h/1 // m/0/2147483647h/1/2147483646h
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)], test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"); "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
// m/0/2147483647h/1/2147483646h // m/0/2147483647h/1/2147483646h/2
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)], test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"); "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
}
// m/0/2147483647h/1/2147483646h/2 #[test]
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)], pub fn encode_decode_childnumber() {
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", use serialize::json;
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
}
#[test] let h1 = Hardened(1);
pub fn encode_decode_childnumber() { let n1 = Normal(1);
use serialize::json;
let h1 = Hardened(1); let h1_str = json::encode(&h1);
let n1 = Normal(1); let n1_str = json::encode(&n1);
let h1_str = json::encode(&h1); assert!(h1 != n1);
let n1_str = json::encode(&n1); assert!(h1_str != n1_str);
assert!(h1 != n1); let h1_dec = json::decode(&h1_str).unwrap();
assert!(h1_str != n1_str); let n1_dec = json::decode(&n1_str).unwrap();
assert_eq!(h1, h1_dec);
assert_eq!(n1, n1_dec);
}
let h1_dec = json::decode(&h1_str).unwrap(); #[bench]
let n1_dec = json::decode(&n1_str).unwrap(); pub fn generate_sequential_normal_children(bh: &mut Bencher) {
assert_eq!(h1, h1_dec); let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
assert_eq!(n1, n1_dec); let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
} let mut i = 0;
bh.iter( || {
black_box(msk.ckd_priv(Normal(i)));
i += 1;
})
}
#[bench] #[bench]
pub fn generate_sequential_normal_children(bh: &mut Bencher) { pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
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(Bitcoin, &seed).unwrap();
let mut i = 0; let mut i = 0;
bh.iter( || { bh.iter( || {
black_box(msk.ckd_priv(Normal(i))); black_box(msk.ckd_priv(Hardened(i)));
i += 1; i += 1;
}) })
} }
#[bench] #[bench]
pub fn generate_sequential_hardened_children(bh: &mut Bencher) { pub fn generate_sequential_public_children(bh: &mut Bencher) {
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(Bitcoin, &seed).unwrap();
let mut i = 0; let mpk = ExtendedPubKey::from_private(&msk);
bh.iter( || {
black_box(msk.ckd_priv(Hardened(i)));
i += 1;
})
}
#[bench] let mut i = 0;
pub fn generate_sequential_public_children(bh: &mut Bencher) { bh.iter( || {
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); black_box(mpk.ckd_pub(Normal(i)));
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); i += 1;
let mpk = ExtendedPubKey::from_private(&msk); })
}
let mut i = 0; #[bench]
bh.iter( || { pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) {
black_box(mpk.ckd_pub(Normal(i))); use wallet::address::Address;
i += 1;
})
}
#[bench] let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) { let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
use wallet::address::Address; let mpk = ExtendedPubKey::from_private(&msk);
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap(); let mut i = 0;
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap(); bh.iter( || {
let mpk = ExtendedPubKey::from_private(&msk); let epk = mpk.ckd_pub(Normal(i)).unwrap();
black_box(Address::from_key(Bitcoin, &epk.public_key));
let mut i = 0; i += 1;
bh.iter( || { })
let epk = mpk.ckd_pub(Normal(i)).unwrap(); }
black_box(Address::from_key(Bitcoin, &epk.public_key));
i += 1;
})
}
} }

View File

@ -1,6 +1,6 @@
// Rust Bitcoin Library // Rust Bitcoin Library
// Written in 2014 by // Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net> // Andrew Poelstra <apoelstra@wpsoftware.net>
// To the extent possible under law, the author(s) have dedicated all // To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to // copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without // the public domain worldwide. This software is distributed without
@ -33,243 +33,243 @@ use wallet::address_index::AddressIndex;
/// A Wallet error /// A Wallet error
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum Error { pub enum Error {
/// Tried to lookup an account by name, but none was found /// Tried to lookup an account by name, but none was found
AccountNotFound, AccountNotFound,
/// Tried to add an account when one already exists with that name /// Tried to add an account when one already exists with that name
DuplicateAccount, DuplicateAccount,
/// An error occured in a BIP32 derivation /// An error occured in a BIP32 derivation
Bip32Error(bip32::Error), Bip32Error(bip32::Error),
/// Tried to use a wallet without an address index /// Tried to use a wallet without an address index
NoAddressIndex NoAddressIndex
} }
/// Each account has two chains, as specified in BIP32 /// Each account has two chains, as specified in BIP32
pub enum AccountChain { pub enum AccountChain {
/// Internal addresses are used within the wallet for change, etc, /// Internal addresses are used within the wallet for change, etc,
/// and in principle every generated one will be used. /// and in principle every generated one will be used.
Internal, Internal,
/// External addresses are shared, and might not be used after generatation, /// External addresses are shared, and might not be used after generatation,
/// complicating recreating the whole wallet from seed. /// complicating recreating the whole wallet from seed.
External External
} }
/// An account /// An account
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct Account { pub struct Account {
internal_path: Vec<ChildNumber>, internal_path: Vec<ChildNumber>,
internal_used: Vec<ChildNumber>, internal_used: Vec<ChildNumber>,
internal_next: u32, internal_next: u32,
external_path: Vec<ChildNumber>, external_path: Vec<ChildNumber>,
external_used: Vec<ChildNumber>, external_used: Vec<ChildNumber>,
external_next: u32 external_next: u32
} }
impl Default for Account { impl Default for Account {
fn default() -> Account { fn default() -> Account {
Account { Account {
internal_path: vec![Hardened(0), Normal(1)], internal_path: vec![Hardened(0), Normal(1)],
internal_used: vec![], internal_used: vec![],
internal_next: 0, internal_next: 0,
external_path: vec![Hardened(0), Normal(0)], external_path: vec![Hardened(0), Normal(0)],
external_used: vec![], external_used: vec![],
external_next: 0 external_next: 0
}
} }
}
} }
/// A wallet /// A wallet
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Wallet { pub struct Wallet {
master: ExtendedPrivKey, master: ExtendedPrivKey,
accounts: HashMap<String, Account>, accounts: HashMap<String, Account>,
index: Option<AddressIndex> index: Option<AddressIndex>
} }
impl Serialize for Wallet { impl Serialize for Wallet {
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
where S: Serializer { where S: Serializer {
let len = self.accounts.len(); let len = self.accounts.len();
try!(self.master.serialize(s)); try!(self.master.serialize(s));
self.accounts.serialize(s) self.accounts.serialize(s)
} }
} }
impl Deserialize for Wallet { 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 {
master: try!(Deserialize::deserialize(d)), master: try!(Deserialize::deserialize(d)),
accounts: try!(Deserialize::deserialize(d)), accounts: try!(Deserialize::deserialize(d)),
index: None index: None
}) })
} }
} }
impl Wallet { impl Wallet {
/// Creates a new wallet from a BIP32 seed /// Creates a new wallet from a BIP32 seed
#[inline] #[inline]
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());
Ok(Wallet { Ok(Wallet {
master: try!(ExtendedPrivKey::new_master(network, seed)), master: try!(ExtendedPrivKey::new_master(network, seed)),
accounts: accounts, accounts: accounts,
index: None index: None
}) })
}
/// Creates the address index
#[inline]
pub fn build_index(&mut self, utxo_set: &UtxoSet) {
let new = AddressIndex::new(utxo_set, self);
self.index = Some(new);
}
/// Accessor for the wallet's address index
#[inline]
pub fn index<'a>(&'a self) -> Option<&'a AddressIndex> {
self.index.as_ref()
}
/// Mutable accessor for the wallet's address index
#[inline]
pub fn index_mut<'a>(&'a mut self) -> Option<&'a mut AddressIndex> {
self.index.as_mut()
}
/// Adds an account to a wallet
pub fn account_insert(&mut self, name: String)
-> Result<(), Error> {
if self.accounts.contains_key(&name) {
return Err(Error::DuplicateAccount);
} }
let idx = self.accounts.len() as u32; /// Creates the address index
self.accounts.insert(name, Account { #[inline]
internal_path: vec![Hardened(idx), Normal(1)], pub fn build_index(&mut self, utxo_set: &UtxoSet) {
internal_used: vec![], let new = AddressIndex::new(utxo_set, self);
internal_next: 0, self.index = Some(new);
external_path: vec![Hardened(idx), Normal(0)],
external_used: vec![],
external_next: 0
});
Ok(())
}
/// Locates an account in a wallet
#[inline]
pub fn account_get<'a>(&'a self, name: &str) -> Option<&'a Account> {
self.accounts.get(name)
}
/// Create a new address
pub fn new_address(&mut self,
account: &str,
chain: AccountChain)
-> Result<Address, Error> {
let account = self.accounts.get_mut(account);
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
let (mut i, master) = match chain {
Internal => (account.internal_next,
try!(ExtendedPrivKey::from_path(
&self.master,
account.internal_path.as_slice()).map_err(Error::Bip32Error))),
External => (account.external_next,
try!(ExtendedPrivKey::from_path(
&self.master,
account.external_path.as_slice()).map_err(Error::Bip32Error))),
};
// Scan for next admissible address
let mut sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
let mut address = Address::from_key(
master.network,
&PublicKey::from_secret_key(&sk.secret_key, true));
while !index.admissible_address(&address) {
i += 1;
sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
address = Address::from_key(
master.network,
&PublicKey::from_secret_key(&sk.secret_key, true));
} }
match chain { /// Accessor for the wallet's address index
Internal => { #[inline]
account.internal_used.push(Normal(i)); pub fn index<'a>(&'a self) -> Option<&'a AddressIndex> {
account.internal_next = i + 1; self.index.as_ref()
}
External => {
account.external_used.push(Normal(i));
account.external_next = i + 1;
}
} }
Ok(address) /// Mutable accessor for the wallet's address index
} #[inline]
pub fn index_mut<'a>(&'a mut self) -> Option<&'a mut AddressIndex> {
/// Returns the network of the wallet self.index.as_mut()
#[inline]
pub fn network(&self) -> Network {
self.master.network
}
/// Returns a key suitable for keying hash functions for DoS protection
#[inline]
pub fn siphash_key(&self) -> (u64, u64) {
(LittleEndian::read_u64(&self.master.chain_code[0..8]),
LittleEndian::read_u64(&self.master.chain_code[8..16]))
}
/// Total balance
pub fn total_balance(&self) -> Result<u64, Error> {
let mut ret = 0;
for (_, account) in self.accounts.iter() {
ret += try!(self.account_balance(account));
}
Ok(ret)
}
/// Account balance
pub fn balance(&self, account: &str) -> Result<u64, Error> {
let account = self.accounts.get(account);
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
self.account_balance(account)
}
fn account_balance(&self, account: &Account) -> Result<u64, Error> {
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
let mut ret = 0;
// Sum internal balance
let master = try!(ExtendedPrivKey::from_path(
&self.master,
account.internal_path.as_slice()).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 addr = Address::from_key(pk.network, &pk.public_key);
for out in index.find_by_script(&addr.script_pubkey()).iter() {
ret += out.txo.value;
}
}
// Sum external balance
let master = try!(ExtendedPrivKey::from_path(
&self.master,
account.external_path.as_slice()).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 addr = Address::from_key(pk.network, &pk.public_key);
for out in index.find_by_script(&addr.script_pubkey()).iter() {
ret += out.txo.value;
}
} }
Ok(ret) /// Adds an account to a wallet
} pub fn account_insert(&mut self, name: String)
-> Result<(), Error> {
if self.accounts.contains_key(&name) {
return Err(Error::DuplicateAccount);
}
let idx = self.accounts.len() as u32;
self.accounts.insert(name, Account {
internal_path: vec![Hardened(idx), Normal(1)],
internal_used: vec![],
internal_next: 0,
external_path: vec![Hardened(idx), Normal(0)],
external_used: vec![],
external_next: 0
});
Ok(())
}
/// Locates an account in a wallet
#[inline]
pub fn account_get<'a>(&'a self, name: &str) -> Option<&'a Account> {
self.accounts.get(name)
}
/// Create a new address
pub fn new_address(&mut self,
account: &str,
chain: AccountChain)
-> Result<Address, Error> {
let account = self.accounts.get_mut(account);
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
let (mut i, master) = match chain {
Internal => (account.internal_next,
try!(ExtendedPrivKey::from_path(
&self.master,
account.internal_path.as_slice()).map_err(Error::Bip32Error))),
External => (account.external_next,
try!(ExtendedPrivKey::from_path(
&self.master,
account.external_path.as_slice()).map_err(Error::Bip32Error))),
};
// Scan for next admissible address
let mut sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
let mut address = Address::from_key(
master.network,
&PublicKey::from_secret_key(&sk.secret_key, true));
while !index.admissible_address(&address) {
i += 1;
sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
address = Address::from_key(
master.network,
&PublicKey::from_secret_key(&sk.secret_key, true));
}
match chain {
Internal => {
account.internal_used.push(Normal(i));
account.internal_next = i + 1;
}
External => {
account.external_used.push(Normal(i));
account.external_next = i + 1;
}
}
Ok(address)
}
/// Returns the network of the wallet
#[inline]
pub fn network(&self) -> Network {
self.master.network
}
/// Returns a key suitable for keying hash functions for DoS protection
#[inline]
pub fn siphash_key(&self) -> (u64, u64) {
(LittleEndian::read_u64(&self.master.chain_code[0..8]),
LittleEndian::read_u64(&self.master.chain_code[8..16]))
}
/// Total balance
pub fn total_balance(&self) -> Result<u64, Error> {
let mut ret = 0;
for (_, account) in self.accounts.iter() {
ret += try!(self.account_balance(account));
}
Ok(ret)
}
/// Account balance
pub fn balance(&self, account: &str) -> Result<u64, Error> {
let account = self.accounts.get(account);
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
self.account_balance(account)
}
fn account_balance(&self, account: &Account) -> Result<u64, Error> {
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
let mut ret = 0;
// Sum internal balance
let master = try!(ExtendedPrivKey::from_path(
&self.master,
account.internal_path.as_slice()).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 addr = Address::from_key(pk.network, &pk.public_key);
for out in index.find_by_script(&addr.script_pubkey()).iter() {
ret += out.txo.value;
}
}
// Sum external balance
let master = try!(ExtendedPrivKey::from_path(
&self.master,
account.external_path.as_slice()).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 addr = Address::from_key(pk.network, &pk.public_key);
for out in index.find_by_script(&addr.script_pubkey()).iter() {
ret += out.txo.value;
}
}
Ok(ret)
}
} }