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:
parent
200e0fe8e3
commit
08a20f8764
|
@ -11,6 +11,9 @@ path = "src/lib.rs"
|
|||
[dependencies.secp256k1]
|
||||
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git"
|
||||
|
||||
[dependencies.eventual]
|
||||
git = "https://github.com/carllerche/eventual"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "*"
|
||||
num_cpus = "*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -34,101 +34,101 @@ use blockdata::transaction::Transaction;
|
|||
/// the actual transactions
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct BlockHeader {
|
||||
/// The protocol version. Should always be 1.
|
||||
pub version: u32,
|
||||
/// Reference to the previous block in the chain
|
||||
pub prev_blockhash: Sha256dHash,
|
||||
/// The root hash of the merkle tree of transactions in the block
|
||||
pub merkle_root: Sha256dHash,
|
||||
/// The timestamp of the block, as claimed by the mainer
|
||||
pub time: u32,
|
||||
/// The target value below which the blockhash must lie, encoded as a
|
||||
/// a float (with well-defined rounding, of course)
|
||||
pub bits: u32,
|
||||
/// The nonce, selected to obtain a low enough blockhash
|
||||
pub nonce: u32,
|
||||
/// The protocol version. Should always be 1.
|
||||
pub version: u32,
|
||||
/// Reference to the previous block in the chain
|
||||
pub prev_blockhash: Sha256dHash,
|
||||
/// The root hash of the merkle tree of transactions in the block
|
||||
pub merkle_root: Sha256dHash,
|
||||
/// The timestamp of the block, as claimed by the mainer
|
||||
pub time: u32,
|
||||
/// The target value below which the blockhash must lie, encoded as a
|
||||
/// a float (with well-defined rounding, of course)
|
||||
pub bits: u32,
|
||||
/// The nonce, selected to obtain a low enough blockhash
|
||||
pub nonce: u32,
|
||||
}
|
||||
|
||||
/// A Bitcoin block, which is a collection of transactions with an attached
|
||||
/// proof of work.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Block {
|
||||
/// The block header
|
||||
pub header: BlockHeader,
|
||||
/// List of transactions contained in the block
|
||||
pub txdata: Vec<Transaction>
|
||||
/// The block header
|
||||
pub header: BlockHeader,
|
||||
/// List of transactions contained in the block
|
||||
pub txdata: Vec<Transaction>
|
||||
}
|
||||
|
||||
/// A block header with txcount attached, which is given in the `headers`
|
||||
/// network message.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct LoneBlockHeader {
|
||||
/// The actual block header
|
||||
pub header: BlockHeader,
|
||||
/// The number of transactions in the block. This will always be zero
|
||||
/// when the LoneBlockHeader is returned as part ef a `headers` message.
|
||||
pub tx_count: VarInt
|
||||
/// The actual block header
|
||||
pub header: BlockHeader,
|
||||
/// The number of transactions in the block. This will always be zero
|
||||
/// when the LoneBlockHeader is returned as part ef a `headers` message.
|
||||
pub tx_count: VarInt
|
||||
}
|
||||
|
||||
impl BlockHeader {
|
||||
/// Computes the target [0, T] that a blockhash must land in to be valid
|
||||
pub fn target(&self) -> Uint256 {
|
||||
// This is a floating-point "compact" encoding originally used by
|
||||
// OpenSSL, which satoshi put into consensus code, so we're stuck
|
||||
// with it. The exponent needs to have 3 subtracted from it, hence
|
||||
// this goofy decoding code:
|
||||
let (mant, expt) = {
|
||||
let unshifted_expt = self.bits >> 24;
|
||||
if unshifted_expt <= 3 {
|
||||
((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as usize), 0)
|
||||
} else {
|
||||
(self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3))
|
||||
}
|
||||
};
|
||||
/// Computes the target [0, T] that a blockhash must land in to be valid
|
||||
pub fn target(&self) -> Uint256 {
|
||||
// This is a floating-point "compact" encoding originally used by
|
||||
// OpenSSL, which satoshi put into consensus code, so we're stuck
|
||||
// with it. The exponent needs to have 3 subtracted from it, hence
|
||||
// this goofy decoding code:
|
||||
let (mant, expt) = {
|
||||
let unshifted_expt = self.bits >> 24;
|
||||
if unshifted_expt <= 3 {
|
||||
((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as usize), 0)
|
||||
} else {
|
||||
(self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3))
|
||||
}
|
||||
};
|
||||
|
||||
// The mantissa is signed but may not be negative
|
||||
if mant > 0x7FFFFF {
|
||||
Zero::zero()
|
||||
} else {
|
||||
from_u64::<Uint256>(mant as u64).unwrap() << (expt as usize)
|
||||
// The mantissa is signed but may not be negative
|
||||
if mant > 0x7FFFFF {
|
||||
Zero::zero()
|
||||
} else {
|
||||
from_u64::<Uint256>(mant as u64).unwrap() << (expt as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// correctly.
|
||||
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> {
|
||||
let ref target = self.target();
|
||||
if target != required_target {
|
||||
return Err(SpvBadTarget);
|
||||
/// 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
|
||||
/// correctly.
|
||||
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> {
|
||||
let ref target = self.target();
|
||||
if target != required_target {
|
||||
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
|
||||
pub fn work(&self) -> Uint256 {
|
||||
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
|
||||
let mut ret = !self.target();
|
||||
let mut ret1 = self.target();
|
||||
ret1.increment();
|
||||
ret = ret.div(&ret1);
|
||||
ret.increment();
|
||||
ret
|
||||
}
|
||||
/// Returns the total work of the block
|
||||
pub fn work(&self) -> Uint256 {
|
||||
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
|
||||
let mut ret = !self.target();
|
||||
let mut ret1 = self.target();
|
||||
ret1.increment();
|
||||
ret = ret / ret1;
|
||||
ret.increment();
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl BitcoinHash for BlockHeader {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
use network::serialize::serialize;
|
||||
Sha256dHash::from_data(serialize(self).unwrap().as_slice())
|
||||
}
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
use network::serialize::serialize;
|
||||
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitcoinHash for Block {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
self.header.bitcoin_hash()
|
||||
}
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
self.header.bitcoin_hash()
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
use serialize::hex::FromHex;
|
||||
use std::io;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use blockdata::block::Block;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use blockdata::block::Block;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn block_test() {
|
||||
let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap();
|
||||
let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap();
|
||||
#[test]
|
||||
fn block_test() {
|
||||
let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap();
|
||||
let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap();
|
||||
|
||||
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
||||
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
||||
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
||||
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
||||
|
||||
let decode: io::Result<Block> = deserialize(some_block.clone());
|
||||
let bad_decode: io::Result<Block> = deserialize(cutoff_block);
|
||||
let decode: io::Result<Block> = deserialize(some_block.clone());
|
||||
let bad_decode: io::Result<Block> = deserialize(cutoff_block);
|
||||
|
||||
assert!(decode.is_ok());
|
||||
assert!(bad_decode.is_err());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.header.version, 1);
|
||||
assert_eq!(serialize(&real_decode.header.prev_blockhash), Ok(prevhash));
|
||||
// [test] TODO: actually compute the merkle root
|
||||
assert_eq!(serialize(&real_decode.header.merkle_root), Ok(merkle));
|
||||
assert_eq!(real_decode.header.time, 1231965655);
|
||||
assert_eq!(real_decode.header.bits, 486604799);
|
||||
assert_eq!(real_decode.header.nonce, 2067413810);
|
||||
// [test] TODO: check the transaction data
|
||||
assert!(decode.is_ok());
|
||||
assert!(bad_decode.is_err());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.header.version, 1);
|
||||
assert_eq!(serialize(&real_decode.header.prev_blockhash), Ok(prevhash));
|
||||
// [test] TODO: actually compute the merkle root
|
||||
assert_eq!(serialize(&real_decode.header.merkle_root), Ok(merkle));
|
||||
assert_eq!(real_decode.header.time, 1231965655);
|
||||
assert_eq!(real_decode.header.bits, 486604799);
|
||||
assert_eq!(real_decode.header.nonce, 2067413810);
|
||||
// [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
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -23,7 +23,7 @@ use std::default::Default;
|
|||
use std::num::from_u64;
|
||||
|
||||
use blockdata::opcodes;
|
||||
use blockdata::script::Script;
|
||||
use blockdata::script::ScriptBuilder;
|
||||
use blockdata::transaction::{Transaction, TxOut, TxIn};
|
||||
use blockdata::block::{Block, BlockHeader};
|
||||
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)
|
||||
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,
|
||||
/// since keeping everything below this value should prevent overflows
|
||||
/// if you are doing anything remotely sane with monetary values).
|
||||
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
|
||||
fn bitcoin_genesis_tx() -> Transaction {
|
||||
// Base
|
||||
let mut ret = Transaction {
|
||||
version: 1,
|
||||
lock_time: 0,
|
||||
input: vec![],
|
||||
output: vec![]
|
||||
};
|
||||
// Base
|
||||
let mut ret = Transaction {
|
||||
version: 1,
|
||||
lock_time: 0,
|
||||
input: vec![],
|
||||
output: vec![]
|
||||
};
|
||||
|
||||
// Inputs
|
||||
let mut in_script = Script::new();
|
||||
in_script.push_scriptint(486604799);
|
||||
in_script.push_scriptint(4);
|
||||
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
||||
ret.input.push(TxIn {
|
||||
prev_hash: Default::default(),
|
||||
prev_index: 0xFFFFFFFF,
|
||||
script_sig: in_script,
|
||||
sequence: MAX_SEQUENCE
|
||||
});
|
||||
// Inputs
|
||||
let mut in_script = ScriptBuilder::new();
|
||||
in_script.push_scriptint(486604799);
|
||||
in_script.push_scriptint(4);
|
||||
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
||||
ret.input.push(TxIn {
|
||||
prev_hash: Default::default(),
|
||||
prev_index: 0xFFFFFFFF,
|
||||
script_sig: in_script.into_script(),
|
||||
sequence: MAX_SEQUENCE
|
||||
});
|
||||
|
||||
// Outputs
|
||||
let mut out_script = Script::new();
|
||||
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
||||
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||
ret.output.push(TxOut {
|
||||
value: 50 * COIN_VALUE,
|
||||
script_pubkey: out_script
|
||||
});
|
||||
// Outputs
|
||||
let mut out_script = ScriptBuilder::new();
|
||||
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
||||
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||
ret.output.push(TxOut {
|
||||
value: 50 * COIN_VALUE,
|
||||
script_pubkey: out_script.into_script()
|
||||
});
|
||||
|
||||
// end
|
||||
ret
|
||||
// end
|
||||
ret
|
||||
}
|
||||
|
||||
/// Constructs and returns the genesis block
|
||||
pub fn genesis_block(network: Network) -> Block {
|
||||
match network {
|
||||
Network::Bitcoin => {
|
||||
let txdata = vec![bitcoin_genesis_tx()];
|
||||
Block {
|
||||
header: BlockHeader {
|
||||
version: 1,
|
||||
prev_blockhash: Default::default(),
|
||||
merkle_root: txdata.merkle_root(),
|
||||
time: 1231006505,
|
||||
bits: 0x1d00ffff,
|
||||
nonce: 2083236893
|
||||
},
|
||||
txdata: txdata
|
||||
}
|
||||
match network {
|
||||
Network::Bitcoin => {
|
||||
let txdata = vec![bitcoin_genesis_tx()];
|
||||
Block {
|
||||
header: BlockHeader {
|
||||
version: 1,
|
||||
prev_blockhash: Default::default(),
|
||||
merkle_root: txdata.merkle_root(),
|
||||
time: 1231006505,
|
||||
bits: 0x1d00ffff,
|
||||
nonce: 2083236893
|
||||
},
|
||||
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)]
|
||||
mod test {
|
||||
use std::default::Default;
|
||||
use serialize::hex::FromHex;
|
||||
use std::default::Default;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use network::constants::Network;
|
||||
use network::serialize::{BitcoinHash, serialize};
|
||||
use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
|
||||
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
|
||||
use network::constants::Network;
|
||||
use network::serialize::{BitcoinHash, serialize};
|
||||
use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
|
||||
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
|
||||
|
||||
#[test]
|
||||
fn bitcoin_genesis_first_transaction() {
|
||||
let gen = bitcoin_genesis_tx();
|
||||
#[test]
|
||||
fn bitcoin_genesis_first_transaction() {
|
||||
let gen = bitcoin_genesis_tx();
|
||||
|
||||
assert_eq!(gen.version, 1);
|
||||
assert_eq!(gen.input.len(), 1);
|
||||
assert_eq!(gen.input[0].prev_hash, Default::default());
|
||||
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
|
||||
assert_eq!(serialize(&gen.input[0].script_sig),
|
||||
Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));
|
||||
assert_eq!(gen.version, 1);
|
||||
assert_eq!(gen.input.len(), 1);
|
||||
assert_eq!(gen.input[0].prev_hash, Default::default());
|
||||
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
|
||||
assert_eq!(serialize(&gen.input[0].script_sig),
|
||||
Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));
|
||||
|
||||
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
|
||||
assert_eq!(gen.output.len(), 1);
|
||||
assert_eq!(serialize(&gen.output[0].script_pubkey),
|
||||
Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
|
||||
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||
assert_eq!(gen.lock_time, 0);
|
||||
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
|
||||
assert_eq!(gen.output.len(), 1);
|
||||
assert_eq!(serialize(&gen.output[0].script_pubkey),
|
||||
Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
|
||||
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||
assert_eq!(gen.lock_time, 0);
|
||||
|
||||
assert_eq!(gen.bitcoin_hash().be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
}
|
||||
assert_eq!(gen.bitcoin_hash().be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitcoin_genesis_full_block() {
|
||||
let gen = genesis_block(network::Bitcoin);
|
||||
#[test]
|
||||
fn bitcoin_genesis_full_block() {
|
||||
let gen = genesis_block(network::Bitcoin);
|
||||
|
||||
assert_eq!(gen.header.version, 1);
|
||||
assert_eq!(gen.header.prev_blockhash, Default::default());
|
||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
assert_eq!(gen.header.time, 1231006505);
|
||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||
assert_eq!(gen.header.nonce, 2083236893);
|
||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
|
||||
}
|
||||
assert_eq!(gen.header.version, 1);
|
||||
assert_eq!(gen.header.prev_blockhash, Default::default());
|
||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
assert_eq!(gen.header.time, 1231006505);
|
||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||
assert_eq!(gen.header.nonce, 2083236893);
|
||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn testnet_genesis_full_block() {
|
||||
let gen = genesis_block(network::Testnet);
|
||||
assert_eq!(gen.header.version, 1);
|
||||
assert_eq!(gen.header.prev_blockhash, Default::default());
|
||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
assert_eq!(gen.header.time, 1296688602);
|
||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||
assert_eq!(gen.header.nonce, 414098458);
|
||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
|
||||
}
|
||||
#[test]
|
||||
fn testnet_genesis_full_block() {
|
||||
let gen = genesis_block(network::Testnet);
|
||||
assert_eq!(gen.header.version, 1);
|
||||
assert_eq!(gen.header.prev_blockhash, Default::default());
|
||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
assert_eq!(gen.header.time, 1296688602);
|
||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||
assert_eq!(gen.header.nonce, 414098458);
|
||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -592,10 +592,10 @@ impl All {
|
|||
// 16 opcodes
|
||||
} else if All::OP_PUSHNUM_1 as u8 <= *self 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
|
||||
} else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 {
|
||||
Class::PushBytes(*self as usize)
|
||||
Class::PushBytes(*self as u32)
|
||||
// 60 opcodes
|
||||
} else {
|
||||
Class::Ordinary(unsafe { transmute(*self) })
|
||||
|
@ -636,9 +636,9 @@ pub static OP_TRUE: All = All::OP_PUSHNUM_1;
|
|||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Class {
|
||||
/// Pushes the given number onto the stack
|
||||
PushNum(isize),
|
||||
PushNum(i32),
|
||||
/// Pushes the given number of bytes onto the stack
|
||||
PushBytes(usize),
|
||||
PushBytes(u32),
|
||||
/// Fails the script if executed
|
||||
ReturnOp,
|
||||
/// Fails the script even if not executed
|
||||
|
|
|
@ -35,7 +35,7 @@ use crypto::digest::Digest;
|
|||
use crypto::ripemd160::Ripemd160;
|
||||
use crypto::sha1::Sha1;
|
||||
use crypto::sha2::Sha256;
|
||||
use secp256k1::Secp256k1;
|
||||
use secp256k1::{self, Secp256k1};
|
||||
use secp256k1::key::PublicKey;
|
||||
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 {
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where H: hash::Hasher
|
||||
{
|
||||
let &Script(ref raw) = self;
|
||||
(&raw[..]).hash(state);
|
||||
(&self.0[..]).hash(state);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -70,8 +73,7 @@ impl hash::Hash for Script {
|
|||
where H: hash::Hasher
|
||||
{
|
||||
for s in data.iter() {
|
||||
let &Script(ref raw) = s;
|
||||
(&raw[..]).hash(state);
|
||||
(&s.0[..]).hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +97,7 @@ pub enum Error {
|
|||
/// OP_CHECKSIG was called with a bad signature
|
||||
BadSignature,
|
||||
/// An ECDSA error
|
||||
Ecdsa(::secp256k1::Error),
|
||||
Ecdsa(secp256k1::Error),
|
||||
/// An OP_ELSE happened while not in an OP_IF tree
|
||||
ElseWithoutIf,
|
||||
/// 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 {
|
||||
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;
|
||||
} else {
|
||||
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()
|
||||
};
|
||||
|
||||
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.
|
||||
|
@ -1719,70 +1724,7 @@ impl Script {
|
|||
pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) }
|
||||
|
||||
/// The length in bytes of the script
|
||||
pub fn len(&self) -> usize {
|
||||
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);
|
||||
}
|
||||
pub fn len(&self) -> usize { self.0.len() }
|
||||
|
||||
/// Trace a script
|
||||
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
|
||||
|
@ -1807,21 +1749,19 @@ impl Script {
|
|||
input_context: Option<(&Transaction, usize)>,
|
||||
mut trace: Option<&mut Vec<TraceIteration>>)
|
||||
-> Result<(), Error> {
|
||||
let &Script(ref raw) = self;
|
||||
|
||||
let mut codeseparator_index = 0;
|
||||
let mut exec_stack = vec![];
|
||||
let mut alt_stack = vec![];
|
||||
|
||||
let mut index = 0;
|
||||
let mut op_count = 0;
|
||||
while index < raw.len() {
|
||||
while index < self.0.len() {
|
||||
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
|
||||
match trace {
|
||||
Some(ref mut t) => {
|
||||
let opcode = opcodes::All::Opcode::from_u8(byte);
|
||||
let opcode = opcodes::All::from_u8(byte);
|
||||
t.push(TraceIteration {
|
||||
index: index,
|
||||
opcode: opcode,
|
||||
|
@ -1837,7 +1777,7 @@ impl Script {
|
|||
op_count += 1;
|
||||
index += 1;
|
||||
// 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
|
||||
(_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode),
|
||||
// Push number
|
||||
|
@ -1846,29 +1786,30 @@ impl Script {
|
|||
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
||||
// Data-reading statements still need to read, even when not executing
|
||||
(_, opcodes::Class::PushBytes(n)) => {
|
||||
if raw.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index, index + n))); }
|
||||
let n = n as usize;
|
||||
if self.0.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index..index + n])); }
|
||||
index += n;
|
||||
}
|
||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => {
|
||||
if raw.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
|
||||
let n = try!(read_uint(&raw[index..], 1));
|
||||
if raw.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 1, index + n + 1))); }
|
||||
if self.0.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
|
||||
let n = try!(read_uint(&self.0[index..], 1));
|
||||
if self.0.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 1..index + n + 1])); }
|
||||
index += 1 + n;
|
||||
}
|
||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => {
|
||||
if raw.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
|
||||
let n = try!(read_uint(&raw[index..], 2));
|
||||
if raw.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 2, index + n + 2))); }
|
||||
if self.0.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
|
||||
let n = try!(read_uint(&self.0[index..], 2));
|
||||
if self.0.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 2..index + n + 2])); }
|
||||
index += 2 + n;
|
||||
}
|
||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => {
|
||||
if raw.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
|
||||
let n = try!(read_uint(&raw[index..], 4));
|
||||
if raw.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 4, index + n + 4))); }
|
||||
if self.0.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
|
||||
let n = try!(read_uint(&self.0[index..], 4));
|
||||
if self.0.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 4..index + n + 4])); }
|
||||
index += 4 + n;
|
||||
}
|
||||
// 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_PICK => {
|
||||
let n = match stack.pop() {
|
||||
Some(data) => try!(read_scriptint(&data)),
|
||||
Some(data) => try!(read_scriptint(&data[..])),
|
||||
None => { return Err(Error::PopEmptyStack); }
|
||||
};
|
||||
if n < 0 { return Err(Error::NegativePick); }
|
||||
|
@ -1951,7 +1892,7 @@ impl Script {
|
|||
}
|
||||
opcodes::Ordinary::OP_ROLL => {
|
||||
let n = match stack.pop() {
|
||||
Some(data) => try!(read_scriptint(&data)),
|
||||
Some(data) => try!(read_scriptint(&data[..])),
|
||||
None => { return Err(Error::PopEmptyStack); }
|
||||
};
|
||||
if n < 0 { return Err(Error::NegativeRoll); }
|
||||
|
@ -2033,11 +1974,12 @@ impl Script {
|
|||
|
||||
// Compute the section of script that needs to be hashed: everything
|
||||
// from the last CODESEPARATOR, except the signature itself.
|
||||
let mut script = (&raw[codeseparator_index..]).to_vec();
|
||||
let mut remove = Script::new();
|
||||
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||
let mut remove = ScriptBuilder::new();
|
||||
remove.push_slice(sig_slice);
|
||||
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, &remove[..]);
|
||||
// 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
|
||||
if input_context.is_none() { return Err(Error::NoTransaction); }
|
||||
|
@ -2053,7 +1995,7 @@ impl Script {
|
|||
opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => {
|
||||
// Read all the keys
|
||||
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 {
|
||||
return Err(Error::MultisigBadKeyCount(n_keys as isize));
|
||||
}
|
||||
|
@ -2066,7 +2008,7 @@ impl Script {
|
|||
|
||||
// Read all the signatures
|
||||
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 {
|
||||
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
|
||||
// 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() {
|
||||
let mut remove = Script::new();
|
||||
remove.push_slice(&sig);
|
||||
script_find_and_remove(&mut script, &remove);
|
||||
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||
let mut remove = ScriptBuilder::new();
|
||||
remove.push_slice(&sig[..]);
|
||||
script_find_and_remove(&mut script, &remove[..]);
|
||||
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
|
||||
|
@ -2105,7 +2047,7 @@ impl Script {
|
|||
// Try to validate the signature with the given key
|
||||
(Some(k), Some(s)) => {
|
||||
// Move to the next signature if it is valid for the current key
|
||||
if check_signature(&s, &k, script.clone(), tx, input_index).is_ok() {
|
||||
if check_signature(&s[..], &k[..], script.clone(), tx, input_index).is_ok() {
|
||||
sig = sig_iter.next();
|
||||
}
|
||||
// Move to the next key in any case
|
||||
|
@ -2142,12 +2084,11 @@ impl Script {
|
|||
/// Checks whether a script pubkey is a p2sh output
|
||||
#[inline]
|
||||
pub fn is_p2sh(&self) -> bool {
|
||||
let &Script(ref raw) = self;
|
||||
unsafe {
|
||||
raw.len() == 23 &&
|
||||
*raw.get(0) == opcodes::All::OP_HASH160 as u8 &&
|
||||
*raw.get(1) == opcodes::All::OP_PUSHBYTES_20 as u8 &&
|
||||
*raw.get(22) == opcodes::All::OP_EQUAL as u8
|
||||
self.0.len() == 23 &&
|
||||
self.0[0] == opcodes::All::OP_HASH160 as u8 &&
|
||||
self.0[1] == opcodes::All::OP_PUSHBYTES_20 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),
|
||||
// Data-reading statements still need to read, even when not executing
|
||||
(_, opcodes::Class::PushBytes(n)) => {
|
||||
let n = n as usize;
|
||||
if script.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||
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;
|
||||
}
|
||||
|
@ -2200,7 +2142,7 @@ impl Script {
|
|||
};
|
||||
if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||
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;
|
||||
}
|
||||
|
@ -2212,7 +2154,7 @@ impl Script {
|
|||
};
|
||||
if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||
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;
|
||||
}
|
||||
|
@ -2223,7 +2165,7 @@ impl Script {
|
|||
};
|
||||
if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||
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;
|
||||
}
|
||||
|
@ -2495,8 +2437,7 @@ impl Script {
|
|||
}
|
||||
}
|
||||
|
||||
let &Script(ref raw) = self;
|
||||
recurse(&raw, AbstractStack::new(), vec![], 1)
|
||||
recurse(&self.0, AbstractStack::new(), vec![], 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2504,62 +2445,93 @@ impl Default for Script {
|
|||
fn default() -> Script { Script(vec![].into_boxed_slice()) }
|
||||
}
|
||||
|
||||
impl_index_newtype!(Script, u8);
|
||||
|
||||
impl ops::Index<usize> for Script {
|
||||
type Output = u8;
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &u8 {
|
||||
let &Script(ref raw) = self;
|
||||
&raw[index]
|
||||
impl ScriptBuilder {
|
||||
/// Creates a new empty script
|
||||
pub fn new() -> ScriptBuilder { ScriptBuilder(vec![]) }
|
||||
|
||||
/// Creates a new script from an existing vector
|
||||
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 {
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
||||
let &Script(ref raw) = self;
|
||||
&raw[index]
|
||||
}
|
||||
/// Adds an individual opcode to the script
|
||||
impl Default for ScriptBuilder {
|
||||
fn default() -> ScriptBuilder { ScriptBuilder(vec![]) }
|
||||
}
|
||||
|
||||
impl ops::Index<ops::RangeTo<usize>> for Script {
|
||||
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[..]
|
||||
}
|
||||
}
|
||||
impl_index_newtype!(ScriptBuilder, u8);
|
||||
|
||||
// User-facing serialization
|
||||
impl serde::Serialize for Script {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: serde::Serializer,
|
||||
{
|
||||
let &Script(ref raw) = self;
|
||||
for dat in raw.iter() {
|
||||
serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap());
|
||||
serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap());
|
||||
for dat in self.0.iter() {
|
||||
try!(serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap()));
|
||||
try!(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 {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
let &Script(ref data) = self;
|
||||
data.consensus_encode(s)
|
||||
self.0.consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -32,271 +32,272 @@ use blockdata::utxoset::UtxoSet;
|
|||
use network::encodable::ConsensusEncodable;
|
||||
use network::serialize::BitcoinHash;
|
||||
use network::constants::Network;
|
||||
use wallet::address::Address;
|
||||
use wallet::address::{Address, ToAddress};
|
||||
|
||||
/// A transaction input, which defines old coins to be consumed
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct TxIn {
|
||||
/// The hash of the transaction whose output is being used an an input
|
||||
pub prev_hash: Sha256dHash,
|
||||
/// The index of the output in the previous transaction, which may have several
|
||||
pub prev_index: u32,
|
||||
/// The script which pushes values on the stack which will cause
|
||||
/// the referenced output's script to accept
|
||||
pub script_sig: Script,
|
||||
/// The sequence number, which suggests to miners which of two
|
||||
/// conflicting transactions should be preferred, or 0xFFFFFFFF
|
||||
/// to ignore this feature. This is generally never used since
|
||||
/// the miner behaviour cannot be enforced.
|
||||
pub sequence: u32,
|
||||
/// The hash of the transaction whose output is being used an an input
|
||||
pub prev_hash: Sha256dHash,
|
||||
/// The index of the output in the previous transaction, which may have several
|
||||
pub prev_index: u32,
|
||||
/// The script which pushes values on the stack which will cause
|
||||
/// the referenced output's script to accept
|
||||
pub script_sig: Script,
|
||||
/// The sequence number, which suggests to miners which of two
|
||||
/// conflicting transactions should be preferred, or 0xFFFFFFFF
|
||||
/// to ignore this feature. This is generally never used since
|
||||
/// the miner behaviour cannot be enforced.
|
||||
pub sequence: u32,
|
||||
}
|
||||
|
||||
/// A transaction output, which defines new coins to be created from old ones.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct TxOut {
|
||||
/// The value of the output, in satoshis
|
||||
pub value: u64,
|
||||
/// The script which must satisfy for the output to be spent
|
||||
pub script_pubkey: Script
|
||||
/// The value of the output, in satoshis
|
||||
pub value: u64,
|
||||
/// The script which must satisfy for the output to be spent
|
||||
pub script_pubkey: Script
|
||||
}
|
||||
|
||||
// This is used as a "null txout" in consensus signing code
|
||||
impl Default for TxOut {
|
||||
fn default() -> TxOut {
|
||||
TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() }
|
||||
}
|
||||
fn default() -> TxOut {
|
||||
TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A classification for script pubkeys
|
||||
pub enum ScriptPubkeyTemplate {
|
||||
/// A pay-to-address output
|
||||
PayToPubkeyHash(Address),
|
||||
/// Another kind of output
|
||||
Unknown
|
||||
/// A pay-to-address output
|
||||
PayToPubkeyHash(Address),
|
||||
/// Another kind of output
|
||||
Unknown
|
||||
}
|
||||
|
||||
impl TxOut {
|
||||
/// Determines the template that this output adheres to, if any
|
||||
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
|
||||
if self.script_pubkey.len() == 25 &&
|
||||
self.script_pubkey.slice_to(3) == &[0x76, 0xa9, 0x14] &&
|
||||
self.script_pubkey.slice_from(23) == &[0x88, 0xac] {
|
||||
ScriptPubkeyTemplate::PayToPubkeyHash(self.script_pubkey.slice(3, 23).to_address(network))
|
||||
} else {
|
||||
ScriptPubkeyTemplate::Unknown
|
||||
/// Determines the template that this output adheres to, if any
|
||||
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
|
||||
if self.script_pubkey.len() == 25 &&
|
||||
&self.script_pubkey[0..3] == &[0x76, 0xa9, 0x14] &&
|
||||
&self.script_pubkey[23..] == &[0x88, 0xac] {
|
||||
ScriptPubkeyTemplate::PayToPubkeyHash((&self.script_pubkey[3..23]).to_address(network))
|
||||
} else {
|
||||
ScriptPubkeyTemplate::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin transaction, which describes an authenticated movement of coins
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Transaction {
|
||||
/// The protocol version, should always be 1.
|
||||
pub version: u32,
|
||||
/// Block number before which this transaction is valid, or 0 for
|
||||
/// valid immediately.
|
||||
pub lock_time: u32,
|
||||
/// List of inputs
|
||||
pub input: Vec<TxIn>,
|
||||
/// List of outputs
|
||||
pub output: Vec<TxOut>
|
||||
/// The protocol version, should always be 1.
|
||||
pub version: u32,
|
||||
/// Block number before which this transaction is valid, or 0 for
|
||||
/// valid immediately.
|
||||
pub lock_time: u32,
|
||||
/// List of inputs
|
||||
pub input: Vec<TxIn>,
|
||||
/// List of outputs
|
||||
pub output: Vec<TxOut>
|
||||
}
|
||||
|
||||
/// A transaction error
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum Error {
|
||||
/// Concatenated script failed in the input half (script error)
|
||||
InputScriptFailure(script::Error),
|
||||
/// Concatenated script failed in the output half (script error)
|
||||
OutputScriptFailure(script::Error),
|
||||
/// P2SH serialized script failed (script error)
|
||||
P2shScriptFailure(script::Error),
|
||||
/// P2SH serialized script ended with false at the top of the stack
|
||||
P2shScriptReturnedFalse,
|
||||
/// P2SH serialized script ended with nothing in the stack
|
||||
P2shScriptReturnedEmptyStack,
|
||||
/// Script ended with false at the top of the stack
|
||||
ScriptReturnedFalse,
|
||||
/// Script ended with nothing in the stack
|
||||
ScriptReturnedEmptyStack,
|
||||
/// Script ended with nothing in the stack (input txid, input vout)
|
||||
InputNotFound(Sha256dHash, u32),
|
||||
/// Concatenated script failed in the input half (script error)
|
||||
InputScriptFailure(script::Error),
|
||||
/// Concatenated script failed in the output half (script error)
|
||||
OutputScriptFailure(script::Error),
|
||||
/// P2SH serialized script failed (script error)
|
||||
P2shScriptFailure(script::Error),
|
||||
/// P2SH serialized script ended with false at the top of the stack
|
||||
P2shScriptReturnedFalse,
|
||||
/// P2SH serialized script ended with nothing in the stack
|
||||
P2shScriptReturnedEmptyStack,
|
||||
/// Script ended with false at the top of the stack
|
||||
ScriptReturnedFalse,
|
||||
/// Script ended with nothing in the stack
|
||||
ScriptReturnedEmptyStack,
|
||||
/// Script ended with nothing in the stack (input txid, input vout)
|
||||
InputNotFound(Sha256dHash, u32),
|
||||
}
|
||||
display_from_debug!(Error);
|
||||
|
||||
impl serde::Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: serde::Serializer,
|
||||
{
|
||||
serializer.visit_str(&self.to_string())
|
||||
}
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: serde::Serializer,
|
||||
{
|
||||
serializer.visit_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// A trace of a transaction input's script execution
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct InputTrace {
|
||||
input_txid: Sha256dHash,
|
||||
input_vout: usize,
|
||||
sig_trace: ScriptTrace,
|
||||
pubkey_trace: Option<ScriptTrace>,
|
||||
p2sh_trace: Option<ScriptTrace>,
|
||||
error: Option<Error>
|
||||
input_txid: Sha256dHash,
|
||||
input_vout: usize,
|
||||
sig_trace: ScriptTrace,
|
||||
pubkey_trace: Option<ScriptTrace>,
|
||||
p2sh_trace: Option<ScriptTrace>,
|
||||
error: Option<Error>
|
||||
}
|
||||
|
||||
/// A trace of a transaction's execution
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct TransactionTrace {
|
||||
txid: Sha256dHash,
|
||||
inputs: Vec<InputTrace>
|
||||
txid: Sha256dHash,
|
||||
inputs: Vec<InputTrace>
|
||||
}
|
||||
|
||||
impl TxIn {
|
||||
/// Check an input's script for validity
|
||||
pub fn validate(&self,
|
||||
utxoset: &UtxoSet,
|
||||
txn: &Transaction,
|
||||
index: usize) -> Result<(), Error> {
|
||||
let txo = utxoset.get_utxo(self.prev_hash, self.prev_index);
|
||||
match txo {
|
||||
Some((_, txo)) => {
|
||||
let mut p2sh_stack = Vec::new();
|
||||
let mut p2sh_script = Script::new();
|
||||
/// Check an input's script for validity
|
||||
pub fn validate(&self,
|
||||
utxoset: &UtxoSet,
|
||||
txn: &Transaction,
|
||||
index: usize) -> Result<(), Error> {
|
||||
let txo = utxoset.get_utxo(self.prev_hash, self.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);
|
||||
match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) {
|
||||
Ok(_) => {}
|
||||
Err(e) => { return Err(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!()
|
||||
};
|
||||
}
|
||||
match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) {
|
||||
Ok(_) => {}
|
||||
Err(e) => { return Err(Error::OutputScriptFailure(e)); }
|
||||
}
|
||||
match stack.pop() {
|
||||
Some(v) => {
|
||||
if !read_scriptbool(&v[..]) {
|
||||
return Err(Error::ScriptReturnedFalse);
|
||||
let mut stack = Vec::with_capacity(6);
|
||||
match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) {
|
||||
Ok(_) => {}
|
||||
Err(e) => { return Err(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!()
|
||||
};
|
||||
}
|
||||
match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) {
|
||||
Ok(_) => {}
|
||||
Err(e) => { return Err(Error::OutputScriptFailure(e)); }
|
||||
}
|
||||
match stack.pop() {
|
||||
Some(v) => {
|
||||
if !read_scriptbool(&v[..]) {
|
||||
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::ScriptReturnedEmptyStack); }
|
||||
None => { return Err(Error::InputNotFound(self.prev_hash, self.prev_index)); }
|
||||
}
|
||||
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)); }
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
/// Check a transaction for validity
|
||||
pub fn validate(&self, utxoset: &UtxoSet) -> Result<(), Error> {
|
||||
for (n, input) in self.input.iter().enumerate() {
|
||||
try!(input.validate(utxoset, self, n));
|
||||
/// Check a transaction for validity
|
||||
pub fn validate(&self, utxoset: &UtxoSet) -> Result<(), Error> {
|
||||
for (n, input) in self.input.iter().enumerate() {
|
||||
try!(input.validate(utxoset, self, n));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Produce a trace of a transaction's execution
|
||||
pub fn trace(&self, utxoset: &UtxoSet) -> TransactionTrace {
|
||||
let mut ret = TransactionTrace { txid: self.bitcoin_hash(),
|
||||
inputs: Vec::with_capacity(self.input.len()) };
|
||||
for (n, input) in self.input.iter().enumerate() {
|
||||
// Setup trace
|
||||
let mut trace = InputTrace {
|
||||
input_txid: input.prev_hash,
|
||||
input_vout: input.prev_index as usize,
|
||||
sig_trace: ScriptTrace {
|
||||
script: Script::new(),
|
||||
initial_stack: vec![],
|
||||
iterations: vec![],
|
||||
error: None
|
||||
},
|
||||
pubkey_trace: None,
|
||||
p2sh_trace: 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!()
|
||||
/// Produce a trace of a transaction's execution
|
||||
pub fn trace(&self, utxoset: &UtxoSet) -> TransactionTrace {
|
||||
let mut ret = TransactionTrace { txid: self.bitcoin_hash(),
|
||||
inputs: Vec::with_capacity(self.input.len()) };
|
||||
for (n, input) in self.input.iter().enumerate() {
|
||||
// Setup trace
|
||||
let mut trace = InputTrace {
|
||||
input_txid: input.prev_hash,
|
||||
input_vout: input.prev_index as usize,
|
||||
sig_trace: ScriptTrace {
|
||||
script: Script::new(),
|
||||
initial_stack: vec![],
|
||||
iterations: vec![],
|
||||
error: None
|
||||
},
|
||||
pubkey_trace: None,
|
||||
p2sh_trace: None,
|
||||
error: None
|
||||
};
|
||||
}
|
||||
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);
|
||||
// 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!()
|
||||
};
|
||||
}
|
||||
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 => { 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::InputNotFound(input.prev_hash, input.prev_index));
|
||||
}
|
||||
None => { trace.error = Some(Error::P2shScriptReturnedEmptyStack); }
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.inputs.push(trace);
|
||||
}
|
||||
None => {
|
||||
trace.error = Some(Error::InputNotFound(input.prev_hash, input.prev_index));
|
||||
}
|
||||
}
|
||||
ret.inputs.push(trace);
|
||||
ret
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl BitcoinHash for Transaction {
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
use network::serialize::serialize;
|
||||
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||
}
|
||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||
use network::serialize::serialize;
|
||||
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use super::{Transaction, TxIn};
|
||||
use super::{Transaction, TxIn};
|
||||
|
||||
use std::io;
|
||||
use std::io;
|
||||
|
||||
use network::serialize::BitcoinHash;
|
||||
use network::serialize::deserialize;
|
||||
use util::misc::hex_bytes;
|
||||
use network::serialize::BitcoinHash;
|
||||
use network::serialize::deserialize;
|
||||
use util::misc::hex_bytes;
|
||||
|
||||
#[test]
|
||||
fn test_txin() {
|
||||
let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
||||
assert!(txin.is_ok());
|
||||
}
|
||||
#[test]
|
||||
fn test_txin() {
|
||||
let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
||||
assert!(txin.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction() {
|
||||
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||
let tx: io::Result<Transaction> = deserialize(hex_tx);
|
||||
assert!(tx.is_ok());
|
||||
let realtx = tx.unwrap();
|
||||
// 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.
|
||||
assert_eq!(realtx.version, 1);
|
||||
assert_eq!(realtx.input.len(), 1);
|
||||
// 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.
|
||||
assert_eq!(realtx.input[0].prev_hash.be_hex_string(),
|
||||
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
|
||||
assert_eq!(realtx.input[0].prev_index, 1);
|
||||
assert_eq!(realtx.output.len(), 1);
|
||||
assert_eq!(realtx.lock_time, 0);
|
||||
#[test]
|
||||
fn test_transaction() {
|
||||
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||
let tx: io::Result<Transaction> = deserialize(hex_tx);
|
||||
assert!(tx.is_ok());
|
||||
let realtx = tx.unwrap();
|
||||
// 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.
|
||||
assert_eq!(realtx.version, 1);
|
||||
assert_eq!(realtx.input.len(), 1);
|
||||
// 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.
|
||||
assert_eq!(realtx.input[0].prev_hash.be_hex_string(),
|
||||
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
|
||||
assert_eq!(realtx.input[0].prev_index, 1);
|
||||
assert_eq!(realtx.output.len(), 1);
|
||||
assert_eq!(realtx.lock_time, 0);
|
||||
|
||||
assert_eq!(realtx.bitcoin_hash().be_hex_string(),
|
||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||
}
|
||||
assert_eq!(realtx.bitcoin_hash().be_hex_string(),
|
||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -21,11 +21,11 @@
|
|||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash::map::Iter;
|
||||
use std::hash::SipHasher;
|
||||
use std::default::Default;
|
||||
use std::mem;
|
||||
use eventual;
|
||||
use eventual::Async;
|
||||
use num_cpus;
|
||||
use std::sync::Future;
|
||||
|
||||
use blockdata::transaction::{self, Transaction, TxOut};
|
||||
use blockdata::constants::genesis_block;
|
||||
|
@ -37,475 +37,469 @@ use util::hash::Sha256dHash;
|
|||
/// The amount of validation to do when updating the UTXO set
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
||||
pub enum ValidationLevel {
|
||||
/// Blindly update the UTXO set (NOT recommended)
|
||||
Nothing,
|
||||
/// Check that the blocks are at least in the right order
|
||||
Chain,
|
||||
/// Check that any inputs are actually txouts in the set
|
||||
Inputs,
|
||||
/// Execute the scripts and ensure they pass
|
||||
Script
|
||||
/// Blindly update the UTXO set (NOT recommended)
|
||||
Nothing,
|
||||
/// Check that the blocks are at least in the right order
|
||||
Chain,
|
||||
/// Check that any inputs are actually txouts in the set
|
||||
Inputs,
|
||||
/// Execute the scripts and ensure they pass
|
||||
Script
|
||||
}
|
||||
|
||||
/// An error returned from a UTXO set operation
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum Error {
|
||||
/// prevhash of the new block is not the hash of the old block (expected, actual)
|
||||
BadPrevHash(Sha256dHash, Sha256dHash),
|
||||
/// A TXID was duplicated
|
||||
DuplicatedTxid(Sha256dHash),
|
||||
/// A tx was invalid (txid, error)
|
||||
InvalidTx(Sha256dHash, transaction::Error),
|
||||
/// prevhash of the new block is not the hash of the old block (expected, actual)
|
||||
BadPrevHash(Sha256dHash, Sha256dHash),
|
||||
/// A TXID was duplicated
|
||||
DuplicatedTxid(Sha256dHash),
|
||||
/// A tx was invalid (txid, error)
|
||||
InvalidTx(Sha256dHash, transaction::Error),
|
||||
}
|
||||
|
||||
struct UtxoNode {
|
||||
/// Blockheight at which this UTXO appeared in the blockchain
|
||||
height: u32,
|
||||
/// Vector of outputs; None indicates a nonexistent or already spent output
|
||||
outputs: Box<[Option<TxOut>]>
|
||||
/// Blockheight at which this UTXO appeared in the blockchain
|
||||
height: u32,
|
||||
/// Vector of outputs; None indicates a nonexistent or already spent output
|
||||
outputs: Box<[Option<TxOut>]>
|
||||
}
|
||||
impl_consensus_encoding!(UtxoNode, height, outputs);
|
||||
|
||||
/// An iterator over UTXOs
|
||||
pub struct UtxoIterator<'a> {
|
||||
tx_iter: Iter<'a, Sha256dHash, UtxoNode>,
|
||||
current_key: Sha256dHash,
|
||||
current: Option<&'a UtxoNode>,
|
||||
tx_index: u32
|
||||
tx_iter: Iter<'a, Sha256dHash, UtxoNode>,
|
||||
current_key: Sha256dHash,
|
||||
current: Option<&'a UtxoNode>,
|
||||
tx_index: u32
|
||||
}
|
||||
|
||||
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)> {
|
||||
while self.current.is_some() {
|
||||
let current = &self.current.unwrap().outputs;
|
||||
while self.tx_index < current.len() as u32 {
|
||||
self.tx_index += 1;
|
||||
if unsafe { current.get(self.tx_index as usize - 1) }.is_some() {
|
||||
return Some((self.current_key,
|
||||
self.tx_index,
|
||||
unsafe { current.get(self.tx_index as usize - 1) }.as_ref().unwrap(),
|
||||
self.current.unwrap().height));
|
||||
fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> {
|
||||
while let Some(current) = self.current {
|
||||
while self.tx_index < current.outputs.len() as u32 {
|
||||
self.tx_index += 1;
|
||||
if let Some(ref cur) = current.outputs[self.tx_index as usize - 1] {
|
||||
return Some((self.current_key, self.tx_index,
|
||||
cur, current.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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
/// The UTXO set
|
||||
pub struct UtxoSet {
|
||||
table: HashMap<Sha256dHash, UtxoNode, SipHasher>,
|
||||
last_hash: Sha256dHash,
|
||||
// A circular buffer of deleted utxos, grouped by block
|
||||
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
|
||||
// The last index into the above buffer that was assigned to
|
||||
spent_idx: u64,
|
||||
n_utxos: u64,
|
||||
n_pruned: u64
|
||||
table: HashMap<Sha256dHash, UtxoNode>,
|
||||
last_hash: Sha256dHash,
|
||||
// A circular buffer of deleted utxos, grouped by block
|
||||
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
|
||||
// The last index into the above buffer that was assigned to
|
||||
spent_idx: u64,
|
||||
n_utxos: u64,
|
||||
n_pruned: u64
|
||||
}
|
||||
|
||||
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, n_pruned, spent_txos, spent_idx, table);
|
||||
|
||||
impl UtxoSet {
|
||||
/// Constructs a new UTXO set
|
||||
pub fn new(network: Network, rewind_limit: usize) -> UtxoSet {
|
||||
// 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
|
||||
// must follow suit, otherwise we will accept a transaction spending it
|
||||
// while the reference client won't, causing us to fork off the network.
|
||||
UtxoSet {
|
||||
table: HashMap::with_hasher(SipHasher::new()),
|
||||
last_hash: genesis_block(network).header.bitcoin_hash(),
|
||||
spent_txos: Vec::from_elem(rewind_limit, vec![]),
|
||||
spent_idx: 0,
|
||||
n_utxos: 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()));
|
||||
/// Constructs a new UTXO set
|
||||
pub fn new(network: Network, rewind_limit: usize) -> UtxoSet {
|
||||
// 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
|
||||
// must follow suit, otherwise we will accept a transaction spending it
|
||||
// while the reference client won't, causing us to fork off the network.
|
||||
UtxoSet {
|
||||
table: HashMap::new(),
|
||||
last_hash: genesis_block(network).header.bitcoin_hash(),
|
||||
spent_txos: vec![vec![]; rewind_limit],
|
||||
spent_idx: 0,
|
||||
n_utxos: 0,
|
||||
n_pruned: 0
|
||||
}
|
||||
}
|
||||
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
|
||||
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.find_mut(&txid) {
|
||||
Some(node) => node,
|
||||
None => return None
|
||||
};
|
||||
/// 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.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
|
||||
if vout as usize >= node.outputs.len() { return None; }
|
||||
let replace = unsafe { node.outputs.get_mut(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);
|
||||
let replace = node.outputs[vout as usize];
|
||||
Some((node.height as usize, replace.as_ref().unwrap()))
|
||||
}
|
||||
|
||||
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.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));
|
||||
}
|
||||
/// 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));
|
||||
}
|
||||
// Didn't replace anything? Good.
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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;
|
||||
(&mut self.spent_txos[spent_idx]).clear();
|
||||
|
||||
let s = self as *mut _ as *const UtxoSet;
|
||||
let txes = &block.txdata as *const _;
|
||||
future_vec.push(Future::spawn(move || {
|
||||
let txes = unsafe {&*txes};
|
||||
for tx in txes.slice(start, end).iter() {
|
||||
match tx.validate(unsafe {&*s}) {
|
||||
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_error = Ok(());
|
||||
for res in future_vec.iter_mut().map(|f| f.get()) {
|
||||
if res.is_err() {
|
||||
last_error = res;
|
||||
}
|
||||
}
|
||||
if last_error.is_err() {
|
||||
return last_error;
|
||||
}
|
||||
}
|
||||
|
||||
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.get_mut(spent_idx).reserve_additional(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) => { 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);
|
||||
// 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.
|
||||
(&mut self.spent_txos[spent_idx]).reserve(replace.outputs.len());
|
||||
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 => {}
|
||||
}
|
||||
}
|
||||
// Otherwise fail the block
|
||||
self.rewind(block);
|
||||
return Err(Error::DuplicatedTxid(txid));
|
||||
}
|
||||
thinvec.push(Some(txo));
|
||||
UtxoNode { outputs: thinvec.into_boxed_slice(), height: height }
|
||||
}
|
||||
}
|
||||
};
|
||||
// Ram it back into the tree
|
||||
self.table.insert(txid, new_node);
|
||||
// Didn't replace anything? Good.
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) %
|
||||
self.spent_txos.len() as u64;
|
||||
self.last_hash = block.header.prev_blockhash;
|
||||
return true;
|
||||
}
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Get the hash of the last block added to the utxo set
|
||||
pub fn last_hash(&self) -> Sha256dHash {
|
||||
self.last_hash
|
||||
}
|
||||
// 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.
|
||||
|
||||
/// Get the number of UTXOs in the set
|
||||
pub fn n_utxos(&self) -> usize {
|
||||
self.n_utxos as usize
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
// Read deleted txouts
|
||||
if skipped_genesis {
|
||||
let mut extract_vec = vec![];
|
||||
mem::swap(&mut extract_vec, (&mut self.spent_txos[self.spent_idx as usize]));
|
||||
for ((txid, n), (height, txo)) in extract_vec.into_iter() {
|
||||
// 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
|
||||
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
|
||||
// Decrement mod the spent txo cache size
|
||||
self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) %
|
||||
self.spent_txos.len() as u64;
|
||||
self.last_hash = block.header.prev_blockhash;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Get the hash of the last block added to the utxo set
|
||||
pub fn last_hash(&self) -> Sha256dHash {
|
||||
self.last_hash
|
||||
}
|
||||
|
||||
/// Get the number of UTXOs in the set
|
||||
pub fn n_utxos(&self) -> usize {
|
||||
self.n_utxos as usize
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
mod tests {
|
||||
use std::prelude::*;
|
||||
use std::io;
|
||||
use serialize::hex::FromHex;
|
||||
use std::prelude::*;
|
||||
use std::io;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use super::{UtxoSet, ValidationLevel};
|
||||
use super::{UtxoSet, ValidationLevel};
|
||||
|
||||
use blockdata::block::Block;
|
||||
use network::constants::Network::Bitcoin;
|
||||
use network::serialize::{BitcoinHash, deserialize, serialize};
|
||||
use blockdata::block::Block;
|
||||
use network::constants::Network::Bitcoin;
|
||||
use network::serialize::{BitcoinHash, deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn utxoset_serialize_test() {
|
||||
let mut empty_set = UtxoSet::new(Bitcoin, 100);
|
||||
#[test]
|
||||
fn utxoset_serialize_test() {
|
||||
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
|
||||
assert!(empty_set.update(&new_block, 1, ValidationLevel::Inputs).is_err());
|
||||
assert_eq!(empty_set.n_utxos(), 0);
|
||||
// Add the block manually so that we'll have some UTXOs for the rest of the test
|
||||
for tx in new_block.txdata.iter() {
|
||||
empty_set.add_utxos(tx, 1);
|
||||
// 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_eq!(empty_set.n_utxos(), 0);
|
||||
// Add the block manually so that we'll have some UTXOs for the rest of the test
|
||||
for tx in new_block.txdata.iter() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -99,45 +99,7 @@ macro_rules! impl_array_newtype {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
|
||||
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_index_newtype!($thing, $ty);
|
||||
|
||||
impl PartialEq for $thing {
|
||||
#[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 {
|
||||
($thing:ident) => {
|
||||
impl ::std::fmt::Display for $thing {
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
extern crate alloc;
|
||||
extern crate byteorder;
|
||||
extern crate collections;
|
||||
extern crate eventual;
|
||||
extern crate num_cpus;
|
||||
extern crate rand;
|
||||
extern crate rustc_serialize as serialize;
|
||||
|
|
227
src/macros.rs
227
src/macros.rs
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -18,128 +18,127 @@
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! nu_select {
|
||||
($($name:pat = $rx:expr => $code:expr),+) => ({
|
||||
nu_select!{ $($name = $rx, recv => $code),+ }
|
||||
});
|
||||
($($name:pat = $rx:expr, $meth:ident => $code:expr),+) => ({
|
||||
use rustrt::local::Local;
|
||||
use rustrt::task::Task;
|
||||
use sync::comm::Packet;
|
||||
($($name:pat = $rx:expr => $code:expr),+) => ({
|
||||
nu_select!{ $($name = $rx, recv => $code),+ }
|
||||
});
|
||||
($($name:pat = $rx:expr, $meth:ident => $code:expr),+) => ({
|
||||
use rustrt::local::Local;
|
||||
use rustrt::task::Task;
|
||||
use sync::comm::Packet;
|
||||
|
||||
// Is anything already ready to receive? Grab it without waiting.
|
||||
$(
|
||||
if (&$rx as &Packet).can_recv() {
|
||||
let $name = $rx.$meth();
|
||||
$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(())
|
||||
// Is anything already ready to receive? Grab it without waiting.
|
||||
$(
|
||||
if (&$rx as &Packet).can_recv() {
|
||||
let $name = $rx.$meth();
|
||||
$code
|
||||
}
|
||||
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+
|
||||
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
|
||||
}
|
||||
})
|
||||
// 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)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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_rules! user_enum {
|
||||
($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => (
|
||||
$(#[$attr])*
|
||||
pub enum $name {
|
||||
$(#[$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()) }
|
||||
}
|
||||
($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => (
|
||||
$(#[$attr])*
|
||||
pub enum $name {
|
||||
$(#[$doc] $elem),*
|
||||
}
|
||||
|
||||
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 {
|
||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||
where S: ::serde::Serializer
|
||||
{
|
||||
s.visit_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
);
|
||||
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 ::serde::Serialize for $name {
|
||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||
where S: ::serde::Serializer
|
||||
{
|
||||
s.visit_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// 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
|
||||
pub struct Address {
|
||||
/// Services provided by the peer whose address this is
|
||||
pub services: u64,
|
||||
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
||||
pub address: [u16; 8],
|
||||
/// Network port
|
||||
pub port: u16
|
||||
/// Services provided by the peer whose address this is
|
||||
pub services: u64,
|
||||
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
||||
pub address: [u16; 8],
|
||||
/// Network port
|
||||
pub port: u16
|
||||
}
|
||||
|
||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Address {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
try!(self.services.consensus_encode(s));
|
||||
try!(self.address.consensus_encode(s));
|
||||
// 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));
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
try!(self.services.consensus_encode(s));
|
||||
try!(self.address.consensus_encode(s));
|
||||
// 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));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for Address {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Address, D::Error> {
|
||||
Ok(Address {
|
||||
services: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
address: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
// Explicitly code the port since it needs to be big-endian
|
||||
port: {
|
||||
let b1: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let b2: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
(b1 as u16 * 0x100) + (b2 as u16)
|
||||
}
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Address, D::Error> {
|
||||
Ok(Address {
|
||||
services: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
address: try!(ConsensusDecodable::consensus_decode(d)),
|
||||
// Explicitly code the port since it needs to be big-endian
|
||||
port: {
|
||||
let b1: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
let b2: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
(b1 as u16 * 0x100) + (b2 as u16)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO: render services and hex-ize address
|
||||
write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}",
|
||||
self.services, &self.address[..], self.port)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO: render services and hex-ize address
|
||||
write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}",
|
||||
self.services, &self.address[..], self.port)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Address {
|
||||
fn clone(&self) -> Address {
|
||||
unsafe {
|
||||
use std::intrinsics::copy_nonoverlapping;
|
||||
use std::mem;
|
||||
let mut ret = mem::uninitialized();
|
||||
copy_nonoverlapping(self,
|
||||
&mut ret,
|
||||
mem::size_of::<Address>());
|
||||
ret
|
||||
fn clone(&self) -> Address {
|
||||
unsafe {
|
||||
use std::intrinsics::copy_nonoverlapping;
|
||||
use std::mem;
|
||||
let mut ret = mem::uninitialized();
|
||||
copy_nonoverlapping(self,
|
||||
&mut ret,
|
||||
mem::size_of::<Address>());
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Address {
|
||||
fn eq(&self, other: &Address) -> bool {
|
||||
self.services == other.services &&
|
||||
&self.address[..] == &other.address[..] &&
|
||||
self.port == other.port
|
||||
}
|
||||
fn eq(&self, other: &Address) -> bool {
|
||||
self.services == other.services &&
|
||||
&self.address[..] == &other.address[..] &&
|
||||
self.port == other.port
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Address {}
|
||||
|
||||
#[cfg(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]
|
||||
fn serialize_address_test() {
|
||||
assert_eq!(serialize(&Address {
|
||||
services: 1,
|
||||
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
|
||||
port: 8333
|
||||
}),
|
||||
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]));
|
||||
}
|
||||
#[test]
|
||||
fn serialize_address_test() {
|
||||
assert_eq!(serialize(&Address {
|
||||
services: 1,
|
||||
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
|
||||
port: 8333
|
||||
}),
|
||||
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]));
|
||||
}
|
||||
|
||||
#[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,
|
||||
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
||||
0, 1, 0x20, 0x8d]);
|
||||
assert!(addr.is_ok());
|
||||
let full = addr.unwrap();
|
||||
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.port == 8333);
|
||||
#[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,
|
||||
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
||||
0, 1, 0x20, 0x8d]);
|
||||
assert!(addr.is_ok());
|
||||
let full = addr.unwrap();
|
||||
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.port == 8333);
|
||||
|
||||
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]);
|
||||
assert!(addr.is_err());
|
||||
}
|
||||
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]);
|
||||
assert!(addr.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -26,12 +26,12 @@ use util::hash::Sha256dHash;
|
|||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
/// The type of an inventory object
|
||||
pub enum InvType {
|
||||
/// Error --- these inventories can be ignored
|
||||
Error,
|
||||
/// Transaction
|
||||
Transaction,
|
||||
/// Block
|
||||
Block
|
||||
/// Error --- these inventories can be ignored
|
||||
Error,
|
||||
/// Transaction
|
||||
Transaction,
|
||||
/// Block
|
||||
Block
|
||||
}
|
||||
|
||||
// Some simple messages
|
||||
|
@ -39,133 +39,133 @@ pub enum InvType {
|
|||
/// The `getblocks` message
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct GetBlocksMessage {
|
||||
/// The protocol version
|
||||
pub version: u32,
|
||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||
/// reply with its longest known chain, starting from a locator hash
|
||||
/// if possible and block 1 otherwise.
|
||||
pub locator_hashes: Vec<Sha256dHash>,
|
||||
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
||||
pub stop_hash: Sha256dHash
|
||||
/// The protocol version
|
||||
pub version: u32,
|
||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||
/// reply with its longest known chain, starting from a locator hash
|
||||
/// if possible and block 1 otherwise.
|
||||
pub locator_hashes: Vec<Sha256dHash>,
|
||||
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
||||
pub stop_hash: Sha256dHash
|
||||
}
|
||||
|
||||
/// The `getheaders` message
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct GetHeadersMessage {
|
||||
/// The protocol version
|
||||
pub version: u32,
|
||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||
/// reply with its longest known chain, starting from a locator hash
|
||||
/// if possible and block 1 otherwise.
|
||||
pub locator_hashes: Vec<Sha256dHash>,
|
||||
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||
pub stop_hash: Sha256dHash
|
||||
/// The protocol version
|
||||
pub version: u32,
|
||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||
/// reply with its longest known chain, starting from a locator hash
|
||||
/// if possible and block 1 otherwise.
|
||||
pub locator_hashes: Vec<Sha256dHash>,
|
||||
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||
pub stop_hash: Sha256dHash
|
||||
}
|
||||
|
||||
/// An inventory object --- a reference to a Bitcoin object
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Inventory {
|
||||
/// The type of object that is referenced
|
||||
pub inv_type: InvType,
|
||||
/// The object's hash
|
||||
pub hash: Sha256dHash
|
||||
/// The type of object that is referenced
|
||||
pub inv_type: InvType,
|
||||
/// The object's hash
|
||||
pub hash: Sha256dHash
|
||||
}
|
||||
|
||||
impl GetBlocksMessage {
|
||||
/// Construct a new `getblocks` message
|
||||
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
|
||||
GetBlocksMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
locator_hashes: locator_hashes.clone(),
|
||||
stop_hash: stop_hash
|
||||
/// Construct a new `getblocks` message
|
||||
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
|
||||
GetBlocksMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
locator_hashes: locator_hashes.clone(),
|
||||
stop_hash: stop_hash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
|
||||
|
||||
impl GetHeadersMessage {
|
||||
/// Construct a new `getheaders` message
|
||||
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
|
||||
GetHeadersMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
locator_hashes: locator_hashes,
|
||||
stop_hash: stop_hash
|
||||
/// Construct a new `getheaders` message
|
||||
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
|
||||
GetHeadersMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
locator_hashes: locator_hashes,
|
||||
stop_hash: stop_hash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
||||
|
||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory {
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
try!(match self.inv_type {
|
||||
InvType::Error => 0u32,
|
||||
InvType::Transaction => 1,
|
||||
InvType::Block => 2
|
||||
}.consensus_encode(s));
|
||||
self.hash.consensus_encode(s)
|
||||
}
|
||||
#[inline]
|
||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
try!(match self.inv_type {
|
||||
InvType::Error => 0u32,
|
||||
InvType::Transaction => 1,
|
||||
InvType::Block => 2
|
||||
}.consensus_encode(s));
|
||||
self.hash.consensus_encode(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
|
||||
let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
Ok(Inventory {
|
||||
inv_type: match int_type {
|
||||
0 => InvType::Error,
|
||||
1 => InvType::Transaction,
|
||||
2 => InvType::Block,
|
||||
// TODO do not fail here
|
||||
_ => { panic!("bad inventory type field") }
|
||||
},
|
||||
hash: try!(ConsensusDecodable::consensus_decode(d))
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
|
||||
let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||
Ok(Inventory {
|
||||
inv_type: match int_type {
|
||||
0 => InvType::Error,
|
||||
1 => InvType::Transaction,
|
||||
2 => InvType::Block,
|
||||
// TODO do not fail here
|
||||
_ => { panic!("bad inventory type field") }
|
||||
},
|
||||
hash: try!(ConsensusDecodable::consensus_decode(d))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||
|
||||
use std::io;
|
||||
use serialize::hex::FromHex;
|
||||
use std::io;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use std::default::Default;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use std::default::Default;
|
||||
|
||||
#[test]
|
||||
fn getblocks_message_test() {
|
||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||
#[test]
|
||||
fn getblocks_message_test() {
|
||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||
|
||||
let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
||||
assert_eq!(real_decode.stop_hash, Default::default());
|
||||
let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
||||
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]
|
||||
fn getheaders_message_test() {
|
||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||
#[test]
|
||||
fn getheaders_message_test() {
|
||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||
|
||||
let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
||||
assert_eq!(real_decode.stop_hash, Default::default());
|
||||
let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone());
|
||||
assert!(decode.is_ok());
|
||||
let real_decode = decode.unwrap();
|
||||
assert_eq!(real_decode.version, 70002);
|
||||
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
||||
assert_eq!(real_decode.stop_hash, Default::default());
|
||||
|
||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||
}
|
||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,24 +34,24 @@ use util::{self, propagate_err};
|
|||
|
||||
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
||||
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
|
||||
match *ipaddr {
|
||||
ip::IpAddr::V4(ref addr) => &addr.to_ipv6_mapped(),
|
||||
ip::IpAddr::V6(ref addr) => addr
|
||||
}.segments()
|
||||
match *ipaddr {
|
||||
ip::IpAddr::V4(ref addr) => &addr.to_ipv6_mapped(),
|
||||
ip::IpAddr::V6(ref addr) => addr
|
||||
}.segments()
|
||||
}
|
||||
|
||||
/// A network socket along with information about the peer
|
||||
#[derive(Clone)]
|
||||
pub struct Socket {
|
||||
/// The underlying TCP socket
|
||||
socket: Arc<Mutex<Option<tcp::TcpStream>>>,
|
||||
/// Services supported by us
|
||||
pub services: u64,
|
||||
/// Our user agent
|
||||
pub user_agent: String,
|
||||
/// Nonce to identify our `version` messages
|
||||
pub version_nonce: u64,
|
||||
/// Network magic
|
||||
/// The underlying TCP socket
|
||||
socket: Arc<Mutex<Option<tcp::TcpStream>>>,
|
||||
/// Services supported by us
|
||||
pub services: u64,
|
||||
/// Our user agent
|
||||
pub user_agent: String,
|
||||
/// Nonce to identify our `version` messages
|
||||
pub version_nonce: u64,
|
||||
/// Network magic
|
||||
pub magic: u32
|
||||
}
|
||||
|
||||
|
|
|
@ -23,204 +23,204 @@ use util::hash::Sha256dHash;
|
|||
/// An error that might occur during base58 decoding
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Error {
|
||||
/// Invalid character encountered
|
||||
BadByte(u8),
|
||||
/// Checksum was not correct (expected, actual)
|
||||
BadChecksum(u32, u32),
|
||||
/// The length (in bytes) of the object was not correct
|
||||
InvalidLength(usize),
|
||||
/// Version byte(s) were not recognized
|
||||
InvalidVersion(Vec<u8>),
|
||||
/// Checked data was less than 4 bytes
|
||||
TooShort(usize),
|
||||
/// Any other error
|
||||
Other(String)
|
||||
/// Invalid character encountered
|
||||
BadByte(u8),
|
||||
/// Checksum was not correct (expected, actual)
|
||||
BadChecksum(u32, u32),
|
||||
/// The length (in bytes) of the object was not correct
|
||||
InvalidLength(usize),
|
||||
/// Version byte(s) were not recognized
|
||||
InvalidVersion(Vec<u8>),
|
||||
/// Checked data was less than 4 bytes
|
||||
TooShort(usize),
|
||||
/// Any other error
|
||||
Other(String)
|
||||
}
|
||||
|
||||
static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
static BASE58_DIGITS: [Option<u8>; 128] = [
|
||||
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, // 16-23
|
||||
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, // 40-47
|
||||
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
|
||||
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(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
|
||||
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(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
|
||||
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, // 16-23
|
||||
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, // 40-47
|
||||
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
|
||||
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(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
|
||||
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(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
|
||||
];
|
||||
|
||||
/// Trait for objects which can be read as base58
|
||||
pub trait FromBase58 {
|
||||
/// Constructs an object flrom the byte-encoding (base 256)
|
||||
/// representation of its base58 format
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
|
||||
pub trait FromBase58: Sized {
|
||||
/// Constructs an object flrom the byte-encoding (base 256)
|
||||
/// representation of its base58 format
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
|
||||
|
||||
/// Obtain an object from its base58 encoding
|
||||
fn from_base58(data: &str) -> Result<Self, Error> {
|
||||
// 11/15 is just over log_256(58)
|
||||
let mut scratch = Vec::from_elem(1 + data.len() * 11 / 15, 0u8);
|
||||
// Build in base 256
|
||||
for d58 in data.bytes() {
|
||||
// Compute "X = X * 58 + next_digit" in base 256
|
||||
if d58 as usize > BASE58_DIGITS.len() {
|
||||
return Err(Error::BadByte(d58));
|
||||
}
|
||||
let mut carry = match BASE58_DIGITS[d58 as usize] {
|
||||
Some(d58) => d58 as u32,
|
||||
None => { return Err(Error::BadByte(d58)); }
|
||||
};
|
||||
for d256 in scratch.iter_mut().rev() {
|
||||
carry += *d256 as u32 * 58;
|
||||
*d256 = carry as u8;
|
||||
carry /= 256;
|
||||
}
|
||||
assert_eq!(carry, 0);
|
||||
/// Obtain an object from its base58 encoding
|
||||
fn from_base58(data: &str) -> Result<Self, Error> {
|
||||
// 11/15 is just over log_256(58)
|
||||
let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
|
||||
// Build in base 256
|
||||
for d58 in data.bytes() {
|
||||
// Compute "X = X * 58 + next_digit" in base 256
|
||||
if d58 as usize > BASE58_DIGITS.len() {
|
||||
return Err(Error::BadByte(d58));
|
||||
}
|
||||
let mut carry = match BASE58_DIGITS[d58 as usize] {
|
||||
Some(d58) => d58 as u32,
|
||||
None => { return Err(Error::BadByte(d58)); }
|
||||
};
|
||||
for d256 in scratch.iter_mut().rev() {
|
||||
carry += *d256 as u32 * 58;
|
||||
*d256 = carry as u8;
|
||||
carry /= 256;
|
||||
}
|
||||
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
|
||||
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)
|
||||
}
|
||||
/// Obtain an object from its base58check encoding
|
||||
fn from_base58check(data: &str) -> Result<Self, Error> {
|
||||
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));
|
||||
}
|
||||
|
||||
/// Obtain an object from its base58check encoding
|
||||
fn from_base58check(data: &str) -> Result<Self, Error> {
|
||||
let mut ret: Vec<u8> = try!(FromBase58::from_base58(data));
|
||||
if ret.len() < 4 {
|
||||
return Err(Error::TooShort(ret.len()));
|
||||
ret.truncate(ck_start);
|
||||
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));
|
||||
}
|
||||
|
||||
ret.truncate(ck_start);
|
||||
FromBase58::from_base58_layout(ret)
|
||||
}
|
||||
}
|
||||
|
||||
/// Directly encode a slice as base58
|
||||
pub fn base58_encode_slice(data: &[u8]) -> String {
|
||||
// 7/5 is just over log_58(256)
|
||||
let mut scratch = vec![0u8; 1 + data.len() * 7 / 5];
|
||||
// Build in base 58
|
||||
for &d256 in data.base58_layout().iter() {
|
||||
// Compute "X = X * 256 + next_digit" in base 58
|
||||
let mut carry = d256 as u32;
|
||||
for d58 in scratch.iter_mut().rev() {
|
||||
carry += (*d58 as u32) << 8;
|
||||
*d58 = (carry % 58) as u8;
|
||||
carry /= 58;
|
||||
// 7/5 is just over log_58(256)
|
||||
let mut scratch = vec![0u8; 1 + data.len() * 7 / 5];
|
||||
// Build in base 58
|
||||
for &d256 in data.base58_layout().iter() {
|
||||
// Compute "X = X * 256 + next_digit" in base 58
|
||||
let mut carry = d256 as u32;
|
||||
for d58 in scratch.iter_mut().rev() {
|
||||
carry += (*d58 as u32) << 8;
|
||||
*d58 = (carry % 58) as u8;
|
||||
carry /= 58;
|
||||
}
|
||||
assert_eq!(carry, 0);
|
||||
}
|
||||
assert_eq!(carry, 0);
|
||||
}
|
||||
|
||||
// Unsafely translate the bytes to a utf8 string
|
||||
unsafe {
|
||||
// Copy leading zeroes directly
|
||||
let mut ret: Vec<u8> = str::from_utf8(data.iter().take_while(|&&x| x == 0)
|
||||
.map(|_| BASE58_CHARS[0])
|
||||
.collect()).unwrap();
|
||||
// Copy rest of string
|
||||
ret.as_mut_vec().extend(scratch.into_iter().skip_while(|&x| x == 0)
|
||||
.map(|x| BASE58_CHARS[x as usize]));
|
||||
ret
|
||||
}
|
||||
// Unsafely translate the bytes to a utf8 string
|
||||
unsafe {
|
||||
// Copy leading zeroes directly
|
||||
let mut ret: Vec<u8> = data.iter().take_while(|&&x| x == 0)
|
||||
.map(|_| BASE58_CHARS[0])
|
||||
.collect();
|
||||
// Copy rest of string
|
||||
ret.extend(scratch.into_iter().skip_while(|&x| x == 0)
|
||||
.map(|x| BASE58_CHARS[x as usize]));
|
||||
String::from_utf8(ret).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for objects which can be written as base58
|
||||
pub trait ToBase58 {
|
||||
/// The serialization to be converted into base58
|
||||
fn base58_layout(&self) -> Vec<u8>;
|
||||
/// The serialization to be converted into base58
|
||||
fn base58_layout(&self) -> Vec<u8>;
|
||||
|
||||
/// Obtain a string with the base58 encoding of the object
|
||||
fn to_base58(&self) -> String {
|
||||
base58_encode_slice(&self.base58_layout()[..])
|
||||
}
|
||||
/// Obtain a string with the base58 encoding of the object
|
||||
fn to_base58(&self) -> String {
|
||||
base58_encode_slice(&self.base58_layout()[..])
|
||||
}
|
||||
|
||||
/// 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.)
|
||||
fn to_base58check(&self) -> String {
|
||||
let mut data = self.base58_layout();
|
||||
let checksum = Sha256dHash::from_data(&data).into_le().low_u32();
|
||||
data.write_u32::<LittleEndian>(checksum);
|
||||
base58_encode_slice(&data)
|
||||
}
|
||||
/// 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.)
|
||||
fn to_base58check(&self) -> String {
|
||||
let mut data = self.base58_layout();
|
||||
let checksum = Sha256dHash::from_data(&data).into_le().low_u32();
|
||||
data.write_u32::<LittleEndian>(checksum);
|
||||
base58_encode_slice(&data)
|
||||
}
|
||||
}
|
||||
|
||||
// Trivial implementations for slices and vectors
|
||||
impl<'a> ToBase58 for &'a [u8] {
|
||||
fn base58_layout(&self) -> Vec<u8> { self.to_vec() }
|
||||
fn to_base58(&self) -> String { base58_encode_slice(*self) }
|
||||
fn base58_layout(&self) -> Vec<u8> { self.to_vec() }
|
||||
fn to_base58(&self) -> String { base58_encode_slice(*self) }
|
||||
}
|
||||
|
||||
impl FromBase58 for Vec<u8> {
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
Ok(data)
|
||||
}
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serialize::hex::FromHex;
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
use super::ToBase58;
|
||||
use super::FromBase58;
|
||||
use super::ToBase58;
|
||||
use super::FromBase58;
|
||||
|
||||
#[test]
|
||||
fn test_base58_encode() {
|
||||
// Basics
|
||||
assert_eq!([0].as_slice().to_base58().as_slice(), "1");
|
||||
assert_eq!([1].as_slice().to_base58().as_slice(), "2");
|
||||
assert_eq!([58].as_slice().to_base58().as_slice(), "21");
|
||||
assert_eq!([13, 36].as_slice().to_base58().as_slice(), "211");
|
||||
#[test]
|
||||
fn test_base58_encode() {
|
||||
// Basics
|
||||
assert_eq!([0].as_slice().to_base58().as_slice(), "1");
|
||||
assert_eq!([1].as_slice().to_base58().as_slice(), "2");
|
||||
assert_eq!([58].as_slice().to_base58().as_slice(), "21");
|
||||
assert_eq!([13, 36].as_slice().to_base58().as_slice(), "211");
|
||||
|
||||
// Leading zeroes
|
||||
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");
|
||||
// Leading zeroes
|
||||
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");
|
||||
|
||||
// Addresses
|
||||
assert_eq!("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check().as_slice(),
|
||||
"1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH");
|
||||
}
|
||||
// Addresses
|
||||
assert_eq!("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check().as_slice(),
|
||||
"1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base58_decode() {
|
||||
// Basics
|
||||
assert_eq!(FromBase58::from_base58("1"), Ok(vec![0u8]));
|
||||
assert_eq!(FromBase58::from_base58("2"), Ok(vec![1u8]));
|
||||
assert_eq!(FromBase58::from_base58("21"), Ok(vec![58u8]));
|
||||
assert_eq!(FromBase58::from_base58("211"), Ok(vec![13u8, 36]));
|
||||
#[test]
|
||||
fn test_base58_decode() {
|
||||
// Basics
|
||||
assert_eq!(FromBase58::from_base58("1"), Ok(vec![0u8]));
|
||||
assert_eq!(FromBase58::from_base58("2"), Ok(vec![1u8]));
|
||||
assert_eq!(FromBase58::from_base58("21"), Ok(vec![58u8]));
|
||||
assert_eq!(FromBase58::from_base58("211"), Ok(vec![13u8, 36]));
|
||||
|
||||
// Leading zeroes
|
||||
assert_eq!(FromBase58::from_base58("1211"), Ok(vec![0u8, 13, 36]));
|
||||
assert_eq!(FromBase58::from_base58("111211"), Ok(vec![0u8, 0, 0, 13, 36]));
|
||||
// Leading zeroes
|
||||
assert_eq!(FromBase58::from_base58("1211"), Ok(vec![0u8, 13, 36]));
|
||||
assert_eq!(FromBase58::from_base58("111211"), Ok(vec![0u8, 0, 0, 13, 36]));
|
||||
|
||||
// Addresses
|
||||
assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"),
|
||||
Ok("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap()))
|
||||
}
|
||||
// Addresses
|
||||
assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"),
|
||||
Ok("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base58_roundtrip() {
|
||||
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
|
||||
let v: Vec<u8> = FromBase58::from_base58check(s).unwrap();
|
||||
assert_eq!(v.to_base58check().as_slice(), s);
|
||||
assert_eq!(FromBase58::from_base58check(v.to_base58check().as_slice()), Ok(v));
|
||||
}
|
||||
#[test]
|
||||
fn test_base58_roundtrip() {
|
||||
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
|
||||
let v: Vec<u8> = FromBase58::from_base58check(s).unwrap();
|
||||
assert_eq!(v.to_base58check().as_slice(), s);
|
||||
assert_eq!(FromBase58::from_base58check(v.to_base58check().as_slice()), Ok(v));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
406
src/util/hash.rs
406
src/util/hash.rs
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// 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 ::std::fmt::Debug for Sha256dHash {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.be_hex_string().as_slice())
|
||||
}
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.be_hex_string().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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));
|
||||
|
||||
impl Ripemd160Hash {
|
||||
/// Create a hash by hashing some data
|
||||
pub fn from_data(data: &[u8]) -> Ripemd160Hash {
|
||||
let mut ret = [0; 20];
|
||||
let mut rmd = Ripemd160::new();
|
||||
rmd.input(data);
|
||||
rmd.result(&mut ret);
|
||||
Ripemd160Hash(ret)
|
||||
}
|
||||
/// Create a hash by hashing some data
|
||||
pub fn from_data(data: &[u8]) -> Ripemd160Hash {
|
||||
let mut ret = [0; 20];
|
||||
let mut rmd = Ripemd160::new();
|
||||
rmd.input(data);
|
||||
rmd.result(&mut ret);
|
||||
Ripemd160Hash(ret)
|
||||
}
|
||||
}
|
||||
|
||||
// This doesn't make much sense to me, but is implicit behaviour
|
||||
// in the C++ reference client
|
||||
impl Default for Sha256dHash {
|
||||
#[inline]
|
||||
fn default() -> Sha256dHash { Sha256dHash([0u8; 32]) }
|
||||
#[inline]
|
||||
fn default() -> Sha256dHash { Sha256dHash([0u8; 32]) }
|
||||
}
|
||||
|
||||
impl Sha256dHash {
|
||||
/// Create a hash by hashing some data
|
||||
pub fn from_data(data: &[u8]) -> Sha256dHash {
|
||||
let Sha256dHash(mut ret): Sha256dHash = Default::default();
|
||||
let mut sha2 = Sha256::new();
|
||||
sha2.input(data);
|
||||
sha2.result(&mut ret);
|
||||
sha2.reset();
|
||||
sha2.input(&ret);
|
||||
sha2.result(&mut 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());
|
||||
/// Create a hash by hashing some data
|
||||
pub fn from_data(data: &[u8]) -> Sha256dHash {
|
||||
let Sha256dHash(mut ret): Sha256dHash = Default::default();
|
||||
let mut sha2 = Sha256::new();
|
||||
sha2.input(data);
|
||||
sha2.result(&mut ret);
|
||||
sha2.reset();
|
||||
sha2.input(&ret);
|
||||
sha2.result(&mut ret);
|
||||
Sha256dHash(ret)
|
||||
}
|
||||
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());
|
||||
/// 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
|
||||
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
|
||||
|
@ -161,48 +161,48 @@ impl Sha256dHash {
|
|||
// little-endian and should be done using the consensus `encodable::ConsensusEncodable`
|
||||
// interface.
|
||||
impl serde::Serialize for Sha256dHash {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: serde::Serializer,
|
||||
{
|
||||
serializer.visit_str(&self.be_hex_string())
|
||||
}
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: serde::Serializer,
|
||||
{
|
||||
serializer.visit_str(&self.be_hex_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Deserialize for Sha256dHash {
|
||||
#[inline]
|
||||
fn deserialize<D>(d: &mut D) -> Result<Sha256dHash, D::Error>
|
||||
where D: serde::Deserializer
|
||||
{
|
||||
use serialize::hex::FromHex;
|
||||
#[inline]
|
||||
fn deserialize<D>(d: &mut D) -> Result<Sha256dHash, D::Error>
|
||||
where D: serde::Deserializer
|
||||
{
|
||||
use serialize::hex::FromHex;
|
||||
|
||||
struct Sha256dHashVisitor;
|
||||
impl serde::de::Visitor for Sha256dHashVisitor {
|
||||
type Value = Sha256dHash;
|
||||
struct Sha256dHashVisitor;
|
||||
impl serde::de::Visitor for Sha256dHashVisitor {
|
||||
type Value = Sha256dHash;
|
||||
|
||||
fn visit_string<E>(&mut self, v: String) -> Result<Sha256dHash, E>
|
||||
where E: serde::de::Error
|
||||
{
|
||||
self.visit_str(&v)
|
||||
}
|
||||
fn visit_string<E>(&mut self, v: String) -> Result<Sha256dHash, E>
|
||||
where E: serde::de::Error
|
||||
{
|
||||
self.visit_str(&v)
|
||||
}
|
||||
|
||||
fn visit_str<E>(&mut self, hex_str: &str) -> Result<Sha256dHash, E>
|
||||
where E: serde::de::Error
|
||||
{
|
||||
if hex_str.len() != 64 {
|
||||
return Err(serde::de::Error::syntax_error());
|
||||
fn visit_str<E>(&mut self, hex_str: &str) -> Result<Sha256dHash, E>
|
||||
where E: serde::de::Error
|
||||
{
|
||||
if hex_str.len() != 64 {
|
||||
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()));
|
||||
let mut ret = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
ret[i] = raw_str[31 - i];
|
||||
}
|
||||
Ok(Sha256dHash(ret))
|
||||
}
|
||||
|
||||
d.visit(Sha256dHashVisitor)
|
||||
}
|
||||
|
||||
d.visit(Sha256dHashVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
// Consensus encoding (little-endian)
|
||||
|
@ -212,108 +212,108 @@ impl_newtype_consensus_encoding!(Hash64);
|
|||
impl_newtype_consensus_encoding!(Sha256dHash);
|
||||
|
||||
impl fmt::LowerHex for Sha256dHash {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let &Sha256dHash(data) = self;
|
||||
let mut rv = [0; 64];
|
||||
for ch in data.iter().rev() {
|
||||
try!(write!(f, "{:02x}", ch));
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let &Sha256dHash(data) = self;
|
||||
let mut rv = [0; 64];
|
||||
for ch in data.iter().rev() {
|
||||
try!(write!(f, "{:02x}", ch));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Any collection of objects for which a merkle root makes sense to calculate
|
||||
pub trait MerkleRoot {
|
||||
/// Construct a merkle tree from a collection, with elements ordered as
|
||||
/// they were in the original collection, and return the merkle root.
|
||||
fn merkle_root(&self) -> Sha256dHash;
|
||||
/// Construct a merkle tree from a collection, with elements ordered as
|
||||
/// they were in the original collection, and return the merkle root.
|
||||
fn merkle_root(&self) -> Sha256dHash;
|
||||
}
|
||||
|
||||
impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
||||
fn merkle_root(&self) -> Sha256dHash {
|
||||
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
|
||||
// Base case
|
||||
if data.len() < 1 {
|
||||
return Default::default();
|
||||
}
|
||||
if data.len() < 2 {
|
||||
return data[0];
|
||||
}
|
||||
// Recursion
|
||||
let mut next = vec![];
|
||||
for idx in 0..((data.len() + 1) / 2) {
|
||||
let idx1 = 2 * idx;
|
||||
let idx2 = min(idx1 + 1, data.len() - 1);
|
||||
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||
next.push(encoder.unwrap().into_inner().bitcoin_hash());
|
||||
}
|
||||
merkle_root(next)
|
||||
fn merkle_root(&self) -> Sha256dHash {
|
||||
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
|
||||
// Base case
|
||||
if data.len() < 1 {
|
||||
return Default::default();
|
||||
}
|
||||
if data.len() < 2 {
|
||||
return data[0];
|
||||
}
|
||||
// Recursion
|
||||
let mut next = vec![];
|
||||
for idx in 0..((data.len() + 1) / 2) {
|
||||
let idx1 = 2 * idx;
|
||||
let idx2 = min(idx1 + 1, data.len() - 1);
|
||||
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||
next.push(encoder.into_inner().into_inner().bitcoin_hash());
|
||||
}
|
||||
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> {
|
||||
fn merkle_root(&self) -> Sha256dHash {
|
||||
(&self[..]).merkle_root()
|
||||
}
|
||||
fn merkle_root(&self) -> Sha256dHash {
|
||||
(&self[..]).merkle_root()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::prelude::*;
|
||||
use std::io::Cursor;
|
||||
use std::str::from_utf8;
|
||||
use serialize::Encodable;
|
||||
use serialize::json;
|
||||
use std::prelude::*;
|
||||
use std::io::Cursor;
|
||||
use std::str::from_utf8;
|
||||
use serialize::Encodable;
|
||||
use serialize::json;
|
||||
|
||||
use network::serialize::{serialize, deserialize};
|
||||
use util::hash::Sha256dHash;
|
||||
use network::serialize::{serialize, deserialize};
|
||||
use util::hash::Sha256dHash;
|
||||
|
||||
#[test]
|
||||
fn test_sha256d() {
|
||||
// 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
|
||||
// of a Uint256 (which is little-endian) after transmutation
|
||||
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
|
||||
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string());
|
||||
assert_eq!(Sha256dHash::from_data(&[]).be_hex_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());
|
||||
#[test]
|
||||
fn test_sha256d() {
|
||||
// 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
|
||||
// of a Uint256 (which is little-endian) after transmutation
|
||||
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
|
||||
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string());
|
||||
assert_eq!(Sha256dHash::from_data(&[]).be_hex_string(),
|
||||
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string());
|
||||
}
|
||||
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));
|
||||
}
|
||||
#[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]
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -19,62 +19,62 @@
|
|||
|
||||
/// An iterator that returns pairs of elements
|
||||
pub struct Pair<I>
|
||||
where I: Iterator
|
||||
where I: Iterator
|
||||
{
|
||||
iter: I,
|
||||
last_elem: Option<I::Item>
|
||||
iter: I,
|
||||
last_elem: Option<I::Item>
|
||||
}
|
||||
|
||||
impl<I: Iterator> Iterator for Pair<I> {
|
||||
type Item = (I::Item, I::Item);
|
||||
type Item = (I::Item, I::Item);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<(I::Item, I::Item)> {
|
||||
let elem1 = self.iter.next();
|
||||
if elem1.is_none() {
|
||||
None
|
||||
} else {
|
||||
let elem2 = self.iter.next();
|
||||
if elem2.is_none() {
|
||||
self.last_elem = elem1;
|
||||
None
|
||||
} else {
|
||||
Some((elem1.unwrap(), elem2.unwrap()))
|
||||
}
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<(I::Item, I::Item)> {
|
||||
let elem1 = self.iter.next();
|
||||
if elem1.is_none() {
|
||||
None
|
||||
} else {
|
||||
let elem2 = self.iter.next();
|
||||
if elem2.is_none() {
|
||||
self.last_elem = elem1;
|
||||
None
|
||||
} else {
|
||||
Some((elem1.unwrap(), elem2.unwrap()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.iter.size_hint() {
|
||||
(n, None) => (n/2, None),
|
||||
(n, Some(m)) => (n/2, Some(m/2))
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.iter.size_hint() {
|
||||
(n, None) => (n/2, None),
|
||||
(n, Some(m)) => (n/2, Some(m/2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> Pair<I> {
|
||||
/// Returns the last element of the iterator if there were an odd
|
||||
/// number of elements remaining before it was Pair-ified.
|
||||
#[inline]
|
||||
pub fn remainder(self) -> Option<I::Item> {
|
||||
self.last_elem
|
||||
}
|
||||
/// Returns the last element of the iterator if there were an odd
|
||||
/// number of elements remaining before it was Pair-ified.
|
||||
#[inline]
|
||||
pub fn remainder(self) -> Option<I::Item> {
|
||||
self.last_elem
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||
pub trait Pairable {
|
||||
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||
fn pair(self) -> Pair<Self>;
|
||||
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||
fn pair(self) -> Pair<Self>;
|
||||
}
|
||||
|
||||
impl<I: Iterator> Pairable for I {
|
||||
/// Creates an iterator that yields pairs ef elements from the underlying
|
||||
/// iterator, yielding `None` when there are fewer than two elements to
|
||||
/// return.
|
||||
#[inline]
|
||||
fn pair(self) -> Pair<I> {
|
||||
Pair {iter: self, last_elem: None }
|
||||
}
|
||||
/// Creates an iterator that yields pairs ef elements from the underlying
|
||||
/// iterator, yielding `None` when there are fewer than two elements to
|
||||
/// return.
|
||||
#[inline]
|
||||
fn pair(self) -> Pair<I> {
|
||||
Pair {iter: self, last_elem: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
192
src/util/misc.rs
192
src/util/misc.rs
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// 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
|
||||
pub fn hex_bytes(s: &str) -> Result<Vec<u8>, Error> {
|
||||
let mut v = vec![];
|
||||
let mut iter = s.chars().pair();
|
||||
// Do the parsing
|
||||
try!(iter.fold(Ok(()), |e, (f, s)|
|
||||
if e.is_err() { e }
|
||||
else {
|
||||
match (f.to_digit(16), s.to_digit(16)) {
|
||||
(None, _) => Err(Error::Detail(
|
||||
format!("expected hex, got {:}", f),
|
||||
Box::new(Error::ParseFailed)
|
||||
let mut v = vec![];
|
||||
let mut iter = s.chars().pair();
|
||||
// Do the parsing
|
||||
try!(iter.fold(Ok(()), |e, (f, s)|
|
||||
if e.is_err() { e }
|
||||
else {
|
||||
match (f.to_digit(16), s.to_digit(16)) {
|
||||
(None, _) => Err(Error::Detail(
|
||||
format!("expected hex, got {:}", f),
|
||||
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(
|
||||
format!("expected hex, got {:}", s),
|
||||
Box::new(Error::ParseFailed)
|
||||
)),
|
||||
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
|
||||
}
|
||||
None => Ok(v)
|
||||
}
|
||||
));
|
||||
// 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
|
||||
/// 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>) {
|
||||
match res {
|
||||
Ok(_) => {},
|
||||
Err(e) => { println!("{}: {:?}", s, e); }
|
||||
};
|
||||
match res {
|
||||
Ok(_) => {},
|
||||
Err(e) => { println!("{}: {:?}", s, e); }
|
||||
};
|
||||
}
|
||||
|
||||
/// Search for `needle` in the vector `haystack` and remove every
|
||||
/// instance of it, returning the number of instances removed.
|
||||
/// Loops through the vector opcode by opcode, skipping pushed data.
|
||||
pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
|
||||
if needle.len() > haystack.len() { return 0; }
|
||||
if needle.len() == 0 { return 0; }
|
||||
if needle.len() > haystack.len() { return 0; }
|
||||
if needle.len() == 0 { return 0; }
|
||||
|
||||
let mut top = haystack.len() - needle.len();
|
||||
let mut n_deleted = 0;
|
||||
let mut top = haystack.len() - needle.len();
|
||||
let mut n_deleted = 0;
|
||||
|
||||
let mut i = 0;
|
||||
while i <= top {
|
||||
if &haystack[i..(i + needle.len())] == needle {
|
||||
let v = &mut haystack;
|
||||
for j in i..top {
|
||||
v.swap(j + needle.len(), j);
|
||||
}
|
||||
n_deleted += 1;
|
||||
// This is ugly but prevents infinite loop in case of overflow
|
||||
let overflow = top < needle.len();
|
||||
top -= needle.len();
|
||||
if overflow { break; }
|
||||
} else {
|
||||
i += match opcodes::All::from_u8((*haystack)[i]).classify() {
|
||||
opcodes::Class::PushBytes(n) => n + 1,
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
|
||||
_ => 1
|
||||
};
|
||||
let mut i = 0;
|
||||
while i <= top {
|
||||
if &haystack[i..(i + needle.len())] == needle {
|
||||
let v = &mut haystack;
|
||||
for j in i..top {
|
||||
v.swap(j + needle.len(), j);
|
||||
}
|
||||
n_deleted += 1;
|
||||
// This is ugly but prevents infinite loop in case of overflow
|
||||
let overflow = top < needle.len();
|
||||
top -= needle.len();
|
||||
if overflow { break; }
|
||||
} else {
|
||||
i += match opcodes::All::from_u8((*haystack)[i]).classify() {
|
||||
opcodes::Class::PushBytes(n) => n as usize + 1,
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
haystack.truncate(top + needle.len());
|
||||
n_deleted
|
||||
haystack.truncate(top + needle.len());
|
||||
n_deleted
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::prelude::*;
|
||||
use std::prelude::*;
|
||||
|
||||
use super::script_find_and_remove;
|
||||
use super::hex_bytes;
|
||||
use super::script_find_and_remove;
|
||||
use super::hex_bytes;
|
||||
|
||||
#[test]
|
||||
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];
|
||||
#[test]
|
||||
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];
|
||||
|
||||
assert_eq!(script_find_and_remove(&mut v, []), 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!(script_find_and_remove(&mut v, []), 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!(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!(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!(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!(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!(script_find_and_remove(&mut v, [101]), 1);
|
||||
assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]);
|
||||
assert_eq!(script_find_and_remove(&mut v, [101]), 1);
|
||||
assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]);
|
||||
|
||||
assert_eq!(script_find_and_remove(&mut v, [102]), 3);
|
||||
assert_eq!(v, vec![103, 104, 103, 104, 103]);
|
||||
assert_eq!(script_find_and_remove(&mut v, [102]), 3);
|
||||
assert_eq!(v, vec![103, 104, 103, 104, 103]);
|
||||
|
||||
assert_eq!(script_find_and_remove(&mut v, [103, 104]), 2);
|
||||
assert_eq!(v, vec![103]);
|
||||
assert_eq!(script_find_and_remove(&mut v, [103, 104]), 2);
|
||||
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]), 0);
|
||||
assert_eq!(script_find_and_remove(&mut v, [103]), 1);
|
||||
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]), 0);
|
||||
assert_eq!(script_find_and_remove(&mut v, [103]), 1);
|
||||
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]), 0);
|
||||
}
|
||||
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
|
||||
assert_eq!(script_find_and_remove(&mut v, [105]), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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];
|
||||
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]);
|
||||
}
|
||||
#[test]
|
||||
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];
|
||||
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]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_bytes() {
|
||||
assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]);
|
||||
assert!(hex_bytes("abcde").is_err());
|
||||
assert!(hex_bytes("aBcDeF").is_ok());
|
||||
assert!(hex_bytes("aBcD4eFL").is_err());
|
||||
}
|
||||
#[test]
|
||||
fn test_hex_bytes() {
|
||||
assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]);
|
||||
assert!(hex_bytes("abcde").is_err());
|
||||
assert!(hex_bytes("aBcDeF").is_ok());
|
||||
assert!(hex_bytes("aBcD4eFL").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
836
src/util/uint.rs
836
src/util/uint.rs
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
|
@ -25,483 +25,483 @@ use network::serialize::RawEncoder;
|
|||
use util::BitArray;
|
||||
|
||||
macro_rules! construct_uint {
|
||||
($name:ident, $n_words:expr) => (
|
||||
/// Little-endian large integer type
|
||||
#[repr(C)]
|
||||
pub struct $name(pub [u64; $n_words]);
|
||||
impl_array_newtype!($name, u64, $n_words);
|
||||
($name:ident, $n_words:expr) => (
|
||||
/// Little-endian large integer type
|
||||
#[repr(C)]
|
||||
pub struct $name(pub [u64; $n_words]);
|
||||
impl_array_newtype!($name, u64, $n_words);
|
||||
|
||||
impl $name {
|
||||
/// Conversion to u32
|
||||
#[inline]
|
||||
pub fn low_u32(&self) -> u32 {
|
||||
let &$name(ref arr) = self;
|
||||
arr[0] as u32
|
||||
}
|
||||
impl $name {
|
||||
/// Conversion to u32
|
||||
#[inline]
|
||||
pub fn low_u32(&self) -> u32 {
|
||||
let &$name(ref arr) = self;
|
||||
arr[0] as u32
|
||||
}
|
||||
|
||||
/// Return the least number of bits needed to represent the number
|
||||
#[inline]
|
||||
pub fn bits(&self) -> usize {
|
||||
let &$name(ref arr) = self;
|
||||
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; }
|
||||
}
|
||||
0x40 - arr[0].leading_zeros() as usize
|
||||
}
|
||||
/// Return the least number of bits needed to represent the number
|
||||
#[inline]
|
||||
pub fn bits(&self) -> usize {
|
||||
let &$name(ref arr) = self;
|
||||
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; }
|
||||
}
|
||||
0x40 - arr[0].leading_zeros() as usize
|
||||
}
|
||||
|
||||
/// Multiplication by u32
|
||||
pub fn mul_u32(self, other: u32) -> $name {
|
||||
let $name(ref arr) = self;
|
||||
let mut carry = [0u64; $n_words];
|
||||
let mut ret = [0u64; $n_words];
|
||||
for i in 0..$n_words {
|
||||
let upper = other as u64 * (arr[i] >> 32);
|
||||
let lower = other as u64 * (arr[i] & 0xFFFFFFFF);
|
||||
if i < 3 {
|
||||
carry[i + 1] += upper >> 32;
|
||||
}
|
||||
ret[i] = lower + (upper << 32);
|
||||
}
|
||||
$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);
|
||||
/// Multiplication by u32
|
||||
pub fn mul_u32(self, other: u32) -> $name {
|
||||
let $name(ref arr) = self;
|
||||
let mut carry = [0u64; $n_words];
|
||||
let mut ret = [0u64; $n_words];
|
||||
for i in 0..$n_words {
|
||||
let upper = other as u64 * (arr[i] >> 32);
|
||||
let lower = other as u64 * (arr[i] & 0xFFFFFFFF);
|
||||
if i < 3 {
|
||||
carry[i + 1] += upper >> 32;
|
||||
}
|
||||
ret[i] = lower + (upper << 32);
|
||||
}
|
||||
$name(ret) + $name(carry)
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
$name(ret)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
impl ::std::num::Zero for $name {
|
||||
fn zero() -> $name { $name([0; $n_words]) }
|
||||
}
|
||||
$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; }
|
||||
impl ::std::num::One for $name {
|
||||
fn one() -> $name {
|
||||
$name({ let mut ret = [0; $n_words]; ret[0] = 1; ret })
|
||||
}
|
||||
}
|
||||
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitAnd<$name> for $name {
|
||||
type Output = $name;
|
||||
impl ::std::ops::Add<$name> for $name {
|
||||
type Output = $name;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, other: $name) -> $name {
|
||||
let $name(ref arr1) = self;
|
||||
let $name(ref arr2) = other;
|
||||
let mut ret = [0u64; $n_words];
|
||||
for i in 0..$n_words {
|
||||
ret[i] = arr1[i] & arr2[i];
|
||||
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) }
|
||||
}
|
||||
}
|
||||
$name(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitXor<$name> for $name {
|
||||
type Output = $name;
|
||||
impl ::std::ops::Sub<$name> for $name {
|
||||
type Output = $name;
|
||||
|
||||
#[inline]
|
||||
fn bitxor(self, other: $name) -> $name {
|
||||
let $name(ref arr1) = self;
|
||||
let $name(ref arr2) = other;
|
||||
let mut ret = [0u64; $n_words];
|
||||
for i in 0..$n_words {
|
||||
ret[i] = arr1[i] ^ arr2[i];
|
||||
#[inline]
|
||||
fn sub(self, other: $name) -> $name {
|
||||
self + !other + One::one()
|
||||
}
|
||||
}
|
||||
$name(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitOr<$name> for $name {
|
||||
type Output = $name;
|
||||
impl ::std::ops::Mul<$name> for $name {
|
||||
type Output = $name;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, other: $name) -> $name {
|
||||
let $name(ref arr1) = self;
|
||||
let $name(ref arr2) = other;
|
||||
let mut ret = [0u64; $n_words];
|
||||
for i in 0..$n_words {
|
||||
ret[i] = arr1[i] | arr2[i];
|
||||
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
|
||||
}
|
||||
}
|
||||
$name(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Not for $name {
|
||||
type Output = $name;
|
||||
impl ::std::ops::Div<$name> for $name {
|
||||
type Output = $name;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> $name {
|
||||
let $name(ref arr) = self;
|
||||
let mut ret = [0u64; $n_words];
|
||||
for i in 0..$n_words {
|
||||
ret[i] = !arr[i];
|
||||
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
|
||||
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 {
|
||||
type Output = $name;
|
||||
impl BitArray for $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 {
|
||||
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);
|
||||
}
|
||||
#[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]
|
||||
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 {
|
||||
type Output = $name;
|
||||
impl ::std::ops::BitAnd<$name> 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);
|
||||
}
|
||||
#[inline]
|
||||
fn bitand(self, other: $name) -> $name {
|
||||
let $name(ref arr1) = self;
|
||||
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)
|
||||
}
|
||||
}
|
||||
$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; }
|
||||
impl ::std::ops::BitXor<$name> for $name {
|
||||
type Output = $name;
|
||||
|
||||
#[inline]
|
||||
fn bitxor(self, other: $name) -> $name {
|
||||
let $name(ref arr1) = self;
|
||||
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 {
|
||||
fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl ::std::ops::BitOr<$name> for $name {
|
||||
type Output = $name;
|
||||
|
||||
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));
|
||||
#[inline]
|
||||
fn bitor(self, other: $name) -> $name {
|
||||
let $name(ref arr1) = self;
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 ::std::ops::Not for $name {
|
||||
type Output = $name;
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
);
|
||||
#[inline]
|
||||
fn not(self) -> $name {
|
||||
let $name(ref arr) = self;
|
||||
let mut ret = [0u64; $n_words];
|
||||
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!(Uint128, 2);
|
||||
|
||||
impl Uint256 {
|
||||
/// Increment by 1
|
||||
#[inline]
|
||||
pub fn increment(&mut self) {
|
||||
let &mut Uint256(ref mut arr) = self;
|
||||
arr[0] += 1;
|
||||
if arr[0] == 0 {
|
||||
arr[1] += 1;
|
||||
if arr[1] == 0 {
|
||||
arr[2] += 1;
|
||||
if arr[2] == 0 {
|
||||
arr[3] += 1;
|
||||
/// Increment by 1
|
||||
#[inline]
|
||||
pub fn increment(&mut self) {
|
||||
let &mut Uint256(ref mut arr) = self;
|
||||
arr[0] += 1;
|
||||
if arr[0] == 0 {
|
||||
arr[1] += 1;
|
||||
if arr[1] == 0 {
|
||||
arr[2] += 1;
|
||||
if arr[2] == 0 {
|
||||
arr[3] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Decay to a uint128
|
||||
#[inline]
|
||||
pub fn low_128(&self) -> Uint128 {
|
||||
let &Uint256(data) = self;
|
||||
Uint128([data[0], data[1]])
|
||||
}
|
||||
/// Decay to a uint128
|
||||
#[inline]
|
||||
pub fn low_128(&self) -> Uint128 {
|
||||
let &Uint256(data) = self;
|
||||
Uint128([data[0], data[1]])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
use std::num::from_u64;
|
||||
use std::io;
|
||||
use std::num::from_u64;
|
||||
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use util::uint::Uint256;
|
||||
use util::BitArray;
|
||||
use network::serialize::{deserialize, serialize};
|
||||
use util::uint::Uint256;
|
||||
use util::BitArray;
|
||||
|
||||
#[test]
|
||||
pub fn uint256_bits_test() {
|
||||
assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8);
|
||||
assert_eq!(from_u64::<Uint256>(256).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>(70000).unwrap().bits(), 17);
|
||||
#[test]
|
||||
pub fn uint256_bits_test() {
|
||||
assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8);
|
||||
assert_eq!(from_u64::<Uint256>(256).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>(70000).unwrap().bits(), 17);
|
||||
|
||||
// Try to read the following lines out loud quickly
|
||||
let mut shl: Uint256 = from_u64(70000).unwrap();
|
||||
shl = shl << 100;
|
||||
assert_eq!(shl.bits(), 117);
|
||||
shl = shl << 100;
|
||||
assert_eq!(shl.bits(), 217);
|
||||
shl = shl << 100;
|
||||
assert_eq!(shl.bits(), 0);
|
||||
// Try to read the following lines out loud quickly
|
||||
let mut shl: Uint256 = from_u64(70000).unwrap();
|
||||
shl = shl << 100;
|
||||
assert_eq!(shl.bits(), 117);
|
||||
shl = shl << 100;
|
||||
assert_eq!(shl.bits(), 217);
|
||||
shl = shl << 100;
|
||||
assert_eq!(shl.bits(), 0);
|
||||
|
||||
// Bit set check
|
||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(0));
|
||||
assert!(from_u64::<Uint256>(10).unwrap().bit(1));
|
||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(2));
|
||||
assert!(from_u64::<Uint256>(10).unwrap().bit(3));
|
||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(4));
|
||||
}
|
||||
// Bit set check
|
||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(0));
|
||||
assert!(from_u64::<Uint256>(10).unwrap().bit(1));
|
||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(2));
|
||||
assert!(from_u64::<Uint256>(10).unwrap().bit(3));
|
||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn uint256_comp_test() {
|
||||
let small = Uint256([10u64, 0, 0, 0]);
|
||||
let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]);
|
||||
#[test]
|
||||
pub fn uint256_comp_test() {
|
||||
let small = Uint256([10u64, 0, 0, 0]);
|
||||
let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]);
|
||||
|
||||
assert!(small < big);
|
||||
assert!(big < bigger);
|
||||
assert!(bigger < biggest);
|
||||
assert!(bigger <= biggest);
|
||||
assert!(biggest <= biggest);
|
||||
assert!(bigger >= big);
|
||||
assert!(bigger >= small);
|
||||
assert!(small <= small);
|
||||
}
|
||||
assert!(small < big);
|
||||
assert!(big < bigger);
|
||||
assert!(bigger < biggest);
|
||||
assert!(bigger <= biggest);
|
||||
assert!(biggest <= biggest);
|
||||
assert!(bigger >= big);
|
||||
assert!(bigger >= small);
|
||||
assert!(small <= small);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn uint256_arithmetic_test() {
|
||||
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
||||
let copy = init;
|
||||
#[test]
|
||||
pub fn uint256_arithmetic_test() {
|
||||
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
||||
let copy = init;
|
||||
|
||||
let add = init + copy;
|
||||
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
||||
// Bitshifts
|
||||
let shl = add << 88;
|
||||
assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0]));
|
||||
let shr = shl >> 40;
|
||||
assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||
// Increment
|
||||
let mut incr = shr;
|
||||
incr.increment();
|
||||
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||
// Subtraction
|
||||
let sub = incr - init;
|
||||
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||
// Multiplication
|
||||
let mult = sub.mul_u32(300);
|
||||
assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]));
|
||||
// Division
|
||||
assert_eq!(from_u64::<Uint256>(105).unwrap() /
|
||||
from_u64::<Uint256>(5).unwrap(),
|
||||
from_u64::<Uint256>(21).unwrap());
|
||||
let div = mult / from_u64::<Uint256>(300).unwrap();
|
||||
assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||
// TODO: bit inversion
|
||||
}
|
||||
let add = init + copy;
|
||||
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
||||
// Bitshifts
|
||||
let shl = add << 88;
|
||||
assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0]));
|
||||
let shr = shl >> 40;
|
||||
assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||
// Increment
|
||||
let mut incr = shr;
|
||||
incr.increment();
|
||||
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||
// Subtraction
|
||||
let sub = incr - init;
|
||||
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||
// Multiplication
|
||||
let mult = sub.mul_u32(300);
|
||||
assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]));
|
||||
// Division
|
||||
assert_eq!(from_u64::<Uint256>(105).unwrap() /
|
||||
from_u64::<Uint256>(5).unwrap(),
|
||||
from_u64::<Uint256>(21).unwrap());
|
||||
let div = mult / from_u64::<Uint256>(300).unwrap();
|
||||
assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||
// TODO: bit inversion
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn uint256_bitslice_test() {
|
||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||
let add = init + (init << 64);
|
||||
assert_eq!(add.bit_slice(64, 128), init);
|
||||
assert_eq!(add.mask(64), init);
|
||||
}
|
||||
#[test]
|
||||
pub fn uint256_bitslice_test() {
|
||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||
let add = init + (init << 64);
|
||||
assert_eq!(add.bit_slice(64, 128), init);
|
||||
assert_eq!(add.mask(64), init);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn uint256_extreme_bitshift_test() {
|
||||
// Shifting a u64 by 64 bits gives an undefined value, so make sure that
|
||||
// we're doing the Right Thing here
|
||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||
#[test]
|
||||
pub fn uint256_extreme_bitshift_test() {
|
||||
// Shifting a u64 by 64 bits gives an undefined value, so make sure that
|
||||
// we're doing the Right Thing here
|
||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||
|
||||
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||
let add = (init << 64) + init;
|
||||
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 >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
|
||||
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
|
||||
}
|
||||
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||
let add = (init << 64) + init;
|
||||
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 >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
|
||||
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn uint256_serialize_test() {
|
||||
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
||||
let serial1 = serialize(&start1).unwrap();
|
||||
let serial2 = serialize(&start2).unwrap();
|
||||
let end1: io::Result<Uint256> = deserialize(serial1);
|
||||
let end2: io::Result<Uint256> = deserialize(serial2);
|
||||
#[test]
|
||||
pub fn uint256_serialize_test() {
|
||||
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
||||
let serial1 = serialize(&start1).unwrap();
|
||||
let serial2 = serialize(&start2).unwrap();
|
||||
let end1: io::Result<Uint256> = deserialize(serial1);
|
||||
let end2: io::Result<Uint256> = deserialize(serial2);
|
||||
|
||||
assert_eq!(end1, Ok(start1));
|
||||
assert_eq!(end2, Ok(start2));
|
||||
}
|
||||
assert_eq!(end1, Ok(start1));
|
||||
assert_eq!(end2, Ok(start2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// the public domain worldwide. This software is distributed without
|
||||
|
@ -21,7 +21,7 @@ use crypto::digest::Digest;
|
|||
use crypto::sha2::Sha256;
|
||||
use std::ops;
|
||||
|
||||
use blockdata::script::Script;
|
||||
use blockdata::script::{Script, ScriptBuilder};
|
||||
use blockdata::opcodes;
|
||||
use network::constants::Network;
|
||||
use util::hash::Ripemd160Hash;
|
||||
|
@ -30,189 +30,189 @@ use util::base58::{self, FromBase58, ToBase58};
|
|||
#[derive(Clone, PartialEq, Eq)]
|
||||
/// A Bitcoin address
|
||||
pub struct Address {
|
||||
/// The network on which this address is usable
|
||||
pub network: Network,
|
||||
/// The pubkeyhash that this address encodes
|
||||
pub hash: Ripemd160Hash
|
||||
/// The network on which this address is usable
|
||||
pub network: Network,
|
||||
/// The pubkeyhash that this address encodes
|
||||
pub hash: Ripemd160Hash
|
||||
}
|
||||
|
||||
impl Address {
|
||||
/// Creates an address from a public key
|
||||
#[inline]
|
||||
pub fn from_key(network: Network, pk: &PublicKey) -> Address {
|
||||
let mut sha = Sha256::new();
|
||||
let mut out = [0;32];
|
||||
sha.input(&pk[..]);
|
||||
sha.result(&mut out);
|
||||
Address {
|
||||
network: network,
|
||||
hash: Ripemd160Hash::from_data(&out)
|
||||
/// Creates an address from a public key
|
||||
#[inline]
|
||||
pub fn from_key(network: Network, pk: &PublicKey) -> Address {
|
||||
let mut sha = Sha256::new();
|
||||
let mut out = [0;32];
|
||||
sha.input(&pk[..]);
|
||||
sha.result(&mut out);
|
||||
Address {
|
||||
network: network,
|
||||
hash: Ripemd160Hash::from_data(&out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a script pubkey spending to this address
|
||||
#[inline]
|
||||
pub fn script_pubkey(&self) -> Script {
|
||||
let mut script = Script::new();
|
||||
script.push_opcode(opcodes::All::OP_DUP);
|
||||
script.push_opcode(opcodes::All::OP_HASH160);
|
||||
script.push_slice(&self.hash[..]);
|
||||
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
||||
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||
script
|
||||
}
|
||||
/// Generates a script pubkey spending to this address
|
||||
#[inline]
|
||||
pub fn script_pubkey(&self) -> Script {
|
||||
let mut script = ScriptBuilder::new();
|
||||
script.push_opcode(opcodes::All::OP_DUP);
|
||||
script.push_opcode(opcodes::All::OP_HASH160);
|
||||
script.push_slice(&self.hash[..]);
|
||||
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
||||
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||
script.into_script()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<usize> for Address {
|
||||
type Output = u8;
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &u8 {
|
||||
&self.hash[index]
|
||||
}
|
||||
type Output = u8;
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &u8 {
|
||||
&self.hash[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<ops::Range<usize>> for Address {
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
||||
&self.hash[index]
|
||||
}
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
||||
&self.hash[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<ops::RangeTo<usize>> for Address {
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
||||
&self.hash[index]
|
||||
}
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
||||
&self.hash[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<ops::RangeFrom<usize>> for Address {
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
||||
&self.hash[index]
|
||||
}
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
||||
&self.hash[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<ops::RangeFull> for Address {
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
||||
&self.hash[..]
|
||||
}
|
||||
type Output = [u8];
|
||||
#[inline]
|
||||
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
||||
&self.hash[..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion from other types into an address
|
||||
pub trait ToAddress {
|
||||
/// Copies `self` into a new `Address`
|
||||
fn to_address(&self, network: Network) -> Address;
|
||||
/// Copies `self` into a new `Address`
|
||||
fn to_address(&self, network: Network) -> Address;
|
||||
}
|
||||
|
||||
impl<'a> ToAddress for &'a [u8] {
|
||||
#[inline]
|
||||
fn to_address(&self, network: Network) -> Address {
|
||||
Address {
|
||||
network: network,
|
||||
hash: Ripemd160Hash::from_slice(*self)
|
||||
#[inline]
|
||||
fn to_address(&self, network: Network) -> Address {
|
||||
Address {
|
||||
network: network,
|
||||
hash: Ripemd160Hash::from_slice(*self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBase58 for Address {
|
||||
fn base58_layout(&self) -> Vec<u8> {
|
||||
let mut ret = vec![
|
||||
match self.network {
|
||||
Network::Bitcoin => 0,
|
||||
Network::Testnet => 111
|
||||
}
|
||||
];
|
||||
ret.push_all(&self.hash[..]);
|
||||
ret
|
||||
}
|
||||
fn base58_layout(&self) -> Vec<u8> {
|
||||
let mut ret = vec![
|
||||
match self.network {
|
||||
Network::Bitcoin => 0,
|
||||
Network::Testnet => 111
|
||||
}
|
||||
];
|
||||
ret.push_all(&self.hash[..]);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBase58 for Address {
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> {
|
||||
if data.len() != 21 {
|
||||
return Err(base58::Error::InvalidLength(data.len()));
|
||||
}
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> {
|
||||
if data.len() != 21 {
|
||||
return Err(base58::Error::InvalidLength(data.len()));
|
||||
}
|
||||
|
||||
Ok(Address {
|
||||
network: match data[0] {
|
||||
0 => Network::Bitcoin,
|
||||
111 => Network::Testnet,
|
||||
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
||||
},
|
||||
hash: Ripemd160Hash::from_slice(&data[1..])
|
||||
})
|
||||
}
|
||||
Ok(Address {
|
||||
network: match data[0] {
|
||||
0 => Network::Bitcoin,
|
||||
111 => Network::Testnet,
|
||||
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
||||
},
|
||||
hash: Ripemd160Hash::from_slice(&data[1..])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for Address {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.to_base58check())
|
||||
}
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.to_base58check())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serialize::hex::FromHex;
|
||||
use test::{Bencher, black_box};
|
||||
use serialize::hex::FromHex;
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
use secp256k1::Secp256k1;
|
||||
use secp256k1::Secp256k1;
|
||||
|
||||
use network::constants::Network::Bitcoin;
|
||||
use util::hash::Ripemd160Hash;
|
||||
use util::base58::{FromBase58, ToBase58};
|
||||
use super::Address;
|
||||
use network::constants::Network::Bitcoin;
|
||||
use util::hash::Ripemd160Hash;
|
||||
use util::base58::{FromBase58, ToBase58};
|
||||
use super::Address;
|
||||
|
||||
#[test]
|
||||
fn test_address_58() {
|
||||
let addr = Address {
|
||||
network: Bitcoin,
|
||||
hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap())
|
||||
};
|
||||
#[test]
|
||||
fn test_address_58() {
|
||||
let addr = Address {
|
||||
network: Bitcoin,
|
||||
hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap())
|
||||
};
|
||||
|
||||
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
||||
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
||||
}
|
||||
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
||||
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn generate_address(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
bh.iter( || {
|
||||
let (sk, pk) = s.generate_keypair(true);
|
||||
black_box(sk);
|
||||
black_box(pk);
|
||||
let addr = Address::from_key(Bitcoin, &pk);
|
||||
black_box(addr);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_address(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
bh.iter( || {
|
||||
let (sk, pk) = s.generate_keypair(true);
|
||||
black_box(sk);
|
||||
black_box(pk);
|
||||
let addr = Address::from_key(Bitcoin, &pk);
|
||||
black_box(addr);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn generate_uncompressed_address(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
bh.iter( || {
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
black_box(sk);
|
||||
black_box(pk);
|
||||
let addr = Address::from_key(Bitcoin, &pk);
|
||||
black_box(addr);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_uncompressed_address(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
bh.iter( || {
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
black_box(sk);
|
||||
black_box(pk);
|
||||
let addr = Address::from_key(Bitcoin, &pk);
|
||||
black_box(addr);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn generate_sequential_address(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let (sk, _) = s.generate_keypair(true);
|
||||
let mut iter = sk.sequence(true);
|
||||
bh.iter( || {
|
||||
let (sk, pk) = iter.next().unwrap();
|
||||
black_box(sk);
|
||||
let addr = Address::from_key(Bitcoin, &pk);
|
||||
black_box(addr);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_sequential_address(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let (sk, _) = s.generate_keypair(true);
|
||||
let mut iter = sk.sequence(true);
|
||||
bh.iter( || {
|
||||
let (sk, pk) = iter.next().unwrap();
|
||||
black_box(sk);
|
||||
let addr = Address::from_key(Bitcoin, &pk);
|
||||
black_box(addr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// the public domain worldwide. This software is distributed without
|
||||
|
@ -35,100 +35,100 @@ use util::hash::Sha256dHash;
|
|||
/// The type of a wallet-spendable txout
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum WalletTxOutType {
|
||||
/// Pay-to-address transaction redeemable using an ECDSA key
|
||||
PayToAddress(SecretKey),
|
||||
/// Undetermined
|
||||
Unknown
|
||||
/// Pay-to-address transaction redeemable using an ECDSA key
|
||||
PayToAddress(SecretKey),
|
||||
/// Undetermined
|
||||
Unknown
|
||||
}
|
||||
|
||||
|
||||
/// A txout that is spendable by the wallet
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct WalletTxOut {
|
||||
/// The TXID of the transaction this output is part of
|
||||
pub txid: Sha256dHash,
|
||||
/// The index of the output in its transaction
|
||||
pub vout: u32,
|
||||
/// The blockheight at which this output appeared in the blockchain
|
||||
pub height: u32,
|
||||
/// The actual output
|
||||
pub txo: TxOut,
|
||||
/// A classification of the output
|
||||
pub kind: WalletTxOutType
|
||||
/// The TXID of the transaction this output is part of
|
||||
pub txid: Sha256dHash,
|
||||
/// The index of the output in its transaction
|
||||
pub vout: u32,
|
||||
/// The blockheight at which this output appeared in the blockchain
|
||||
pub height: u32,
|
||||
/// The actual output
|
||||
pub txo: TxOut,
|
||||
/// A classification of the output
|
||||
pub kind: WalletTxOutType
|
||||
}
|
||||
|
||||
/// An address index
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct AddressIndex {
|
||||
tentative_index: HashMap<Script, Vec<WalletTxOut>>,
|
||||
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>,
|
||||
network: Network,
|
||||
k1: u64,
|
||||
k2: u64
|
||||
tentative_index: HashMap<Script, Vec<WalletTxOut>>,
|
||||
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>,
|
||||
network: Network,
|
||||
k1: u64,
|
||||
k2: u64
|
||||
}
|
||||
|
||||
impl AddressIndex {
|
||||
/// 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).
|
||||
pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex {
|
||||
let (k1, k2) = wallet.siphash_key();
|
||||
let mut ret = AddressIndex {
|
||||
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
|
||||
index: HashMap::new(),
|
||||
network: wallet.network(),
|
||||
k1: k1,
|
||||
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
|
||||
/// 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).
|
||||
pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex {
|
||||
let (k1, k2) = wallet.siphash_key();
|
||||
let mut ret = AddressIndex {
|
||||
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
|
||||
index: HashMap::new(),
|
||||
network: wallet.network(),
|
||||
k1: k1,
|
||||
k2: k2
|
||||
};
|
||||
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![]);
|
||||
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![]);
|
||||
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
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(&[])
|
||||
}
|
||||
/// 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
|
||||
/// 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(&[])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// 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 Default for Fingerprint {
|
||||
fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) }
|
||||
fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) }
|
||||
}
|
||||
|
||||
/// Extended private key
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub struct ExtendedPrivKey {
|
||||
/// The network this key is to be used on
|
||||
pub network: Network,
|
||||
/// How many derivations this key is from the master (which is 0)
|
||||
pub depth: u8,
|
||||
/// Fingerprint of the parent key (0 for master)
|
||||
pub parent_fingerprint: Fingerprint,
|
||||
/// Child number of the key used to derive from parent (0 for master)
|
||||
pub child_number: ChildNumber,
|
||||
/// Secret key
|
||||
pub secret_key: SecretKey,
|
||||
/// Chain code
|
||||
pub chain_code: ChainCode
|
||||
/// The network this key is to be used on
|
||||
pub network: Network,
|
||||
/// How many derivations this key is from the master (which is 0)
|
||||
pub depth: u8,
|
||||
/// Fingerprint of the parent key (0 for master)
|
||||
pub parent_fingerprint: Fingerprint,
|
||||
/// Child number of the key used to derive from parent (0 for master)
|
||||
pub child_number: ChildNumber,
|
||||
/// Secret key
|
||||
pub secret_key: SecretKey,
|
||||
/// Chain code
|
||||
pub chain_code: ChainCode
|
||||
}
|
||||
|
||||
/// Extended public key
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub struct ExtendedPubKey {
|
||||
/// The network this key is to be used on
|
||||
pub network: Network,
|
||||
/// How many derivations this key is from the master (which is 0)
|
||||
pub depth: u8,
|
||||
/// Fingerprint of the parent key
|
||||
pub parent_fingerprint: Fingerprint,
|
||||
/// Child number of the key used to derive from parent (0 for master)
|
||||
pub child_number: ChildNumber,
|
||||
/// Public key
|
||||
pub public_key: PublicKey,
|
||||
/// Chain code
|
||||
pub chain_code: ChainCode
|
||||
/// The network this key is to be used on
|
||||
pub network: Network,
|
||||
/// How many derivations this key is from the master (which is 0)
|
||||
pub depth: u8,
|
||||
/// Fingerprint of the parent key
|
||||
pub parent_fingerprint: Fingerprint,
|
||||
/// Child number of the key used to derive from parent (0 for master)
|
||||
pub child_number: ChildNumber,
|
||||
/// Public key
|
||||
pub public_key: PublicKey,
|
||||
/// Chain code
|
||||
pub chain_code: ChainCode
|
||||
}
|
||||
|
||||
/// A child number for a derived key
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum ChildNumber {
|
||||
/// Hardened key index, within [0, 2^31 - 1]
|
||||
Hardened(u32),
|
||||
/// Non-hardened key, within [0, 2^31 - 1]
|
||||
Normal(u32),
|
||||
/// Hardened key index, within [0, 2^31 - 1]
|
||||
Hardened(u32),
|
||||
/// Non-hardened key, within [0, 2^31 - 1]
|
||||
Normal(u32),
|
||||
}
|
||||
|
||||
impl Serialize for ChildNumber {
|
||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
|
||||
ChildNumber::Normal(n) => n.serialize(s)
|
||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
|
||||
ChildNumber::Normal(n) => n.serialize(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for ChildNumber {
|
||||
fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error>
|
||||
where D: Deserializer {
|
||||
let n: u32 = try!(Deserialize::deserialize(d));
|
||||
if n < (1 << 31) {
|
||||
Ok(ChildNumber::Normal(n))
|
||||
} else {
|
||||
Ok(ChildNumber::Hardened(n - (1 << 31)))
|
||||
fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error>
|
||||
where D: Deserializer {
|
||||
let n: u32 = try!(Deserialize::deserialize(d));
|
||||
if n < (1 << 31) {
|
||||
Ok(ChildNumber::Normal(n))
|
||||
} else {
|
||||
Ok(ChildNumber::Hardened(n - (1 << 31)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A BIP32 error
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
/// A pk->pk derivation was attempted on a hardened key
|
||||
CannotDeriveFromHardenedKey,
|
||||
/// A secp256k1 error occured
|
||||
Ecdsa(secp256k1::Error),
|
||||
/// A child number was provided that was out of range
|
||||
InvalidChildNumber(ChildNumber),
|
||||
/// Error creating a master seed --- for application use
|
||||
RngError(String)
|
||||
/// A pk->pk derivation was attempted on a hardened key
|
||||
CannotDeriveFromHardenedKey,
|
||||
/// A secp256k1 error occured
|
||||
Ecdsa(secp256k1::Error),
|
||||
/// A child number was provided that was out of range
|
||||
InvalidChildNumber(ChildNumber),
|
||||
/// Error creating a master seed --- for application use
|
||||
RngError(String)
|
||||
}
|
||||
|
||||
impl ExtendedPrivKey {
|
||||
/// Construct a new master key from a seed value
|
||||
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||
let mut result = [0; 64];
|
||||
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
||||
hmac.input(seed);
|
||||
hmac.raw_result(&mut result);
|
||||
/// Construct a new master key from a seed value
|
||||
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||
let mut result = [0; 64];
|
||||
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
||||
hmac.input(seed);
|
||||
hmac.raw_result(&mut result);
|
||||
|
||||
Ok(ExtendedPrivKey {
|
||||
network: network,
|
||||
depth: 0,
|
||||
parent_fingerprint: Default::default(),
|
||||
child_number: ChildNumber::Normal(0),
|
||||
secret_key: try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)),
|
||||
chain_code: ChainCode::from_slice(&result[32..])
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a privkey from a path
|
||||
pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber])
|
||||
-> Result<ExtendedPrivKey, Error> {
|
||||
let mut sk = *master;
|
||||
for &num in path.iter() {
|
||||
sk = try!(sk.ckd_priv(num));
|
||||
Ok(ExtendedPrivKey {
|
||||
network: network,
|
||||
depth: 0,
|
||||
parent_fingerprint: Default::default(),
|
||||
child_number: ChildNumber::Normal(0),
|
||||
secret_key: try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)),
|
||||
chain_code: ChainCode::from_slice(&result[32..])
|
||||
})
|
||||
}
|
||||
Ok(sk)
|
||||
}
|
||||
|
||||
/// Private->Private child key derivation
|
||||
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
||||
let mut result = [0; 64];
|
||||
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
||||
let mut be_n = [0; 32];
|
||||
match i {
|
||||
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));
|
||||
}
|
||||
/// 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)
|
||||
}
|
||||
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 {
|
||||
network: self.network,
|
||||
depth: self.depth + 1,
|
||||
parent_fingerprint: self.fingerprint(),
|
||||
child_number: i,
|
||||
secret_key: sk,
|
||||
chain_code: ChainCode::from_slice(&result[32..])
|
||||
})
|
||||
}
|
||||
/// Private->Private child key derivation
|
||||
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
||||
let mut result = [0; 64];
|
||||
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
||||
let mut be_n = [0; 32];
|
||||
match i {
|
||||
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
|
||||
pub fn identifier(&self) -> [u8; 20] {
|
||||
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
|
||||
}
|
||||
Ok(ExtendedPrivKey {
|
||||
network: self.network,
|
||||
depth: self.depth + 1,
|
||||
parent_fingerprint: self.fingerprint(),
|
||||
child_number: i,
|
||||
secret_key: sk,
|
||||
chain_code: ChainCode::from_slice(&result[32..])
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the first four bytes of the identifier
|
||||
pub fn fingerprint(&self) -> Fingerprint {
|
||||
Fingerprint::from_slice(&self.identifier()[0..4])
|
||||
}
|
||||
/// Returns the HASH160 of the chaincode
|
||||
pub fn identifier(&self) -> [u8; 20] {
|
||||
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 {
|
||||
/// Derives a public key from a private key
|
||||
pub fn from_private(sk: &ExtendedPrivKey) -> ExtendedPubKey {
|
||||
secp256k1::init();
|
||||
ExtendedPubKey {
|
||||
network: sk.network,
|
||||
depth: sk.depth,
|
||||
parent_fingerprint: sk.parent_fingerprint,
|
||||
child_number: sk.child_number,
|
||||
public_key: PublicKey::from_secret_key(&sk.secret_key, true),
|
||||
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)
|
||||
/// Derives a public key from a private key
|
||||
pub fn from_private(sk: &ExtendedPrivKey) -> ExtendedPubKey {
|
||||
secp256k1::init();
|
||||
ExtendedPubKey {
|
||||
network: sk.network,
|
||||
depth: sk.depth,
|
||||
parent_fingerprint: sk.parent_fingerprint,
|
||||
child_number: sk.child_number,
|
||||
public_key: PublicKey::from_secret_key(&sk.secret_key, true),
|
||||
chain_code: sk.chain_code
|
||||
}
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
/// 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);
|
||||
|
||||
/// Returns the first four bytes of the identifier
|
||||
pub fn fingerprint(&self) -> Fingerprint {
|
||||
Fingerprint::from_slice(&self.identifier()[0..4])
|
||||
}
|
||||
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
|
||||
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 {
|
||||
fn base58_layout(&self) -> Vec<u8> {
|
||||
let mut ret = Vec::with_capacity(78);
|
||||
ret.push_all(match self.network {
|
||||
Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4],
|
||||
Network::Testnet => &[0x04, 0x35, 0x83, 0x94]
|
||||
});
|
||||
ret.push(self.depth as u8);
|
||||
ret.push_all(&self.parent_fingerprint[..]);
|
||||
let mut be_n = [0; 32];
|
||||
match self.child_number {
|
||||
ChildNumber::Hardened(n) => {
|
||||
write_u32_be(&mut be_n, n + (1 << 31));
|
||||
}
|
||||
ChildNumber::Normal(n) => {
|
||||
write_u32_be(&mut be_n, n);
|
||||
}
|
||||
fn base58_layout(&self) -> Vec<u8> {
|
||||
let mut ret = Vec::with_capacity(78);
|
||||
ret.push_all(match self.network {
|
||||
Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4],
|
||||
Network::Testnet => &[0x04, 0x35, 0x83, 0x94]
|
||||
});
|
||||
ret.push(self.depth as u8);
|
||||
ret.push_all(&self.parent_fingerprint[..]);
|
||||
let mut be_n = [0; 32];
|
||||
match self.child_number {
|
||||
ChildNumber::Hardened(n) => {
|
||||
write_u32_be(&mut be_n, n + (1 << 31));
|
||||
}
|
||||
ChildNumber::Normal(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 {
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> {
|
||||
if data.len() != 78 {
|
||||
return Err(base58::Error::InvalidLength(data.len()));
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> {
|
||||
if data.len() != 78 {
|
||||
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 {
|
||||
fn base58_layout(&self) -> Vec<u8> {
|
||||
assert!(self.public_key.is_compressed());
|
||||
let mut ret = Vec::with_capacity(78);
|
||||
ret.push_all(match self.network {
|
||||
Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E],
|
||||
Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF]
|
||||
});
|
||||
ret.push(self.depth as u8);
|
||||
ret.push_all(&self.parent_fingerprint[..]);
|
||||
let mut be_n = [0; 32];
|
||||
match self.child_number {
|
||||
ChildNumber::Hardened(n) => {
|
||||
write_u32_be(&mut be_n, n + (1 << 31));
|
||||
}
|
||||
ChildNumber::Normal(n) => {
|
||||
write_u32_be(&mut be_n, n);
|
||||
}
|
||||
fn base58_layout(&self) -> Vec<u8> {
|
||||
assert!(self.public_key.is_compressed());
|
||||
let mut ret = Vec::with_capacity(78);
|
||||
ret.push_all(match self.network {
|
||||
Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E],
|
||||
Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF]
|
||||
});
|
||||
ret.push(self.depth as u8);
|
||||
ret.push_all(&self.parent_fingerprint[..]);
|
||||
let mut be_n = [0; 32];
|
||||
match self.child_number {
|
||||
ChildNumber::Hardened(n) => {
|
||||
write_u32_be(&mut be_n, n + (1 << 31));
|
||||
}
|
||||
ChildNumber::Normal(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 {
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> {
|
||||
if data.len() != 78 {
|
||||
return Err(base58::Error::InvalidLength(data.len()));
|
||||
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> {
|
||||
if data.len() != 78 {
|
||||
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)]
|
||||
mod tests {
|
||||
use serialize::hex::FromHex;
|
||||
use test::{Bencher, black_box};
|
||||
use serialize::hex::FromHex;
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
use network::constants::Network::{self, Bitcoin};
|
||||
use util::base58::{FromBase58, ToBase58};
|
||||
use network::constants::Network::{self, Bitcoin};
|
||||
use util::base58::{FromBase58, ToBase58};
|
||||
|
||||
use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
|
||||
use super::ChildNumber::{Hardened, Normal};
|
||||
use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
|
||||
use super::ChildNumber::{Hardened, Normal};
|
||||
|
||||
fn test_path(network: Network,
|
||||
seed: &[u8],
|
||||
path: &[ChildNumber],
|
||||
expected_sk: &str,
|
||||
expected_pk: &str) {
|
||||
fn test_path(network: Network,
|
||||
seed: &[u8],
|
||||
path: &[ChildNumber],
|
||||
expected_sk: &str,
|
||||
expected_pk: &str) {
|
||||
|
||||
let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap();
|
||||
let mut pk = ExtendedPubKey::from_private(&sk);
|
||||
// Derive keys, checking hardened and non-hardened derivation
|
||||
for &num in path.iter() {
|
||||
sk = sk.ckd_priv(num).unwrap();
|
||||
match num {
|
||||
Normal(_) => {
|
||||
let pk2 = pk.ckd_pub(num).unwrap();
|
||||
pk = ExtendedPubKey::from_private(&sk);
|
||||
assert_eq!(pk, pk2);
|
||||
let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap();
|
||||
let mut pk = ExtendedPubKey::from_private(&sk);
|
||||
// Derive keys, checking hardened and non-hardened derivation
|
||||
for &num in path.iter() {
|
||||
sk = sk.ckd_priv(num).unwrap();
|
||||
match num {
|
||||
Normal(_) => {
|
||||
let pk2 = pk.ckd_pub(num).unwrap();
|
||||
pk = ExtendedPubKey::from_private(&sk);
|
||||
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
|
||||
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);
|
||||
}
|
||||
#[test]
|
||||
fn test_vector_1() {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
// m
|
||||
test_path(Bitcoin, &seed, [],
|
||||
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
||||
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
||||
|
||||
#[test]
|
||||
fn test_vector_1() {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
// m
|
||||
test_path(Bitcoin, &seed, [],
|
||||
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
||||
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
||||
// m/0h
|
||||
test_path(Bitcoin, &seed, [Hardened(0)],
|
||||
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
||||
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
||||
|
||||
// m/0h
|
||||
test_path(Bitcoin, &seed, [Hardened(0)],
|
||||
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
||||
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
||||
// m/0h/1
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1)],
|
||||
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
||||
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
||||
|
||||
// m/0h/1
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1)],
|
||||
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
||||
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
||||
// m/0h/1/2h
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)],
|
||||
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
||||
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
||||
|
||||
// m/0h/1/2h
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)],
|
||||
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
||||
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
||||
// m/0h/1/2h/2
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)],
|
||||
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
||||
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
||||
|
||||
// m/0h/1/2h/2
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)],
|
||||
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
||||
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
||||
// m/0h/1/2h/2/1000000000
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
|
||||
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
||||
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
||||
}
|
||||
|
||||
// m/0h/1/2h/2/1000000000
|
||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
|
||||
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
||||
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
||||
}
|
||||
#[test]
|
||||
fn test_vector_2() {
|
||||
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
|
||||
|
||||
#[test]
|
||||
fn test_vector_2() {
|
||||
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
|
||||
// m
|
||||
test_path(Bitcoin, &seed, [],
|
||||
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
||||
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
||||
|
||||
// m
|
||||
test_path(Bitcoin, &seed, [],
|
||||
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
||||
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
||||
// m/0
|
||||
test_path(Bitcoin, &seed, [Normal(0)],
|
||||
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
||||
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
||||
|
||||
// m/0
|
||||
test_path(Bitcoin, &seed, [Normal(0)],
|
||||
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
||||
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
||||
// m/0/2147483647h
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)],
|
||||
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
||||
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
||||
|
||||
// m/0/2147483647h
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)],
|
||||
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
||||
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
||||
// m/0/2147483647h/1
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)],
|
||||
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
||||
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
||||
|
||||
// m/0/2147483647h/1
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)],
|
||||
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
||||
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
||||
// m/0/2147483647h/1/2147483646h
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
|
||||
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
||||
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
||||
|
||||
// m/0/2147483647h/1/2147483646h
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
|
||||
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
||||
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
||||
// m/0/2147483647h/1/2147483646h/2
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
|
||||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
||||
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
||||
}
|
||||
|
||||
// m/0/2147483647h/1/2147483646h/2
|
||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
|
||||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
||||
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
||||
}
|
||||
#[test]
|
||||
pub fn encode_decode_childnumber() {
|
||||
use serialize::json;
|
||||
|
||||
#[test]
|
||||
pub fn encode_decode_childnumber() {
|
||||
use serialize::json;
|
||||
let h1 = Hardened(1);
|
||||
let n1 = Normal(1);
|
||||
|
||||
let h1 = Hardened(1);
|
||||
let n1 = Normal(1);
|
||||
let h1_str = json::encode(&h1);
|
||||
let n1_str = json::encode(&n1);
|
||||
|
||||
let h1_str = json::encode(&h1);
|
||||
let n1_str = json::encode(&n1);
|
||||
assert!(h1 != n1);
|
||||
assert!(h1_str != n1_str);
|
||||
|
||||
assert!(h1 != n1);
|
||||
assert!(h1_str != n1_str);
|
||||
let h1_dec = json::decode(&h1_str).unwrap();
|
||||
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();
|
||||
let n1_dec = json::decode(&n1_str).unwrap();
|
||||
assert_eq!(h1, h1_dec);
|
||||
assert_eq!(n1, n1_dec);
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_sequential_normal_children(bh: &mut Bencher) {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
black_box(msk.ckd_priv(Normal(i)));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn generate_sequential_normal_children(bh: &mut Bencher) {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
black_box(msk.ckd_priv(Normal(i)));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
black_box(msk.ckd_priv(Hardened(i)));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
black_box(msk.ckd_priv(Hardened(i)));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_sequential_public_children(bh: &mut Bencher) {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mpk = ExtendedPubKey::from_private(&msk);
|
||||
|
||||
#[bench]
|
||||
pub fn generate_sequential_public_children(bh: &mut Bencher) {
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mpk = ExtendedPubKey::from_private(&msk);
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
black_box(mpk.ckd_pub(Normal(i)));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
black_box(mpk.ckd_pub(Normal(i)));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) {
|
||||
use wallet::address::Address;
|
||||
|
||||
#[bench]
|
||||
pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) {
|
||||
use wallet::address::Address;
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mpk = ExtendedPubKey::from_private(&msk);
|
||||
|
||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||
let mpk = ExtendedPubKey::from_private(&msk);
|
||||
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
let epk = mpk.ckd_pub(Normal(i)).unwrap();
|
||||
black_box(Address::from_key(Bitcoin, &epk.public_key));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
let mut i = 0;
|
||||
bh.iter( || {
|
||||
let epk = mpk.ckd_pub(Normal(i)).unwrap();
|
||||
black_box(Address::from_key(Bitcoin, &epk.public_key));
|
||||
i += 1;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Rust Bitcoin Library
|
||||
// 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
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// the public domain worldwide. This software is distributed without
|
||||
|
@ -33,243 +33,243 @@ use wallet::address_index::AddressIndex;
|
|||
/// A Wallet error
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
/// Tried to lookup an account by name, but none was found
|
||||
AccountNotFound,
|
||||
/// Tried to add an account when one already exists with that name
|
||||
DuplicateAccount,
|
||||
/// An error occured in a BIP32 derivation
|
||||
Bip32Error(bip32::Error),
|
||||
/// Tried to use a wallet without an address index
|
||||
NoAddressIndex
|
||||
/// Tried to lookup an account by name, but none was found
|
||||
AccountNotFound,
|
||||
/// Tried to add an account when one already exists with that name
|
||||
DuplicateAccount,
|
||||
/// An error occured in a BIP32 derivation
|
||||
Bip32Error(bip32::Error),
|
||||
/// Tried to use a wallet without an address index
|
||||
NoAddressIndex
|
||||
}
|
||||
|
||||
/// Each account has two chains, as specified in BIP32
|
||||
pub enum AccountChain {
|
||||
/// Internal addresses are used within the wallet for change, etc,
|
||||
/// and in principle every generated one will be used.
|
||||
Internal,
|
||||
/// External addresses are shared, and might not be used after generatation,
|
||||
/// complicating recreating the whole wallet from seed.
|
||||
External
|
||||
/// Internal addresses are used within the wallet for change, etc,
|
||||
/// and in principle every generated one will be used.
|
||||
Internal,
|
||||
/// External addresses are shared, and might not be used after generatation,
|
||||
/// complicating recreating the whole wallet from seed.
|
||||
External
|
||||
}
|
||||
|
||||
/// An account
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub struct Account {
|
||||
internal_path: Vec<ChildNumber>,
|
||||
internal_used: Vec<ChildNumber>,
|
||||
internal_next: u32,
|
||||
external_path: Vec<ChildNumber>,
|
||||
external_used: Vec<ChildNumber>,
|
||||
external_next: u32
|
||||
internal_path: Vec<ChildNumber>,
|
||||
internal_used: Vec<ChildNumber>,
|
||||
internal_next: u32,
|
||||
external_path: Vec<ChildNumber>,
|
||||
external_used: Vec<ChildNumber>,
|
||||
external_next: u32
|
||||
}
|
||||
|
||||
impl Default for Account {
|
||||
fn default() -> Account {
|
||||
Account {
|
||||
internal_path: vec![Hardened(0), Normal(1)],
|
||||
internal_used: vec![],
|
||||
internal_next: 0,
|
||||
external_path: vec![Hardened(0), Normal(0)],
|
||||
external_used: vec![],
|
||||
external_next: 0
|
||||
fn default() -> Account {
|
||||
Account {
|
||||
internal_path: vec![Hardened(0), Normal(1)],
|
||||
internal_used: vec![],
|
||||
internal_next: 0,
|
||||
external_path: vec![Hardened(0), Normal(0)],
|
||||
external_used: vec![],
|
||||
external_next: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wallet
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Wallet {
|
||||
master: ExtendedPrivKey,
|
||||
accounts: HashMap<String, Account>,
|
||||
index: Option<AddressIndex>
|
||||
master: ExtendedPrivKey,
|
||||
accounts: HashMap<String, Account>,
|
||||
index: Option<AddressIndex>
|
||||
}
|
||||
|
||||
impl Serialize for Wallet {
|
||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
let len = self.accounts.len();
|
||||
try!(self.master.serialize(s));
|
||||
self.accounts.serialize(s)
|
||||
}
|
||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
let len = self.accounts.len();
|
||||
try!(self.master.serialize(s));
|
||||
self.accounts.serialize(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Wallet {
|
||||
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
|
||||
where D: Deserializer {
|
||||
Ok(Wallet {
|
||||
master: try!(Deserialize::deserialize(d)),
|
||||
accounts: try!(Deserialize::deserialize(d)),
|
||||
index: None
|
||||
})
|
||||
}
|
||||
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
|
||||
where D: Deserializer {
|
||||
Ok(Wallet {
|
||||
master: try!(Deserialize::deserialize(d)),
|
||||
accounts: try!(Deserialize::deserialize(d)),
|
||||
index: None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
/// Creates a new wallet from a BIP32 seed
|
||||
#[inline]
|
||||
pub fn from_seed(network: Network, seed: &[u8]) -> Result<Wallet, bip32::Error> {
|
||||
let mut accounts = HashMap::new();
|
||||
accounts.insert(String::new(), Default::default());
|
||||
/// Creates a new wallet from a BIP32 seed
|
||||
#[inline]
|
||||
pub fn from_seed(network: Network, seed: &[u8]) -> Result<Wallet, bip32::Error> {
|
||||
let mut accounts = HashMap::new();
|
||||
accounts.insert(String::new(), Default::default());
|
||||
|
||||
Ok(Wallet {
|
||||
master: try!(ExtendedPrivKey::new_master(network, seed)),
|
||||
accounts: accounts,
|
||||
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);
|
||||
Ok(Wallet {
|
||||
master: try!(ExtendedPrivKey::new_master(network, seed)),
|
||||
accounts: accounts,
|
||||
index: None
|
||||
})
|
||||
}
|
||||
|
||||
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));
|
||||
/// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/// Accessor for the wallet's address index
|
||||
#[inline]
|
||||
pub fn index<'a>(&'a self) -> Option<&'a AddressIndex> {
|
||||
self.index.as_ref()
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/// 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()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue