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]
|
[dependencies.secp256k1]
|
||||||
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git"
|
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git"
|
||||||
|
|
||||||
|
[dependencies.eventual]
|
||||||
|
git = "https://github.com/carllerche/eventual"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "*"
|
byteorder = "*"
|
||||||
num_cpus = "*"
|
num_cpus = "*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -34,101 +34,101 @@ use blockdata::transaction::Transaction;
|
||||||
/// the actual transactions
|
/// the actual transactions
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct BlockHeader {
|
pub struct BlockHeader {
|
||||||
/// The protocol version. Should always be 1.
|
/// The protocol version. Should always be 1.
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// Reference to the previous block in the chain
|
/// Reference to the previous block in the chain
|
||||||
pub prev_blockhash: Sha256dHash,
|
pub prev_blockhash: Sha256dHash,
|
||||||
/// The root hash of the merkle tree of transactions in the block
|
/// The root hash of the merkle tree of transactions in the block
|
||||||
pub merkle_root: Sha256dHash,
|
pub merkle_root: Sha256dHash,
|
||||||
/// The timestamp of the block, as claimed by the mainer
|
/// The timestamp of the block, as claimed by the mainer
|
||||||
pub time: u32,
|
pub time: u32,
|
||||||
/// The target value below which the blockhash must lie, encoded as a
|
/// The target value below which the blockhash must lie, encoded as a
|
||||||
/// a float (with well-defined rounding, of course)
|
/// a float (with well-defined rounding, of course)
|
||||||
pub bits: u32,
|
pub bits: u32,
|
||||||
/// The nonce, selected to obtain a low enough blockhash
|
/// The nonce, selected to obtain a low enough blockhash
|
||||||
pub nonce: u32,
|
pub nonce: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Bitcoin block, which is a collection of transactions with an attached
|
/// A Bitcoin block, which is a collection of transactions with an attached
|
||||||
/// proof of work.
|
/// proof of work.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
/// The block header
|
/// The block header
|
||||||
pub header: BlockHeader,
|
pub header: BlockHeader,
|
||||||
/// List of transactions contained in the block
|
/// List of transactions contained in the block
|
||||||
pub txdata: Vec<Transaction>
|
pub txdata: Vec<Transaction>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A block header with txcount attached, which is given in the `headers`
|
/// A block header with txcount attached, which is given in the `headers`
|
||||||
/// network message.
|
/// network message.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct LoneBlockHeader {
|
pub struct LoneBlockHeader {
|
||||||
/// The actual block header
|
/// The actual block header
|
||||||
pub header: BlockHeader,
|
pub header: BlockHeader,
|
||||||
/// The number of transactions in the block. This will always be zero
|
/// The number of transactions in the block. This will always be zero
|
||||||
/// when the LoneBlockHeader is returned as part ef a `headers` message.
|
/// when the LoneBlockHeader is returned as part ef a `headers` message.
|
||||||
pub tx_count: VarInt
|
pub tx_count: VarInt
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockHeader {
|
impl BlockHeader {
|
||||||
/// Computes the target [0, T] that a blockhash must land in to be valid
|
/// Computes the target [0, T] that a blockhash must land in to be valid
|
||||||
pub fn target(&self) -> Uint256 {
|
pub fn target(&self) -> Uint256 {
|
||||||
// This is a floating-point "compact" encoding originally used by
|
// This is a floating-point "compact" encoding originally used by
|
||||||
// OpenSSL, which satoshi put into consensus code, so we're stuck
|
// OpenSSL, which satoshi put into consensus code, so we're stuck
|
||||||
// with it. The exponent needs to have 3 subtracted from it, hence
|
// with it. The exponent needs to have 3 subtracted from it, hence
|
||||||
// this goofy decoding code:
|
// this goofy decoding code:
|
||||||
let (mant, expt) = {
|
let (mant, expt) = {
|
||||||
let unshifted_expt = self.bits >> 24;
|
let unshifted_expt = self.bits >> 24;
|
||||||
if unshifted_expt <= 3 {
|
if unshifted_expt <= 3 {
|
||||||
((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as usize), 0)
|
((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as usize), 0)
|
||||||
} else {
|
} else {
|
||||||
(self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3))
|
(self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The mantissa is signed but may not be negative
|
// The mantissa is signed but may not be negative
|
||||||
if mant > 0x7FFFFF {
|
if mant > 0x7FFFFF {
|
||||||
Zero::zero()
|
Zero::zero()
|
||||||
} else {
|
} else {
|
||||||
from_u64::<Uint256>(mant as u64).unwrap() << (expt as usize)
|
from_u64::<Uint256>(mant as u64).unwrap() << (expt as usize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs an SPV validation of a block, which confirms that the proof-of-work
|
/// Performs an SPV validation of a block, which confirms that the proof-of-work
|
||||||
/// is correct, but does not verify that the transactions are valid or encoded
|
/// is correct, but does not verify that the transactions are valid or encoded
|
||||||
/// correctly.
|
/// correctly.
|
||||||
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> {
|
pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), util::Error> {
|
||||||
let ref target = self.target();
|
let ref target = self.target();
|
||||||
if target != required_target {
|
if target != required_target {
|
||||||
return Err(SpvBadTarget);
|
return Err(SpvBadTarget);
|
||||||
|
}
|
||||||
|
let ref hash = self.bitcoin_hash().into_le();
|
||||||
|
if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
|
||||||
}
|
}
|
||||||
let ref hash = self.bitcoin_hash().into_le();
|
|
||||||
if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the total work of the block
|
/// Returns the total work of the block
|
||||||
pub fn work(&self) -> Uint256 {
|
pub fn work(&self) -> Uint256 {
|
||||||
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
|
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
|
||||||
let mut ret = !self.target();
|
let mut ret = !self.target();
|
||||||
let mut ret1 = self.target();
|
let mut ret1 = self.target();
|
||||||
ret1.increment();
|
ret1.increment();
|
||||||
ret = ret.div(&ret1);
|
ret = ret / ret1;
|
||||||
ret.increment();
|
ret.increment();
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for BlockHeader {
|
impl BitcoinHash for BlockHeader {
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||||
use network::serialize::serialize;
|
use network::serialize::serialize;
|
||||||
Sha256dHash::from_data(serialize(self).unwrap().as_slice())
|
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for Block {
|
impl BitcoinHash for Block {
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||||
self.header.bitcoin_hash()
|
self.header.bitcoin_hash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce);
|
impl_consensus_encoding!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce);
|
||||||
|
@ -137,36 +137,36 @@ impl_consensus_encoding!(LoneBlockHeader, header, tx_count);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use blockdata::block::Block;
|
use blockdata::block::Block;
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_test() {
|
fn block_test() {
|
||||||
let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap();
|
let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap();
|
||||||
let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap();
|
let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap();
|
||||||
|
|
||||||
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
||||||
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: io::Result<Block> = deserialize(some_block.clone());
|
let decode: io::Result<Block> = deserialize(some_block.clone());
|
||||||
let bad_decode: io::Result<Block> = deserialize(cutoff_block);
|
let bad_decode: io::Result<Block> = deserialize(cutoff_block);
|
||||||
|
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
assert!(bad_decode.is_err());
|
assert!(bad_decode.is_err());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
assert_eq!(real_decode.header.version, 1);
|
assert_eq!(real_decode.header.version, 1);
|
||||||
assert_eq!(serialize(&real_decode.header.prev_blockhash), Ok(prevhash));
|
assert_eq!(serialize(&real_decode.header.prev_blockhash), Ok(prevhash));
|
||||||
// [test] TODO: actually compute the merkle root
|
// [test] TODO: actually compute the merkle root
|
||||||
assert_eq!(serialize(&real_decode.header.merkle_root), Ok(merkle));
|
assert_eq!(serialize(&real_decode.header.merkle_root), Ok(merkle));
|
||||||
assert_eq!(real_decode.header.time, 1231965655);
|
assert_eq!(real_decode.header.time, 1231965655);
|
||||||
assert_eq!(real_decode.header.bits, 486604799);
|
assert_eq!(real_decode.header.bits, 486604799);
|
||||||
assert_eq!(real_decode.header.nonce, 2067413810);
|
assert_eq!(real_decode.header.nonce, 2067413810);
|
||||||
// [test] TODO: check the transaction data
|
// [test] TODO: check the transaction data
|
||||||
|
|
||||||
assert_eq!(serialize(&real_decode), Ok(some_block));
|
assert_eq!(serialize(&real_decode), Ok(some_block));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -23,7 +23,7 @@ use std::default::Default;
|
||||||
use std::num::from_u64;
|
use std::num::from_u64;
|
||||||
|
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::ScriptBuilder;
|
||||||
use blockdata::transaction::{Transaction, TxOut, TxIn};
|
use blockdata::transaction::{Transaction, TxOut, TxIn};
|
||||||
use blockdata::block::{Block, BlockHeader};
|
use blockdata::block::{Block, BlockHeader};
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
|
@ -39,144 +39,144 @@ pub static DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600;
|
||||||
|
|
||||||
/// In Bitcoind this is insanely described as ~((u256)0 >> 32)
|
/// In Bitcoind this is insanely described as ~((u256)0 >> 32)
|
||||||
pub fn max_target(_: Network) -> Uint256 {
|
pub fn max_target(_: Network) -> Uint256 {
|
||||||
from_u64::<Uint256>(0xFFFF).unwrap() << 208
|
from_u64::<Uint256>(0xFFFF).unwrap() << 208
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum value allowed in an output (useful for sanity checking,
|
/// The maximum value allowed in an output (useful for sanity checking,
|
||||||
/// since keeping everything below this value should prevent overflows
|
/// since keeping everything below this value should prevent overflows
|
||||||
/// if you are doing anything remotely sane with monetary values).
|
/// if you are doing anything remotely sane with monetary values).
|
||||||
pub fn max_money(_: Network) -> u64 {
|
pub fn max_money(_: Network) -> u64 {
|
||||||
21_000_000 * COIN_VALUE
|
21_000_000 * COIN_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block
|
/// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block
|
||||||
fn bitcoin_genesis_tx() -> Transaction {
|
fn bitcoin_genesis_tx() -> Transaction {
|
||||||
// Base
|
// Base
|
||||||
let mut ret = Transaction {
|
let mut ret = Transaction {
|
||||||
version: 1,
|
version: 1,
|
||||||
lock_time: 0,
|
lock_time: 0,
|
||||||
input: vec![],
|
input: vec![],
|
||||||
output: vec![]
|
output: vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
let mut in_script = Script::new();
|
let mut in_script = ScriptBuilder::new();
|
||||||
in_script.push_scriptint(486604799);
|
in_script.push_scriptint(486604799);
|
||||||
in_script.push_scriptint(4);
|
in_script.push_scriptint(4);
|
||||||
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
||||||
ret.input.push(TxIn {
|
ret.input.push(TxIn {
|
||||||
prev_hash: Default::default(),
|
prev_hash: Default::default(),
|
||||||
prev_index: 0xFFFFFFFF,
|
prev_index: 0xFFFFFFFF,
|
||||||
script_sig: in_script,
|
script_sig: in_script.into_script(),
|
||||||
sequence: MAX_SEQUENCE
|
sequence: MAX_SEQUENCE
|
||||||
});
|
});
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
let mut out_script = Script::new();
|
let mut out_script = ScriptBuilder::new();
|
||||||
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
||||||
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||||
ret.output.push(TxOut {
|
ret.output.push(TxOut {
|
||||||
value: 50 * COIN_VALUE,
|
value: 50 * COIN_VALUE,
|
||||||
script_pubkey: out_script
|
script_pubkey: out_script.into_script()
|
||||||
});
|
});
|
||||||
|
|
||||||
// end
|
// end
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs and returns the genesis block
|
/// Constructs and returns the genesis block
|
||||||
pub fn genesis_block(network: Network) -> Block {
|
pub fn genesis_block(network: Network) -> Block {
|
||||||
match network {
|
match network {
|
||||||
Network::Bitcoin => {
|
Network::Bitcoin => {
|
||||||
let txdata = vec![bitcoin_genesis_tx()];
|
let txdata = vec![bitcoin_genesis_tx()];
|
||||||
Block {
|
Block {
|
||||||
header: BlockHeader {
|
header: BlockHeader {
|
||||||
version: 1,
|
version: 1,
|
||||||
prev_blockhash: Default::default(),
|
prev_blockhash: Default::default(),
|
||||||
merkle_root: txdata.merkle_root(),
|
merkle_root: txdata.merkle_root(),
|
||||||
time: 1231006505,
|
time: 1231006505,
|
||||||
bits: 0x1d00ffff,
|
bits: 0x1d00ffff,
|
||||||
nonce: 2083236893
|
nonce: 2083236893
|
||||||
},
|
},
|
||||||
txdata: txdata
|
txdata: txdata
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Network::Testnet => {
|
||||||
|
let txdata = vec![bitcoin_genesis_tx()];
|
||||||
|
Block {
|
||||||
|
header: BlockHeader {
|
||||||
|
version: 1,
|
||||||
|
prev_blockhash: Default::default(),
|
||||||
|
merkle_root: txdata.merkle_root(),
|
||||||
|
time: 1296688602,
|
||||||
|
bits: 0x1d00ffff,
|
||||||
|
nonce: 414098458
|
||||||
|
},
|
||||||
|
txdata: txdata
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Network::Testnet => {
|
|
||||||
let txdata = vec![bitcoin_genesis_tx()];
|
|
||||||
Block {
|
|
||||||
header: BlockHeader {
|
|
||||||
version: 1,
|
|
||||||
prev_blockhash: Default::default(),
|
|
||||||
merkle_root: txdata.merkle_root(),
|
|
||||||
time: 1296688602,
|
|
||||||
bits: 0x1d00ffff,
|
|
||||||
nonce: 414098458
|
|
||||||
},
|
|
||||||
txdata: txdata
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use network::serialize::{BitcoinHash, serialize};
|
use network::serialize::{BitcoinHash, serialize};
|
||||||
use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
|
use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
|
||||||
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
|
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bitcoin_genesis_first_transaction() {
|
fn bitcoin_genesis_first_transaction() {
|
||||||
let gen = bitcoin_genesis_tx();
|
let gen = bitcoin_genesis_tx();
|
||||||
|
|
||||||
assert_eq!(gen.version, 1);
|
assert_eq!(gen.version, 1);
|
||||||
assert_eq!(gen.input.len(), 1);
|
assert_eq!(gen.input.len(), 1);
|
||||||
assert_eq!(gen.input[0].prev_hash, Default::default());
|
assert_eq!(gen.input[0].prev_hash, Default::default());
|
||||||
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
|
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
|
||||||
assert_eq!(serialize(&gen.input[0].script_sig),
|
assert_eq!(serialize(&gen.input[0].script_sig),
|
||||||
Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));
|
Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));
|
||||||
|
|
||||||
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
|
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
|
||||||
assert_eq!(gen.output.len(), 1);
|
assert_eq!(gen.output.len(), 1);
|
||||||
assert_eq!(serialize(&gen.output[0].script_pubkey),
|
assert_eq!(serialize(&gen.output[0].script_pubkey),
|
||||||
Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
|
Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
|
||||||
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||||
assert_eq!(gen.lock_time, 0);
|
assert_eq!(gen.lock_time, 0);
|
||||||
|
|
||||||
assert_eq!(gen.bitcoin_hash().be_hex_string(),
|
assert_eq!(gen.bitcoin_hash().be_hex_string(),
|
||||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bitcoin_genesis_full_block() {
|
fn bitcoin_genesis_full_block() {
|
||||||
let gen = genesis_block(network::Bitcoin);
|
let gen = genesis_block(network::Bitcoin);
|
||||||
|
|
||||||
assert_eq!(gen.header.version, 1);
|
assert_eq!(gen.header.version, 1);
|
||||||
assert_eq!(gen.header.prev_blockhash, Default::default());
|
assert_eq!(gen.header.prev_blockhash, Default::default());
|
||||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||||
assert_eq!(gen.header.time, 1231006505);
|
assert_eq!(gen.header.time, 1231006505);
|
||||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||||
assert_eq!(gen.header.nonce, 2083236893);
|
assert_eq!(gen.header.nonce, 2083236893);
|
||||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||||
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
|
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testnet_genesis_full_block() {
|
fn testnet_genesis_full_block() {
|
||||||
let gen = genesis_block(network::Testnet);
|
let gen = genesis_block(network::Testnet);
|
||||||
assert_eq!(gen.header.version, 1);
|
assert_eq!(gen.header.version, 1);
|
||||||
assert_eq!(gen.header.prev_blockhash, Default::default());
|
assert_eq!(gen.header.prev_blockhash, Default::default());
|
||||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||||
assert_eq!(gen.header.time, 1296688602);
|
assert_eq!(gen.header.time, 1296688602);
|
||||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||||
assert_eq!(gen.header.nonce, 414098458);
|
assert_eq!(gen.header.nonce, 414098458);
|
||||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||||
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
|
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -592,10 +592,10 @@ impl All {
|
||||||
// 16 opcodes
|
// 16 opcodes
|
||||||
} else if All::OP_PUSHNUM_1 as u8 <= *self as u8 &&
|
} else if All::OP_PUSHNUM_1 as u8 <= *self as u8 &&
|
||||||
*self as u8 <= All::OP_PUSHNUM_16 as u8 {
|
*self as u8 <= All::OP_PUSHNUM_16 as u8 {
|
||||||
Class::PushNum(1 + *self as isize - All::OP_PUSHNUM_1 as isize)
|
Class::PushNum(1 + *self as i32 - All::OP_PUSHNUM_1 as i32)
|
||||||
// 76 opcodes
|
// 76 opcodes
|
||||||
} else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 {
|
} else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 {
|
||||||
Class::PushBytes(*self as usize)
|
Class::PushBytes(*self as u32)
|
||||||
// 60 opcodes
|
// 60 opcodes
|
||||||
} else {
|
} else {
|
||||||
Class::Ordinary(unsafe { transmute(*self) })
|
Class::Ordinary(unsafe { transmute(*self) })
|
||||||
|
@ -636,9 +636,9 @@ pub static OP_TRUE: All = All::OP_PUSHNUM_1;
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Class {
|
pub enum Class {
|
||||||
/// Pushes the given number onto the stack
|
/// Pushes the given number onto the stack
|
||||||
PushNum(isize),
|
PushNum(i32),
|
||||||
/// Pushes the given number of bytes onto the stack
|
/// Pushes the given number of bytes onto the stack
|
||||||
PushBytes(usize),
|
PushBytes(u32),
|
||||||
/// Fails the script if executed
|
/// Fails the script if executed
|
||||||
ReturnOp,
|
ReturnOp,
|
||||||
/// Fails the script even if not executed
|
/// Fails the script even if not executed
|
||||||
|
|
|
@ -35,7 +35,7 @@ use crypto::digest::Digest;
|
||||||
use crypto::ripemd160::Ripemd160;
|
use crypto::ripemd160::Ripemd160;
|
||||||
use crypto::sha1::Sha1;
|
use crypto::sha1::Sha1;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use secp256k1::Secp256k1;
|
use secp256k1::{self, Secp256k1};
|
||||||
use secp256k1::key::PublicKey;
|
use secp256k1::key::PublicKey;
|
||||||
use serde;
|
use serde;
|
||||||
|
|
||||||
|
@ -56,13 +56,16 @@ impl Clone for Script {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Display)]
|
||||||
|
/// An object which can be used to construct a script piece by piece
|
||||||
|
pub struct ScriptBuilder(Vec<u8>);
|
||||||
|
|
||||||
impl hash::Hash for Script {
|
impl hash::Hash for Script {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where H: hash::Hasher
|
where H: hash::Hasher
|
||||||
{
|
{
|
||||||
let &Script(ref raw) = self;
|
(&self.0[..]).hash(state);
|
||||||
(&raw[..]).hash(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -70,8 +73,7 @@ impl hash::Hash for Script {
|
||||||
where H: hash::Hasher
|
where H: hash::Hasher
|
||||||
{
|
{
|
||||||
for s in data.iter() {
|
for s in data.iter() {
|
||||||
let &Script(ref raw) = s;
|
(&s.0[..]).hash(state);
|
||||||
(&raw[..]).hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ pub enum Error {
|
||||||
/// OP_CHECKSIG was called with a bad signature
|
/// OP_CHECKSIG was called with a bad signature
|
||||||
BadSignature,
|
BadSignature,
|
||||||
/// An ECDSA error
|
/// An ECDSA error
|
||||||
Ecdsa(::secp256k1::Error),
|
Ecdsa(secp256k1::Error),
|
||||||
/// An OP_ELSE happened while not in an OP_IF tree
|
/// An OP_ELSE happened while not in an OP_IF tree
|
||||||
ElseWithoutIf,
|
ElseWithoutIf,
|
||||||
/// An OP_ENDIF happened while not in an OP_IF tree
|
/// An OP_ENDIF happened while not in an OP_IF tree
|
||||||
|
@ -1558,7 +1560,7 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
|
||||||
for _ in 0..input_index {
|
for _ in 0..input_index {
|
||||||
new_outs.push(Default::default())
|
new_outs.push(Default::default())
|
||||||
}
|
}
|
||||||
new_outs.push(tx_copy.output.swap_remove(input_index).unwrap());
|
new_outs.push(tx_copy.output.swap_remove(input_index));
|
||||||
tx_copy.output = new_outs;
|
tx_copy.output = new_outs;
|
||||||
} else {
|
} else {
|
||||||
sighash_single_bug = true;
|
sighash_single_bug = true;
|
||||||
|
@ -1581,7 +1583,10 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
|
||||||
serialize(&Sha256dHash::from_data(&data_to_sign[..])).unwrap()
|
serialize(&Sha256dHash::from_data(&data_to_sign[..])).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
Secp256k1::verify_raw(&signature_hash[..], sig_slice, &pubkey).map_err(Error::Ecdsa)
|
// We can unwrap -- only failure mode is on length, which is fixed to 32
|
||||||
|
let msg = secp256k1::Message::from_slice(&signature_hash[..]).unwrap();
|
||||||
|
|
||||||
|
Secp256k1::verify_raw(&msg, sig_slice, &pubkey).map_err(Error::Ecdsa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macro to translate English stack instructions into Rust code.
|
// Macro to translate English stack instructions into Rust code.
|
||||||
|
@ -1719,70 +1724,7 @@ impl Script {
|
||||||
pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) }
|
pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) }
|
||||||
|
|
||||||
/// The length in bytes of the script
|
/// The length in bytes of the script
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
let &Script(ref raw) = self;
|
|
||||||
raw.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds instructions to push an integer onto the stack. Integers are
|
|
||||||
/// encoded as little-endian signed-magnitude numbers, but there are
|
|
||||||
/// dedicated opcodes to push some small integers.
|
|
||||||
pub fn push_int(&mut self, data: i64) {
|
|
||||||
// We can special-case -1, 1-16
|
|
||||||
if data == -1 || (data >= 1 && data <=16) {
|
|
||||||
let &Script(ref mut raw) = self;
|
|
||||||
raw.push(data as u8 + opcodes::All::OP_TRUE as u8);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We can also special-case zero
|
|
||||||
if data == 0 {
|
|
||||||
let &Script(ref mut raw) = self;
|
|
||||||
raw.push(opcodes::All::OP_FALSE as u8);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Otherwise encode it as data
|
|
||||||
self.push_scriptint(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds instructions to push an integer onto the stack, using the explicit
|
|
||||||
/// encoding regardless of the availability of dedicated opcodes.
|
|
||||||
pub fn push_scriptint(&mut self, data: i64) {
|
|
||||||
self.push_slice(&build_scriptint(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds instructions to push some arbitrary data onto the stack
|
|
||||||
pub fn push_slice(&mut self, data: &[u8]) {
|
|
||||||
let &Script(ref mut raw) = self;
|
|
||||||
// Start with a PUSH opcode
|
|
||||||
match data.len() {
|
|
||||||
n if n < opcodes::Ordinary::OP_PUSHDATA1 as usize => { raw.push(n as u8); },
|
|
||||||
n if n < 0x100 => {
|
|
||||||
raw.push(opcodes::Ordinary::OP_PUSHDATA1 as u8);
|
|
||||||
raw.push(n as u8);
|
|
||||||
},
|
|
||||||
n if n < 0x10000 => {
|
|
||||||
raw.push(opcodes::Ordinary::OP_PUSHDATA2 as u8);
|
|
||||||
raw.push((n % 0x100) as u8);
|
|
||||||
raw.push((n / 0x100) as u8);
|
|
||||||
},
|
|
||||||
n if n < 0x100000000 => {
|
|
||||||
raw.push(opcodes::Ordinary::OP_PUSHDATA4 as u8);
|
|
||||||
raw.push((n % 0x100) as u8);
|
|
||||||
raw.push(((n / 0x100) % 0x100) as u8);
|
|
||||||
raw.push(((n / 0x10000) % 0x100) as u8);
|
|
||||||
raw.push((n / 0x1000000) as u8);
|
|
||||||
}
|
|
||||||
_ => panic!("tried to put a 4bn+ sized object into a script!")
|
|
||||||
}
|
|
||||||
// Then push the acraw
|
|
||||||
raw.extend(data.iter().map(|n| *n));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds an individual opcode to the script
|
|
||||||
pub fn push_opcode(&mut self, data: opcodes::All) {
|
|
||||||
let &Script(ref mut raw) = self;
|
|
||||||
raw.push(data as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trace a script
|
/// Trace a script
|
||||||
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
|
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
|
||||||
|
@ -1807,21 +1749,19 @@ impl Script {
|
||||||
input_context: Option<(&Transaction, usize)>,
|
input_context: Option<(&Transaction, usize)>,
|
||||||
mut trace: Option<&mut Vec<TraceIteration>>)
|
mut trace: Option<&mut Vec<TraceIteration>>)
|
||||||
-> Result<(), Error> {
|
-> Result<(), Error> {
|
||||||
let &Script(ref raw) = self;
|
|
||||||
|
|
||||||
let mut codeseparator_index = 0;
|
let mut codeseparator_index = 0;
|
||||||
let mut exec_stack = vec![];
|
let mut exec_stack = vec![];
|
||||||
let mut alt_stack = vec![];
|
let mut alt_stack = vec![];
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let mut op_count = 0;
|
let mut op_count = 0;
|
||||||
while index < raw.len() {
|
while index < self.0.len() {
|
||||||
let executing = exec_stack.iter().all(|e| *e);
|
let executing = exec_stack.iter().all(|e| *e);
|
||||||
let byte = unsafe { *raw.get(index) };
|
let byte = self.0[index];
|
||||||
// Write out the trace, except the stack which we don't know yet
|
// Write out the trace, except the stack which we don't know yet
|
||||||
match trace {
|
match trace {
|
||||||
Some(ref mut t) => {
|
Some(ref mut t) => {
|
||||||
let opcode = opcodes::All::Opcode::from_u8(byte);
|
let opcode = opcodes::All::from_u8(byte);
|
||||||
t.push(TraceIteration {
|
t.push(TraceIteration {
|
||||||
index: index,
|
index: index,
|
||||||
opcode: opcode,
|
opcode: opcode,
|
||||||
|
@ -1837,7 +1777,7 @@ impl Script {
|
||||||
op_count += 1;
|
op_count += 1;
|
||||||
index += 1;
|
index += 1;
|
||||||
// The definitions of all these categories are in opcodes.rs
|
// The definitions of all these categories are in opcodes.rs
|
||||||
match (executing, opcodes::All::Opcode::from_u8(byte).classify()) {
|
match (executing, opcodes::All::from_u8(byte).classify()) {
|
||||||
// Illegal operations mean failure regardless of execution state
|
// Illegal operations mean failure regardless of execution state
|
||||||
(_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode),
|
(_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode),
|
||||||
// Push number
|
// Push number
|
||||||
|
@ -1846,29 +1786,30 @@ impl Script {
|
||||||
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
||||||
// Data-reading statements still need to read, even when not executing
|
// Data-reading statements still need to read, even when not executing
|
||||||
(_, opcodes::Class::PushBytes(n)) => {
|
(_, opcodes::Class::PushBytes(n)) => {
|
||||||
if raw.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
let n = n as usize;
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index, index + n))); }
|
if self.0.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||||
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index..index + n])); }
|
||||||
index += n;
|
index += n;
|
||||||
}
|
}
|
||||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => {
|
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => {
|
||||||
if raw.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
|
||||||
let n = try!(read_uint(&raw[index..], 1));
|
let n = try!(read_uint(&self.0[index..], 1));
|
||||||
if raw.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 1, index + n + 1))); }
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 1..index + n + 1])); }
|
||||||
index += 1 + n;
|
index += 1 + n;
|
||||||
}
|
}
|
||||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => {
|
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => {
|
||||||
if raw.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
|
||||||
let n = try!(read_uint(&raw[index..], 2));
|
let n = try!(read_uint(&self.0[index..], 2));
|
||||||
if raw.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 2, index + n + 2))); }
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 2..index + n + 2])); }
|
||||||
index += 2 + n;
|
index += 2 + n;
|
||||||
}
|
}
|
||||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => {
|
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => {
|
||||||
if raw.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
|
||||||
let n = try!(read_uint(&raw[index..], 4));
|
let n = try!(read_uint(&self.0[index..], 4));
|
||||||
if raw.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 4, index + n + 4))); }
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 4..index + n + 4])); }
|
||||||
index += 4 + n;
|
index += 4 + n;
|
||||||
}
|
}
|
||||||
// If-statements take effect when not executing
|
// If-statements take effect when not executing
|
||||||
|
@ -1942,7 +1883,7 @@ impl Script {
|
||||||
opcodes::Ordinary::OP_OVER => stack_opcode!(stack(2): copy 2),
|
opcodes::Ordinary::OP_OVER => stack_opcode!(stack(2): copy 2),
|
||||||
opcodes::Ordinary::OP_PICK => {
|
opcodes::Ordinary::OP_PICK => {
|
||||||
let n = match stack.pop() {
|
let n = match stack.pop() {
|
||||||
Some(data) => try!(read_scriptint(&data)),
|
Some(data) => try!(read_scriptint(&data[..])),
|
||||||
None => { return Err(Error::PopEmptyStack); }
|
None => { return Err(Error::PopEmptyStack); }
|
||||||
};
|
};
|
||||||
if n < 0 { return Err(Error::NegativePick); }
|
if n < 0 { return Err(Error::NegativePick); }
|
||||||
|
@ -1951,7 +1892,7 @@ impl Script {
|
||||||
}
|
}
|
||||||
opcodes::Ordinary::OP_ROLL => {
|
opcodes::Ordinary::OP_ROLL => {
|
||||||
let n = match stack.pop() {
|
let n = match stack.pop() {
|
||||||
Some(data) => try!(read_scriptint(&data)),
|
Some(data) => try!(read_scriptint(&data[..])),
|
||||||
None => { return Err(Error::PopEmptyStack); }
|
None => { return Err(Error::PopEmptyStack); }
|
||||||
};
|
};
|
||||||
if n < 0 { return Err(Error::NegativeRoll); }
|
if n < 0 { return Err(Error::NegativeRoll); }
|
||||||
|
@ -2033,11 +1974,12 @@ impl Script {
|
||||||
|
|
||||||
// Compute the section of script that needs to be hashed: everything
|
// Compute the section of script that needs to be hashed: everything
|
||||||
// from the last CODESEPARATOR, except the signature itself.
|
// from the last CODESEPARATOR, except the signature itself.
|
||||||
let mut script = (&raw[codeseparator_index..]).to_vec();
|
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||||
let mut remove = Script::new();
|
let mut remove = ScriptBuilder::new();
|
||||||
remove.push_slice(sig_slice);
|
remove.push_slice(sig_slice);
|
||||||
script_find_and_remove(&mut script, &remove);
|
script_find_and_remove(&mut script, &remove[..]);
|
||||||
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
// Also all of the OP_CODESEPARATORS, even the unevaluated ones
|
||||||
|
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||||
|
|
||||||
// This is as far as we can go without a transaction, so fail here
|
// This is as far as we can go without a transaction, so fail here
|
||||||
if input_context.is_none() { return Err(Error::NoTransaction); }
|
if input_context.is_none() { return Err(Error::NoTransaction); }
|
||||||
|
@ -2053,7 +1995,7 @@ impl Script {
|
||||||
opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => {
|
opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => {
|
||||||
// Read all the keys
|
// Read all the keys
|
||||||
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
||||||
let n_keys = try!(read_scriptint(&stack.pop().unwrap()));
|
let n_keys = try!(read_scriptint(&stack.pop().unwrap()[..]));
|
||||||
if n_keys < 0 || n_keys > 20 {
|
if n_keys < 0 || n_keys > 20 {
|
||||||
return Err(Error::MultisigBadKeyCount(n_keys as isize));
|
return Err(Error::MultisigBadKeyCount(n_keys as isize));
|
||||||
}
|
}
|
||||||
|
@ -2066,7 +2008,7 @@ impl Script {
|
||||||
|
|
||||||
// Read all the signatures
|
// Read all the signatures
|
||||||
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
||||||
let n_sigs = try!(read_scriptint(&stack.pop().unwrap()));
|
let n_sigs = try!(read_scriptint(&stack.pop().unwrap()[..]));
|
||||||
if n_sigs < 0 || n_sigs > n_keys {
|
if n_sigs < 0 || n_sigs > n_keys {
|
||||||
return Err(Error::MultisigBadSigCount(n_sigs as isize));
|
return Err(Error::MultisigBadSigCount(n_sigs as isize));
|
||||||
}
|
}
|
||||||
|
@ -2082,12 +2024,12 @@ impl Script {
|
||||||
|
|
||||||
// Compute the section of script that needs to be hashed: everything
|
// Compute the section of script that needs to be hashed: everything
|
||||||
// from the last CODESEPARATOR, except the signatures themselves.
|
// from the last CODESEPARATOR, except the signatures themselves.
|
||||||
let mut script = (&raw[codeseparator_index..]).to_vec();
|
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||||
for sig in sigs.iter() {
|
for sig in sigs.iter() {
|
||||||
let mut remove = Script::new();
|
let mut remove = ScriptBuilder::new();
|
||||||
remove.push_slice(&sig);
|
remove.push_slice(&sig[..]);
|
||||||
script_find_and_remove(&mut script, &remove);
|
script_find_and_remove(&mut script, &remove[..]);
|
||||||
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is as far as we can go without a transaction, so fail here
|
// This is as far as we can go without a transaction, so fail here
|
||||||
|
@ -2105,7 +2047,7 @@ impl Script {
|
||||||
// Try to validate the signature with the given key
|
// Try to validate the signature with the given key
|
||||||
(Some(k), Some(s)) => {
|
(Some(k), Some(s)) => {
|
||||||
// Move to the next signature if it is valid for the current key
|
// Move to the next signature if it is valid for the current key
|
||||||
if check_signature(&s, &k, script.clone(), tx, input_index).is_ok() {
|
if check_signature(&s[..], &k[..], script.clone(), tx, input_index).is_ok() {
|
||||||
sig = sig_iter.next();
|
sig = sig_iter.next();
|
||||||
}
|
}
|
||||||
// Move to the next key in any case
|
// Move to the next key in any case
|
||||||
|
@ -2142,12 +2084,11 @@ impl Script {
|
||||||
/// Checks whether a script pubkey is a p2sh output
|
/// Checks whether a script pubkey is a p2sh output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_p2sh(&self) -> bool {
|
pub fn is_p2sh(&self) -> bool {
|
||||||
let &Script(ref raw) = self;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
raw.len() == 23 &&
|
self.0.len() == 23 &&
|
||||||
*raw.get(0) == opcodes::All::OP_HASH160 as u8 &&
|
self.0[0] == opcodes::All::OP_HASH160 as u8 &&
|
||||||
*raw.get(1) == opcodes::All::OP_PUSHBYTES_20 as u8 &&
|
self.0[1] == opcodes::All::OP_PUSHBYTES_20 as u8 &&
|
||||||
*raw.get(22) == opcodes::All::OP_EQUAL as u8
|
self.0[22] == opcodes::All::OP_EQUAL as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2186,9 +2127,10 @@ impl Script {
|
||||||
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
||||||
// Data-reading statements still need to read, even when not executing
|
// Data-reading statements still need to read, even when not executing
|
||||||
(_, opcodes::Class::PushBytes(n)) => {
|
(_, opcodes::Class::PushBytes(n)) => {
|
||||||
|
let n = n as usize;
|
||||||
if script.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index, index + n)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index..index + n]));
|
||||||
}
|
}
|
||||||
index += n;
|
index += n;
|
||||||
}
|
}
|
||||||
|
@ -2200,7 +2142,7 @@ impl Script {
|
||||||
};
|
};
|
||||||
if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 1, index + n + 1)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index + 1..index + n + 1]));
|
||||||
}
|
}
|
||||||
index += 1 + n;
|
index += 1 + n;
|
||||||
}
|
}
|
||||||
|
@ -2212,7 +2154,7 @@ impl Script {
|
||||||
};
|
};
|
||||||
if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 2, index + n + 2)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index + 2..index + n + 2]));
|
||||||
}
|
}
|
||||||
index += 2 + n;
|
index += 2 + n;
|
||||||
}
|
}
|
||||||
|
@ -2223,7 +2165,7 @@ impl Script {
|
||||||
};
|
};
|
||||||
if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 4, index + n + 4)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index + 4..index + n + 4]));
|
||||||
}
|
}
|
||||||
index += 4 + n;
|
index += 4 + n;
|
||||||
}
|
}
|
||||||
|
@ -2495,8 +2437,7 @@ impl Script {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let &Script(ref raw) = self;
|
recurse(&self.0, AbstractStack::new(), vec![], 1)
|
||||||
recurse(&raw, AbstractStack::new(), vec![], 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2504,62 +2445,93 @@ impl Default for Script {
|
||||||
fn default() -> Script { Script(vec![].into_boxed_slice()) }
|
fn default() -> Script { Script(vec![].into_boxed_slice()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_index_newtype!(Script, u8);
|
||||||
|
|
||||||
impl ops::Index<usize> for Script {
|
impl ScriptBuilder {
|
||||||
type Output = u8;
|
/// Creates a new empty script
|
||||||
#[inline]
|
pub fn new() -> ScriptBuilder { ScriptBuilder(vec![]) }
|
||||||
fn index(&self, index: usize) -> &u8 {
|
|
||||||
let &Script(ref raw) = self;
|
/// Creates a new script from an existing vector
|
||||||
&raw[index]
|
pub fn from_vec(v: Vec<u8>) -> ScriptBuilder { ScriptBuilder(v) }
|
||||||
|
|
||||||
|
/// The length in bytes of the script
|
||||||
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
|
|
||||||
|
/// Adds instructions to push an integer onto the stack. Integers are
|
||||||
|
/// encoded as little-endian signed-magnitude numbers, but there are
|
||||||
|
/// dedicated opcodes to push some small integers.
|
||||||
|
pub fn push_int(&mut self, data: i64) {
|
||||||
|
// We can special-case -1, 1-16
|
||||||
|
if data == -1 || (data >= 1 && data <=16) {
|
||||||
|
self.0.push(data as u8 + opcodes::OP_TRUE as u8);
|
||||||
|
}
|
||||||
|
// We can also special-case zero
|
||||||
|
else if data == 0 {
|
||||||
|
self.0.push(opcodes::OP_FALSE as u8);
|
||||||
|
}
|
||||||
|
// Otherwise encode it as data
|
||||||
|
else { self.push_scriptint(data); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds instructions to push an integer onto the stack, using the explicit
|
||||||
|
/// encoding regardless of the availability of dedicated opcodes.
|
||||||
|
pub fn push_scriptint(&mut self, data: i64) {
|
||||||
|
self.push_slice(&build_scriptint(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds instructions to push some arbitrary data onto the stack
|
||||||
|
pub fn push_slice(&mut self, data: &[u8]) {
|
||||||
|
// Start with a PUSH opcode
|
||||||
|
match data.len() {
|
||||||
|
n if n < opcodes::Ordinary::OP_PUSHDATA1 as usize => { self.0.push(n as u8); },
|
||||||
|
n if n < 0x100 => {
|
||||||
|
self.0.push(opcodes::Ordinary::OP_PUSHDATA1 as u8);
|
||||||
|
self.0.push(n as u8);
|
||||||
|
},
|
||||||
|
n if n < 0x10000 => {
|
||||||
|
self.0.push(opcodes::Ordinary::OP_PUSHDATA2 as u8);
|
||||||
|
self.0.push((n % 0x100) as u8);
|
||||||
|
self.0.push((n / 0x100) as u8);
|
||||||
|
},
|
||||||
|
n if n < 0x100000000 => {
|
||||||
|
self.0.push(opcodes::Ordinary::OP_PUSHDATA4 as u8);
|
||||||
|
self.0.push((n % 0x100) as u8);
|
||||||
|
self.0.push(((n / 0x100) % 0x100) as u8);
|
||||||
|
self.0.push(((n / 0x10000) % 0x100) as u8);
|
||||||
|
self.0.push((n / 0x1000000) as u8);
|
||||||
|
}
|
||||||
|
_ => panic!("tried to put a 4bn+ sized object into a script!")
|
||||||
|
}
|
||||||
|
// Then push the acraw
|
||||||
|
self.0.extend(data.iter().map(|n| *n));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_opcode(&mut self, data: opcodes::All) {
|
||||||
|
self.0.push(data as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_script(self) -> Script {
|
||||||
|
Script(self.0.into_boxed_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::Range<usize>> for Script {
|
/// Adds an individual opcode to the script
|
||||||
type Output = [u8];
|
impl Default for ScriptBuilder {
|
||||||
#[inline]
|
fn default() -> ScriptBuilder { ScriptBuilder(vec![]) }
|
||||||
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[index]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::RangeTo<usize>> for Script {
|
impl_index_newtype!(ScriptBuilder, u8);
|
||||||
type Output = [u8];
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFrom<usize>> for Script {
|
|
||||||
type Output = [u8];
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFull> for Script {
|
|
||||||
type Output = [u8];
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User-facing serialization
|
// User-facing serialization
|
||||||
impl serde::Serialize for Script {
|
impl serde::Serialize for Script {
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer,
|
where S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let &Script(ref raw) = self;
|
for dat in self.0.iter() {
|
||||||
for dat in raw.iter() {
|
try!(serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap()));
|
||||||
serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap());
|
try!(serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap()));
|
||||||
serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap());
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2567,8 +2539,7 @@ impl serde::Serialize for Script {
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Script {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for Script {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &Script(ref data) = self;
|
self.0.consensus_encode(s)
|
||||||
data.consensus_encode(s)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -32,271 +32,272 @@ use blockdata::utxoset::UtxoSet;
|
||||||
use network::encodable::ConsensusEncodable;
|
use network::encodable::ConsensusEncodable;
|
||||||
use network::serialize::BitcoinHash;
|
use network::serialize::BitcoinHash;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use wallet::address::Address;
|
use wallet::address::{Address, ToAddress};
|
||||||
|
|
||||||
/// A transaction input, which defines old coins to be consumed
|
/// A transaction input, which defines old coins to be consumed
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct TxIn {
|
pub struct TxIn {
|
||||||
/// The hash of the transaction whose output is being used an an input
|
/// The hash of the transaction whose output is being used an an input
|
||||||
pub prev_hash: Sha256dHash,
|
pub prev_hash: Sha256dHash,
|
||||||
/// The index of the output in the previous transaction, which may have several
|
/// The index of the output in the previous transaction, which may have several
|
||||||
pub prev_index: u32,
|
pub prev_index: u32,
|
||||||
/// The script which pushes values on the stack which will cause
|
/// The script which pushes values on the stack which will cause
|
||||||
/// the referenced output's script to accept
|
/// the referenced output's script to accept
|
||||||
pub script_sig: Script,
|
pub script_sig: Script,
|
||||||
/// The sequence number, which suggests to miners which of two
|
/// The sequence number, which suggests to miners which of two
|
||||||
/// conflicting transactions should be preferred, or 0xFFFFFFFF
|
/// conflicting transactions should be preferred, or 0xFFFFFFFF
|
||||||
/// to ignore this feature. This is generally never used since
|
/// to ignore this feature. This is generally never used since
|
||||||
/// the miner behaviour cannot be enforced.
|
/// the miner behaviour cannot be enforced.
|
||||||
pub sequence: u32,
|
pub sequence: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A transaction output, which defines new coins to be created from old ones.
|
/// A transaction output, which defines new coins to be created from old ones.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct TxOut {
|
pub struct TxOut {
|
||||||
/// The value of the output, in satoshis
|
/// The value of the output, in satoshis
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
/// The script which must satisfy for the output to be spent
|
/// The script which must satisfy for the output to be spent
|
||||||
pub script_pubkey: Script
|
pub script_pubkey: Script
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used as a "null txout" in consensus signing code
|
// This is used as a "null txout" in consensus signing code
|
||||||
impl Default for TxOut {
|
impl Default for TxOut {
|
||||||
fn default() -> TxOut {
|
fn default() -> TxOut {
|
||||||
TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() }
|
TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A classification for script pubkeys
|
/// A classification for script pubkeys
|
||||||
pub enum ScriptPubkeyTemplate {
|
pub enum ScriptPubkeyTemplate {
|
||||||
/// A pay-to-address output
|
/// A pay-to-address output
|
||||||
PayToPubkeyHash(Address),
|
PayToPubkeyHash(Address),
|
||||||
/// Another kind of output
|
/// Another kind of output
|
||||||
Unknown
|
Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TxOut {
|
impl TxOut {
|
||||||
/// Determines the template that this output adheres to, if any
|
/// Determines the template that this output adheres to, if any
|
||||||
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
|
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
|
||||||
if self.script_pubkey.len() == 25 &&
|
if self.script_pubkey.len() == 25 &&
|
||||||
self.script_pubkey.slice_to(3) == &[0x76, 0xa9, 0x14] &&
|
&self.script_pubkey[0..3] == &[0x76, 0xa9, 0x14] &&
|
||||||
self.script_pubkey.slice_from(23) == &[0x88, 0xac] {
|
&self.script_pubkey[23..] == &[0x88, 0xac] {
|
||||||
ScriptPubkeyTemplate::PayToPubkeyHash(self.script_pubkey.slice(3, 23).to_address(network))
|
ScriptPubkeyTemplate::PayToPubkeyHash((&self.script_pubkey[3..23]).to_address(network))
|
||||||
} else {
|
} else {
|
||||||
ScriptPubkeyTemplate::Unknown
|
ScriptPubkeyTemplate::Unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Bitcoin transaction, which describes an authenticated movement of coins
|
/// A Bitcoin transaction, which describes an authenticated movement of coins
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
/// The protocol version, should always be 1.
|
/// The protocol version, should always be 1.
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// Block number before which this transaction is valid, or 0 for
|
/// Block number before which this transaction is valid, or 0 for
|
||||||
/// valid immediately.
|
/// valid immediately.
|
||||||
pub lock_time: u32,
|
pub lock_time: u32,
|
||||||
/// List of inputs
|
/// List of inputs
|
||||||
pub input: Vec<TxIn>,
|
pub input: Vec<TxIn>,
|
||||||
/// List of outputs
|
/// List of outputs
|
||||||
pub output: Vec<TxOut>
|
pub output: Vec<TxOut>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A transaction error
|
/// A transaction error
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Concatenated script failed in the input half (script error)
|
/// Concatenated script failed in the input half (script error)
|
||||||
InputScriptFailure(script::Error),
|
InputScriptFailure(script::Error),
|
||||||
/// Concatenated script failed in the output half (script error)
|
/// Concatenated script failed in the output half (script error)
|
||||||
OutputScriptFailure(script::Error),
|
OutputScriptFailure(script::Error),
|
||||||
/// P2SH serialized script failed (script error)
|
/// P2SH serialized script failed (script error)
|
||||||
P2shScriptFailure(script::Error),
|
P2shScriptFailure(script::Error),
|
||||||
/// P2SH serialized script ended with false at the top of the stack
|
/// P2SH serialized script ended with false at the top of the stack
|
||||||
P2shScriptReturnedFalse,
|
P2shScriptReturnedFalse,
|
||||||
/// P2SH serialized script ended with nothing in the stack
|
/// P2SH serialized script ended with nothing in the stack
|
||||||
P2shScriptReturnedEmptyStack,
|
P2shScriptReturnedEmptyStack,
|
||||||
/// Script ended with false at the top of the stack
|
/// Script ended with false at the top of the stack
|
||||||
ScriptReturnedFalse,
|
ScriptReturnedFalse,
|
||||||
/// Script ended with nothing in the stack
|
/// Script ended with nothing in the stack
|
||||||
ScriptReturnedEmptyStack,
|
ScriptReturnedEmptyStack,
|
||||||
/// Script ended with nothing in the stack (input txid, input vout)
|
/// Script ended with nothing in the stack (input txid, input vout)
|
||||||
InputNotFound(Sha256dHash, u32),
|
InputNotFound(Sha256dHash, u32),
|
||||||
}
|
}
|
||||||
|
display_from_debug!(Error);
|
||||||
|
|
||||||
impl serde::Serialize for Error {
|
impl serde::Serialize for Error {
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer,
|
where S: serde::Serializer,
|
||||||
{
|
{
|
||||||
serializer.visit_str(&self.to_string())
|
serializer.visit_str(&self.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trace of a transaction input's script execution
|
/// A trace of a transaction input's script execution
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct InputTrace {
|
pub struct InputTrace {
|
||||||
input_txid: Sha256dHash,
|
input_txid: Sha256dHash,
|
||||||
input_vout: usize,
|
input_vout: usize,
|
||||||
sig_trace: ScriptTrace,
|
sig_trace: ScriptTrace,
|
||||||
pubkey_trace: Option<ScriptTrace>,
|
pubkey_trace: Option<ScriptTrace>,
|
||||||
p2sh_trace: Option<ScriptTrace>,
|
p2sh_trace: Option<ScriptTrace>,
|
||||||
error: Option<Error>
|
error: Option<Error>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trace of a transaction's execution
|
/// A trace of a transaction's execution
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct TransactionTrace {
|
pub struct TransactionTrace {
|
||||||
txid: Sha256dHash,
|
txid: Sha256dHash,
|
||||||
inputs: Vec<InputTrace>
|
inputs: Vec<InputTrace>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TxIn {
|
impl TxIn {
|
||||||
/// Check an input's script for validity
|
/// Check an input's script for validity
|
||||||
pub fn validate(&self,
|
pub fn validate(&self,
|
||||||
utxoset: &UtxoSet,
|
utxoset: &UtxoSet,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
index: usize) -> Result<(), Error> {
|
index: usize) -> Result<(), Error> {
|
||||||
let txo = utxoset.get_utxo(self.prev_hash, self.prev_index);
|
let txo = utxoset.get_utxo(self.prev_hash, self.prev_index);
|
||||||
match txo {
|
match txo {
|
||||||
Some((_, txo)) => {
|
Some((_, txo)) => {
|
||||||
let mut p2sh_stack = Vec::new();
|
let mut p2sh_stack = Vec::new();
|
||||||
let mut p2sh_script = Script::new();
|
let mut p2sh_script = Script::new();
|
||||||
|
|
||||||
let mut stack = Vec::with_capacity(6);
|
let mut stack = Vec::with_capacity(6);
|
||||||
match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) {
|
match self.script_sig.evaluate(&mut stack, Some((txn, index)), None) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => { return Err(Error::InputScriptFailure(e)); }
|
Err(e) => { return Err(Error::InputScriptFailure(e)); }
|
||||||
}
|
}
|
||||||
if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
|
if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
|
||||||
p2sh_stack = stack.clone();
|
p2sh_stack = stack.clone();
|
||||||
p2sh_script = match p2sh_stack.pop() {
|
p2sh_script = match p2sh_stack.pop() {
|
||||||
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
|
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
|
||||||
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
|
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
|
||||||
None => unreachable!()
|
None => unreachable!()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) {
|
match txo.script_pubkey.evaluate(&mut stack, Some((txn, index)), None) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => { return Err(Error::OutputScriptFailure(e)); }
|
Err(e) => { return Err(Error::OutputScriptFailure(e)); }
|
||||||
}
|
}
|
||||||
match stack.pop() {
|
match stack.pop() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
if !read_scriptbool(&v[..]) {
|
if !read_scriptbool(&v[..]) {
|
||||||
return Err(Error::ScriptReturnedFalse);
|
return Err(Error::ScriptReturnedFalse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => { return Err(Error::ScriptReturnedEmptyStack); }
|
||||||
|
}
|
||||||
|
if txo.script_pubkey.is_p2sh() {
|
||||||
|
match p2sh_script.evaluate(&mut p2sh_stack, Some((txn, index)), None) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => { return Err(Error::P2shScriptFailure(e)); }
|
||||||
|
}
|
||||||
|
match p2sh_stack.pop() {
|
||||||
|
Some(v) => {
|
||||||
|
if !read_scriptbool(&v[..]) {
|
||||||
|
return Err(Error::P2shScriptReturnedFalse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => { return Err(Error::P2shScriptReturnedEmptyStack); }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
None => { return Err(Error::InputNotFound(self.prev_hash, self.prev_index)); }
|
||||||
None => { return Err(Error::ScriptReturnedEmptyStack); }
|
|
||||||
}
|
}
|
||||||
if txo.script_pubkey.is_p2sh() {
|
Ok(())
|
||||||
match p2sh_script.evaluate(&mut p2sh_stack, Some((txn, index)), None) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => { return Err(Error::P2shScriptFailure(e)); }
|
|
||||||
}
|
|
||||||
match p2sh_stack.pop() {
|
|
||||||
Some(v) => {
|
|
||||||
if !read_scriptbool(&v[..]) {
|
|
||||||
return Err(Error::P2shScriptReturnedFalse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => { return Err(Error::P2shScriptReturnedEmptyStack); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => { return Err(Error::InputNotFound(self.prev_hash, self.prev_index)); }
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
/// Check a transaction for validity
|
/// Check a transaction for validity
|
||||||
pub fn validate(&self, utxoset: &UtxoSet) -> Result<(), Error> {
|
pub fn validate(&self, utxoset: &UtxoSet) -> Result<(), Error> {
|
||||||
for (n, input) in self.input.iter().enumerate() {
|
for (n, input) in self.input.iter().enumerate() {
|
||||||
try!(input.validate(utxoset, self, n));
|
try!(input.validate(utxoset, self, n));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce a trace of a transaction's execution
|
/// Produce a trace of a transaction's execution
|
||||||
pub fn trace(&self, utxoset: &UtxoSet) -> TransactionTrace {
|
pub fn trace(&self, utxoset: &UtxoSet) -> TransactionTrace {
|
||||||
let mut ret = TransactionTrace { txid: self.bitcoin_hash(),
|
let mut ret = TransactionTrace { txid: self.bitcoin_hash(),
|
||||||
inputs: Vec::with_capacity(self.input.len()) };
|
inputs: Vec::with_capacity(self.input.len()) };
|
||||||
for (n, input) in self.input.iter().enumerate() {
|
for (n, input) in self.input.iter().enumerate() {
|
||||||
// Setup trace
|
// Setup trace
|
||||||
let mut trace = InputTrace {
|
let mut trace = InputTrace {
|
||||||
input_txid: input.prev_hash,
|
input_txid: input.prev_hash,
|
||||||
input_vout: input.prev_index as usize,
|
input_vout: input.prev_index as usize,
|
||||||
sig_trace: ScriptTrace {
|
sig_trace: ScriptTrace {
|
||||||
script: Script::new(),
|
script: Script::new(),
|
||||||
initial_stack: vec![],
|
initial_stack: vec![],
|
||||||
iterations: vec![],
|
iterations: vec![],
|
||||||
error: None
|
error: None
|
||||||
},
|
},
|
||||||
pubkey_trace: None,
|
pubkey_trace: None,
|
||||||
p2sh_trace: None,
|
p2sh_trace: None,
|
||||||
error: None
|
error: None
|
||||||
};
|
|
||||||
// Run through the input
|
|
||||||
let txo = utxoset.get_utxo(input.prev_hash, input.prev_index);
|
|
||||||
match txo {
|
|
||||||
Some((_, txo)) => {
|
|
||||||
let mut p2sh_stack = Vec::new();
|
|
||||||
let mut p2sh_script = Script::new();
|
|
||||||
|
|
||||||
let mut stack = Vec::with_capacity(6);
|
|
||||||
trace.sig_trace = input.script_sig.trace(&mut stack, Some((self, n)));
|
|
||||||
let err = trace.sig_trace.error.as_ref().map(|e| e.clone());
|
|
||||||
err.map(|e| trace.error = Some(Error::InputScriptFailure(e)));
|
|
||||||
|
|
||||||
if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
|
|
||||||
p2sh_stack = stack.clone();
|
|
||||||
p2sh_script = match p2sh_stack.pop() {
|
|
||||||
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
|
|
||||||
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
|
|
||||||
None => unreachable!()
|
|
||||||
};
|
};
|
||||||
}
|
// Run through the input
|
||||||
if trace.error.is_none() {
|
let txo = utxoset.get_utxo(input.prev_hash, input.prev_index);
|
||||||
trace.pubkey_trace = Some(txo.script_pubkey.trace(&mut stack, Some((self, n))));
|
match txo {
|
||||||
let err = trace.pubkey_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone());
|
Some((_, txo)) => {
|
||||||
err.map(|e| trace.error = Some(Error::OutputScriptFailure(e)));
|
let mut p2sh_stack = Vec::new();
|
||||||
match stack.pop() {
|
let mut p2sh_script = Script::new();
|
||||||
Some(v) => {
|
|
||||||
if !read_scriptbool(&v[..]) {
|
let mut stack = Vec::with_capacity(6);
|
||||||
trace.error = Some(Error::ScriptReturnedFalse);
|
trace.sig_trace = input.script_sig.trace(&mut stack, Some((self, n)));
|
||||||
|
let err = trace.sig_trace.error.as_ref().map(|e| e.clone());
|
||||||
|
err.map(|e| trace.error = Some(Error::InputScriptFailure(e)));
|
||||||
|
|
||||||
|
if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
|
||||||
|
p2sh_stack = stack.clone();
|
||||||
|
p2sh_script = match p2sh_stack.pop() {
|
||||||
|
Some(script::MaybeOwned::Owned(v)) => Script::from_vec(v),
|
||||||
|
Some(script::MaybeOwned::Borrowed(s)) => Script::from_vec(s.to_vec()),
|
||||||
|
None => unreachable!()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if trace.error.is_none() {
|
||||||
|
trace.pubkey_trace = Some(txo.script_pubkey.trace(&mut stack, Some((self, n))));
|
||||||
|
let err = trace.pubkey_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone());
|
||||||
|
err.map(|e| trace.error = Some(Error::OutputScriptFailure(e)));
|
||||||
|
match stack.pop() {
|
||||||
|
Some(v) => {
|
||||||
|
if !read_scriptbool(&v[..]) {
|
||||||
|
trace.error = Some(Error::ScriptReturnedFalse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => { trace.error = Some(Error::ScriptReturnedEmptyStack); }
|
||||||
|
}
|
||||||
|
if trace.error.is_none() && txo.script_pubkey.is_p2sh() {
|
||||||
|
trace.p2sh_trace = Some(p2sh_script.trace(&mut p2sh_stack, Some((self, n))));
|
||||||
|
let err = trace.p2sh_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone());
|
||||||
|
err.map(|e| trace.error = Some(Error::P2shScriptFailure(e)));
|
||||||
|
match p2sh_stack.pop() {
|
||||||
|
Some(v) => {
|
||||||
|
if !read_scriptbool(&v[..]) {
|
||||||
|
trace.error = Some(Error::P2shScriptReturnedFalse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => { trace.error = Some(Error::P2shScriptReturnedEmptyStack); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
None => {
|
||||||
None => { trace.error = Some(Error::ScriptReturnedEmptyStack); }
|
trace.error = Some(Error::InputNotFound(input.prev_hash, input.prev_index));
|
||||||
}
|
|
||||||
if trace.error.is_none() && txo.script_pubkey.is_p2sh() {
|
|
||||||
trace.p2sh_trace = Some(p2sh_script.trace(&mut p2sh_stack, Some((self, n))));
|
|
||||||
let err = trace.p2sh_trace.as_ref().unwrap().error.as_ref().map(|e| e.clone());
|
|
||||||
err.map(|e| trace.error = Some(Error::P2shScriptFailure(e)));
|
|
||||||
match p2sh_stack.pop() {
|
|
||||||
Some(v) => {
|
|
||||||
if !read_scriptbool(&v[..]) {
|
|
||||||
trace.error = Some(Error::P2shScriptReturnedFalse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => { trace.error = Some(Error::P2shScriptReturnedEmptyStack); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
ret.inputs.push(trace);
|
||||||
}
|
}
|
||||||
None => {
|
ret
|
||||||
trace.error = Some(Error::InputNotFound(input.prev_hash, input.prev_index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.inputs.push(trace);
|
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitcoinHash for Transaction {
|
impl BitcoinHash for Transaction {
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||||
use network::serialize::serialize;
|
use network::serialize::serialize;
|
||||||
Sha256dHash::from_data(&serialize(self).unwrap())
|
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(TxIn, prev_hash, prev_index, script_sig, sequence);
|
impl_consensus_encoding!(TxIn, prev_hash, prev_index, script_sig, sequence);
|
||||||
|
@ -305,40 +306,40 @@ impl_consensus_encoding!(Transaction, version, input, output, lock_time);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Transaction, TxIn};
|
use super::{Transaction, TxIn};
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use network::serialize::BitcoinHash;
|
use network::serialize::BitcoinHash;
|
||||||
use network::serialize::deserialize;
|
use network::serialize::deserialize;
|
||||||
use util::misc::hex_bytes;
|
use util::misc::hex_bytes;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_txin() {
|
fn test_txin() {
|
||||||
let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
let txin: io::Result<TxIn> = deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap());
|
||||||
assert!(txin.is_ok());
|
assert!(txin.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transaction() {
|
fn test_transaction() {
|
||||||
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||||
let tx: io::Result<Transaction> = deserialize(hex_tx);
|
let tx: io::Result<Transaction> = deserialize(hex_tx);
|
||||||
assert!(tx.is_ok());
|
assert!(tx.is_ok());
|
||||||
let realtx = tx.unwrap();
|
let realtx = tx.unwrap();
|
||||||
// All these tests aren't really needed because if they fail, the hash check at the end
|
// All these tests aren't really needed because if they fail, the hash check at the end
|
||||||
// will also fail. But these will show you where the failure is so I'll leave them in.
|
// will also fail. But these will show you where the failure is so I'll leave them in.
|
||||||
assert_eq!(realtx.version, 1);
|
assert_eq!(realtx.version, 1);
|
||||||
assert_eq!(realtx.input.len(), 1);
|
assert_eq!(realtx.input.len(), 1);
|
||||||
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
|
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
|
||||||
// as little-endian 256-bit numbers rather than as data strings.
|
// as little-endian 256-bit numbers rather than as data strings.
|
||||||
assert_eq!(realtx.input[0].prev_hash.be_hex_string(),
|
assert_eq!(realtx.input[0].prev_hash.be_hex_string(),
|
||||||
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
|
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
|
||||||
assert_eq!(realtx.input[0].prev_index, 1);
|
assert_eq!(realtx.input[0].prev_index, 1);
|
||||||
assert_eq!(realtx.output.len(), 1);
|
assert_eq!(realtx.output.len(), 1);
|
||||||
assert_eq!(realtx.lock_time, 0);
|
assert_eq!(realtx.lock_time, 0);
|
||||||
|
|
||||||
assert_eq!(realtx.bitcoin_hash().be_hex_string(),
|
assert_eq!(realtx.bitcoin_hash().be_hex_string(),
|
||||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -21,11 +21,11 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash::map::Iter;
|
use std::collections::hash::map::Iter;
|
||||||
use std::hash::SipHasher;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use eventual;
|
||||||
|
use eventual::Async;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
use std::sync::Future;
|
|
||||||
|
|
||||||
use blockdata::transaction::{self, Transaction, TxOut};
|
use blockdata::transaction::{self, Transaction, TxOut};
|
||||||
use blockdata::constants::genesis_block;
|
use blockdata::constants::genesis_block;
|
||||||
|
@ -37,475 +37,469 @@ use util::hash::Sha256dHash;
|
||||||
/// The amount of validation to do when updating the UTXO set
|
/// The amount of validation to do when updating the UTXO set
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
||||||
pub enum ValidationLevel {
|
pub enum ValidationLevel {
|
||||||
/// Blindly update the UTXO set (NOT recommended)
|
/// Blindly update the UTXO set (NOT recommended)
|
||||||
Nothing,
|
Nothing,
|
||||||
/// Check that the blocks are at least in the right order
|
/// Check that the blocks are at least in the right order
|
||||||
Chain,
|
Chain,
|
||||||
/// Check that any inputs are actually txouts in the set
|
/// Check that any inputs are actually txouts in the set
|
||||||
Inputs,
|
Inputs,
|
||||||
/// Execute the scripts and ensure they pass
|
/// Execute the scripts and ensure they pass
|
||||||
Script
|
Script
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error returned from a UTXO set operation
|
/// An error returned from a UTXO set operation
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// prevhash of the new block is not the hash of the old block (expected, actual)
|
/// prevhash of the new block is not the hash of the old block (expected, actual)
|
||||||
BadPrevHash(Sha256dHash, Sha256dHash),
|
BadPrevHash(Sha256dHash, Sha256dHash),
|
||||||
/// A TXID was duplicated
|
/// A TXID was duplicated
|
||||||
DuplicatedTxid(Sha256dHash),
|
DuplicatedTxid(Sha256dHash),
|
||||||
/// A tx was invalid (txid, error)
|
/// A tx was invalid (txid, error)
|
||||||
InvalidTx(Sha256dHash, transaction::Error),
|
InvalidTx(Sha256dHash, transaction::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UtxoNode {
|
struct UtxoNode {
|
||||||
/// Blockheight at which this UTXO appeared in the blockchain
|
/// Blockheight at which this UTXO appeared in the blockchain
|
||||||
height: u32,
|
height: u32,
|
||||||
/// Vector of outputs; None indicates a nonexistent or already spent output
|
/// Vector of outputs; None indicates a nonexistent or already spent output
|
||||||
outputs: Box<[Option<TxOut>]>
|
outputs: Box<[Option<TxOut>]>
|
||||||
}
|
}
|
||||||
impl_consensus_encoding!(UtxoNode, height, outputs);
|
impl_consensus_encoding!(UtxoNode, height, outputs);
|
||||||
|
|
||||||
/// An iterator over UTXOs
|
/// An iterator over UTXOs
|
||||||
pub struct UtxoIterator<'a> {
|
pub struct UtxoIterator<'a> {
|
||||||
tx_iter: Iter<'a, Sha256dHash, UtxoNode>,
|
tx_iter: Iter<'a, Sha256dHash, UtxoNode>,
|
||||||
current_key: Sha256dHash,
|
current_key: Sha256dHash,
|
||||||
current: Option<&'a UtxoNode>,
|
current: Option<&'a UtxoNode>,
|
||||||
tx_index: u32
|
tx_index: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for UtxoIterator<'a> {
|
impl<'a> Iterator for UtxoIterator<'a> {
|
||||||
type Item = (Sha256dHash, u32, &'a TxOut, u32);
|
type Item = (Sha256dHash, u32, &'a TxOut, u32);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> {
|
fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> {
|
||||||
while self.current.is_some() {
|
while let Some(current) = self.current {
|
||||||
let current = &self.current.unwrap().outputs;
|
while self.tx_index < current.outputs.len() as u32 {
|
||||||
while self.tx_index < current.len() as u32 {
|
self.tx_index += 1;
|
||||||
self.tx_index += 1;
|
if let Some(ref cur) = current.outputs[self.tx_index as usize - 1] {
|
||||||
if unsafe { current.get(self.tx_index as usize - 1) }.is_some() {
|
return Some((self.current_key, self.tx_index,
|
||||||
return Some((self.current_key,
|
cur, current.height));
|
||||||
self.tx_index,
|
}
|
||||||
unsafe { current.get(self.tx_index as usize - 1) }.as_ref().unwrap(),
|
}
|
||||||
self.current.unwrap().height));
|
match self.tx_iter.next() {
|
||||||
|
Some((&x, y)) => {
|
||||||
|
self.tx_index = 0;
|
||||||
|
self.current_key = x;
|
||||||
|
self.current = Some(y);
|
||||||
|
}
|
||||||
|
None => { self.current = None; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return None;
|
||||||
match self.tx_iter.next() {
|
|
||||||
Some((&x, y)) => {
|
|
||||||
self.tx_index = 0;
|
|
||||||
self.current_key = x;
|
|
||||||
self.current = Some(y);
|
|
||||||
}
|
|
||||||
None => { self.current = None; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The UTXO set
|
/// The UTXO set
|
||||||
pub struct UtxoSet {
|
pub struct UtxoSet {
|
||||||
table: HashMap<Sha256dHash, UtxoNode, SipHasher>,
|
table: HashMap<Sha256dHash, UtxoNode>,
|
||||||
last_hash: Sha256dHash,
|
last_hash: Sha256dHash,
|
||||||
// A circular buffer of deleted utxos, grouped by block
|
// A circular buffer of deleted utxos, grouped by block
|
||||||
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
|
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
|
||||||
// The last index into the above buffer that was assigned to
|
// The last index into the above buffer that was assigned to
|
||||||
spent_idx: u64,
|
spent_idx: u64,
|
||||||
n_utxos: u64,
|
n_utxos: u64,
|
||||||
n_pruned: u64
|
n_pruned: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, n_pruned, spent_txos, spent_idx, table);
|
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, n_pruned, spent_txos, spent_idx, table);
|
||||||
|
|
||||||
impl UtxoSet {
|
impl UtxoSet {
|
||||||
/// Constructs a new UTXO set
|
/// Constructs a new UTXO set
|
||||||
pub fn new(network: Network, rewind_limit: usize) -> UtxoSet {
|
pub fn new(network: Network, rewind_limit: usize) -> UtxoSet {
|
||||||
// There is in fact a transaction in the genesis block, but the Bitcoin
|
// There is in fact a transaction in the genesis block, but the Bitcoin
|
||||||
// reference client does not add its sole output to the UTXO set. We
|
// reference client does not add its sole output to the UTXO set. We
|
||||||
// must follow suit, otherwise we will accept a transaction spending it
|
// must follow suit, otherwise we will accept a transaction spending it
|
||||||
// while the reference client won't, causing us to fork off the network.
|
// while the reference client won't, causing us to fork off the network.
|
||||||
UtxoSet {
|
UtxoSet {
|
||||||
table: HashMap::with_hasher(SipHasher::new()),
|
table: HashMap::new(),
|
||||||
last_hash: genesis_block(network).header.bitcoin_hash(),
|
last_hash: genesis_block(network).header.bitcoin_hash(),
|
||||||
spent_txos: Vec::from_elem(rewind_limit, vec![]),
|
spent_txos: vec![vec![]; rewind_limit],
|
||||||
spent_idx: 0,
|
spent_idx: 0,
|
||||||
n_utxos: 0,
|
n_utxos: 0,
|
||||||
n_pruned: 0
|
n_pruned: 0
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add all the UTXOs of a transaction to the set
|
|
||||||
fn add_utxos(&mut self, tx: &Transaction, height: u32) -> Option<UtxoNode> {
|
|
||||||
let txid = tx.bitcoin_hash();
|
|
||||||
// Locate node if it's already there
|
|
||||||
let new_node = unsafe {
|
|
||||||
let mut new_node = Vec::with_capacity(tx.output.len());
|
|
||||||
for txo in tx.output.iter() {
|
|
||||||
// Unsafe since we are not uninitializing the old data in the vector
|
|
||||||
if txo.script_pubkey.is_provably_unspendable() {
|
|
||||||
new_node.push(None);
|
|
||||||
self.n_utxos -= 1;
|
|
||||||
self.n_pruned += 1;
|
|
||||||
} else {
|
|
||||||
new_node.push(Some(txo.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
UtxoNode { outputs: new_node.into_boxed_slice(), height: height }
|
|
||||||
};
|
|
||||||
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
|
|
||||||
// and the other comments in this file referring to it)
|
|
||||||
let ret = self.table.swap(txid, new_node.into_boxed_slice());
|
|
||||||
if ret.is_none() {
|
|
||||||
self.n_utxos += tx.output.len() as u64;
|
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a UTXO from the set and return it
|
/// Add all the UTXOs of a transaction to the set
|
||||||
fn take_utxo(&mut self, txid: Sha256dHash, vout: u32) -> Option<(u32, TxOut)> {
|
fn add_utxos(&mut self, tx: &Transaction, height: u32) -> Option<UtxoNode> {
|
||||||
// This whole function has awkward scoping thx to lexical borrow scoping :(
|
let txid = tx.bitcoin_hash();
|
||||||
let (height, ret, should_delete) = {
|
// Locate node if it's already there
|
||||||
// Locate the UTXO, failing if not found
|
let new_node = unsafe {
|
||||||
let node = match self.table.find_mut(&txid) {
|
let mut new_node = Vec::with_capacity(tx.output.len());
|
||||||
Some(node) => node,
|
for txo in tx.output.iter() {
|
||||||
None => return None
|
// Unsafe since we are not uninitializing the old data in the vector
|
||||||
};
|
if txo.script_pubkey.is_provably_unspendable() {
|
||||||
|
new_node.push(None);
|
||||||
|
self.n_utxos -= 1;
|
||||||
|
self.n_pruned += 1;
|
||||||
|
} else {
|
||||||
|
new_node.push(Some(txo.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UtxoNode { outputs: new_node.into_boxed_slice(), height: height }
|
||||||
|
};
|
||||||
|
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
|
||||||
|
// and the other comments in this file referring to it)
|
||||||
|
let ret = self.table.insert(txid, new_node);
|
||||||
|
if ret.is_none() {
|
||||||
|
self.n_utxos += tx.output.len() as u64;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
let ret = {
|
/// Remove a UTXO from the set and return it
|
||||||
|
fn take_utxo(&mut self, txid: Sha256dHash, vout: u32) -> Option<(u32, TxOut)> {
|
||||||
|
// This whole function has awkward scoping thx to lexical borrow scoping :(
|
||||||
|
let (height, ret, should_delete) = {
|
||||||
|
// Locate the UTXO, failing if not found
|
||||||
|
let node = match self.table.get_mut(&txid) {
|
||||||
|
Some(node) => node,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
// Check that this specific output is there
|
||||||
|
if vout as usize >= node.outputs.len() { return None; }
|
||||||
|
let replace = &mut node.outputs[vout as usize];
|
||||||
|
replace.take()
|
||||||
|
};
|
||||||
|
|
||||||
|
let should_delete = node.outputs.iter().filter(|slot| slot.is_some()).count() == 0;
|
||||||
|
(node.height, ret, should_delete)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete the whole node if it is no longer being used
|
||||||
|
if should_delete {
|
||||||
|
self.table.remove(&txid);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.n_utxos -= if ret.is_some() { 1 } else { 0 };
|
||||||
|
ret.map(|o| (height, o))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to a UTXO in the set
|
||||||
|
pub fn get_utxo<'a>(&'a self, txid: Sha256dHash, vout: u32) -> Option<(usize, &'a TxOut)> {
|
||||||
|
// Locate the UTXO, failing if not found
|
||||||
|
let node = match self.table.get(&txid) {
|
||||||
|
Some(node) => node,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
// Check that this specific output is there
|
// Check that this specific output is there
|
||||||
if vout as usize >= node.outputs.len() { return None; }
|
if vout as usize >= node.outputs.len() { return None; }
|
||||||
let replace = unsafe { node.outputs.get_mut(vout as usize) };
|
let replace = node.outputs[vout as usize];
|
||||||
replace.take()
|
Some((node.height as usize, replace.as_ref().unwrap()))
|
||||||
};
|
|
||||||
|
|
||||||
let should_delete = node.outputs.iter().filter(|slot| slot.is_some()).count() == 0;
|
|
||||||
(node.height, ret, should_delete)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Delete the whole node if it is no longer being used
|
|
||||||
if should_delete {
|
|
||||||
self.table.remove(&txid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.n_utxos -= if ret.is_some() { 1 } else { 0 };
|
/// Apply the transactions contained in a block
|
||||||
ret.map(|o| (height, o))
|
pub fn update(&mut self, block: &Block, blockheight: usize, validation: ValidationLevel)
|
||||||
}
|
-> Result<(), Error> {
|
||||||
|
// Make sure we are extending the UTXO set in order
|
||||||
/// Get a reference to a UTXO in the set
|
if validation >= ValidationLevel::Chain &&
|
||||||
pub fn get_utxo<'a>(&'a self, txid: Sha256dHash, vout: u32) -> Option<(usize, &'a TxOut)> {
|
self.last_hash != block.header.prev_blockhash {
|
||||||
// Locate the UTXO, failing if not found
|
return Err(Error::BadPrevHash(self.last_hash, block.header.prev_blockhash));
|
||||||
let node = match self.table.find(&txid) {
|
|
||||||
Some(node) => node,
|
|
||||||
None => return None
|
|
||||||
};
|
|
||||||
// Check that this specific output is there
|
|
||||||
if vout as usize >= node.outputs.len() { return None; }
|
|
||||||
let replace = unsafe { node.outputs.get(vout as usize) };
|
|
||||||
Some((node.height as usize, replace.as_ref().unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply the transactions contained in a block
|
|
||||||
pub fn update(&mut self, block: &Block, blockheight: usize, validation: ValidationLevel)
|
|
||||||
-> Result<(), Error> {
|
|
||||||
// Make sure we are extending the UTXO set in order
|
|
||||||
if validation >= ValidationLevel::Chain &&
|
|
||||||
self.last_hash != block.header.prev_blockhash {
|
|
||||||
return Err(Error::BadPrevHash(self.last_hash, block.header.prev_blockhash));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the next hash immediately so that if anything goes wrong,
|
|
||||||
// we can rewind from the point that we're at.
|
|
||||||
self.last_hash = block.header.bitcoin_hash();
|
|
||||||
let spent_idx = self.spent_idx as usize;
|
|
||||||
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
|
|
||||||
self.spent_txos.get_mut(spent_idx).clear();
|
|
||||||
|
|
||||||
// Add all the utxos so that we can have chained transactions within the
|
|
||||||
// same block. (Note that Bitcoin requires chained transactions to be in
|
|
||||||
// the correct order, which we do not check, so we are minorly too permissive.
|
|
||||||
// TODO this is a consensus bug.)
|
|
||||||
for tx in block.txdata.iter() {
|
|
||||||
let txid = tx.bitcoin_hash();
|
|
||||||
// Add outputs -- add_utxos returns the original transaction if this is a dupe.
|
|
||||||
// Note that this can only happen with coinbases, and in this case the block
|
|
||||||
// is invalid, -except- for two historic blocks which appeared in the
|
|
||||||
// blockchain before the dupes were noticed.
|
|
||||||
// See bitcoind commit `ab91bf39` and BIP30.
|
|
||||||
match self.add_utxos(tx, blockheight as u32) {
|
|
||||||
Some(mut replace) => {
|
|
||||||
let blockhash = block.header.bitcoin_hash().be_hex_string();
|
|
||||||
if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
|
|
||||||
blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
|
|
||||||
// For these specific blocks, overwrite the old UTXOs.
|
|
||||||
// (Actually add_utxos() already did this, so we do nothing.)
|
|
||||||
} else {
|
|
||||||
// Otherwise put the replaced txouts into the `deleted` cache
|
|
||||||
// so that rewind will put them back.
|
|
||||||
self.spent_txos.get_mut(spent_idx).reserve_additional(replace.outputs.len());
|
|
||||||
for (n, input) in replace.outputs.iter_mut().enumerate() {
|
|
||||||
match input.take() {
|
|
||||||
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(((txid, n as u32), (replace.height, txo))); }
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise fail the block
|
|
||||||
self.rewind(block);
|
|
||||||
return Err(Error::DuplicatedTxid(txid));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Didn't replace anything? Good.
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are validating scripts, do all that now in parallel
|
// Set the next hash immediately so that if anything goes wrong,
|
||||||
if validation >= ValidationLevel::Script {
|
// we can rewind from the point that we're at.
|
||||||
let mut future_vec = Vec::with_capacity(block.txdata.len() - 1);
|
self.last_hash = block.header.bitcoin_hash();
|
||||||
// skip the genesis since we don't validate this script. (TODO this might
|
let spent_idx = self.spent_idx as usize;
|
||||||
// be a consensus bug since we don't even check that the opcodes make sense.)
|
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
|
||||||
let n_threads = cmp::min(block.txdata.len() - 1, num_cpus::get());
|
(&mut self.spent_txos[spent_idx]).clear();
|
||||||
for j in 0..n_threads {
|
|
||||||
let n_elems = block.txdata.len() - 1;
|
|
||||||
let start = 1 + j * n_elems / n_threads;
|
|
||||||
let end = cmp::min(n_elems, 1 + (j + 1) * n_elems / n_threads);
|
|
||||||
|
|
||||||
let s = self as *mut _ as *const UtxoSet;
|
// Add all the utxos so that we can have chained transactions within the
|
||||||
let txes = &block.txdata as *const _;
|
// same block. (Note that Bitcoin requires chained transactions to be in
|
||||||
future_vec.push(Future::spawn(move || {
|
// the correct order, which we do not check, so we are minorly too permissive.
|
||||||
let txes = unsafe {&*txes};
|
// TODO this is a consensus bug.)
|
||||||
for tx in txes.slice(start, end).iter() {
|
for tx in block.txdata.iter() {
|
||||||
match tx.validate(unsafe {&*s}) {
|
let txid = tx.bitcoin_hash();
|
||||||
Ok(_) => {},
|
// Add outputs -- add_utxos returns the original transaction if this is a dupe.
|
||||||
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); }
|
// Note that this can only happen with coinbases, and in this case the block
|
||||||
}
|
// is invalid, -except- for two historic blocks which appeared in the
|
||||||
}
|
// blockchain before the dupes were noticed.
|
||||||
Ok(())
|
// See bitcoind commit `ab91bf39` and BIP30.
|
||||||
}));
|
match self.add_utxos(tx, blockheight as u32) {
|
||||||
}
|
Some(mut replace) => {
|
||||||
// Return the last error since we need to finish every future before
|
let blockhash = block.header.bitcoin_hash().be_hex_string();
|
||||||
// leaving this function, and given that, it's easier to return the last.
|
if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
|
||||||
let mut last_error = Ok(());
|
blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
|
||||||
for res in future_vec.iter_mut().map(|f| f.get()) {
|
// For these specific blocks, overwrite the old UTXOs.
|
||||||
if res.is_err() {
|
// (Actually add_utxos() already did this, so we do nothing.)
|
||||||
last_error = res;
|
} else {
|
||||||
}
|
// Otherwise put the replaced txouts into the `deleted` cache
|
||||||
}
|
// so that rewind will put them back.
|
||||||
if last_error.is_err() {
|
(&mut self.spent_txos[spent_idx]).reserve(replace.outputs.len());
|
||||||
return last_error;
|
for (n, input) in replace.outputs.iter_mut().enumerate() {
|
||||||
}
|
match input.take() {
|
||||||
}
|
Some(txo) => { (&mut self.spent_txos[spent_idx]).push(((txid, n as u32), (replace.height, txo))); }
|
||||||
|
None => {}
|
||||||
for tx in block.txdata.iter().skip(1) {
|
}
|
||||||
let txid = tx.bitcoin_hash();
|
}
|
||||||
// Put the removed utxos into the stxo cache, in case we need to rewind
|
// Otherwise fail the block
|
||||||
self.spent_txos.get_mut(spent_idx).reserve_additional(tx.input.len());
|
self.rewind(block);
|
||||||
for (n, input) in tx.input.iter().enumerate() {
|
return Err(Error::DuplicatedTxid(txid));
|
||||||
let taken = self.take_utxo(input.prev_hash, input.prev_index);
|
|
||||||
match taken {
|
|
||||||
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(((txid, n as u32), txo)); }
|
|
||||||
None => {
|
|
||||||
if validation >= ValidationLevel::Inputs {
|
|
||||||
self.rewind(block);
|
|
||||||
return Err(Error::InvalidTx(txid,
|
|
||||||
transaction::Error::InputNotFound(input.prev_hash, input.prev_index)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we made it here, success!
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unapply the transactions contained in a block
|
|
||||||
pub fn rewind(&mut self, block: &Block) -> bool {
|
|
||||||
// Make sure we are rewinding the latest block
|
|
||||||
if self.last_hash != block.header.bitcoin_hash() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We deliberately do no error checking here, since we may be rewinding
|
|
||||||
// from halfway through the new block addition, in which case many of
|
|
||||||
// the utxos we try to remove may be missing; the ones we try to add,
|
|
||||||
// we stored ourselves when we removed them, so they won't be unaddable
|
|
||||||
// for any reason.
|
|
||||||
// Plus we don't care too much about efficiency, not many blocks should
|
|
||||||
// get rewound.
|
|
||||||
|
|
||||||
// Delete added txouts
|
|
||||||
let mut skipped_genesis = false;
|
|
||||||
for tx in block.txdata.iter() {
|
|
||||||
let txhash = tx.bitcoin_hash();
|
|
||||||
for n in 0..tx.output.len() {
|
|
||||||
// Just bomb out the whole transaction
|
|
||||||
// TODO: this does not conform to BIP30: if a duplicate txid occurs,
|
|
||||||
// the block will be (rightly) rejected, causing it to be
|
|
||||||
// unwound. But when we get here, we can't see the duplicate,
|
|
||||||
// so we wind up deleting the old txid! This is very bad, and
|
|
||||||
// if it occurs, an affected user will have to recreate his
|
|
||||||
// whole UTXO index to get the original txid back.
|
|
||||||
self.take_utxo(txhash, n as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read deleted txouts
|
|
||||||
if skipped_genesis {
|
|
||||||
let mut extract_vec = vec![];
|
|
||||||
mem::swap(&mut extract_vec, self.spent_txos.get_mut(self.spent_idx as usize));
|
|
||||||
for ((txid, n), (height, txo)) in extract_vec.into_iter() {
|
|
||||||
// Remove the tx's utxo list and patch the txo into place
|
|
||||||
let new_node =
|
|
||||||
match self.table.pop(&txid) {
|
|
||||||
Some(mut node) => {
|
|
||||||
node.outputs[n as usize] = Some(txo);
|
|
||||||
node
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
unsafe {
|
|
||||||
let mut thinvec = Vec::with_capacity(n + 1);
|
|
||||||
for _ in 0..n {
|
|
||||||
thinvec.push(None);
|
|
||||||
}
|
}
|
||||||
thinvec.push(Some(txo));
|
|
||||||
UtxoNode { outputs: thinvec.into_boxed_slice(), height: height }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
// Didn't replace anything? Good.
|
||||||
// Ram it back into the tree
|
None => {}
|
||||||
self.table.insert(txid, new_node);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
skipped_genesis = true;
|
// If we are validating scripts, do all that now in parallel
|
||||||
|
if validation >= ValidationLevel::Script {
|
||||||
|
let mut future_vec = Vec::with_capacity(block.txdata.len() - 1);
|
||||||
|
// skip the genesis since we don't validate this script. (TODO this might
|
||||||
|
// be a consensus bug since we don't even check that the opcodes make sense.)
|
||||||
|
let n_threads = cmp::min(block.txdata.len() - 1, num_cpus::get());
|
||||||
|
for j in 0..n_threads {
|
||||||
|
let n_elems = block.txdata.len() - 1;
|
||||||
|
let start = 1 + j * n_elems / n_threads;
|
||||||
|
let end = cmp::min(n_elems, 1 + (j + 1) * n_elems / n_threads);
|
||||||
|
|
||||||
|
// WARNING: we are asserting that these variables will outlive the Futures;
|
||||||
|
// this means that we need to await all Futures before leaving the
|
||||||
|
// function or else risk use-after-free in the async threads.
|
||||||
|
let static_txes = unsafe { &*(&block.txdata as *const Vec<Transaction>) };
|
||||||
|
let static_self = unsafe { &*(self as *const UtxoSet) };
|
||||||
|
future_vec.push(eventual::Future::spawn(move || {
|
||||||
|
for tx in static_txes[start..end].iter() {
|
||||||
|
match tx.validate(static_self) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// Return the last error since we need to finish every future before
|
||||||
|
// leaving this function, and given that, it's easier to return the last.
|
||||||
|
let mut last_err = Ok(());
|
||||||
|
for res in future_vec.iter_mut().map(|f| f.await().unwrap()) {
|
||||||
|
if res.is_err() { last_err = res; }
|
||||||
|
}
|
||||||
|
if last_err.is_err() { return last_err; }
|
||||||
|
}
|
||||||
|
|
||||||
|
for tx in block.txdata.iter().skip(1) {
|
||||||
|
let txid = tx.bitcoin_hash();
|
||||||
|
// Put the removed utxos into the stxo cache, in case we need to rewind
|
||||||
|
(&self.spent_txos[spent_idx]).reserve(tx.input.len());
|
||||||
|
for (n, input) in tx.input.iter().enumerate() {
|
||||||
|
let taken = self.take_utxo(input.prev_hash, input.prev_index);
|
||||||
|
match taken {
|
||||||
|
Some(txo) => { (&mut self.spent_txos[spent_idx]).push(((txid, n as u32), txo)); }
|
||||||
|
None => {
|
||||||
|
if validation >= ValidationLevel::Inputs {
|
||||||
|
self.rewind(block);
|
||||||
|
return Err(Error::InvalidTx(txid,
|
||||||
|
transaction::Error::InputNotFound(input.prev_hash, input.prev_index)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we made it here, success!
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrement mod the spent txo cache size
|
/// Unapply the transactions contained in a block
|
||||||
self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) %
|
pub fn rewind(&mut self, block: &Block) -> bool {
|
||||||
self.spent_txos.len() as u64;
|
// Make sure we are rewinding the latest block
|
||||||
self.last_hash = block.header.prev_blockhash;
|
if self.last_hash != block.header.bitcoin_hash() {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the hash of the last block added to the utxo set
|
// We deliberately do no error checking here, since we may be rewinding
|
||||||
pub fn last_hash(&self) -> Sha256dHash {
|
// from halfway through the new block addition, in which case many of
|
||||||
self.last_hash
|
// the utxos we try to remove may be missing; the ones we try to add,
|
||||||
}
|
// we stored ourselves when we removed them, so they won't be unaddable
|
||||||
|
// for any reason.
|
||||||
|
// Plus we don't care too much about efficiency, not many blocks should
|
||||||
|
// get rewound.
|
||||||
|
|
||||||
/// Get the number of UTXOs in the set
|
// Delete added txouts
|
||||||
pub fn n_utxos(&self) -> usize {
|
let mut skipped_genesis = false;
|
||||||
self.n_utxos as usize
|
for tx in block.txdata.iter() {
|
||||||
}
|
let txhash = tx.bitcoin_hash();
|
||||||
|
for n in 0..tx.output.len() {
|
||||||
|
// Just bomb out the whole transaction
|
||||||
|
// TODO: this does not conform to BIP30: if a duplicate txid occurs,
|
||||||
|
// the block will be (rightly) rejected, causing it to be
|
||||||
|
// unwound. But when we get here, we can't see the duplicate,
|
||||||
|
// so we wind up deleting the old txid! This is very bad, and
|
||||||
|
// if it occurs, an affected user will have to recreate his
|
||||||
|
// whole UTXO index to get the original txid back.
|
||||||
|
self.take_utxo(txhash, n as u32);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the number of UTXOs ever pruned from the set (this is not updated
|
// Read deleted txouts
|
||||||
/// during reorgs, so it may return a higher number than is realistic).
|
if skipped_genesis {
|
||||||
pub fn n_pruned(&self) -> usize {
|
let mut extract_vec = vec![];
|
||||||
self.n_pruned as usize
|
mem::swap(&mut extract_vec, (&mut self.spent_txos[self.spent_idx as usize]));
|
||||||
}
|
for ((txid, n), (height, txo)) in extract_vec.into_iter() {
|
||||||
|
// Remove the tx's utxo list and patch the txo into place
|
||||||
|
let new_node = match self.table.remove(&txid) {
|
||||||
|
Some(mut node) => {
|
||||||
|
node.outputs[n as usize] = Some(txo);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
unsafe {
|
||||||
|
let mut thinvec = Vec::with_capacity(n as usize + 1);
|
||||||
|
for _ in 0..n {
|
||||||
|
thinvec.push(None);
|
||||||
|
}
|
||||||
|
thinvec.push(Some(txo));
|
||||||
|
UtxoNode { outputs: thinvec.into_boxed_slice(), height: height }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Ram it back into the tree
|
||||||
|
self.table.insert(txid, new_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skipped_genesis = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get an iterator over all UTXOs
|
// Decrement mod the spent txo cache size
|
||||||
pub fn iter<'a>(&'a self) -> UtxoIterator<'a> {
|
self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) %
|
||||||
let mut iter = self.table.iter();
|
self.spent_txos.len() as u64;
|
||||||
let first = iter.next();
|
self.last_hash = block.header.prev_blockhash;
|
||||||
match first {
|
return true;
|
||||||
Some((&key, val)) => UtxoIterator {
|
}
|
||||||
current_key: key,
|
|
||||||
current: Some(val),
|
/// Get the hash of the last block added to the utxo set
|
||||||
tx_iter: iter,
|
pub fn last_hash(&self) -> Sha256dHash {
|
||||||
tx_index: 0
|
self.last_hash
|
||||||
},
|
}
|
||||||
None => UtxoIterator {
|
|
||||||
current_key: Default::default(),
|
/// Get the number of UTXOs in the set
|
||||||
current: None,
|
pub fn n_utxos(&self) -> usize {
|
||||||
tx_iter: iter,
|
self.n_utxos as usize
|
||||||
tx_index: 0
|
}
|
||||||
|
|
||||||
|
/// Get the number of UTXOs ever pruned from the set (this is not updated
|
||||||
|
/// during reorgs, so it may return a higher number than is realistic).
|
||||||
|
pub fn n_pruned(&self) -> usize {
|
||||||
|
self.n_pruned as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an iterator over all UTXOs
|
||||||
|
pub fn iter<'a>(&'a self) -> UtxoIterator<'a> {
|
||||||
|
let mut iter = self.table.iter();
|
||||||
|
let first = iter.next();
|
||||||
|
match first {
|
||||||
|
Some((&key, val)) => UtxoIterator {
|
||||||
|
current_key: key,
|
||||||
|
current: Some(val),
|
||||||
|
tx_iter: iter,
|
||||||
|
tx_index: 0
|
||||||
|
},
|
||||||
|
None => UtxoIterator {
|
||||||
|
current_key: Default::default(),
|
||||||
|
current: None,
|
||||||
|
tx_iter: iter,
|
||||||
|
tx_index: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::prelude::*;
|
use std::prelude::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use super::{UtxoSet, ValidationLevel};
|
use super::{UtxoSet, ValidationLevel};
|
||||||
|
|
||||||
use blockdata::block::Block;
|
use blockdata::block::Block;
|
||||||
use network::constants::Network::Bitcoin;
|
use network::constants::Network::Bitcoin;
|
||||||
use network::serialize::{BitcoinHash, deserialize, serialize};
|
use network::serialize::{BitcoinHash, deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn utxoset_serialize_test() {
|
fn utxoset_serialize_test() {
|
||||||
let mut empty_set = UtxoSet::new(Bitcoin, 100);
|
let mut empty_set = UtxoSet::new(Bitcoin, 100);
|
||||||
|
|
||||||
let new_block: Block = deserialize("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap()).unwrap();
|
let new_block: Block = deserialize("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap()).unwrap();
|
||||||
|
|
||||||
// Make sure we can't add the block directly, since we are missing the inputs
|
// Make sure we can't add the block directly, since we are missing the inputs
|
||||||
assert!(empty_set.update(&new_block, 1, ValidationLevel::Inputs).is_err());
|
assert!(empty_set.update(&new_block, 1, ValidationLevel::Inputs).is_err());
|
||||||
assert_eq!(empty_set.n_utxos(), 0);
|
assert_eq!(empty_set.n_utxos(), 0);
|
||||||
// Add the block manually so that we'll have some UTXOs for the rest of the test
|
// Add the block manually so that we'll have some UTXOs for the rest of the test
|
||||||
for tx in new_block.txdata.iter() {
|
for tx in new_block.txdata.iter() {
|
||||||
empty_set.add_utxos(tx, 1);
|
empty_set.add_utxos(tx, 1);
|
||||||
|
}
|
||||||
|
empty_set.last_hash = new_block.header.bitcoin_hash();
|
||||||
|
|
||||||
|
// Check that all the UTXOs were added
|
||||||
|
assert_eq!(empty_set.n_utxos(), 2);
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.bitcoin_hash();
|
||||||
|
for (n, out) in tx.output.iter().enumerate() {
|
||||||
|
let n = n as u32;
|
||||||
|
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check again that we can't add the block, and that this doesn't mess up the
|
||||||
|
// existing UTXOs
|
||||||
|
assert!(empty_set.update(&new_block, 2, ValidationLevel::Inputs).is_err());
|
||||||
|
assert_eq!(empty_set.n_utxos(), 2);
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.bitcoin_hash();
|
||||||
|
for (n, out) in tx.output.iter().enumerate() {
|
||||||
|
let n = n as u32;
|
||||||
|
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize/deserialize the resulting UTXO set
|
||||||
|
let serial = serialize(&empty_set).unwrap();
|
||||||
|
|
||||||
|
let deserial: io::Result<UtxoSet> = deserialize(serial.clone());
|
||||||
|
assert!(deserial.is_ok());
|
||||||
|
|
||||||
|
// Check that all outputs are there
|
||||||
|
let mut read_set = deserial.unwrap();
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.bitcoin_hash();
|
||||||
|
|
||||||
|
for (n, out) in tx.output.iter().enumerate() {
|
||||||
|
let n = n as u32;
|
||||||
|
// Try taking non-existent UTXO
|
||||||
|
assert_eq!(read_set.take_utxo(hash, 100 + n), None);
|
||||||
|
// Check take of real UTXO
|
||||||
|
let ret = read_set.take_utxo(hash, n);
|
||||||
|
assert_eq!(ret, Some((1, out.clone())));
|
||||||
|
// Try double-take
|
||||||
|
assert_eq!(read_set.take_utxo(hash, n), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let deserial_again: io::Result<UtxoSet> = deserialize(serial);
|
||||||
|
let mut read_again = deserial_again.unwrap();
|
||||||
|
assert!(read_again.rewind(&new_block));
|
||||||
|
assert_eq!(read_again.n_utxos(), 0);
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.bitcoin_hash();
|
||||||
|
|
||||||
|
for n in 0..tx.output.len() {
|
||||||
|
let n = n as u32;
|
||||||
|
let ret = read_again.take_utxo(hash, n);
|
||||||
|
assert_eq!(ret, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
empty_set.last_hash = new_block.header.bitcoin_hash();
|
|
||||||
|
|
||||||
// Check that all the UTXOs were added
|
|
||||||
assert_eq!(empty_set.n_utxos(), 2);
|
|
||||||
for tx in new_block.txdata.iter() {
|
|
||||||
let hash = tx.bitcoin_hash();
|
|
||||||
for (n, out) in tx.output.iter().enumerate() {
|
|
||||||
let n = n as u32;
|
|
||||||
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check again that we can't add the block, and that this doesn't mess up the
|
|
||||||
// existing UTXOs
|
|
||||||
assert!(empty_set.update(&new_block, 2, ValidationLevel::Inputs).is_err());
|
|
||||||
assert_eq!(empty_set.n_utxos(), 2);
|
|
||||||
for tx in new_block.txdata.iter() {
|
|
||||||
let hash = tx.bitcoin_hash();
|
|
||||||
for (n, out) in tx.output.iter().enumerate() {
|
|
||||||
let n = n as u32;
|
|
||||||
assert_eq!(empty_set.get_utxo(hash, n), Some((1, &out.clone())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize/deserialize the resulting UTXO set
|
|
||||||
let serial = serialize(&empty_set).unwrap();
|
|
||||||
|
|
||||||
let deserial: io::Result<UtxoSet> = deserialize(serial.clone());
|
|
||||||
assert!(deserial.is_ok());
|
|
||||||
|
|
||||||
// Check that all outputs are there
|
|
||||||
let mut read_set = deserial.unwrap();
|
|
||||||
for tx in new_block.txdata.iter() {
|
|
||||||
let hash = tx.bitcoin_hash();
|
|
||||||
|
|
||||||
for (n, out) in tx.output.iter().enumerate() {
|
|
||||||
let n = n as u32;
|
|
||||||
// Try taking non-existent UTXO
|
|
||||||
assert_eq!(read_set.take_utxo(hash, 100 + n), None);
|
|
||||||
// Check take of real UTXO
|
|
||||||
let ret = read_set.take_utxo(hash, n);
|
|
||||||
assert_eq!(ret, Some((1, out.clone())));
|
|
||||||
// Try double-take
|
|
||||||
assert_eq!(read_set.take_utxo(hash, n), None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let deserial_again: io::Result<UtxoSet> = deserialize(serial);
|
|
||||||
let mut read_again = deserial_again.unwrap();
|
|
||||||
assert!(read_again.rewind(&new_block));
|
|
||||||
assert_eq!(read_again.n_utxos(), 0);
|
|
||||||
for tx in new_block.txdata.iter() {
|
|
||||||
let hash = tx.bitcoin_hash();
|
|
||||||
|
|
||||||
for n in 0..tx.output.len() {
|
|
||||||
let n = n as u32;
|
|
||||||
let ret = read_again.take_utxo(hash, n);
|
|
||||||
assert_eq!(ret, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -99,45 +99,7 @@ macro_rules! impl_array_newtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
|
impl_index_newtype!($thing, $ty);
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
|
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
|
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
|
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for $thing {
|
impl PartialEq for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -234,6 +196,47 @@ macro_rules! impl_array_newtype_show {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_index_newtype {
|
||||||
|
($thing:ident, $ty:ty) => {
|
||||||
|
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
|
||||||
|
&self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! display_from_debug {
|
macro_rules! display_from_debug {
|
||||||
($thing:ident) => {
|
($thing:ident) => {
|
||||||
impl ::std::fmt::Display for $thing {
|
impl ::std::fmt::Display for $thing {
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate collections;
|
extern crate collections;
|
||||||
|
extern crate eventual;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate rustc_serialize as serialize;
|
extern crate rustc_serialize as serialize;
|
||||||
|
|
227
src/macros.rs
227
src/macros.rs
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -18,128 +18,127 @@
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! nu_select {
|
macro_rules! nu_select {
|
||||||
($($name:pat = $rx:expr => $code:expr),+) => ({
|
($($name:pat = $rx:expr => $code:expr),+) => ({
|
||||||
nu_select!{ $($name = $rx, recv => $code),+ }
|
nu_select!{ $($name = $rx, recv => $code),+ }
|
||||||
});
|
});
|
||||||
($($name:pat = $rx:expr, $meth:ident => $code:expr),+) => ({
|
($($name:pat = $rx:expr, $meth:ident => $code:expr),+) => ({
|
||||||
use rustrt::local::Local;
|
use rustrt::local::Local;
|
||||||
use rustrt::task::Task;
|
use rustrt::task::Task;
|
||||||
use sync::comm::Packet;
|
use sync::comm::Packet;
|
||||||
|
|
||||||
// Is anything already ready to receive? Grab it without waiting.
|
// Is anything already ready to receive? Grab it without waiting.
|
||||||
$(
|
$(
|
||||||
if (&$rx as &Packet).can_recv() {
|
if (&$rx as &Packet).can_recv() {
|
||||||
let $name = $rx.$meth();
|
let $name = $rx.$meth();
|
||||||
$code
|
$code
|
||||||
}
|
|
||||||
)else+
|
|
||||||
else {
|
|
||||||
|
|
||||||
// Start selecting on as many as we need to before getting a bite.
|
|
||||||
// Keep count of how many, since we need to abort every selection
|
|
||||||
// that we started.
|
|
||||||
let mut started_count = 0;
|
|
||||||
// Restrict lifetime of borrows in `packets`
|
|
||||||
{
|
|
||||||
let packets = [ $( &$rx as &Packet, )+ ];
|
|
||||||
|
|
||||||
let task: Box<Task> = Local::take();
|
|
||||||
task.deschedule(packets.len(), |task| {
|
|
||||||
match packets[started_count].start_selection(task) {
|
|
||||||
Ok(()) => {
|
|
||||||
started_count += 1;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Err(task) => Err(task)
|
)else+
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
let ret = $(
|
|
||||||
// Abort the receivers, stopping at the first ready one to get its data.
|
|
||||||
if { i += 1; i <= started_count } &&
|
|
||||||
// If start_selection() failed, abort_selection() will fail too,
|
|
||||||
// but it still counts as "data available".
|
|
||||||
($rx.abort_selection() || i == started_count) {
|
|
||||||
// React to the first
|
|
||||||
let $name = $rx.$meth();
|
|
||||||
$code
|
|
||||||
})else+
|
|
||||||
else {
|
else {
|
||||||
fail!("we didn't find the ready receiver, but we should have had one");
|
// Start selecting on as many as we need to before getting a bite.
|
||||||
};
|
// Keep count of how many, since we need to abort every selection
|
||||||
// At this point, the first i receivers have been aborted. We need to abort the rest:
|
// that we started.
|
||||||
$(if i > 0 {
|
let mut started_count = 0;
|
||||||
i -= 1;
|
// Restrict lifetime of borrows in `packets`
|
||||||
} else {
|
{
|
||||||
$rx.abort_selection();
|
let packets = [ $( &$rx as &Packet, )+ ];
|
||||||
})+
|
|
||||||
let _ = i; // Shut up `i -= 1 but i is never read` warning
|
let task: Box<Task> = Local::take();
|
||||||
// Return
|
task.deschedule(packets.len(), |task| {
|
||||||
ret
|
match packets[started_count].start_selection(task) {
|
||||||
}
|
Ok(()) => {
|
||||||
})
|
started_count += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(task) => Err(task)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let ret = $(
|
||||||
|
// Abort the receivers, stopping at the first ready one to get its data.
|
||||||
|
if { i += 1; i <= started_count } &&
|
||||||
|
// If start_selection() failed, abort_selection() will fail too,
|
||||||
|
// but it still counts as "data available".
|
||||||
|
($rx.abort_selection() || i == started_count) {
|
||||||
|
// React to the first
|
||||||
|
let $name = $rx.$meth();
|
||||||
|
$code
|
||||||
|
})else+
|
||||||
|
else {
|
||||||
|
fail!("we didn't find the ready receiver, but we should have had one");
|
||||||
|
};
|
||||||
|
// At this point, the first i receivers have been aborted. We need to abort the rest:
|
||||||
|
$(if i > 0 {
|
||||||
|
i -= 1;
|
||||||
|
} else {
|
||||||
|
$rx.abort_selection();
|
||||||
|
})+
|
||||||
|
let _ = i; // Shut up `i -= 1 but i is never read` warning
|
||||||
|
// Return
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! user_enum {
|
macro_rules! user_enum {
|
||||||
($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => (
|
($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => (
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub enum $name {
|
pub enum $name {
|
||||||
$(#[$doc] $elem),*
|
$(#[$doc] $elem),*
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Debug for $name {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
||||||
f.pad(match *self {
|
|
||||||
$($elem => $txt),*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Display for $name {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
||||||
f.pad(match *self {
|
|
||||||
$($elem => $txt),*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::serde::Deserialize for $name {
|
|
||||||
#[inline]
|
|
||||||
fn deserialize<D>(d: &mut D) -> Result<$name, D::Error>
|
|
||||||
where D: ::serde::Deserializer
|
|
||||||
{
|
|
||||||
struct Visitor;
|
|
||||||
impl ::serde::de::Visitor for Visitor {
|
|
||||||
type Value = $name;
|
|
||||||
|
|
||||||
fn visit_string<E>(&mut self, v: String) -> Result<$name, E>
|
|
||||||
where E: ::serde::de::Error
|
|
||||||
{
|
|
||||||
self.visit_str(&v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(&mut self, s: &str) -> Result<$name, E>
|
|
||||||
where E: ::serde::de::Error
|
|
||||||
{
|
|
||||||
$( if s == $txt { Ok($name::$elem) } )else*
|
|
||||||
else { Err(::serde::de::Error::syntax_error()) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.visit(Visitor)
|
impl ::std::fmt::Debug for $name {
|
||||||
}
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
}
|
f.pad(match *self {
|
||||||
|
$($elem => $txt),*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ::serde::Serialize for $name {
|
impl ::std::fmt::Display for $name {
|
||||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
where S: ::serde::Serializer
|
f.pad(match *self {
|
||||||
{
|
$($elem => $txt),*
|
||||||
s.visit_str(&self.to_string())
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
impl ::serde::Deserialize for $name {
|
||||||
|
#[inline]
|
||||||
|
fn deserialize<D>(d: &mut D) -> Result<$name, D::Error>
|
||||||
|
where D: ::serde::Deserializer
|
||||||
|
{
|
||||||
|
struct Visitor;
|
||||||
|
impl ::serde::de::Visitor for Visitor {
|
||||||
|
type Value = $name;
|
||||||
|
|
||||||
|
fn visit_string<E>(&mut self, v: String) -> Result<$name, E>
|
||||||
|
where E: ::serde::de::Error
|
||||||
|
{
|
||||||
|
self.visit_str(&v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, s: &str) -> Result<$name, E>
|
||||||
|
where E: ::serde::de::Error
|
||||||
|
{
|
||||||
|
$( if s == $txt { Ok($name::$elem) } )else*
|
||||||
|
else { Err(::serde::de::Error::syntax_error()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.visit(Visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::serde::Serialize for $name {
|
||||||
|
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||||
|
where S: ::serde::Serializer
|
||||||
|
{
|
||||||
|
s.visit_str(&self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -25,107 +25,107 @@ use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||||
|
|
||||||
/// A message which can be sent on the Bitcoin network
|
/// A message which can be sent on the Bitcoin network
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
/// Services provided by the peer whose address this is
|
/// Services provided by the peer whose address this is
|
||||||
pub services: u64,
|
pub services: u64,
|
||||||
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
||||||
pub address: [u16; 8],
|
pub address: [u16; 8],
|
||||||
/// Network port
|
/// Network port
|
||||||
pub port: u16
|
pub port: u16
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Address {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for Address {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
try!(self.services.consensus_encode(s));
|
try!(self.services.consensus_encode(s));
|
||||||
try!(self.address.consensus_encode(s));
|
try!(self.address.consensus_encode(s));
|
||||||
// Explicitly code the port since it needs to be big-endian
|
// Explicitly code the port since it needs to be big-endian
|
||||||
try!(((self.port / 0x100) as u8).consensus_encode(s));
|
try!(((self.port / 0x100) as u8).consensus_encode(s));
|
||||||
try!(((self.port % 0x100) as u8).consensus_encode(s));
|
try!(((self.port % 0x100) as u8).consensus_encode(s));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for Address {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for Address {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Address, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Address, D::Error> {
|
||||||
Ok(Address {
|
Ok(Address {
|
||||||
services: try!(ConsensusDecodable::consensus_decode(d)),
|
services: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
address: try!(ConsensusDecodable::consensus_decode(d)),
|
address: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
// Explicitly code the port since it needs to be big-endian
|
// Explicitly code the port since it needs to be big-endian
|
||||||
port: {
|
port: {
|
||||||
let b1: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
let b1: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let b2: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
let b2: u8 = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
(b1 as u16 * 0x100) + (b2 as u16)
|
(b1 as u16 * 0x100) + (b2 as u16)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Address {
|
impl fmt::Debug for Address {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
// TODO: render services and hex-ize address
|
// TODO: render services and hex-ize address
|
||||||
write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}",
|
write!(f, "Address {{services: {:?}, address: {:?}, port: {:?}}}",
|
||||||
self.services, &self.address[..], self.port)
|
self.services, &self.address[..], self.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Address {
|
impl Clone for Address {
|
||||||
fn clone(&self) -> Address {
|
fn clone(&self) -> Address {
|
||||||
unsafe {
|
unsafe {
|
||||||
use std::intrinsics::copy_nonoverlapping;
|
use std::intrinsics::copy_nonoverlapping;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
let mut ret = mem::uninitialized();
|
let mut ret = mem::uninitialized();
|
||||||
copy_nonoverlapping(self,
|
copy_nonoverlapping(self,
|
||||||
&mut ret,
|
&mut ret,
|
||||||
mem::size_of::<Address>());
|
mem::size_of::<Address>());
|
||||||
ret
|
ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Address {
|
impl PartialEq for Address {
|
||||||
fn eq(&self, other: &Address) -> bool {
|
fn eq(&self, other: &Address) -> bool {
|
||||||
self.services == other.services &&
|
self.services == other.services &&
|
||||||
&self.address[..] == &other.address[..] &&
|
&self.address[..] == &other.address[..] &&
|
||||||
self.port == other.port
|
self.port == other.port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Address {}
|
impl Eq for Address {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::Address;
|
use super::Address;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_address_test() {
|
fn serialize_address_test() {
|
||||||
assert_eq!(serialize(&Address {
|
assert_eq!(serialize(&Address {
|
||||||
services: 1,
|
services: 1,
|
||||||
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
|
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
|
||||||
port: 8333
|
port: 8333
|
||||||
}),
|
}),
|
||||||
Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
Ok(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1, 0x20, 0x8d]));
|
0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1, 0x20, 0x8d]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_address_test() {
|
fn deserialize_address_test() {
|
||||||
let mut addr: io::Result<Address> = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
let mut addr: io::Result<Address> = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0,
|
||||||
0, 1, 0x20, 0x8d]);
|
0, 1, 0x20, 0x8d]);
|
||||||
assert!(addr.is_ok());
|
assert!(addr.is_ok());
|
||||||
let full = addr.unwrap();
|
let full = addr.unwrap();
|
||||||
assert!(full.services == 1);
|
assert!(full.services == 1);
|
||||||
assert!(full.address == [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
assert!(full.address == [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
||||||
assert!(full.port == 8333);
|
assert!(full.port == 8333);
|
||||||
|
|
||||||
addr = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0,
|
addr = deserialize(vec![1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
||||||
assert!(addr.is_err());
|
assert!(addr.is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -26,12 +26,12 @@ use util::hash::Sha256dHash;
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
/// The type of an inventory object
|
/// The type of an inventory object
|
||||||
pub enum InvType {
|
pub enum InvType {
|
||||||
/// Error --- these inventories can be ignored
|
/// Error --- these inventories can be ignored
|
||||||
Error,
|
Error,
|
||||||
/// Transaction
|
/// Transaction
|
||||||
Transaction,
|
Transaction,
|
||||||
/// Block
|
/// Block
|
||||||
Block
|
Block
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some simple messages
|
// Some simple messages
|
||||||
|
@ -39,133 +39,133 @@ pub enum InvType {
|
||||||
/// The `getblocks` message
|
/// The `getblocks` message
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct GetBlocksMessage {
|
pub struct GetBlocksMessage {
|
||||||
/// The protocol version
|
/// The protocol version
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||||
/// reply with its longest known chain, starting from a locator hash
|
/// reply with its longest known chain, starting from a locator hash
|
||||||
/// if possible and block 1 otherwise.
|
/// if possible and block 1 otherwise.
|
||||||
pub locator_hashes: Vec<Sha256dHash>,
|
pub locator_hashes: Vec<Sha256dHash>,
|
||||||
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
||||||
pub stop_hash: Sha256dHash
|
pub stop_hash: Sha256dHash
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `getheaders` message
|
/// The `getheaders` message
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct GetHeadersMessage {
|
pub struct GetHeadersMessage {
|
||||||
/// The protocol version
|
/// The protocol version
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||||
/// reply with its longest known chain, starting from a locator hash
|
/// reply with its longest known chain, starting from a locator hash
|
||||||
/// if possible and block 1 otherwise.
|
/// if possible and block 1 otherwise.
|
||||||
pub locator_hashes: Vec<Sha256dHash>,
|
pub locator_hashes: Vec<Sha256dHash>,
|
||||||
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||||
pub stop_hash: Sha256dHash
|
pub stop_hash: Sha256dHash
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An inventory object --- a reference to a Bitcoin object
|
/// An inventory object --- a reference to a Bitcoin object
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct Inventory {
|
pub struct Inventory {
|
||||||
/// The type of object that is referenced
|
/// The type of object that is referenced
|
||||||
pub inv_type: InvType,
|
pub inv_type: InvType,
|
||||||
/// The object's hash
|
/// The object's hash
|
||||||
pub hash: Sha256dHash
|
pub hash: Sha256dHash
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetBlocksMessage {
|
impl GetBlocksMessage {
|
||||||
/// Construct a new `getblocks` message
|
/// Construct a new `getblocks` message
|
||||||
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
|
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
|
||||||
GetBlocksMessage {
|
GetBlocksMessage {
|
||||||
version: constants::PROTOCOL_VERSION,
|
version: constants::PROTOCOL_VERSION,
|
||||||
locator_hashes: locator_hashes.clone(),
|
locator_hashes: locator_hashes.clone(),
|
||||||
stop_hash: stop_hash
|
stop_hash: stop_hash
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
|
impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
|
||||||
|
|
||||||
impl GetHeadersMessage {
|
impl GetHeadersMessage {
|
||||||
/// Construct a new `getheaders` message
|
/// Construct a new `getheaders` message
|
||||||
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
|
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
|
||||||
GetHeadersMessage {
|
GetHeadersMessage {
|
||||||
version: constants::PROTOCOL_VERSION,
|
version: constants::PROTOCOL_VERSION,
|
||||||
locator_hashes: locator_hashes,
|
locator_hashes: locator_hashes,
|
||||||
stop_hash: stop_hash
|
stop_hash: stop_hash
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for Inventory {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
try!(match self.inv_type {
|
try!(match self.inv_type {
|
||||||
InvType::Error => 0u32,
|
InvType::Error => 0u32,
|
||||||
InvType::Transaction => 1,
|
InvType::Transaction => 1,
|
||||||
InvType::Block => 2
|
InvType::Block => 2
|
||||||
}.consensus_encode(s));
|
}.consensus_encode(s));
|
||||||
self.hash.consensus_encode(s)
|
self.hash.consensus_encode(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for Inventory {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
|
fn consensus_decode(d: &mut D) -> Result<Inventory, D::Error> {
|
||||||
let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
let int_type: u32 = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
Ok(Inventory {
|
Ok(Inventory {
|
||||||
inv_type: match int_type {
|
inv_type: match int_type {
|
||||||
0 => InvType::Error,
|
0 => InvType::Error,
|
||||||
1 => InvType::Transaction,
|
1 => InvType::Transaction,
|
||||||
2 => InvType::Block,
|
2 => InvType::Block,
|
||||||
// TODO do not fail here
|
// TODO do not fail here
|
||||||
_ => { panic!("bad inventory type field") }
|
_ => { panic!("bad inventory type field") }
|
||||||
},
|
},
|
||||||
hash: try!(ConsensusDecodable::consensus_decode(d))
|
hash: try!(ConsensusDecodable::consensus_decode(d))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn getblocks_message_test() {
|
fn getblocks_message_test() {
|
||||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone());
|
let decode: io::Result<GetBlocksMessage> = deserialize(from_sat.clone());
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
assert_eq!(real_decode.version, 70002);
|
assert_eq!(real_decode.version, 70002);
|
||||||
assert_eq!(real_decode.locator_hashes.len(), 1);
|
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||||
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
||||||
assert_eq!(real_decode.stop_hash, Default::default());
|
assert_eq!(real_decode.stop_hash, Default::default());
|
||||||
|
|
||||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn getheaders_message_test() {
|
fn getheaders_message_test() {
|
||||||
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||||
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||||
|
|
||||||
let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone());
|
let decode: io::Result<GetHeadersMessage> = deserialize(from_sat.clone());
|
||||||
assert!(decode.is_ok());
|
assert!(decode.is_ok());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
assert_eq!(real_decode.version, 70002);
|
assert_eq!(real_decode.version, 70002);
|
||||||
assert_eq!(real_decode.locator_hashes.len(), 1);
|
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||||
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
assert_eq!(serialize(&real_decode.locator_hashes[0]), Ok(genhash));
|
||||||
assert_eq!(real_decode.stop_hash, Default::default());
|
assert_eq!(real_decode.stop_hash, Default::default());
|
||||||
|
|
||||||
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
assert_eq!(serialize(&real_decode), Ok(from_sat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,24 +34,24 @@ use util::{self, propagate_err};
|
||||||
|
|
||||||
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
||||||
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
|
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u16; 8] {
|
||||||
match *ipaddr {
|
match *ipaddr {
|
||||||
ip::IpAddr::V4(ref addr) => &addr.to_ipv6_mapped(),
|
ip::IpAddr::V4(ref addr) => &addr.to_ipv6_mapped(),
|
||||||
ip::IpAddr::V6(ref addr) => addr
|
ip::IpAddr::V6(ref addr) => addr
|
||||||
}.segments()
|
}.segments()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A network socket along with information about the peer
|
/// A network socket along with information about the peer
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Socket {
|
pub struct Socket {
|
||||||
/// The underlying TCP socket
|
/// The underlying TCP socket
|
||||||
socket: Arc<Mutex<Option<tcp::TcpStream>>>,
|
socket: Arc<Mutex<Option<tcp::TcpStream>>>,
|
||||||
/// Services supported by us
|
/// Services supported by us
|
||||||
pub services: u64,
|
pub services: u64,
|
||||||
/// Our user agent
|
/// Our user agent
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
/// Nonce to identify our `version` messages
|
/// Nonce to identify our `version` messages
|
||||||
pub version_nonce: u64,
|
pub version_nonce: u64,
|
||||||
/// Network magic
|
/// Network magic
|
||||||
pub magic: u32
|
pub magic: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,204 +23,204 @@ use util::hash::Sha256dHash;
|
||||||
/// An error that might occur during base58 decoding
|
/// An error that might occur during base58 decoding
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Invalid character encountered
|
/// Invalid character encountered
|
||||||
BadByte(u8),
|
BadByte(u8),
|
||||||
/// Checksum was not correct (expected, actual)
|
/// Checksum was not correct (expected, actual)
|
||||||
BadChecksum(u32, u32),
|
BadChecksum(u32, u32),
|
||||||
/// The length (in bytes) of the object was not correct
|
/// The length (in bytes) of the object was not correct
|
||||||
InvalidLength(usize),
|
InvalidLength(usize),
|
||||||
/// Version byte(s) were not recognized
|
/// Version byte(s) were not recognized
|
||||||
InvalidVersion(Vec<u8>),
|
InvalidVersion(Vec<u8>),
|
||||||
/// Checked data was less than 4 bytes
|
/// Checked data was less than 4 bytes
|
||||||
TooShort(usize),
|
TooShort(usize),
|
||||||
/// Any other error
|
/// Any other error
|
||||||
Other(String)
|
Other(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
|
|
||||||
static BASE58_DIGITS: [Option<u8>; 128] = [
|
static BASE58_DIGITS: [Option<u8>; 128] = [
|
||||||
None, None, None, None, None, None, None, None, // 0-7
|
None, None, None, None, None, None, None, None, // 0-7
|
||||||
None, None, None, None, None, None, None, None, // 8-15
|
None, None, None, None, None, None, None, None, // 8-15
|
||||||
None, None, None, None, None, None, None, None, // 16-23
|
None, None, None, None, None, None, None, None, // 16-23
|
||||||
None, None, None, None, None, None, None, None, // 24-31
|
None, None, None, None, None, None, None, None, // 24-31
|
||||||
None, None, None, None, None, None, None, None, // 32-39
|
None, None, None, None, None, None, None, None, // 32-39
|
||||||
None, None, None, None, None, None, None, None, // 40-47
|
None, None, None, None, None, None, None, None, // 40-47
|
||||||
None, Some(0), Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), // 48-55
|
None, Some(0), Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), // 48-55
|
||||||
Some(7), Some(8), None, None, None, None, None, None, // 56-63
|
Some(7), Some(8), None, None, None, None, None, None, // 56-63
|
||||||
None, Some(9), Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), // 64-71
|
None, Some(9), Some(10), Some(11), Some(12), Some(13), Some(14), Some(15), // 64-71
|
||||||
Some(16), None, Some(17), Some(18), Some(19), Some(20), Some(21), None, // 72-79
|
Some(16), None, Some(17), Some(18), Some(19), Some(20), Some(21), None, // 72-79
|
||||||
Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), // 80-87
|
Some(22), Some(23), Some(24), Some(25), Some(26), Some(27), Some(28), Some(29), // 80-87
|
||||||
Some(30), Some(31), Some(32), None, None, None, None, None, // 88-95
|
Some(30), Some(31), Some(32), None, None, None, None, None, // 88-95
|
||||||
None, Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), // 96-103
|
None, Some(33), Some(34), Some(35), Some(36), Some(37), Some(38), Some(39), // 96-103
|
||||||
Some(40), Some(41), Some(42), Some(43), None, Some(44), Some(45), Some(46), // 104-111
|
Some(40), Some(41), Some(42), Some(43), None, Some(44), Some(45), Some(46), // 104-111
|
||||||
Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), // 112-119
|
Some(47), Some(48), Some(49), Some(50), Some(51), Some(52), Some(53), Some(54), // 112-119
|
||||||
Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127
|
Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Trait for objects which can be read as base58
|
/// Trait for objects which can be read as base58
|
||||||
pub trait FromBase58 {
|
pub trait FromBase58: Sized {
|
||||||
/// Constructs an object flrom the byte-encoding (base 256)
|
/// Constructs an object flrom the byte-encoding (base 256)
|
||||||
/// representation of its base58 format
|
/// representation of its base58 format
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
|
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
|
||||||
|
|
||||||
/// Obtain an object from its base58 encoding
|
/// Obtain an object from its base58 encoding
|
||||||
fn from_base58(data: &str) -> Result<Self, Error> {
|
fn from_base58(data: &str) -> Result<Self, Error> {
|
||||||
// 11/15 is just over log_256(58)
|
// 11/15 is just over log_256(58)
|
||||||
let mut scratch = Vec::from_elem(1 + data.len() * 11 / 15, 0u8);
|
let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
|
||||||
// Build in base 256
|
// Build in base 256
|
||||||
for d58 in data.bytes() {
|
for d58 in data.bytes() {
|
||||||
// Compute "X = X * 58 + next_digit" in base 256
|
// Compute "X = X * 58 + next_digit" in base 256
|
||||||
if d58 as usize > BASE58_DIGITS.len() {
|
if d58 as usize > BASE58_DIGITS.len() {
|
||||||
return Err(Error::BadByte(d58));
|
return Err(Error::BadByte(d58));
|
||||||
}
|
}
|
||||||
let mut carry = match BASE58_DIGITS[d58 as usize] {
|
let mut carry = match BASE58_DIGITS[d58 as usize] {
|
||||||
Some(d58) => d58 as u32,
|
Some(d58) => d58 as u32,
|
||||||
None => { return Err(Error::BadByte(d58)); }
|
None => { return Err(Error::BadByte(d58)); }
|
||||||
};
|
};
|
||||||
for d256 in scratch.iter_mut().rev() {
|
for d256 in scratch.iter_mut().rev() {
|
||||||
carry += *d256 as u32 * 58;
|
carry += *d256 as u32 * 58;
|
||||||
*d256 = carry as u8;
|
*d256 = carry as u8;
|
||||||
carry /= 256;
|
carry /= 256;
|
||||||
}
|
}
|
||||||
assert_eq!(carry, 0);
|
assert_eq!(carry, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy leading zeroes directly
|
||||||
|
let mut ret: Vec<u8> = data.bytes().take_while(|&x| x == BASE58_CHARS[0])
|
||||||
|
.map(|_| 0)
|
||||||
|
.collect();
|
||||||
|
// Copy rest of string
|
||||||
|
ret.extend(scratch.into_iter().skip_while(|&x| x == 0));
|
||||||
|
FromBase58::from_base58_layout(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy leading zeroes directly
|
/// Obtain an object from its base58check encoding
|
||||||
let mut ret: Vec<u8> = data.bytes().take_while(|&x| x == BASE58_CHARS[0])
|
fn from_base58check(data: &str) -> Result<Self, Error> {
|
||||||
.map(|_| 0)
|
let mut ret: Vec<u8> = try!(FromBase58::from_base58(data));
|
||||||
.collect();
|
if ret.len() < 4 {
|
||||||
// Copy rest of string
|
return Err(Error::TooShort(ret.len()));
|
||||||
ret.extend(scratch.into_iter().skip_while(|&x| x == 0));
|
}
|
||||||
FromBase58::from_base58_layout(ret)
|
let ck_start = ret.len() - 4;
|
||||||
}
|
let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32();
|
||||||
|
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
|
||||||
|
if expected != actual {
|
||||||
|
return Err(Error::BadChecksum(expected, actual));
|
||||||
|
}
|
||||||
|
|
||||||
/// Obtain an object from its base58check encoding
|
ret.truncate(ck_start);
|
||||||
fn from_base58check(data: &str) -> Result<Self, Error> {
|
FromBase58::from_base58_layout(ret)
|
||||||
let mut ret: Vec<u8> = try!(FromBase58::from_base58(data));
|
|
||||||
if ret.len() < 4 {
|
|
||||||
return Err(Error::TooShort(ret.len()));
|
|
||||||
}
|
}
|
||||||
let ck_start = ret.len() - 4;
|
|
||||||
let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32();
|
|
||||||
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
|
|
||||||
if expected != actual {
|
|
||||||
return Err(Error::BadChecksum(expected, actual));
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.truncate(ck_start);
|
|
||||||
FromBase58::from_base58_layout(ret)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Directly encode a slice as base58
|
/// Directly encode a slice as base58
|
||||||
pub fn base58_encode_slice(data: &[u8]) -> String {
|
pub fn base58_encode_slice(data: &[u8]) -> String {
|
||||||
// 7/5 is just over log_58(256)
|
// 7/5 is just over log_58(256)
|
||||||
let mut scratch = vec![0u8; 1 + data.len() * 7 / 5];
|
let mut scratch = vec![0u8; 1 + data.len() * 7 / 5];
|
||||||
// Build in base 58
|
// Build in base 58
|
||||||
for &d256 in data.base58_layout().iter() {
|
for &d256 in data.base58_layout().iter() {
|
||||||
// Compute "X = X * 256 + next_digit" in base 58
|
// Compute "X = X * 256 + next_digit" in base 58
|
||||||
let mut carry = d256 as u32;
|
let mut carry = d256 as u32;
|
||||||
for d58 in scratch.iter_mut().rev() {
|
for d58 in scratch.iter_mut().rev() {
|
||||||
carry += (*d58 as u32) << 8;
|
carry += (*d58 as u32) << 8;
|
||||||
*d58 = (carry % 58) as u8;
|
*d58 = (carry % 58) as u8;
|
||||||
carry /= 58;
|
carry /= 58;
|
||||||
|
}
|
||||||
|
assert_eq!(carry, 0);
|
||||||
}
|
}
|
||||||
assert_eq!(carry, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsafely translate the bytes to a utf8 string
|
// Unsafely translate the bytes to a utf8 string
|
||||||
unsafe {
|
unsafe {
|
||||||
// Copy leading zeroes directly
|
// Copy leading zeroes directly
|
||||||
let mut ret: Vec<u8> = str::from_utf8(data.iter().take_while(|&&x| x == 0)
|
let mut ret: Vec<u8> = data.iter().take_while(|&&x| x == 0)
|
||||||
.map(|_| BASE58_CHARS[0])
|
.map(|_| BASE58_CHARS[0])
|
||||||
.collect()).unwrap();
|
.collect();
|
||||||
// Copy rest of string
|
// Copy rest of string
|
||||||
ret.as_mut_vec().extend(scratch.into_iter().skip_while(|&x| x == 0)
|
ret.extend(scratch.into_iter().skip_while(|&x| x == 0)
|
||||||
.map(|x| BASE58_CHARS[x as usize]));
|
.map(|x| BASE58_CHARS[x as usize]));
|
||||||
ret
|
String::from_utf8(ret).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for objects which can be written as base58
|
/// Trait for objects which can be written as base58
|
||||||
pub trait ToBase58 {
|
pub trait ToBase58 {
|
||||||
/// The serialization to be converted into base58
|
/// The serialization to be converted into base58
|
||||||
fn base58_layout(&self) -> Vec<u8>;
|
fn base58_layout(&self) -> Vec<u8>;
|
||||||
|
|
||||||
/// Obtain a string with the base58 encoding of the object
|
/// Obtain a string with the base58 encoding of the object
|
||||||
fn to_base58(&self) -> String {
|
fn to_base58(&self) -> String {
|
||||||
base58_encode_slice(&self.base58_layout()[..])
|
base58_encode_slice(&self.base58_layout()[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a string with the base58check encoding of the object
|
/// Obtain a string with the base58check encoding of the object
|
||||||
/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.)
|
/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.)
|
||||||
fn to_base58check(&self) -> String {
|
fn to_base58check(&self) -> String {
|
||||||
let mut data = self.base58_layout();
|
let mut data = self.base58_layout();
|
||||||
let checksum = Sha256dHash::from_data(&data).into_le().low_u32();
|
let checksum = Sha256dHash::from_data(&data).into_le().low_u32();
|
||||||
data.write_u32::<LittleEndian>(checksum);
|
data.write_u32::<LittleEndian>(checksum);
|
||||||
base58_encode_slice(&data)
|
base58_encode_slice(&data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trivial implementations for slices and vectors
|
// Trivial implementations for slices and vectors
|
||||||
impl<'a> ToBase58 for &'a [u8] {
|
impl<'a> ToBase58 for &'a [u8] {
|
||||||
fn base58_layout(&self) -> Vec<u8> { self.to_vec() }
|
fn base58_layout(&self) -> Vec<u8> { self.to_vec() }
|
||||||
fn to_base58(&self) -> String { base58_encode_slice(*self) }
|
fn to_base58(&self) -> String { base58_encode_slice(*self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromBase58 for Vec<u8> {
|
impl FromBase58 for Vec<u8> {
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
use super::ToBase58;
|
use super::ToBase58;
|
||||||
use super::FromBase58;
|
use super::FromBase58;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_base58_encode() {
|
fn test_base58_encode() {
|
||||||
// Basics
|
// Basics
|
||||||
assert_eq!([0].as_slice().to_base58().as_slice(), "1");
|
assert_eq!([0].as_slice().to_base58().as_slice(), "1");
|
||||||
assert_eq!([1].as_slice().to_base58().as_slice(), "2");
|
assert_eq!([1].as_slice().to_base58().as_slice(), "2");
|
||||||
assert_eq!([58].as_slice().to_base58().as_slice(), "21");
|
assert_eq!([58].as_slice().to_base58().as_slice(), "21");
|
||||||
assert_eq!([13, 36].as_slice().to_base58().as_slice(), "211");
|
assert_eq!([13, 36].as_slice().to_base58().as_slice(), "211");
|
||||||
|
|
||||||
// Leading zeroes
|
// Leading zeroes
|
||||||
assert_eq!([0, 13, 36].as_slice().to_base58().as_slice(), "1211");
|
assert_eq!([0, 13, 36].as_slice().to_base58().as_slice(), "1211");
|
||||||
assert_eq!([0, 0, 0, 0, 13, 36].as_slice().to_base58().as_slice(), "1111211");
|
assert_eq!([0, 0, 0, 0, 13, 36].as_slice().to_base58().as_slice(), "1111211");
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
assert_eq!("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check().as_slice(),
|
assert_eq!("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check().as_slice(),
|
||||||
"1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH");
|
"1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_base58_decode() {
|
fn test_base58_decode() {
|
||||||
// Basics
|
// Basics
|
||||||
assert_eq!(FromBase58::from_base58("1"), Ok(vec![0u8]));
|
assert_eq!(FromBase58::from_base58("1"), Ok(vec![0u8]));
|
||||||
assert_eq!(FromBase58::from_base58("2"), Ok(vec![1u8]));
|
assert_eq!(FromBase58::from_base58("2"), Ok(vec![1u8]));
|
||||||
assert_eq!(FromBase58::from_base58("21"), Ok(vec![58u8]));
|
assert_eq!(FromBase58::from_base58("21"), Ok(vec![58u8]));
|
||||||
assert_eq!(FromBase58::from_base58("211"), Ok(vec![13u8, 36]));
|
assert_eq!(FromBase58::from_base58("211"), Ok(vec![13u8, 36]));
|
||||||
|
|
||||||
// Leading zeroes
|
// Leading zeroes
|
||||||
assert_eq!(FromBase58::from_base58("1211"), Ok(vec![0u8, 13, 36]));
|
assert_eq!(FromBase58::from_base58("1211"), Ok(vec![0u8, 13, 36]));
|
||||||
assert_eq!(FromBase58::from_base58("111211"), Ok(vec![0u8, 0, 0, 13, 36]));
|
assert_eq!(FromBase58::from_base58("111211"), Ok(vec![0u8, 0, 0, 13, 36]));
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"),
|
assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"),
|
||||||
Ok("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap()))
|
Ok("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_base58_roundtrip() {
|
fn test_base58_roundtrip() {
|
||||||
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
|
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
|
||||||
let v: Vec<u8> = FromBase58::from_base58check(s).unwrap();
|
let v: Vec<u8> = FromBase58::from_base58check(s).unwrap();
|
||||||
assert_eq!(v.to_base58check().as_slice(), s);
|
assert_eq!(v.to_base58check().as_slice(), s);
|
||||||
assert_eq!(FromBase58::from_base58check(v.to_base58check().as_slice()), Ok(v));
|
assert_eq!(FromBase58::from_base58check(v.to_base58check().as_slice()), Ok(v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
406
src/util/hash.rs
406
src/util/hash.rs
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
// the public domain worldwide. This software is distributed without
|
// the public domain worldwide. This software is distributed without
|
||||||
|
@ -38,9 +38,9 @@ pub struct Sha256dHash([u8; 32]);
|
||||||
impl_array_newtype!(Sha256dHash, u8, 32);
|
impl_array_newtype!(Sha256dHash, u8, 32);
|
||||||
|
|
||||||
impl ::std::fmt::Debug for Sha256dHash {
|
impl ::std::fmt::Debug for Sha256dHash {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
write!(f, "{}", self.be_hex_string().as_slice())
|
write!(f, "{}", self.be_hex_string().as_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A RIPEMD-160 hash
|
/// A RIPEMD-160 hash
|
||||||
|
@ -60,100 +60,100 @@ pub struct Hash48((u8, u8, u8, u8, u8, u8));
|
||||||
pub struct Hash64((u8, u8, u8, u8, u8, u8, u8, u8));
|
pub struct Hash64((u8, u8, u8, u8, u8, u8, u8, u8));
|
||||||
|
|
||||||
impl Ripemd160Hash {
|
impl Ripemd160Hash {
|
||||||
/// Create a hash by hashing some data
|
/// Create a hash by hashing some data
|
||||||
pub fn from_data(data: &[u8]) -> Ripemd160Hash {
|
pub fn from_data(data: &[u8]) -> Ripemd160Hash {
|
||||||
let mut ret = [0; 20];
|
let mut ret = [0; 20];
|
||||||
let mut rmd = Ripemd160::new();
|
let mut rmd = Ripemd160::new();
|
||||||
rmd.input(data);
|
rmd.input(data);
|
||||||
rmd.result(&mut ret);
|
rmd.result(&mut ret);
|
||||||
Ripemd160Hash(ret)
|
Ripemd160Hash(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This doesn't make much sense to me, but is implicit behaviour
|
// This doesn't make much sense to me, but is implicit behaviour
|
||||||
// in the C++ reference client
|
// in the C++ reference client
|
||||||
impl Default for Sha256dHash {
|
impl Default for Sha256dHash {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Sha256dHash { Sha256dHash([0u8; 32]) }
|
fn default() -> Sha256dHash { Sha256dHash([0u8; 32]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sha256dHash {
|
impl Sha256dHash {
|
||||||
/// Create a hash by hashing some data
|
/// Create a hash by hashing some data
|
||||||
pub fn from_data(data: &[u8]) -> Sha256dHash {
|
pub fn from_data(data: &[u8]) -> Sha256dHash {
|
||||||
let Sha256dHash(mut ret): Sha256dHash = Default::default();
|
let Sha256dHash(mut ret): Sha256dHash = Default::default();
|
||||||
let mut sha2 = Sha256::new();
|
let mut sha2 = Sha256::new();
|
||||||
sha2.input(data);
|
sha2.input(data);
|
||||||
sha2.result(&mut ret);
|
sha2.result(&mut ret);
|
||||||
sha2.reset();
|
sha2.reset();
|
||||||
sha2.input(&ret);
|
sha2.input(&ret);
|
||||||
sha2.result(&mut ret);
|
sha2.result(&mut ret);
|
||||||
Sha256dHash(ret)
|
Sha256dHash(ret)
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a hash to a little-endian Uint256
|
|
||||||
#[inline]
|
|
||||||
pub fn into_le(self) -> Uint256 {
|
|
||||||
let Sha256dHash(data) = self;
|
|
||||||
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
|
||||||
for x in (&mut ret).iter_mut() { *x = x.to_le(); }
|
|
||||||
Uint256(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a hash to a big-endian Uint256
|
|
||||||
#[inline]
|
|
||||||
pub fn into_be(self) -> Uint256 {
|
|
||||||
let Sha256dHash(mut data) = self;
|
|
||||||
data.reverse();
|
|
||||||
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
|
||||||
for x in (&mut ret).iter_mut() { *x = x.to_be(); }
|
|
||||||
Uint256(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a hash to a Hash32 by truncation
|
|
||||||
#[inline]
|
|
||||||
pub fn into_hash32(self) -> Hash32 {
|
|
||||||
let Sha256dHash(data) = self;
|
|
||||||
unsafe { transmute([data[0], data[8], data[16], data[24]]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a hash to a Hash48 by truncation
|
|
||||||
#[inline]
|
|
||||||
pub fn into_hash48(self) -> Hash48 {
|
|
||||||
let Sha256dHash(data) = self;
|
|
||||||
unsafe { transmute([data[0], data[6], data[12], data[18], data[24], data[30]]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Human-readable hex output
|
|
||||||
|
|
||||||
/// Converts a hash to a Hash64 by truncation
|
|
||||||
#[inline]
|
|
||||||
pub fn into_hash64(self) -> Hash64 {
|
|
||||||
let Sha256dHash(data) = self;
|
|
||||||
unsafe { transmute([data[0], data[4], data[8], data[12],
|
|
||||||
data[16], data[20], data[24], data[28]]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Human-readable hex output
|
|
||||||
pub fn le_hex_string(&self) -> String {
|
|
||||||
let &Sha256dHash(data) = self;
|
|
||||||
let mut ret = String::with_capacity(64);
|
|
||||||
for i in 0..32 {
|
|
||||||
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
|
|
||||||
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
|
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Human-readable hex output
|
/// Converts a hash to a little-endian Uint256
|
||||||
pub fn be_hex_string(&self) -> String {
|
#[inline]
|
||||||
let &Sha256dHash(data) = self;
|
pub fn into_le(self) -> Uint256 {
|
||||||
let mut ret = String::with_capacity(64);
|
let Sha256dHash(data) = self;
|
||||||
for i in (0..32).rev() {
|
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
||||||
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
|
for x in (&mut ret).iter_mut() { *x = x.to_le(); }
|
||||||
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
|
Uint256(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a hash to a big-endian Uint256
|
||||||
|
#[inline]
|
||||||
|
pub fn into_be(self) -> Uint256 {
|
||||||
|
let Sha256dHash(mut data) = self;
|
||||||
|
data.reverse();
|
||||||
|
let mut ret: [u64; 4] = unsafe { transmute(data) };
|
||||||
|
for x in (&mut ret).iter_mut() { *x = x.to_be(); }
|
||||||
|
Uint256(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a hash to a Hash32 by truncation
|
||||||
|
#[inline]
|
||||||
|
pub fn into_hash32(self) -> Hash32 {
|
||||||
|
let Sha256dHash(data) = self;
|
||||||
|
unsafe { transmute([data[0], data[8], data[16], data[24]]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a hash to a Hash48 by truncation
|
||||||
|
#[inline]
|
||||||
|
pub fn into_hash48(self) -> Hash48 {
|
||||||
|
let Sha256dHash(data) = self;
|
||||||
|
unsafe { transmute([data[0], data[6], data[12], data[18], data[24], data[30]]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable hex output
|
||||||
|
|
||||||
|
/// Converts a hash to a Hash64 by truncation
|
||||||
|
#[inline]
|
||||||
|
pub fn into_hash64(self) -> Hash64 {
|
||||||
|
let Sha256dHash(data) = self;
|
||||||
|
unsafe { transmute([data[0], data[4], data[8], data[12],
|
||||||
|
data[16], data[20], data[24], data[28]]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable hex output
|
||||||
|
pub fn le_hex_string(&self) -> String {
|
||||||
|
let &Sha256dHash(data) = self;
|
||||||
|
let mut ret = String::with_capacity(64);
|
||||||
|
for i in 0..32 {
|
||||||
|
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
|
||||||
|
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable hex output
|
||||||
|
pub fn be_hex_string(&self) -> String {
|
||||||
|
let &Sha256dHash(data) = self;
|
||||||
|
let mut ret = String::with_capacity(64);
|
||||||
|
for i in (0..32).rev() {
|
||||||
|
ret.push(from_digit((data[i] / 0x10) as u32, 16).unwrap());
|
||||||
|
ret.push(from_digit((data[i] & 0x0f) as u32, 16).unwrap());
|
||||||
|
}
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that this outputs hashes as big endian hex numbers, so this should be
|
// Note that this outputs hashes as big endian hex numbers, so this should be
|
||||||
|
@ -161,48 +161,48 @@ impl Sha256dHash {
|
||||||
// little-endian and should be done using the consensus `encodable::ConsensusEncodable`
|
// little-endian and should be done using the consensus `encodable::ConsensusEncodable`
|
||||||
// interface.
|
// interface.
|
||||||
impl serde::Serialize for Sha256dHash {
|
impl serde::Serialize for Sha256dHash {
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer,
|
where S: serde::Serializer,
|
||||||
{
|
{
|
||||||
serializer.visit_str(&self.be_hex_string())
|
serializer.visit_str(&self.be_hex_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Deserialize for Sha256dHash {
|
impl serde::Deserialize for Sha256dHash {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deserialize<D>(d: &mut D) -> Result<Sha256dHash, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<Sha256dHash, D::Error>
|
||||||
where D: serde::Deserializer
|
where D: serde::Deserializer
|
||||||
{
|
{
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
struct Sha256dHashVisitor;
|
struct Sha256dHashVisitor;
|
||||||
impl serde::de::Visitor for Sha256dHashVisitor {
|
impl serde::de::Visitor for Sha256dHashVisitor {
|
||||||
type Value = Sha256dHash;
|
type Value = Sha256dHash;
|
||||||
|
|
||||||
fn visit_string<E>(&mut self, v: String) -> Result<Sha256dHash, E>
|
fn visit_string<E>(&mut self, v: String) -> Result<Sha256dHash, E>
|
||||||
where E: serde::de::Error
|
where E: serde::de::Error
|
||||||
{
|
{
|
||||||
self.visit_str(&v)
|
self.visit_str(&v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E>(&mut self, hex_str: &str) -> Result<Sha256dHash, E>
|
fn visit_str<E>(&mut self, hex_str: &str) -> Result<Sha256dHash, E>
|
||||||
where E: serde::de::Error
|
where E: serde::de::Error
|
||||||
{
|
{
|
||||||
if hex_str.len() != 64 {
|
if hex_str.len() != 64 {
|
||||||
return Err(serde::de::Error::syntax_error());
|
return Err(serde::de::Error::syntax_error());
|
||||||
|
}
|
||||||
|
let raw_str = try!(hex_str.from_hex()
|
||||||
|
.map_err(|_| serde::de::Error::syntax_error()));
|
||||||
|
let mut ret = [0u8; 32];
|
||||||
|
for i in 0..32 {
|
||||||
|
ret[i] = raw_str[31 - i];
|
||||||
|
}
|
||||||
|
Ok(Sha256dHash(ret))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let raw_str = try!(hex_str.from_hex()
|
|
||||||
.map_err(|_| serde::de::Error::syntax_error()));
|
d.visit(Sha256dHashVisitor)
|
||||||
let mut ret = [0u8; 32];
|
|
||||||
for i in 0..32 {
|
|
||||||
ret[i] = raw_str[31 - i];
|
|
||||||
}
|
|
||||||
Ok(Sha256dHash(ret))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.visit(Sha256dHashVisitor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consensus encoding (little-endian)
|
// Consensus encoding (little-endian)
|
||||||
|
@ -212,108 +212,108 @@ impl_newtype_consensus_encoding!(Hash64);
|
||||||
impl_newtype_consensus_encoding!(Sha256dHash);
|
impl_newtype_consensus_encoding!(Sha256dHash);
|
||||||
|
|
||||||
impl fmt::LowerHex for Sha256dHash {
|
impl fmt::LowerHex for Sha256dHash {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let &Sha256dHash(data) = self;
|
let &Sha256dHash(data) = self;
|
||||||
let mut rv = [0; 64];
|
let mut rv = [0; 64];
|
||||||
for ch in data.iter().rev() {
|
for ch in data.iter().rev() {
|
||||||
try!(write!(f, "{:02x}", ch));
|
try!(write!(f, "{:02x}", ch));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Any collection of objects for which a merkle root makes sense to calculate
|
/// Any collection of objects for which a merkle root makes sense to calculate
|
||||||
pub trait MerkleRoot {
|
pub trait MerkleRoot {
|
||||||
/// Construct a merkle tree from a collection, with elements ordered as
|
/// Construct a merkle tree from a collection, with elements ordered as
|
||||||
/// they were in the original collection, and return the merkle root.
|
/// they were in the original collection, and return the merkle root.
|
||||||
fn merkle_root(&self) -> Sha256dHash;
|
fn merkle_root(&self) -> Sha256dHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
||||||
fn merkle_root(&self) -> Sha256dHash {
|
fn merkle_root(&self) -> Sha256dHash {
|
||||||
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
|
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
|
||||||
// Base case
|
// Base case
|
||||||
if data.len() < 1 {
|
if data.len() < 1 {
|
||||||
return Default::default();
|
return Default::default();
|
||||||
}
|
}
|
||||||
if data.len() < 2 {
|
if data.len() < 2 {
|
||||||
return data[0];
|
return data[0];
|
||||||
}
|
}
|
||||||
// Recursion
|
// Recursion
|
||||||
let mut next = vec![];
|
let mut next = vec![];
|
||||||
for idx in 0..((data.len() + 1) / 2) {
|
for idx in 0..((data.len() + 1) / 2) {
|
||||||
let idx1 = 2 * idx;
|
let idx1 = 2 * idx;
|
||||||
let idx2 = min(idx1 + 1, data.len() - 1);
|
let idx2 = min(idx1 + 1, data.len() - 1);
|
||||||
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
||||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||||
next.push(encoder.unwrap().into_inner().bitcoin_hash());
|
next.push(encoder.into_inner().into_inner().bitcoin_hash());
|
||||||
}
|
}
|
||||||
merkle_root(next)
|
merkle_root(next)
|
||||||
|
}
|
||||||
|
merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect())
|
||||||
}
|
}
|
||||||
merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <T: BitcoinHash> MerkleRoot for Vec<T> {
|
impl <T: BitcoinHash> MerkleRoot for Vec<T> {
|
||||||
fn merkle_root(&self) -> Sha256dHash {
|
fn merkle_root(&self) -> Sha256dHash {
|
||||||
(&self[..]).merkle_root()
|
(&self[..]).merkle_root()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::prelude::*;
|
use std::prelude::*;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use serialize::Encodable;
|
use serialize::Encodable;
|
||||||
use serialize::json;
|
use serialize::json;
|
||||||
|
|
||||||
use network::serialize::{serialize, deserialize};
|
use network::serialize::{serialize, deserialize};
|
||||||
use util::hash::Sha256dHash;
|
use util::hash::Sha256dHash;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sha256d() {
|
fn test_sha256d() {
|
||||||
// nb the 5df6... output is the one you get from sha256sum. this is the
|
// nb the 5df6... output is the one you get from sha256sum. this is the
|
||||||
// "little-endian" hex string since it matches the in-memory representation
|
// "little-endian" hex string since it matches the in-memory representation
|
||||||
// of a Uint256 (which is little-endian) after transmutation
|
// of a Uint256 (which is little-endian) after transmutation
|
||||||
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
|
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
|
||||||
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string());
|
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string());
|
||||||
assert_eq!(Sha256dHash::from_data(&[]).be_hex_string(),
|
assert_eq!(Sha256dHash::from_data(&[]).be_hex_string(),
|
||||||
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string());
|
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string());
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_consenus_encode_roundtrip() {
|
|
||||||
let hash = Sha256dHash::from_data(&[]);
|
|
||||||
let serial = serialize(&hash).unwrap();
|
|
||||||
let deserial = deserialize(serial).unwrap();
|
|
||||||
assert_eq!(hash, deserial);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hash_encode_decode() {
|
|
||||||
let hash = Sha256dHash::from_data(&[]);
|
|
||||||
let mut writer = Cursor::new(vec![]);
|
|
||||||
{
|
|
||||||
let mut encoder = json::Encoder::new(&mut writer);
|
|
||||||
assert!(hash.encode(&mut encoder).is_ok());
|
|
||||||
}
|
}
|
||||||
let res = writer.unwrap();
|
|
||||||
assert_eq!(&res.as_slice(),
|
|
||||||
"\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes());
|
|
||||||
assert_eq!(json::decode(from_utf8(res.as_slice()).unwrap()), Ok(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sighash_single_vec() {
|
fn test_consenus_encode_roundtrip() {
|
||||||
let one = Sha256dHash([1, 0, 0, 0, 0, 0, 0, 0,
|
let hash = Sha256dHash::from_data(&[]);
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
let serial = serialize(&hash).unwrap();
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
let deserial = deserialize(serial).unwrap();
|
||||||
0, 0, 0, 0, 0, 0, 0, 0]);
|
assert_eq!(hash, deserial);
|
||||||
assert_eq!(Some(one.into_le()), FromPrimitive::from_u64(1));
|
}
|
||||||
assert_eq!(Some(one.into_le().low_128()), FromPrimitive::from_u64(1));
|
|
||||||
}
|
#[test]
|
||||||
|
fn test_hash_encode_decode() {
|
||||||
|
let hash = Sha256dHash::from_data(&[]);
|
||||||
|
let mut writer = Cursor::new(vec![]);
|
||||||
|
{
|
||||||
|
let mut encoder = json::Encoder::new(&mut writer);
|
||||||
|
assert!(hash.encode(&mut encoder).is_ok());
|
||||||
|
}
|
||||||
|
let res = writer.unwrap();
|
||||||
|
assert_eq!(&res.as_slice(),
|
||||||
|
"\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes());
|
||||||
|
assert_eq!(json::decode(from_utf8(res.as_slice()).unwrap()), Ok(hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sighash_single_vec() {
|
||||||
|
let one = Sha256dHash([1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assert_eq!(Some(one.into_le()), FromPrimitive::from_u64(1));
|
||||||
|
assert_eq!(Some(one.into_le().low_128()), FromPrimitive::from_u64(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -19,62 +19,62 @@
|
||||||
|
|
||||||
/// An iterator that returns pairs of elements
|
/// An iterator that returns pairs of elements
|
||||||
pub struct Pair<I>
|
pub struct Pair<I>
|
||||||
where I: Iterator
|
where I: Iterator
|
||||||
{
|
{
|
||||||
iter: I,
|
iter: I,
|
||||||
last_elem: Option<I::Item>
|
last_elem: Option<I::Item>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator> Iterator for Pair<I> {
|
impl<I: Iterator> Iterator for Pair<I> {
|
||||||
type Item = (I::Item, I::Item);
|
type Item = (I::Item, I::Item);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<(I::Item, I::Item)> {
|
fn next(&mut self) -> Option<(I::Item, I::Item)> {
|
||||||
let elem1 = self.iter.next();
|
let elem1 = self.iter.next();
|
||||||
if elem1.is_none() {
|
if elem1.is_none() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let elem2 = self.iter.next();
|
let elem2 = self.iter.next();
|
||||||
if elem2.is_none() {
|
if elem2.is_none() {
|
||||||
self.last_elem = elem1;
|
self.last_elem = elem1;
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((elem1.unwrap(), elem2.unwrap()))
|
Some((elem1.unwrap(), elem2.unwrap()))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
match self.iter.size_hint() {
|
match self.iter.size_hint() {
|
||||||
(n, None) => (n/2, None),
|
(n, None) => (n/2, None),
|
||||||
(n, Some(m)) => (n/2, Some(m/2))
|
(n, Some(m)) => (n/2, Some(m/2))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator> Pair<I> {
|
impl<I: Iterator> Pair<I> {
|
||||||
/// Returns the last element of the iterator if there were an odd
|
/// Returns the last element of the iterator if there were an odd
|
||||||
/// number of elements remaining before it was Pair-ified.
|
/// number of elements remaining before it was Pair-ified.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remainder(self) -> Option<I::Item> {
|
pub fn remainder(self) -> Option<I::Item> {
|
||||||
self.last_elem
|
self.last_elem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||||
pub trait Pairable {
|
pub trait Pairable {
|
||||||
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||||
fn pair(self) -> Pair<Self>;
|
fn pair(self) -> Pair<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator> Pairable for I {
|
impl<I: Iterator> Pairable for I {
|
||||||
/// Creates an iterator that yields pairs ef elements from the underlying
|
/// Creates an iterator that yields pairs ef elements from the underlying
|
||||||
/// iterator, yielding `None` when there are fewer than two elements to
|
/// iterator, yielding `None` when there are fewer than two elements to
|
||||||
/// return.
|
/// return.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pair(self) -> Pair<I> {
|
fn pair(self) -> Pair<I> {
|
||||||
Pair {iter: self, last_elem: None }
|
Pair {iter: self, last_elem: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
192
src/util/misc.rs
192
src/util/misc.rs
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -22,132 +22,132 @@ use util::iter::Pairable;
|
||||||
|
|
||||||
/// Convert a hexadecimal-encoded string to its corresponding bytes
|
/// Convert a hexadecimal-encoded string to its corresponding bytes
|
||||||
pub fn hex_bytes(s: &str) -> Result<Vec<u8>, Error> {
|
pub fn hex_bytes(s: &str) -> Result<Vec<u8>, Error> {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
let mut iter = s.chars().pair();
|
let mut iter = s.chars().pair();
|
||||||
// Do the parsing
|
// Do the parsing
|
||||||
try!(iter.fold(Ok(()), |e, (f, s)|
|
try!(iter.fold(Ok(()), |e, (f, s)|
|
||||||
if e.is_err() { e }
|
if e.is_err() { e }
|
||||||
else {
|
else {
|
||||||
match (f.to_digit(16), s.to_digit(16)) {
|
match (f.to_digit(16), s.to_digit(16)) {
|
||||||
(None, _) => Err(Error::Detail(
|
(None, _) => Err(Error::Detail(
|
||||||
format!("expected hex, got {:}", f),
|
format!("expected hex, got {:}", f),
|
||||||
Box::new(Error::ParseFailed)
|
Box::new(Error::ParseFailed)
|
||||||
|
)),
|
||||||
|
(_, None) => Err(Error::Detail(
|
||||||
|
format!("expected hex, got {:}", s),
|
||||||
|
Box::new(Error::ParseFailed)
|
||||||
|
)),
|
||||||
|
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
// Check that there was no remainder
|
||||||
|
match iter.remainder() {
|
||||||
|
Some(_) => Err(Error::Detail(
|
||||||
|
format!("hexstring of odd length"),
|
||||||
|
Box::new(Error::ParseFailed)
|
||||||
)),
|
)),
|
||||||
(_, None) => Err(Error::Detail(
|
None => Ok(v)
|
||||||
format!("expected hex, got {:}", s),
|
|
||||||
Box::new(Error::ParseFailed)
|
|
||||||
)),
|
|
||||||
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
));
|
|
||||||
// Check that there was no remainder
|
|
||||||
match iter.remainder() {
|
|
||||||
Some(_) => Err(Error::Detail(
|
|
||||||
format!("hexstring of odd length"),
|
|
||||||
Box::new(Error::ParseFailed)
|
|
||||||
)),
|
|
||||||
None => Ok(v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dump an error message to the screen
|
/// Dump an error message to the screen
|
||||||
/// TODO all uses of this should be replaced with some sort of logging infrastructure
|
/// TODO all uses of this should be replaced with some sort of logging infrastructure
|
||||||
pub fn consume_err<T>(s: &str, res: Result<T, Error>) {
|
pub fn consume_err<T>(s: &str, res: Result<T, Error>) {
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => { println!("{}: {:?}", s, e); }
|
Err(e) => { println!("{}: {:?}", s, e); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for `needle` in the vector `haystack` and remove every
|
/// Search for `needle` in the vector `haystack` and remove every
|
||||||
/// instance of it, returning the number of instances removed.
|
/// instance of it, returning the number of instances removed.
|
||||||
/// Loops through the vector opcode by opcode, skipping pushed data.
|
/// Loops through the vector opcode by opcode, skipping pushed data.
|
||||||
pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
|
pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
|
||||||
if needle.len() > haystack.len() { return 0; }
|
if needle.len() > haystack.len() { return 0; }
|
||||||
if needle.len() == 0 { return 0; }
|
if needle.len() == 0 { return 0; }
|
||||||
|
|
||||||
let mut top = haystack.len() - needle.len();
|
let mut top = haystack.len() - needle.len();
|
||||||
let mut n_deleted = 0;
|
let mut n_deleted = 0;
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i <= top {
|
while i <= top {
|
||||||
if &haystack[i..(i + needle.len())] == needle {
|
if &haystack[i..(i + needle.len())] == needle {
|
||||||
let v = &mut haystack;
|
let v = &mut haystack;
|
||||||
for j in i..top {
|
for j in i..top {
|
||||||
v.swap(j + needle.len(), j);
|
v.swap(j + needle.len(), j);
|
||||||
}
|
}
|
||||||
n_deleted += 1;
|
n_deleted += 1;
|
||||||
// This is ugly but prevents infinite loop in case of overflow
|
// This is ugly but prevents infinite loop in case of overflow
|
||||||
let overflow = top < needle.len();
|
let overflow = top < needle.len();
|
||||||
top -= needle.len();
|
top -= needle.len();
|
||||||
if overflow { break; }
|
if overflow { break; }
|
||||||
} else {
|
} else {
|
||||||
i += match opcodes::All::from_u8((*haystack)[i]).classify() {
|
i += match opcodes::All::from_u8((*haystack)[i]).classify() {
|
||||||
opcodes::Class::PushBytes(n) => n + 1,
|
opcodes::Class::PushBytes(n) => n as usize + 1,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
|
||||||
_ => 1
|
_ => 1
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
haystack.truncate(top + needle.len());
|
||||||
haystack.truncate(top + needle.len());
|
n_deleted
|
||||||
n_deleted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::prelude::*;
|
use std::prelude::*;
|
||||||
|
|
||||||
use super::script_find_and_remove;
|
use super::script_find_and_remove;
|
||||||
use super::hex_bytes;
|
use super::hex_bytes;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_script_find_and_remove() {
|
fn test_script_find_and_remove() {
|
||||||
let mut v = vec![101u8, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109];
|
let mut v = vec![101u8, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109];
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, []), 0);
|
assert_eq!(script_find_and_remove(&mut v, []), 0);
|
||||||
assert_eq!(script_find_and_remove(&mut v, [105, 105, 105]), 0);
|
assert_eq!(script_find_and_remove(&mut v, [105, 105, 105]), 0);
|
||||||
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109]);
|
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [105, 106, 107]), 1);
|
assert_eq!(script_find_and_remove(&mut v, [105, 106, 107]), 1);
|
||||||
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 108, 109]);
|
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 108, 109]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [104, 108, 109]), 1);
|
assert_eq!(script_find_and_remove(&mut v, [104, 108, 109]), 1);
|
||||||
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103]);
|
assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [101]), 1);
|
assert_eq!(script_find_and_remove(&mut v, [101]), 1);
|
||||||
assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]);
|
assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [102]), 3);
|
assert_eq!(script_find_and_remove(&mut v, [102]), 3);
|
||||||
assert_eq!(v, vec![103, 104, 103, 104, 103]);
|
assert_eq!(v, vec![103, 104, 103, 104, 103]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [103, 104]), 2);
|
assert_eq!(script_find_and_remove(&mut v, [103, 104]), 2);
|
||||||
assert_eq!(v, vec![103]);
|
assert_eq!(v, vec![103]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
|
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
|
||||||
assert_eq!(script_find_and_remove(&mut v, [105]), 0);
|
assert_eq!(script_find_and_remove(&mut v, [105]), 0);
|
||||||
assert_eq!(script_find_and_remove(&mut v, [103]), 1);
|
assert_eq!(script_find_and_remove(&mut v, [103]), 1);
|
||||||
assert_eq!(v, vec![]);
|
assert_eq!(v, vec![]);
|
||||||
|
|
||||||
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
|
assert_eq!(script_find_and_remove(&mut v, [105, 105, 5]), 0);
|
||||||
assert_eq!(script_find_and_remove(&mut v, [105]), 0);
|
assert_eq!(script_find_and_remove(&mut v, [105]), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_script_codesep_remove() {
|
fn test_script_codesep_remove() {
|
||||||
let mut s = vec![33u8, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 81];
|
let mut s = vec![33u8, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 81];
|
||||||
assert_eq!(script_find_and_remove(&mut s, [171]), 2);
|
assert_eq!(script_find_and_remove(&mut s, [171]), 2);
|
||||||
assert_eq!(s, vec![33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 81]);
|
assert_eq!(s, vec![33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 81]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hex_bytes() {
|
fn test_hex_bytes() {
|
||||||
assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]);
|
assert_eq!(&hex_bytes("abcd").unwrap(), &[171u8, 205]);
|
||||||
assert!(hex_bytes("abcde").is_err());
|
assert!(hex_bytes("abcde").is_err());
|
||||||
assert!(hex_bytes("aBcDeF").is_ok());
|
assert!(hex_bytes("aBcDeF").is_ok());
|
||||||
assert!(hex_bytes("aBcD4eFL").is_err());
|
assert!(hex_bytes("aBcD4eFL").is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
836
src/util/uint.rs
836
src/util/uint.rs
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
//
|
//
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
|
@ -25,483 +25,483 @@ use network::serialize::RawEncoder;
|
||||||
use util::BitArray;
|
use util::BitArray;
|
||||||
|
|
||||||
macro_rules! construct_uint {
|
macro_rules! construct_uint {
|
||||||
($name:ident, $n_words:expr) => (
|
($name:ident, $n_words:expr) => (
|
||||||
/// Little-endian large integer type
|
/// Little-endian large integer type
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct $name(pub [u64; $n_words]);
|
pub struct $name(pub [u64; $n_words]);
|
||||||
impl_array_newtype!($name, u64, $n_words);
|
impl_array_newtype!($name, u64, $n_words);
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
/// Conversion to u32
|
/// Conversion to u32
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn low_u32(&self) -> u32 {
|
pub fn low_u32(&self) -> u32 {
|
||||||
let &$name(ref arr) = self;
|
let &$name(ref arr) = self;
|
||||||
arr[0] as u32
|
arr[0] as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the least number of bits needed to represent the number
|
/// Return the least number of bits needed to represent the number
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bits(&self) -> usize {
|
pub fn bits(&self) -> usize {
|
||||||
let &$name(ref arr) = self;
|
let &$name(ref arr) = self;
|
||||||
for i in 1..$n_words {
|
for i in 1..$n_words {
|
||||||
if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; }
|
if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; }
|
||||||
}
|
}
|
||||||
0x40 - arr[0].leading_zeros() as usize
|
0x40 - arr[0].leading_zeros() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multiplication by u32
|
/// Multiplication by u32
|
||||||
pub fn mul_u32(self, other: u32) -> $name {
|
pub fn mul_u32(self, other: u32) -> $name {
|
||||||
let $name(ref arr) = self;
|
let $name(ref arr) = self;
|
||||||
let mut carry = [0u64; $n_words];
|
let mut carry = [0u64; $n_words];
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
for i in 0..$n_words {
|
||||||
let upper = other as u64 * (arr[i] >> 32);
|
let upper = other as u64 * (arr[i] >> 32);
|
||||||
let lower = other as u64 * (arr[i] & 0xFFFFFFFF);
|
let lower = other as u64 * (arr[i] & 0xFFFFFFFF);
|
||||||
if i < 3 {
|
if i < 3 {
|
||||||
carry[i + 1] += upper >> 32;
|
carry[i + 1] += upper >> 32;
|
||||||
}
|
}
|
||||||
ret[i] = lower + (upper << 32);
|
ret[i] = lower + (upper << 32);
|
||||||
}
|
}
|
||||||
$name(ret) + $name(carry)
|
$name(ret) + $name(carry)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::num::FromPrimitive for $name {
|
|
||||||
#[inline]
|
|
||||||
fn from_u64(init: u64) -> Option<$name> {
|
|
||||||
let mut ret = [0; $n_words];
|
|
||||||
ret[0] = init;
|
|
||||||
Some($name(ret))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_i64(init: i64) -> Option<$name> {
|
|
||||||
::std::num::FromPrimitive::from_u64(init as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::num::Zero for $name {
|
|
||||||
fn zero() -> $name { $name([0; $n_words]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::num::One for $name {
|
|
||||||
fn one() -> $name {
|
|
||||||
$name({ let mut ret = [0; $n_words]; ret[0] = 1; ret })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Add<$name> for $name {
|
|
||||||
type Output = $name;
|
|
||||||
|
|
||||||
fn add(self, other: $name) -> $name {
|
|
||||||
let $name(ref me) = self;
|
|
||||||
let $name(ref you) = other;
|
|
||||||
let mut ret = [0u64; $n_words];
|
|
||||||
let mut carry = [0u64; $n_words];
|
|
||||||
let mut b_carry = false;
|
|
||||||
for i in 0..$n_words {
|
|
||||||
ret[i] = me[i] + you[i];
|
|
||||||
if i < $n_words - 1 && ret[i] < me[i] {
|
|
||||||
carry[i + 1] = 1;
|
|
||||||
b_carry = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b_carry { $name(ret) + $name(carry) } else { $name(ret) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Sub<$name> for $name {
|
|
||||||
type Output = $name;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, other: $name) -> $name {
|
|
||||||
self + !other + One::one()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Mul<$name> for $name {
|
|
||||||
type Output = $name;
|
|
||||||
|
|
||||||
fn mul(self, other: $name) -> $name {
|
|
||||||
let mut me = self;
|
|
||||||
// TODO: be more efficient about this
|
|
||||||
for i in 0..(2 * $n_words) {
|
|
||||||
me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
|
|
||||||
}
|
|
||||||
me
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Div<$name> for $name {
|
|
||||||
type Output = $name;
|
|
||||||
|
|
||||||
fn div(self, other: $name) -> $name {
|
|
||||||
let mut sub_copy = self;
|
|
||||||
let mut shift_copy = other;
|
|
||||||
let mut ret = [0u64; $n_words];
|
|
||||||
|
|
||||||
let my_bits = self.bits();
|
|
||||||
let your_bits = other.bits();
|
|
||||||
|
|
||||||
// Check for division by 0
|
|
||||||
assert!(your_bits != 0);
|
|
||||||
|
|
||||||
// Early return in case we are dividing by a larger number than us
|
|
||||||
if my_bits < your_bits {
|
|
||||||
return $name(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bitwise long division
|
impl ::std::num::FromPrimitive for $name {
|
||||||
let mut shift = my_bits - your_bits;
|
#[inline]
|
||||||
shift_copy = shift_copy << shift;
|
fn from_u64(init: u64) -> Option<$name> {
|
||||||
loop {
|
let mut ret = [0; $n_words];
|
||||||
if sub_copy >= shift_copy {
|
ret[0] = init;
|
||||||
ret[shift / 64] |= 1 << (shift % 64);
|
Some($name(ret))
|
||||||
sub_copy = sub_copy - shift_copy;
|
}
|
||||||
}
|
|
||||||
shift_copy = shift_copy >> 1;
|
#[inline]
|
||||||
if shift == 0 { break; }
|
fn from_i64(init: i64) -> Option<$name> {
|
||||||
shift -= 1;
|
::std::num::FromPrimitive::from_u64(init as u64)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$name(ret)
|
impl ::std::num::Zero for $name {
|
||||||
}
|
fn zero() -> $name { $name([0; $n_words]) }
|
||||||
}
|
|
||||||
|
|
||||||
impl BitArray for $name {
|
|
||||||
#[inline]
|
|
||||||
fn bit(&self, index: usize) -> bool {
|
|
||||||
let &$name(ref arr) = self;
|
|
||||||
arr[index / 64] & (1 << (index % 64)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bit_slice(&self, start: usize, end: usize) -> $name {
|
|
||||||
(*self >> start).mask(end - start)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mask(&self, n: usize) -> $name {
|
|
||||||
let &$name(ref arr) = self;
|
|
||||||
let mut ret = [0; $n_words];
|
|
||||||
for i in 0..$n_words {
|
|
||||||
if n >= 0x40 * (i + 1) {
|
|
||||||
ret[i] = arr[i];
|
|
||||||
} else {
|
|
||||||
ret[i] = arr[i] & ((1 << (n - 0x40 * i)) - 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
impl ::std::num::One for $name {
|
||||||
fn trailing_zeros(&self) -> usize {
|
fn one() -> $name {
|
||||||
let &$name(ref arr) = self;
|
$name({ let mut ret = [0; $n_words]; ret[0] = 1; ret })
|
||||||
for i in 0..($n_words - 1) {
|
}
|
||||||
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros() as usize; }
|
|
||||||
}
|
}
|
||||||
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::BitAnd<$name> for $name {
|
impl ::std::ops::Add<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
fn add(self, other: $name) -> $name {
|
||||||
fn bitand(self, other: $name) -> $name {
|
let $name(ref me) = self;
|
||||||
let $name(ref arr1) = self;
|
let $name(ref you) = other;
|
||||||
let $name(ref arr2) = other;
|
let mut ret = [0u64; $n_words];
|
||||||
let mut ret = [0u64; $n_words];
|
let mut carry = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
let mut b_carry = false;
|
||||||
ret[i] = arr1[i] & arr2[i];
|
for i in 0..$n_words {
|
||||||
|
ret[i] = me[i] + you[i];
|
||||||
|
if i < $n_words - 1 && ret[i] < me[i] {
|
||||||
|
carry[i + 1] = 1;
|
||||||
|
b_carry = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b_carry { $name(ret) + $name(carry) } else { $name(ret) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::BitXor<$name> for $name {
|
impl ::std::ops::Sub<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitxor(self, other: $name) -> $name {
|
fn sub(self, other: $name) -> $name {
|
||||||
let $name(ref arr1) = self;
|
self + !other + One::one()
|
||||||
let $name(ref arr2) = other;
|
}
|
||||||
let mut ret = [0u64; $n_words];
|
|
||||||
for i in 0..$n_words {
|
|
||||||
ret[i] = arr1[i] ^ arr2[i];
|
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::BitOr<$name> for $name {
|
impl ::std::ops::Mul<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
fn mul(self, other: $name) -> $name {
|
||||||
fn bitor(self, other: $name) -> $name {
|
let mut me = self;
|
||||||
let $name(ref arr1) = self;
|
// TODO: be more efficient about this
|
||||||
let $name(ref arr2) = other;
|
for i in 0..(2 * $n_words) {
|
||||||
let mut ret = [0u64; $n_words];
|
me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
|
||||||
for i in 0..$n_words {
|
}
|
||||||
ret[i] = arr1[i] | arr2[i];
|
me
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Not for $name {
|
impl ::std::ops::Div<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[inline]
|
fn div(self, other: $name) -> $name {
|
||||||
fn not(self) -> $name {
|
let mut sub_copy = self;
|
||||||
let $name(ref arr) = self;
|
let mut shift_copy = other;
|
||||||
let mut ret = [0u64; $n_words];
|
let mut ret = [0u64; $n_words];
|
||||||
for i in 0..$n_words {
|
|
||||||
ret[i] = !arr[i];
|
let my_bits = self.bits();
|
||||||
|
let your_bits = other.bits();
|
||||||
|
|
||||||
|
// Check for division by 0
|
||||||
|
assert!(your_bits != 0);
|
||||||
|
|
||||||
|
// Early return in case we are dividing by a larger number than us
|
||||||
|
if my_bits < your_bits {
|
||||||
|
return $name(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise long division
|
||||||
|
let mut shift = my_bits - your_bits;
|
||||||
|
shift_copy = shift_copy << shift;
|
||||||
|
loop {
|
||||||
|
if sub_copy >= shift_copy {
|
||||||
|
ret[shift / 64] |= 1 << (shift % 64);
|
||||||
|
sub_copy = sub_copy - shift_copy;
|
||||||
|
}
|
||||||
|
shift_copy = shift_copy >> 1;
|
||||||
|
if shift == 0 { break; }
|
||||||
|
shift -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Shl<usize> for $name {
|
impl BitArray for $name {
|
||||||
type Output = $name;
|
#[inline]
|
||||||
|
fn bit(&self, index: usize) -> bool {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
arr[index / 64] & (1 << (index % 64)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
fn shl(self, shift: usize) -> $name {
|
#[inline]
|
||||||
let $name(ref original) = self;
|
fn bit_slice(&self, start: usize, end: usize) -> $name {
|
||||||
let mut ret = [0u64; $n_words];
|
(*self >> start).mask(end - start)
|
||||||
let word_shift = shift / 64;
|
}
|
||||||
let bit_shift = shift % 64;
|
|
||||||
for i in 0..$n_words {
|
#[inline]
|
||||||
// Shift
|
fn mask(&self, n: usize) -> $name {
|
||||||
if bit_shift < 64 && i + word_shift < $n_words {
|
let &$name(ref arr) = self;
|
||||||
ret[i + word_shift] += original[i] << bit_shift;
|
let mut ret = [0; $n_words];
|
||||||
}
|
for i in 0..$n_words {
|
||||||
// Carry
|
if n >= 0x40 * (i + 1) {
|
||||||
if bit_shift > 0 && i + word_shift + 1 < $n_words {
|
ret[i] = arr[i];
|
||||||
ret[i + word_shift + 1] += original[i] >> (64 - bit_shift);
|
} else {
|
||||||
}
|
ret[i] = arr[i] & ((1 << (n - 0x40 * i)) - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn trailing_zeros(&self) -> usize {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
for i in 0..($n_words - 1) {
|
||||||
|
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros() as usize; }
|
||||||
|
}
|
||||||
|
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Shr<usize> for $name {
|
impl ::std::ops::BitAnd<$name> for $name {
|
||||||
type Output = $name;
|
type Output = $name;
|
||||||
|
|
||||||
#[allow(unsigned_negate)]
|
#[inline]
|
||||||
fn shr(self, shift: usize) -> $name {
|
fn bitand(self, other: $name) -> $name {
|
||||||
let $name(ref original) = self;
|
let $name(ref arr1) = self;
|
||||||
let mut ret = [0u64; $n_words];
|
let $name(ref arr2) = other;
|
||||||
let word_shift = shift / 64;
|
let mut ret = [0u64; $n_words];
|
||||||
let bit_shift = shift % 64;
|
for i in 0..$n_words {
|
||||||
for i in 0..$n_words {
|
ret[i] = arr1[i] & arr2[i];
|
||||||
// Shift
|
}
|
||||||
if bit_shift < 64 && i - word_shift < $n_words {
|
$name(ret)
|
||||||
ret[i - word_shift] += original[i] >> bit_shift;
|
}
|
||||||
}
|
|
||||||
// Carry
|
|
||||||
if bit_shift > 0 && i - word_shift - 1 < $n_words {
|
|
||||||
ret[i - word_shift - 1] += original[i] << (64 - bit_shift);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$name(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::cmp::Ord for $name {
|
impl ::std::ops::BitXor<$name> for $name {
|
||||||
fn cmp(&self, other: &$name) -> ::std::cmp::Ordering {
|
type Output = $name;
|
||||||
let &$name(ref me) = self;
|
|
||||||
let &$name(ref you) = other;
|
#[inline]
|
||||||
for i in 0..$n_words {
|
fn bitxor(self, other: $name) -> $name {
|
||||||
if me[$n_words - 1 - i] < you[$n_words - 1 - i] { return ::std::cmp::Ordering::Less; }
|
let $name(ref arr1) = self;
|
||||||
if me[$n_words - 1 - i] > you[$n_words - 1 - i] { return ::std::cmp::Ordering::Greater; }
|
let $name(ref arr2) = other;
|
||||||
|
let mut ret = [0u64; $n_words];
|
||||||
|
for i in 0..$n_words {
|
||||||
|
ret[i] = arr1[i] ^ arr2[i];
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ::std::cmp::Ordering::Equal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::cmp::PartialOrd for $name {
|
impl ::std::ops::BitOr<$name> for $name {
|
||||||
fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> {
|
type Output = $name;
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for $name {
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn bitor(self, other: $name) -> $name {
|
||||||
let &$name(ref data) = self;
|
let $name(ref arr1) = self;
|
||||||
try!(write!(f, "0x"));
|
let $name(ref arr2) = other;
|
||||||
for ch in data.iter().rev() {
|
let mut ret = [0u64; $n_words];
|
||||||
try!(write!(f, "{:02x}", ch));
|
for i in 0..$n_words {
|
||||||
|
ret[i] = arr1[i] | arr2[i];
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $name {
|
impl ::std::ops::Not for $name {
|
||||||
#[inline]
|
type Output = $name;
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
|
||||||
use network::encodable::ConsensusEncodable;
|
|
||||||
let &$name(ref data) = self;
|
|
||||||
for word in data.iter() { try!(word.consensus_encode(s)); }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $name {
|
#[inline]
|
||||||
fn consensus_decode(d: &mut D) -> Result<$name, D::Error> {
|
fn not(self) -> $name {
|
||||||
use network::encodable::ConsensusDecodable;
|
let $name(ref arr) = self;
|
||||||
let ret: [u64; $n_words] = try!(ConsensusDecodable::consensus_decode(d));
|
let mut ret = [0u64; $n_words];
|
||||||
Ok($name(ret))
|
for i in 0..$n_words {
|
||||||
}
|
ret[i] = !arr[i];
|
||||||
}
|
}
|
||||||
);
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Shl<usize> for $name {
|
||||||
|
type Output = $name;
|
||||||
|
|
||||||
|
fn shl(self, shift: usize) -> $name {
|
||||||
|
let $name(ref original) = self;
|
||||||
|
let mut ret = [0u64; $n_words];
|
||||||
|
let word_shift = shift / 64;
|
||||||
|
let bit_shift = shift % 64;
|
||||||
|
for i in 0..$n_words {
|
||||||
|
// Shift
|
||||||
|
if bit_shift < 64 && i + word_shift < $n_words {
|
||||||
|
ret[i + word_shift] += original[i] << bit_shift;
|
||||||
|
}
|
||||||
|
// Carry
|
||||||
|
if bit_shift > 0 && i + word_shift + 1 < $n_words {
|
||||||
|
ret[i + word_shift + 1] += original[i] >> (64 - bit_shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Shr<usize> for $name {
|
||||||
|
type Output = $name;
|
||||||
|
|
||||||
|
#[allow(unsigned_negate)]
|
||||||
|
fn shr(self, shift: usize) -> $name {
|
||||||
|
let $name(ref original) = self;
|
||||||
|
let mut ret = [0u64; $n_words];
|
||||||
|
let word_shift = shift / 64;
|
||||||
|
let bit_shift = shift % 64;
|
||||||
|
for i in 0..$n_words {
|
||||||
|
// Shift
|
||||||
|
if bit_shift < 64 && i - word_shift < $n_words {
|
||||||
|
ret[i - word_shift] += original[i] >> bit_shift;
|
||||||
|
}
|
||||||
|
// Carry
|
||||||
|
if bit_shift > 0 && i - word_shift - 1 < $n_words {
|
||||||
|
ret[i - word_shift - 1] += original[i] << (64 - bit_shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::cmp::Ord for $name {
|
||||||
|
fn cmp(&self, other: &$name) -> ::std::cmp::Ordering {
|
||||||
|
let &$name(ref me) = self;
|
||||||
|
let &$name(ref you) = other;
|
||||||
|
for i in 0..$n_words {
|
||||||
|
if me[$n_words - 1 - i] < you[$n_words - 1 - i] { return ::std::cmp::Ordering::Less; }
|
||||||
|
if me[$n_words - 1 - i] > you[$n_words - 1 - i] { return ::std::cmp::Ordering::Greater; }
|
||||||
|
}
|
||||||
|
return ::std::cmp::Ordering::Equal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::cmp::PartialOrd for $name {
|
||||||
|
fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for $name {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let &$name(ref data) = self;
|
||||||
|
try!(write!(f, "0x"));
|
||||||
|
for ch in data.iter().rev() {
|
||||||
|
try!(write!(f, "{:02x}", ch));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: ::network::serialize::SimpleEncoder> ::network::encodable::ConsensusEncodable<S> for $name {
|
||||||
|
#[inline]
|
||||||
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
|
use network::encodable::ConsensusEncodable;
|
||||||
|
let &$name(ref data) = self;
|
||||||
|
for word in data.iter() { try!(word.consensus_encode(s)); }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: ::network::serialize::SimpleDecoder> ::network::encodable::ConsensusDecodable<D> for $name {
|
||||||
|
fn consensus_decode(d: &mut D) -> Result<$name, D::Error> {
|
||||||
|
use network::encodable::ConsensusDecodable;
|
||||||
|
let ret: [u64; $n_words] = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
|
Ok($name(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
construct_uint!(Uint256, 4);
|
construct_uint!(Uint256, 4);
|
||||||
construct_uint!(Uint128, 2);
|
construct_uint!(Uint128, 2);
|
||||||
|
|
||||||
impl Uint256 {
|
impl Uint256 {
|
||||||
/// Increment by 1
|
/// Increment by 1
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn increment(&mut self) {
|
pub fn increment(&mut self) {
|
||||||
let &mut Uint256(ref mut arr) = self;
|
let &mut Uint256(ref mut arr) = self;
|
||||||
arr[0] += 1;
|
arr[0] += 1;
|
||||||
if arr[0] == 0 {
|
if arr[0] == 0 {
|
||||||
arr[1] += 1;
|
arr[1] += 1;
|
||||||
if arr[1] == 0 {
|
if arr[1] == 0 {
|
||||||
arr[2] += 1;
|
arr[2] += 1;
|
||||||
if arr[2] == 0 {
|
if arr[2] == 0 {
|
||||||
arr[3] += 1;
|
arr[3] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Decay to a uint128
|
/// Decay to a uint128
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn low_128(&self) -> Uint128 {
|
pub fn low_128(&self) -> Uint128 {
|
||||||
let &Uint256(data) = self;
|
let &Uint256(data) = self;
|
||||||
Uint128([data[0], data[1]])
|
Uint128([data[0], data[1]])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::num::from_u64;
|
use std::num::from_u64;
|
||||||
|
|
||||||
use network::serialize::{deserialize, serialize};
|
use network::serialize::{deserialize, serialize};
|
||||||
use util::uint::Uint256;
|
use util::uint::Uint256;
|
||||||
use util::BitArray;
|
use util::BitArray;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn uint256_bits_test() {
|
pub fn uint256_bits_test() {
|
||||||
assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8);
|
assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8);
|
||||||
assert_eq!(from_u64::<Uint256>(256).unwrap().bits(), 9);
|
assert_eq!(from_u64::<Uint256>(256).unwrap().bits(), 9);
|
||||||
assert_eq!(from_u64::<Uint256>(300).unwrap().bits(), 9);
|
assert_eq!(from_u64::<Uint256>(300).unwrap().bits(), 9);
|
||||||
assert_eq!(from_u64::<Uint256>(60000).unwrap().bits(), 16);
|
assert_eq!(from_u64::<Uint256>(60000).unwrap().bits(), 16);
|
||||||
assert_eq!(from_u64::<Uint256>(70000).unwrap().bits(), 17);
|
assert_eq!(from_u64::<Uint256>(70000).unwrap().bits(), 17);
|
||||||
|
|
||||||
// Try to read the following lines out loud quickly
|
// Try to read the following lines out loud quickly
|
||||||
let mut shl: Uint256 = from_u64(70000).unwrap();
|
let mut shl: Uint256 = from_u64(70000).unwrap();
|
||||||
shl = shl << 100;
|
shl = shl << 100;
|
||||||
assert_eq!(shl.bits(), 117);
|
assert_eq!(shl.bits(), 117);
|
||||||
shl = shl << 100;
|
shl = shl << 100;
|
||||||
assert_eq!(shl.bits(), 217);
|
assert_eq!(shl.bits(), 217);
|
||||||
shl = shl << 100;
|
shl = shl << 100;
|
||||||
assert_eq!(shl.bits(), 0);
|
assert_eq!(shl.bits(), 0);
|
||||||
|
|
||||||
// Bit set check
|
// Bit set check
|
||||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(0));
|
assert!(!from_u64::<Uint256>(10).unwrap().bit(0));
|
||||||
assert!(from_u64::<Uint256>(10).unwrap().bit(1));
|
assert!(from_u64::<Uint256>(10).unwrap().bit(1));
|
||||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(2));
|
assert!(!from_u64::<Uint256>(10).unwrap().bit(2));
|
||||||
assert!(from_u64::<Uint256>(10).unwrap().bit(3));
|
assert!(from_u64::<Uint256>(10).unwrap().bit(3));
|
||||||
assert!(!from_u64::<Uint256>(10).unwrap().bit(4));
|
assert!(!from_u64::<Uint256>(10).unwrap().bit(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn uint256_comp_test() {
|
pub fn uint256_comp_test() {
|
||||||
let small = Uint256([10u64, 0, 0, 0]);
|
let small = Uint256([10u64, 0, 0, 0]);
|
||||||
let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]);
|
let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]);
|
||||||
|
|
||||||
assert!(small < big);
|
assert!(small < big);
|
||||||
assert!(big < bigger);
|
assert!(big < bigger);
|
||||||
assert!(bigger < biggest);
|
assert!(bigger < biggest);
|
||||||
assert!(bigger <= biggest);
|
assert!(bigger <= biggest);
|
||||||
assert!(biggest <= biggest);
|
assert!(biggest <= biggest);
|
||||||
assert!(bigger >= big);
|
assert!(bigger >= big);
|
||||||
assert!(bigger >= small);
|
assert!(bigger >= small);
|
||||||
assert!(small <= small);
|
assert!(small <= small);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn uint256_arithmetic_test() {
|
pub fn uint256_arithmetic_test() {
|
||||||
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
let copy = init;
|
let copy = init;
|
||||||
|
|
||||||
let add = init + copy;
|
let add = init + copy;
|
||||||
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
||||||
// Bitshifts
|
// Bitshifts
|
||||||
let shl = add << 88;
|
let shl = add << 88;
|
||||||
assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0]));
|
assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0]));
|
||||||
let shr = shl >> 40;
|
let shr = shl >> 40;
|
||||||
assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||||
// Increment
|
// Increment
|
||||||
let mut incr = shr;
|
let mut incr = shr;
|
||||||
incr.increment();
|
incr.increment();
|
||||||
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||||
// Subtraction
|
// Subtraction
|
||||||
let sub = incr - init;
|
let sub = incr - init;
|
||||||
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||||
// Multiplication
|
// Multiplication
|
||||||
let mult = sub.mul_u32(300);
|
let mult = sub.mul_u32(300);
|
||||||
assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]));
|
assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]));
|
||||||
// Division
|
// Division
|
||||||
assert_eq!(from_u64::<Uint256>(105).unwrap() /
|
assert_eq!(from_u64::<Uint256>(105).unwrap() /
|
||||||
from_u64::<Uint256>(5).unwrap(),
|
from_u64::<Uint256>(5).unwrap(),
|
||||||
from_u64::<Uint256>(21).unwrap());
|
from_u64::<Uint256>(21).unwrap());
|
||||||
let div = mult / from_u64::<Uint256>(300).unwrap();
|
let div = mult / from_u64::<Uint256>(300).unwrap();
|
||||||
assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||||
// TODO: bit inversion
|
// TODO: bit inversion
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn uint256_bitslice_test() {
|
pub fn uint256_bitslice_test() {
|
||||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
let add = init + (init << 64);
|
let add = init + (init << 64);
|
||||||
assert_eq!(add.bit_slice(64, 128), init);
|
assert_eq!(add.bit_slice(64, 128), init);
|
||||||
assert_eq!(add.mask(64), init);
|
assert_eq!(add.mask(64), init);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn uint256_extreme_bitshift_test() {
|
pub fn uint256_extreme_bitshift_test() {
|
||||||
// Shifting a u64 by 64 bits gives an undefined value, so make sure that
|
// Shifting a u64 by 64 bits gives an undefined value, so make sure that
|
||||||
// we're doing the Right Thing here
|
// we're doing the Right Thing here
|
||||||
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
|
|
||||||
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
let add = (init << 64) + init;
|
let add = (init << 64) + init;
|
||||||
assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
|
assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
|
||||||
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
|
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn uint256_serialize_test() {
|
pub fn uint256_serialize_test() {
|
||||||
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
||||||
let serial1 = serialize(&start1).unwrap();
|
let serial1 = serialize(&start1).unwrap();
|
||||||
let serial2 = serialize(&start2).unwrap();
|
let serial2 = serialize(&start2).unwrap();
|
||||||
let end1: io::Result<Uint256> = deserialize(serial1);
|
let end1: io::Result<Uint256> = deserialize(serial1);
|
||||||
let end2: io::Result<Uint256> = deserialize(serial2);
|
let end2: io::Result<Uint256> = deserialize(serial2);
|
||||||
|
|
||||||
assert_eq!(end1, Ok(start1));
|
assert_eq!(end1, Ok(start1));
|
||||||
assert_eq!(end2, Ok(start2));
|
assert_eq!(end2, Ok(start2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
// the public domain worldwide. This software is distributed without
|
// the public domain worldwide. This software is distributed without
|
||||||
|
@ -21,7 +21,7 @@ use crypto::digest::Digest;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::{Script, ScriptBuilder};
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::hash::Ripemd160Hash;
|
use util::hash::Ripemd160Hash;
|
||||||
|
@ -30,189 +30,189 @@ use util::base58::{self, FromBase58, ToBase58};
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
/// A Bitcoin address
|
/// A Bitcoin address
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
/// The network on which this address is usable
|
/// The network on which this address is usable
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
/// The pubkeyhash that this address encodes
|
/// The pubkeyhash that this address encodes
|
||||||
pub hash: Ripemd160Hash
|
pub hash: Ripemd160Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
/// Creates an address from a public key
|
/// Creates an address from a public key
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_key(network: Network, pk: &PublicKey) -> Address {
|
pub fn from_key(network: Network, pk: &PublicKey) -> Address {
|
||||||
let mut sha = Sha256::new();
|
let mut sha = Sha256::new();
|
||||||
let mut out = [0;32];
|
let mut out = [0;32];
|
||||||
sha.input(&pk[..]);
|
sha.input(&pk[..]);
|
||||||
sha.result(&mut out);
|
sha.result(&mut out);
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
hash: Ripemd160Hash::from_data(&out)
|
hash: Ripemd160Hash::from_data(&out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a script pubkey spending to this address
|
/// Generates a script pubkey spending to this address
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn script_pubkey(&self) -> Script {
|
pub fn script_pubkey(&self) -> Script {
|
||||||
let mut script = Script::new();
|
let mut script = ScriptBuilder::new();
|
||||||
script.push_opcode(opcodes::All::OP_DUP);
|
script.push_opcode(opcodes::All::OP_DUP);
|
||||||
script.push_opcode(opcodes::All::OP_HASH160);
|
script.push_opcode(opcodes::All::OP_HASH160);
|
||||||
script.push_slice(&self.hash[..]);
|
script.push_slice(&self.hash[..]);
|
||||||
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
||||||
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||||
script
|
script.into_script()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<usize> for Address {
|
impl ops::Index<usize> for Address {
|
||||||
type Output = u8;
|
type Output = u8;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: usize) -> &u8 {
|
fn index(&self, index: usize) -> &u8 {
|
||||||
&self.hash[index]
|
&self.hash[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::Range<usize>> for Address {
|
impl ops::Index<ops::Range<usize>> for Address {
|
||||||
type Output = [u8];
|
type Output = [u8];
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
||||||
&self.hash[index]
|
&self.hash[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::RangeTo<usize>> for Address {
|
impl ops::Index<ops::RangeTo<usize>> for Address {
|
||||||
type Output = [u8];
|
type Output = [u8];
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
||||||
&self.hash[index]
|
&self.hash[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFrom<usize>> for Address {
|
impl ops::Index<ops::RangeFrom<usize>> for Address {
|
||||||
type Output = [u8];
|
type Output = [u8];
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
||||||
&self.hash[index]
|
&self.hash[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFull> for Address {
|
impl ops::Index<ops::RangeFull> for Address {
|
||||||
type Output = [u8];
|
type Output = [u8];
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
||||||
&self.hash[..]
|
&self.hash[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conversion from other types into an address
|
/// Conversion from other types into an address
|
||||||
pub trait ToAddress {
|
pub trait ToAddress {
|
||||||
/// Copies `self` into a new `Address`
|
/// Copies `self` into a new `Address`
|
||||||
fn to_address(&self, network: Network) -> Address;
|
fn to_address(&self, network: Network) -> Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToAddress for &'a [u8] {
|
impl<'a> ToAddress for &'a [u8] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_address(&self, network: Network) -> Address {
|
fn to_address(&self, network: Network) -> Address {
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
hash: Ripemd160Hash::from_slice(*self)
|
hash: Ripemd160Hash::from_slice(*self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBase58 for Address {
|
impl ToBase58 for Address {
|
||||||
fn base58_layout(&self) -> Vec<u8> {
|
fn base58_layout(&self) -> Vec<u8> {
|
||||||
let mut ret = vec![
|
let mut ret = vec![
|
||||||
match self.network {
|
match self.network {
|
||||||
Network::Bitcoin => 0,
|
Network::Bitcoin => 0,
|
||||||
Network::Testnet => 111
|
Network::Testnet => 111
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
ret.push_all(&self.hash[..]);
|
ret.push_all(&self.hash[..]);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromBase58 for Address {
|
impl FromBase58 for Address {
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> {
|
fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> {
|
||||||
if data.len() != 21 {
|
if data.len() != 21 {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Address {
|
Ok(Address {
|
||||||
network: match data[0] {
|
network: match data[0] {
|
||||||
0 => Network::Bitcoin,
|
0 => Network::Bitcoin,
|
||||||
111 => Network::Testnet,
|
111 => Network::Testnet,
|
||||||
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
||||||
},
|
},
|
||||||
hash: Ripemd160Hash::from_slice(&data[1..])
|
hash: Ripemd160Hash::from_slice(&data[1..])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Debug for Address {
|
impl ::std::fmt::Debug for Address {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
write!(f, "{}", self.to_base58check())
|
write!(f, "{}", self.to_base58check())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
use test::{Bencher, black_box};
|
use test::{Bencher, black_box};
|
||||||
|
|
||||||
use secp256k1::Secp256k1;
|
use secp256k1::Secp256k1;
|
||||||
|
|
||||||
use network::constants::Network::Bitcoin;
|
use network::constants::Network::Bitcoin;
|
||||||
use util::hash::Ripemd160Hash;
|
use util::hash::Ripemd160Hash;
|
||||||
use util::base58::{FromBase58, ToBase58};
|
use util::base58::{FromBase58, ToBase58};
|
||||||
use super::Address;
|
use super::Address;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_address_58() {
|
fn test_address_58() {
|
||||||
let addr = Address {
|
let addr = Address {
|
||||||
network: Bitcoin,
|
network: Bitcoin,
|
||||||
hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap())
|
hash: Ripemd160Hash::from_slice(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
||||||
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_address(bh: &mut Bencher) {
|
pub fn generate_address(bh: &mut Bencher) {
|
||||||
let mut s = Secp256k1::new().unwrap();
|
let mut s = Secp256k1::new().unwrap();
|
||||||
bh.iter( || {
|
bh.iter( || {
|
||||||
let (sk, pk) = s.generate_keypair(true);
|
let (sk, pk) = s.generate_keypair(true);
|
||||||
black_box(sk);
|
black_box(sk);
|
||||||
black_box(pk);
|
black_box(pk);
|
||||||
let addr = Address::from_key(Bitcoin, &pk);
|
let addr = Address::from_key(Bitcoin, &pk);
|
||||||
black_box(addr);
|
black_box(addr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_uncompressed_address(bh: &mut Bencher) {
|
pub fn generate_uncompressed_address(bh: &mut Bencher) {
|
||||||
let mut s = Secp256k1::new().unwrap();
|
let mut s = Secp256k1::new().unwrap();
|
||||||
bh.iter( || {
|
bh.iter( || {
|
||||||
let (sk, pk) = s.generate_keypair(false);
|
let (sk, pk) = s.generate_keypair(false);
|
||||||
black_box(sk);
|
black_box(sk);
|
||||||
black_box(pk);
|
black_box(pk);
|
||||||
let addr = Address::from_key(Bitcoin, &pk);
|
let addr = Address::from_key(Bitcoin, &pk);
|
||||||
black_box(addr);
|
black_box(addr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_sequential_address(bh: &mut Bencher) {
|
pub fn generate_sequential_address(bh: &mut Bencher) {
|
||||||
let mut s = Secp256k1::new().unwrap();
|
let mut s = Secp256k1::new().unwrap();
|
||||||
let (sk, _) = s.generate_keypair(true);
|
let (sk, _) = s.generate_keypair(true);
|
||||||
let mut iter = sk.sequence(true);
|
let mut iter = sk.sequence(true);
|
||||||
bh.iter( || {
|
bh.iter( || {
|
||||||
let (sk, pk) = iter.next().unwrap();
|
let (sk, pk) = iter.next().unwrap();
|
||||||
black_box(sk);
|
black_box(sk);
|
||||||
let addr = Address::from_key(Bitcoin, &pk);
|
let addr = Address::from_key(Bitcoin, &pk);
|
||||||
black_box(addr);
|
black_box(addr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
// the public domain worldwide. This software is distributed without
|
// the public domain worldwide. This software is distributed without
|
||||||
|
@ -35,100 +35,100 @@ use util::hash::Sha256dHash;
|
||||||
/// The type of a wallet-spendable txout
|
/// The type of a wallet-spendable txout
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum WalletTxOutType {
|
pub enum WalletTxOutType {
|
||||||
/// Pay-to-address transaction redeemable using an ECDSA key
|
/// Pay-to-address transaction redeemable using an ECDSA key
|
||||||
PayToAddress(SecretKey),
|
PayToAddress(SecretKey),
|
||||||
/// Undetermined
|
/// Undetermined
|
||||||
Unknown
|
Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A txout that is spendable by the wallet
|
/// A txout that is spendable by the wallet
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct WalletTxOut {
|
pub struct WalletTxOut {
|
||||||
/// The TXID of the transaction this output is part of
|
/// The TXID of the transaction this output is part of
|
||||||
pub txid: Sha256dHash,
|
pub txid: Sha256dHash,
|
||||||
/// The index of the output in its transaction
|
/// The index of the output in its transaction
|
||||||
pub vout: u32,
|
pub vout: u32,
|
||||||
/// The blockheight at which this output appeared in the blockchain
|
/// The blockheight at which this output appeared in the blockchain
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
/// The actual output
|
/// The actual output
|
||||||
pub txo: TxOut,
|
pub txo: TxOut,
|
||||||
/// A classification of the output
|
/// A classification of the output
|
||||||
pub kind: WalletTxOutType
|
pub kind: WalletTxOutType
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An address index
|
/// An address index
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct AddressIndex {
|
pub struct AddressIndex {
|
||||||
tentative_index: HashMap<Script, Vec<WalletTxOut>>,
|
tentative_index: HashMap<Script, Vec<WalletTxOut>>,
|
||||||
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>,
|
index: HashMap<(Sha256dHash, u32), Vec<WalletTxOut>>,
|
||||||
network: Network,
|
network: Network,
|
||||||
k1: u64,
|
k1: u64,
|
||||||
k2: u64
|
k2: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddressIndex {
|
impl AddressIndex {
|
||||||
/// Creates a new address index from a wallet (which provides an authenticated
|
/// Creates a new address index from a wallet (which provides an authenticated
|
||||||
/// hash function for prefix filtering) and UTXO set (which is what gets filtered).
|
/// hash function for prefix filtering) and UTXO set (which is what gets filtered).
|
||||||
pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex {
|
pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex {
|
||||||
let (k1, k2) = wallet.siphash_key();
|
let (k1, k2) = wallet.siphash_key();
|
||||||
let mut ret = AddressIndex {
|
let mut ret = AddressIndex {
|
||||||
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
|
tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256),
|
||||||
index: HashMap::new(),
|
index: HashMap::new(),
|
||||||
network: wallet.network(),
|
network: wallet.network(),
|
||||||
k1: k1,
|
k1: k1,
|
||||||
k2: k2
|
k2: k2
|
||||||
};
|
|
||||||
for (key, idx, txo, height) in utxo_set.iter() {
|
|
||||||
if ret.admissible_txo(txo) {
|
|
||||||
let new = WalletTxOut {
|
|
||||||
txid: key,
|
|
||||||
vout: idx,
|
|
||||||
height: height,
|
|
||||||
txo: txo.clone(),
|
|
||||||
kind: WalletTxOutType::Unknown
|
|
||||||
};
|
};
|
||||||
let mut entry = ret.tentative_index.entry(txo.script_pubkey.clone());
|
for (key, idx, txo, height) in utxo_set.iter() {
|
||||||
|
if ret.admissible_txo(txo) {
|
||||||
|
let new = WalletTxOut {
|
||||||
|
txid: key,
|
||||||
|
vout: idx,
|
||||||
|
height: height,
|
||||||
|
txo: txo.clone(),
|
||||||
|
kind: WalletTxOutType::Unknown
|
||||||
|
};
|
||||||
|
let mut entry = ret.tentative_index.entry(txo.script_pubkey.clone());
|
||||||
|
let txos = entry.or_insert(vec![]);
|
||||||
|
txos.push(new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
#[inline]
|
||||||
|
pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) {
|
||||||
|
let mut new = wtx.clone();
|
||||||
|
new.kind = kind;
|
||||||
|
let mut entry = self.index.entry((wtx.txid, wtx.vout));
|
||||||
let txos = entry.or_insert(vec![]);
|
let txos = entry.or_insert(vec![]);
|
||||||
txos.push(new);
|
txos.push(new);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
/// A filtering function used for creating a small address index.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) {
|
pub fn admissible_address(&self, addr: &Address) -> bool {
|
||||||
let mut new = wtx.clone();
|
let mut hasher = SipHasher::new_with_keys(self.k1, self.k2);
|
||||||
new.kind = kind;
|
(&addr[..]).hash(&mut hasher);
|
||||||
let mut entry = self.index.entry((wtx.txid, wtx.vout));
|
hasher.finish() & 0xFF == 0
|
||||||
let txos = entry.or_insert(vec![]);
|
|
||||||
txos.push(new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A filtering function used for creating a small address index.
|
|
||||||
#[inline]
|
|
||||||
pub fn admissible_address(&self, addr: &Address) -> bool {
|
|
||||||
let mut hasher = SipHasher::new_with_keys(self.k1, self.k2);
|
|
||||||
(&addr[..]).hash(&mut hasher);
|
|
||||||
hasher.finish() & 0xFF == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A filtering function used for creating a small address index.
|
|
||||||
#[inline]
|
|
||||||
pub fn admissible_txo(&self, out: &TxOut) -> bool {
|
|
||||||
match out.classify(self.network) {
|
|
||||||
PayToPubkeyHash(addr) => self.admissible_address(&addr),
|
|
||||||
_ => false
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Lookup a txout by its scriptpubkey. Returns a slice because there
|
/// A filtering function used for creating a small address index.
|
||||||
/// may be more than one for any given scriptpubkey.
|
#[inline]
|
||||||
#[inline]
|
pub fn admissible_txo(&self, out: &TxOut) -> bool {
|
||||||
pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] {
|
match out.classify(self.network) {
|
||||||
self.tentative_index.get(pubkey).map(|v| &v[..]).unwrap_or(&[])
|
PayToPubkeyHash(addr) => self.admissible_address(&addr),
|
||||||
}
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup a txout by its scriptpubkey. Returns a slice because there
|
||||||
|
/// may be more than one for any given scriptpubkey.
|
||||||
|
#[inline]
|
||||||
|
pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] {
|
||||||
|
self.tentative_index.get(pubkey).map(|v| &v[..]).unwrap_or(&[])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
// the public domain worldwide. This software is distributed without
|
// the public domain worldwide. This software is distributed without
|
||||||
|
@ -46,535 +46,535 @@ impl_array_newtype_show!(Fingerprint);
|
||||||
impl_array_newtype_encodable!(Fingerprint, u8, 4);
|
impl_array_newtype_encodable!(Fingerprint, u8, 4);
|
||||||
|
|
||||||
impl Default for Fingerprint {
|
impl Default for Fingerprint {
|
||||||
fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) }
|
fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extended private key
|
/// Extended private key
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||||
pub struct ExtendedPrivKey {
|
pub struct ExtendedPrivKey {
|
||||||
/// The network this key is to be used on
|
/// The network this key is to be used on
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
/// How many derivations this key is from the master (which is 0)
|
/// How many derivations this key is from the master (which is 0)
|
||||||
pub depth: u8,
|
pub depth: u8,
|
||||||
/// Fingerprint of the parent key (0 for master)
|
/// Fingerprint of the parent key (0 for master)
|
||||||
pub parent_fingerprint: Fingerprint,
|
pub parent_fingerprint: Fingerprint,
|
||||||
/// Child number of the key used to derive from parent (0 for master)
|
/// Child number of the key used to derive from parent (0 for master)
|
||||||
pub child_number: ChildNumber,
|
pub child_number: ChildNumber,
|
||||||
/// Secret key
|
/// Secret key
|
||||||
pub secret_key: SecretKey,
|
pub secret_key: SecretKey,
|
||||||
/// Chain code
|
/// Chain code
|
||||||
pub chain_code: ChainCode
|
pub chain_code: ChainCode
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extended public key
|
/// Extended public key
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||||
pub struct ExtendedPubKey {
|
pub struct ExtendedPubKey {
|
||||||
/// The network this key is to be used on
|
/// The network this key is to be used on
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
/// How many derivations this key is from the master (which is 0)
|
/// How many derivations this key is from the master (which is 0)
|
||||||
pub depth: u8,
|
pub depth: u8,
|
||||||
/// Fingerprint of the parent key
|
/// Fingerprint of the parent key
|
||||||
pub parent_fingerprint: Fingerprint,
|
pub parent_fingerprint: Fingerprint,
|
||||||
/// Child number of the key used to derive from parent (0 for master)
|
/// Child number of the key used to derive from parent (0 for master)
|
||||||
pub child_number: ChildNumber,
|
pub child_number: ChildNumber,
|
||||||
/// Public key
|
/// Public key
|
||||||
pub public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
/// Chain code
|
/// Chain code
|
||||||
pub chain_code: ChainCode
|
pub chain_code: ChainCode
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A child number for a derived key
|
/// A child number for a derived key
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum ChildNumber {
|
pub enum ChildNumber {
|
||||||
/// Hardened key index, within [0, 2^31 - 1]
|
/// Hardened key index, within [0, 2^31 - 1]
|
||||||
Hardened(u32),
|
Hardened(u32),
|
||||||
/// Non-hardened key, within [0, 2^31 - 1]
|
/// Non-hardened key, within [0, 2^31 - 1]
|
||||||
Normal(u32),
|
Normal(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for ChildNumber {
|
impl Serialize for ChildNumber {
|
||||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||||
where S: Serializer {
|
where S: Serializer {
|
||||||
match *self {
|
match *self {
|
||||||
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
|
ChildNumber::Hardened(n) => (n + (1 << 31)).serialize(s),
|
||||||
ChildNumber::Normal(n) => n.serialize(s)
|
ChildNumber::Normal(n) => n.serialize(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deserialize for ChildNumber {
|
impl Deserialize for ChildNumber {
|
||||||
fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<ChildNumber, D::Error>
|
||||||
where D: Deserializer {
|
where D: Deserializer {
|
||||||
let n: u32 = try!(Deserialize::deserialize(d));
|
let n: u32 = try!(Deserialize::deserialize(d));
|
||||||
if n < (1 << 31) {
|
if n < (1 << 31) {
|
||||||
Ok(ChildNumber::Normal(n))
|
Ok(ChildNumber::Normal(n))
|
||||||
} else {
|
} else {
|
||||||
Ok(ChildNumber::Hardened(n - (1 << 31)))
|
Ok(ChildNumber::Hardened(n - (1 << 31)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A BIP32 error
|
/// A BIP32 error
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// A pk->pk derivation was attempted on a hardened key
|
/// A pk->pk derivation was attempted on a hardened key
|
||||||
CannotDeriveFromHardenedKey,
|
CannotDeriveFromHardenedKey,
|
||||||
/// A secp256k1 error occured
|
/// A secp256k1 error occured
|
||||||
Ecdsa(secp256k1::Error),
|
Ecdsa(secp256k1::Error),
|
||||||
/// A child number was provided that was out of range
|
/// A child number was provided that was out of range
|
||||||
InvalidChildNumber(ChildNumber),
|
InvalidChildNumber(ChildNumber),
|
||||||
/// Error creating a master seed --- for application use
|
/// Error creating a master seed --- for application use
|
||||||
RngError(String)
|
RngError(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtendedPrivKey {
|
impl ExtendedPrivKey {
|
||||||
/// Construct a new master key from a seed value
|
/// Construct a new master key from a seed value
|
||||||
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||||
let mut result = [0; 64];
|
let mut result = [0; 64];
|
||||||
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
||||||
hmac.input(seed);
|
hmac.input(seed);
|
||||||
hmac.raw_result(&mut result);
|
hmac.raw_result(&mut result);
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
Ok(ExtendedPrivKey {
|
||||||
network: network,
|
network: network,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
parent_fingerprint: Default::default(),
|
parent_fingerprint: Default::default(),
|
||||||
child_number: ChildNumber::Normal(0),
|
child_number: ChildNumber::Normal(0),
|
||||||
secret_key: try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)),
|
secret_key: try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa)),
|
||||||
chain_code: ChainCode::from_slice(&result[32..])
|
chain_code: ChainCode::from_slice(&result[32..])
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a privkey from a path
|
|
||||||
pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber])
|
|
||||||
-> Result<ExtendedPrivKey, Error> {
|
|
||||||
let mut sk = *master;
|
|
||||||
for &num in path.iter() {
|
|
||||||
sk = try!(sk.ckd_priv(num));
|
|
||||||
}
|
}
|
||||||
Ok(sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Private->Private child key derivation
|
/// Creates a privkey from a path
|
||||||
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
pub fn from_path(master: &ExtendedPrivKey, path: &[ChildNumber])
|
||||||
let mut result = [0; 64];
|
-> Result<ExtendedPrivKey, Error> {
|
||||||
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
let mut sk = *master;
|
||||||
let mut be_n = [0; 32];
|
for &num in path.iter() {
|
||||||
match i {
|
sk = try!(sk.ckd_priv(num));
|
||||||
ChildNumber::Normal(n) => {
|
}
|
||||||
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
Ok(sk)
|
||||||
// Non-hardened key: compute public data and use that
|
|
||||||
secp256k1::init();
|
|
||||||
// Note the unwrap: this is fine, we checked the SK when we created it
|
|
||||||
hmac.input(&PublicKey::from_secret_key(&self.secret_key, true)[..]);
|
|
||||||
write_u32_be(&mut be_n, n);
|
|
||||||
}
|
|
||||||
ChildNumber::Hardened(n) => {
|
|
||||||
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
|
||||||
// Hardened key: use only secret data to prevent public derivation
|
|
||||||
hmac.input(&[0u8]);
|
|
||||||
hmac.input(&self.secret_key[..]);
|
|
||||||
write_u32_be(&mut be_n, n + (1 << 31));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hmac.input(&be_n);
|
|
||||||
hmac.raw_result(&mut result);
|
|
||||||
let mut sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
|
|
||||||
try!(sk.add_assign(&self.secret_key).map_err(Error::Ecdsa));
|
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
/// Private->Private child key derivation
|
||||||
network: self.network,
|
pub fn ckd_priv(&self, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
||||||
depth: self.depth + 1,
|
let mut result = [0; 64];
|
||||||
parent_fingerprint: self.fingerprint(),
|
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
||||||
child_number: i,
|
let mut be_n = [0; 32];
|
||||||
secret_key: sk,
|
match i {
|
||||||
chain_code: ChainCode::from_slice(&result[32..])
|
ChildNumber::Normal(n) => {
|
||||||
})
|
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
||||||
}
|
// Non-hardened key: compute public data and use that
|
||||||
|
secp256k1::init();
|
||||||
|
// Note the unwrap: this is fine, we checked the SK when we created it
|
||||||
|
hmac.input(&PublicKey::from_secret_key(&self.secret_key, true)[..]);
|
||||||
|
write_u32_be(&mut be_n, n);
|
||||||
|
}
|
||||||
|
ChildNumber::Hardened(n) => {
|
||||||
|
if n >= (1 << 31) { return Err(Error::InvalidChildNumber(i)) }
|
||||||
|
// Hardened key: use only secret data to prevent public derivation
|
||||||
|
hmac.input(&[0u8]);
|
||||||
|
hmac.input(&self.secret_key[..]);
|
||||||
|
write_u32_be(&mut be_n, n + (1 << 31));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hmac.input(&be_n);
|
||||||
|
hmac.raw_result(&mut result);
|
||||||
|
let mut sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
|
||||||
|
try!(sk.add_assign(&self.secret_key).map_err(Error::Ecdsa));
|
||||||
|
|
||||||
/// Returns the HASH160 of the chaincode
|
Ok(ExtendedPrivKey {
|
||||||
pub fn identifier(&self) -> [u8; 20] {
|
network: self.network,
|
||||||
let mut sha2_res = [0; 32];
|
depth: self.depth + 1,
|
||||||
let mut ripemd_res = [0; 20];
|
parent_fingerprint: self.fingerprint(),
|
||||||
// Compute extended public key
|
child_number: i,
|
||||||
let pk = ExtendedPubKey::from_private(self);
|
secret_key: sk,
|
||||||
// Do SHA256 of just the ECDSA pubkey
|
chain_code: ChainCode::from_slice(&result[32..])
|
||||||
let mut sha2 = Sha256::new();
|
})
|
||||||
sha2.input(&pk.public_key[..]);
|
}
|
||||||
sha2.result(&mut sha2_res);
|
|
||||||
// do RIPEMD160
|
|
||||||
let mut ripemd = Ripemd160::new();
|
|
||||||
ripemd.input(&sha2_res);
|
|
||||||
ripemd.result(&mut ripemd_res);
|
|
||||||
// Return
|
|
||||||
ripemd_res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the first four bytes of the identifier
|
/// Returns the HASH160 of the chaincode
|
||||||
pub fn fingerprint(&self) -> Fingerprint {
|
pub fn identifier(&self) -> [u8; 20] {
|
||||||
Fingerprint::from_slice(&self.identifier()[0..4])
|
let mut sha2_res = [0; 32];
|
||||||
}
|
let mut ripemd_res = [0; 20];
|
||||||
|
// Compute extended public key
|
||||||
|
let pk = ExtendedPubKey::from_private(self);
|
||||||
|
// Do SHA256 of just the ECDSA pubkey
|
||||||
|
let mut sha2 = Sha256::new();
|
||||||
|
sha2.input(&pk.public_key[..]);
|
||||||
|
sha2.result(&mut sha2_res);
|
||||||
|
// do RIPEMD160
|
||||||
|
let mut ripemd = Ripemd160::new();
|
||||||
|
ripemd.input(&sha2_res);
|
||||||
|
ripemd.result(&mut ripemd_res);
|
||||||
|
// Return
|
||||||
|
ripemd_res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the first four bytes of the identifier
|
||||||
|
pub fn fingerprint(&self) -> Fingerprint {
|
||||||
|
Fingerprint::from_slice(&self.identifier()[0..4])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtendedPubKey {
|
impl ExtendedPubKey {
|
||||||
/// Derives a public key from a private key
|
/// Derives a public key from a private key
|
||||||
pub fn from_private(sk: &ExtendedPrivKey) -> ExtendedPubKey {
|
pub fn from_private(sk: &ExtendedPrivKey) -> ExtendedPubKey {
|
||||||
secp256k1::init();
|
secp256k1::init();
|
||||||
ExtendedPubKey {
|
ExtendedPubKey {
|
||||||
network: sk.network,
|
network: sk.network,
|
||||||
depth: sk.depth,
|
depth: sk.depth,
|
||||||
parent_fingerprint: sk.parent_fingerprint,
|
parent_fingerprint: sk.parent_fingerprint,
|
||||||
child_number: sk.child_number,
|
child_number: sk.child_number,
|
||||||
public_key: PublicKey::from_secret_key(&sk.secret_key, true),
|
public_key: PublicKey::from_secret_key(&sk.secret_key, true),
|
||||||
chain_code: sk.chain_code
|
chain_code: sk.chain_code
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Public->Public child key derivation
|
|
||||||
pub fn ckd_pub(&self, i: ChildNumber) -> Result<ExtendedPubKey, Error> {
|
|
||||||
match i {
|
|
||||||
ChildNumber::Hardened(n) => {
|
|
||||||
if n >= (1 << 31) {
|
|
||||||
Err(Error::InvalidChildNumber(i))
|
|
||||||
} else {
|
|
||||||
Err(Error::CannotDeriveFromHardenedKey)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ChildNumber::Normal(n) => {
|
|
||||||
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
|
||||||
hmac.input(&self.public_key[..]);
|
|
||||||
let mut be_n = [0; 32];
|
|
||||||
write_u32_be(&mut be_n, n);
|
|
||||||
hmac.input(&be_n);
|
|
||||||
|
|
||||||
let mut result = [0; 64];
|
|
||||||
hmac.raw_result(&mut result);
|
|
||||||
|
|
||||||
let sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
|
|
||||||
let mut pk = self.public_key.clone();
|
|
||||||
try!(pk.add_exp_assign(&sk).map_err(Error::Ecdsa));
|
|
||||||
|
|
||||||
Ok(ExtendedPubKey {
|
|
||||||
network: self.network,
|
|
||||||
depth: self.depth + 1,
|
|
||||||
parent_fingerprint: self.fingerprint(),
|
|
||||||
child_number: i,
|
|
||||||
public_key: pk,
|
|
||||||
chain_code: ChainCode::from_slice(&result[32..])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the HASH160 of the chaincode
|
/// Public->Public child key derivation
|
||||||
pub fn identifier(&self) -> [u8; 20] {
|
pub fn ckd_pub(&self, i: ChildNumber) -> Result<ExtendedPubKey, Error> {
|
||||||
let mut sha2_res = [0; 32];
|
match i {
|
||||||
let mut ripemd_res = [0; 20];
|
ChildNumber::Hardened(n) => {
|
||||||
// Do SHA256 of just the ECDSA pubkey
|
if n >= (1 << 31) {
|
||||||
let mut sha2 = Sha256::new();
|
Err(Error::InvalidChildNumber(i))
|
||||||
sha2.input(&self.public_key[..]);
|
} else {
|
||||||
sha2.result(&mut sha2_res);
|
Err(Error::CannotDeriveFromHardenedKey)
|
||||||
// do RIPEMD160
|
}
|
||||||
let mut ripemd = Ripemd160::new();
|
}
|
||||||
ripemd.input(&sha2_res);
|
ChildNumber::Normal(n) => {
|
||||||
ripemd.result(&mut ripemd_res);
|
let mut hmac = Hmac::new(Sha512::new(), &self.chain_code[..]);
|
||||||
// Return
|
hmac.input(&self.public_key[..]);
|
||||||
ripemd_res
|
let mut be_n = [0; 32];
|
||||||
}
|
write_u32_be(&mut be_n, n);
|
||||||
|
hmac.input(&be_n);
|
||||||
|
|
||||||
/// Returns the first four bytes of the identifier
|
let mut result = [0; 64];
|
||||||
pub fn fingerprint(&self) -> Fingerprint {
|
hmac.raw_result(&mut result);
|
||||||
Fingerprint::from_slice(&self.identifier()[0..4])
|
|
||||||
}
|
let sk = try!(SecretKey::from_slice(&result[..32]).map_err(Error::Ecdsa));
|
||||||
|
let mut pk = self.public_key.clone();
|
||||||
|
try!(pk.add_exp_assign(&sk).map_err(Error::Ecdsa));
|
||||||
|
|
||||||
|
Ok(ExtendedPubKey {
|
||||||
|
network: self.network,
|
||||||
|
depth: self.depth + 1,
|
||||||
|
parent_fingerprint: self.fingerprint(),
|
||||||
|
child_number: i,
|
||||||
|
public_key: pk,
|
||||||
|
chain_code: ChainCode::from_slice(&result[32..])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the HASH160 of the chaincode
|
||||||
|
pub fn identifier(&self) -> [u8; 20] {
|
||||||
|
let mut sha2_res = [0; 32];
|
||||||
|
let mut ripemd_res = [0; 20];
|
||||||
|
// Do SHA256 of just the ECDSA pubkey
|
||||||
|
let mut sha2 = Sha256::new();
|
||||||
|
sha2.input(&self.public_key[..]);
|
||||||
|
sha2.result(&mut sha2_res);
|
||||||
|
// do RIPEMD160
|
||||||
|
let mut ripemd = Ripemd160::new();
|
||||||
|
ripemd.input(&sha2_res);
|
||||||
|
ripemd.result(&mut ripemd_res);
|
||||||
|
// Return
|
||||||
|
ripemd_res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the first four bytes of the identifier
|
||||||
|
pub fn fingerprint(&self) -> Fingerprint {
|
||||||
|
Fingerprint::from_slice(&self.identifier()[0..4])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBase58 for ExtendedPrivKey {
|
impl ToBase58 for ExtendedPrivKey {
|
||||||
fn base58_layout(&self) -> Vec<u8> {
|
fn base58_layout(&self) -> Vec<u8> {
|
||||||
let mut ret = Vec::with_capacity(78);
|
let mut ret = Vec::with_capacity(78);
|
||||||
ret.push_all(match self.network {
|
ret.push_all(match self.network {
|
||||||
Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4],
|
Network::Bitcoin => &[0x04, 0x88, 0xAD, 0xE4],
|
||||||
Network::Testnet => &[0x04, 0x35, 0x83, 0x94]
|
Network::Testnet => &[0x04, 0x35, 0x83, 0x94]
|
||||||
});
|
});
|
||||||
ret.push(self.depth as u8);
|
ret.push(self.depth as u8);
|
||||||
ret.push_all(&self.parent_fingerprint[..]);
|
ret.push_all(&self.parent_fingerprint[..]);
|
||||||
let mut be_n = [0; 32];
|
let mut be_n = [0; 32];
|
||||||
match self.child_number {
|
match self.child_number {
|
||||||
ChildNumber::Hardened(n) => {
|
ChildNumber::Hardened(n) => {
|
||||||
write_u32_be(&mut be_n, n + (1 << 31));
|
write_u32_be(&mut be_n, n + (1 << 31));
|
||||||
}
|
}
|
||||||
ChildNumber::Normal(n) => {
|
ChildNumber::Normal(n) => {
|
||||||
write_u32_be(&mut be_n, n);
|
write_u32_be(&mut be_n, n);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ret.push_all(&be_n);
|
||||||
|
ret.push_all(&self.chain_code[..]);
|
||||||
|
ret.push(0);
|
||||||
|
ret.push_all(&self.secret_key[..]);
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
ret.push_all(&be_n);
|
|
||||||
ret.push_all(&self.chain_code[..]);
|
|
||||||
ret.push(0);
|
|
||||||
ret.push_all(&self.secret_key[..]);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromBase58 for ExtendedPrivKey {
|
impl FromBase58 for ExtendedPrivKey {
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> {
|
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> {
|
||||||
if data.len() != 78 {
|
if data.len() != 78 {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cn_int = read_u32_be(&data[9..13]);
|
||||||
|
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
||||||
|
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
||||||
|
|
||||||
|
Ok(ExtendedPrivKey {
|
||||||
|
network: match &data[0..4] {
|
||||||
|
[0x04u8, 0x88, 0xAD, 0xE4] => Network::Bitcoin,
|
||||||
|
[0x04u8, 0x35, 0x83, 0x94] => Network::Testnet,
|
||||||
|
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
|
||||||
|
},
|
||||||
|
depth: data[4],
|
||||||
|
parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
|
||||||
|
child_number: child_number,
|
||||||
|
chain_code: ChainCode::from_slice(&data[13..45]),
|
||||||
|
secret_key: try!(SecretKey::from_slice(
|
||||||
|
&data[46..78]).map_err(|e|
|
||||||
|
base58::Error::Other(e.to_string())))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let cn_int = read_u32_be(&data[9..13]);
|
|
||||||
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
|
||||||
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
|
||||||
network: match &data[0..4] {
|
|
||||||
[0x04u8, 0x88, 0xAD, 0xE4] => Network::Bitcoin,
|
|
||||||
[0x04u8, 0x35, 0x83, 0x94] => Network::Testnet,
|
|
||||||
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
|
|
||||||
},
|
|
||||||
depth: data[4],
|
|
||||||
parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
|
|
||||||
child_number: child_number,
|
|
||||||
chain_code: ChainCode::from_slice(&data[13..45]),
|
|
||||||
secret_key: try!(SecretKey::from_slice(
|
|
||||||
&data[46..78]).map_err(|e|
|
|
||||||
base58::Error::Other(e.to_string())))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBase58 for ExtendedPubKey {
|
impl ToBase58 for ExtendedPubKey {
|
||||||
fn base58_layout(&self) -> Vec<u8> {
|
fn base58_layout(&self) -> Vec<u8> {
|
||||||
assert!(self.public_key.is_compressed());
|
assert!(self.public_key.is_compressed());
|
||||||
let mut ret = Vec::with_capacity(78);
|
let mut ret = Vec::with_capacity(78);
|
||||||
ret.push_all(match self.network {
|
ret.push_all(match self.network {
|
||||||
Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E],
|
Network::Bitcoin => &[0x04u8, 0x88, 0xB2, 0x1E],
|
||||||
Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF]
|
Network::Testnet => &[0x04u8, 0x35, 0x87, 0xCF]
|
||||||
});
|
});
|
||||||
ret.push(self.depth as u8);
|
ret.push(self.depth as u8);
|
||||||
ret.push_all(&self.parent_fingerprint[..]);
|
ret.push_all(&self.parent_fingerprint[..]);
|
||||||
let mut be_n = [0; 32];
|
let mut be_n = [0; 32];
|
||||||
match self.child_number {
|
match self.child_number {
|
||||||
ChildNumber::Hardened(n) => {
|
ChildNumber::Hardened(n) => {
|
||||||
write_u32_be(&mut be_n, n + (1 << 31));
|
write_u32_be(&mut be_n, n + (1 << 31));
|
||||||
}
|
}
|
||||||
ChildNumber::Normal(n) => {
|
ChildNumber::Normal(n) => {
|
||||||
write_u32_be(&mut be_n, n);
|
write_u32_be(&mut be_n, n);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ret.push_all(&be_n);
|
||||||
|
ret.push_all(&self.chain_code[..]);
|
||||||
|
ret.push_all(&self.public_key[..]);
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
ret.push_all(&be_n);
|
|
||||||
ret.push_all(&self.chain_code[..]);
|
|
||||||
ret.push_all(&self.public_key[..]);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromBase58 for ExtendedPubKey {
|
impl FromBase58 for ExtendedPubKey {
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> {
|
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> {
|
||||||
if data.len() != 78 {
|
if data.len() != 78 {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cn_int = read_u32_be(&data[9..13]);
|
||||||
|
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
||||||
|
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
||||||
|
|
||||||
|
Ok(ExtendedPubKey {
|
||||||
|
network: match &data[0..4] {
|
||||||
|
[0x04, 0x88, 0xB2, 0x1E] => Network::Bitcoin,
|
||||||
|
[0x04, 0x35, 0x87, 0xCF] => Network::Testnet,
|
||||||
|
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
|
||||||
|
},
|
||||||
|
depth: data[4],
|
||||||
|
parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
|
||||||
|
child_number: child_number,
|
||||||
|
chain_code: ChainCode::from_slice(&data[13..45]),
|
||||||
|
public_key: try!(PublicKey::from_slice(
|
||||||
|
&data[45..78]).map_err(|e|
|
||||||
|
base58::Error::Other(e.to_string())))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let cn_int = read_u32_be(&data[9..13]);
|
|
||||||
let child_number = if cn_int < (1 << 31) { ChildNumber::Normal(cn_int) }
|
|
||||||
else { ChildNumber::Hardened(cn_int - (1 << 31)) };
|
|
||||||
|
|
||||||
Ok(ExtendedPubKey {
|
|
||||||
network: match &data[0..4] {
|
|
||||||
[0x04, 0x88, 0xB2, 0x1E] => Network::Bitcoin,
|
|
||||||
[0x04, 0x35, 0x87, 0xCF] => Network::Testnet,
|
|
||||||
_ => { return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec())); }
|
|
||||||
},
|
|
||||||
depth: data[4],
|
|
||||||
parent_fingerprint: Fingerprint::from_slice(&data[5..9]),
|
|
||||||
child_number: child_number,
|
|
||||||
chain_code: ChainCode::from_slice(&data[13..45]),
|
|
||||||
public_key: try!(PublicKey::from_slice(
|
|
||||||
&data[45..78]).map_err(|e|
|
|
||||||
base58::Error::Other(e.to_string())))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serialize::hex::FromHex;
|
use serialize::hex::FromHex;
|
||||||
use test::{Bencher, black_box};
|
use test::{Bencher, black_box};
|
||||||
|
|
||||||
use network::constants::Network::{self, Bitcoin};
|
use network::constants::Network::{self, Bitcoin};
|
||||||
use util::base58::{FromBase58, ToBase58};
|
use util::base58::{FromBase58, ToBase58};
|
||||||
|
|
||||||
use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
|
use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
|
||||||
use super::ChildNumber::{Hardened, Normal};
|
use super::ChildNumber::{Hardened, Normal};
|
||||||
|
|
||||||
fn test_path(network: Network,
|
fn test_path(network: Network,
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
path: &[ChildNumber],
|
path: &[ChildNumber],
|
||||||
expected_sk: &str,
|
expected_sk: &str,
|
||||||
expected_pk: &str) {
|
expected_pk: &str) {
|
||||||
|
|
||||||
let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap();
|
let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap();
|
||||||
let mut pk = ExtendedPubKey::from_private(&sk);
|
let mut pk = ExtendedPubKey::from_private(&sk);
|
||||||
// Derive keys, checking hardened and non-hardened derivation
|
// Derive keys, checking hardened and non-hardened derivation
|
||||||
for &num in path.iter() {
|
for &num in path.iter() {
|
||||||
sk = sk.ckd_priv(num).unwrap();
|
sk = sk.ckd_priv(num).unwrap();
|
||||||
match num {
|
match num {
|
||||||
Normal(_) => {
|
Normal(_) => {
|
||||||
let pk2 = pk.ckd_pub(num).unwrap();
|
let pk2 = pk.ckd_pub(num).unwrap();
|
||||||
pk = ExtendedPubKey::from_private(&sk);
|
pk = ExtendedPubKey::from_private(&sk);
|
||||||
assert_eq!(pk, pk2);
|
assert_eq!(pk, pk2);
|
||||||
|
}
|
||||||
|
Hardened(_) => {
|
||||||
|
pk = ExtendedPubKey::from_private(&sk);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Hardened(_) => {
|
|
||||||
pk = ExtendedPubKey::from_private(&sk);
|
// Check result against expected base58
|
||||||
}
|
assert_eq!(&sk.to_base58check()[..], expected_sk);
|
||||||
}
|
assert_eq!(&pk.to_base58check()[..], expected_pk);
|
||||||
|
// Check decoded base58 against result
|
||||||
|
let decoded_sk = FromBase58::from_base58check(expected_sk);
|
||||||
|
let decoded_pk = FromBase58::from_base58check(expected_pk);
|
||||||
|
assert_eq!(Ok(sk), decoded_sk);
|
||||||
|
assert_eq!(Ok(pk), decoded_pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check result against expected base58
|
#[test]
|
||||||
assert_eq!(&sk.to_base58check()[..], expected_sk);
|
fn test_vector_1() {
|
||||||
assert_eq!(&pk.to_base58check()[..], expected_pk);
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
// Check decoded base58 against result
|
// m
|
||||||
let decoded_sk = FromBase58::from_base58check(expected_sk);
|
test_path(Bitcoin, &seed, [],
|
||||||
let decoded_pk = FromBase58::from_base58check(expected_pk);
|
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
||||||
assert_eq!(Ok(sk), decoded_sk);
|
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
||||||
assert_eq!(Ok(pk), decoded_pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
// m/0h
|
||||||
fn test_vector_1() {
|
test_path(Bitcoin, &seed, [Hardened(0)],
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
||||||
// m
|
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
||||||
test_path(Bitcoin, &seed, [],
|
|
||||||
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
|
||||||
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
|
||||||
|
|
||||||
// m/0h
|
// m/0h/1
|
||||||
test_path(Bitcoin, &seed, [Hardened(0)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1)],
|
||||||
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
||||||
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
||||||
|
|
||||||
// m/0h/1
|
// m/0h/1/2h
|
||||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)],
|
||||||
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
||||||
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
||||||
|
|
||||||
// m/0h/1/2h
|
// m/0h/1/2h/2
|
||||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)],
|
||||||
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
||||||
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
||||||
|
|
||||||
// m/0h/1/2h/2
|
// m/0h/1/2h/2/1000000000
|
||||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2)],
|
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
|
||||||
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
||||||
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
||||||
|
}
|
||||||
|
|
||||||
// m/0h/1/2h/2/1000000000
|
#[test]
|
||||||
test_path(Bitcoin, &seed, [Hardened(0), Normal(1), Hardened(2), Normal(2), Normal(1000000000)],
|
fn test_vector_2() {
|
||||||
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
|
||||||
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
// m
|
||||||
fn test_vector_2() {
|
test_path(Bitcoin, &seed, [],
|
||||||
let seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".from_hex().unwrap();
|
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
||||||
|
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
||||||
|
|
||||||
// m
|
// m/0
|
||||||
test_path(Bitcoin, &seed, [],
|
test_path(Bitcoin, &seed, [Normal(0)],
|
||||||
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
||||||
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
||||||
|
|
||||||
// m/0
|
// m/0/2147483647h
|
||||||
test_path(Bitcoin, &seed, [Normal(0)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)],
|
||||||
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
||||||
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
||||||
|
|
||||||
// m/0/2147483647h
|
// m/0/2147483647h/1
|
||||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)],
|
||||||
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
||||||
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
||||||
|
|
||||||
// m/0/2147483647h/1
|
// m/0/2147483647h/1/2147483646h
|
||||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
|
||||||
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
||||||
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
||||||
|
|
||||||
// m/0/2147483647h/1/2147483646h
|
// m/0/2147483647h/1/2147483646h/2
|
||||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646)],
|
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
|
||||||
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
||||||
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
||||||
|
}
|
||||||
|
|
||||||
// m/0/2147483647h/1/2147483646h/2
|
#[test]
|
||||||
test_path(Bitcoin, &seed, [Normal(0), Hardened(2147483647), Normal(1), Hardened(2147483646), Normal(2)],
|
pub fn encode_decode_childnumber() {
|
||||||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
use serialize::json;
|
||||||
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let h1 = Hardened(1);
|
||||||
pub fn encode_decode_childnumber() {
|
let n1 = Normal(1);
|
||||||
use serialize::json;
|
|
||||||
|
|
||||||
let h1 = Hardened(1);
|
let h1_str = json::encode(&h1);
|
||||||
let n1 = Normal(1);
|
let n1_str = json::encode(&n1);
|
||||||
|
|
||||||
let h1_str = json::encode(&h1);
|
assert!(h1 != n1);
|
||||||
let n1_str = json::encode(&n1);
|
assert!(h1_str != n1_str);
|
||||||
|
|
||||||
assert!(h1 != n1);
|
let h1_dec = json::decode(&h1_str).unwrap();
|
||||||
assert!(h1_str != n1_str);
|
let n1_dec = json::decode(&n1_str).unwrap();
|
||||||
|
assert_eq!(h1, h1_dec);
|
||||||
|
assert_eq!(n1, n1_dec);
|
||||||
|
}
|
||||||
|
|
||||||
let h1_dec = json::decode(&h1_str).unwrap();
|
#[bench]
|
||||||
let n1_dec = json::decode(&n1_str).unwrap();
|
pub fn generate_sequential_normal_children(bh: &mut Bencher) {
|
||||||
assert_eq!(h1, h1_dec);
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
assert_eq!(n1, n1_dec);
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
}
|
let mut i = 0;
|
||||||
|
bh.iter( || {
|
||||||
|
black_box(msk.ckd_priv(Normal(i)));
|
||||||
|
i += 1;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_sequential_normal_children(bh: &mut Bencher) {
|
pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
bh.iter( || {
|
bh.iter( || {
|
||||||
black_box(msk.ckd_priv(Normal(i)));
|
black_box(msk.ckd_priv(Hardened(i)));
|
||||||
i += 1;
|
i += 1;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn generate_sequential_hardened_children(bh: &mut Bencher) {
|
pub fn generate_sequential_public_children(bh: &mut Bencher) {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
let mut i = 0;
|
let mpk = ExtendedPubKey::from_private(&msk);
|
||||||
bh.iter( || {
|
|
||||||
black_box(msk.ckd_priv(Hardened(i)));
|
|
||||||
i += 1;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
let mut i = 0;
|
||||||
pub fn generate_sequential_public_children(bh: &mut Bencher) {
|
bh.iter( || {
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
black_box(mpk.ckd_pub(Normal(i)));
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
i += 1;
|
||||||
let mpk = ExtendedPubKey::from_private(&msk);
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let mut i = 0;
|
#[bench]
|
||||||
bh.iter( || {
|
pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) {
|
||||||
black_box(mpk.ckd_pub(Normal(i)));
|
use wallet::address::Address;
|
||||||
i += 1;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
pub fn generate_sequential_public_child_addresses(bh: &mut Bencher) {
|
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
||||||
use wallet::address::Address;
|
let mpk = ExtendedPubKey::from_private(&msk);
|
||||||
|
|
||||||
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
let mut i = 0;
|
||||||
let msk = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
bh.iter( || {
|
||||||
let mpk = ExtendedPubKey::from_private(&msk);
|
let epk = mpk.ckd_pub(Normal(i)).unwrap();
|
||||||
|
black_box(Address::from_key(Bitcoin, &epk.public_key));
|
||||||
let mut i = 0;
|
i += 1;
|
||||||
bh.iter( || {
|
})
|
||||||
let epk = mpk.ckd_pub(Normal(i)).unwrap();
|
}
|
||||||
black_box(Address::from_key(Bitcoin, &epk.public_key));
|
|
||||||
i += 1;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Rust Bitcoin Library
|
// Rust Bitcoin Library
|
||||||
// Written in 2014 by
|
// Written in 2014 by
|
||||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
// To the extent possible under law, the author(s) have dedicated all
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
// copyright and related and neighboring rights to this software to
|
// copyright and related and neighboring rights to this software to
|
||||||
// the public domain worldwide. This software is distributed without
|
// the public domain worldwide. This software is distributed without
|
||||||
|
@ -33,243 +33,243 @@ use wallet::address_index::AddressIndex;
|
||||||
/// A Wallet error
|
/// A Wallet error
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Tried to lookup an account by name, but none was found
|
/// Tried to lookup an account by name, but none was found
|
||||||
AccountNotFound,
|
AccountNotFound,
|
||||||
/// Tried to add an account when one already exists with that name
|
/// Tried to add an account when one already exists with that name
|
||||||
DuplicateAccount,
|
DuplicateAccount,
|
||||||
/// An error occured in a BIP32 derivation
|
/// An error occured in a BIP32 derivation
|
||||||
Bip32Error(bip32::Error),
|
Bip32Error(bip32::Error),
|
||||||
/// Tried to use a wallet without an address index
|
/// Tried to use a wallet without an address index
|
||||||
NoAddressIndex
|
NoAddressIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Each account has two chains, as specified in BIP32
|
/// Each account has two chains, as specified in BIP32
|
||||||
pub enum AccountChain {
|
pub enum AccountChain {
|
||||||
/// Internal addresses are used within the wallet for change, etc,
|
/// Internal addresses are used within the wallet for change, etc,
|
||||||
/// and in principle every generated one will be used.
|
/// and in principle every generated one will be used.
|
||||||
Internal,
|
Internal,
|
||||||
/// External addresses are shared, and might not be used after generatation,
|
/// External addresses are shared, and might not be used after generatation,
|
||||||
/// complicating recreating the whole wallet from seed.
|
/// complicating recreating the whole wallet from seed.
|
||||||
External
|
External
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An account
|
/// An account
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
internal_path: Vec<ChildNumber>,
|
internal_path: Vec<ChildNumber>,
|
||||||
internal_used: Vec<ChildNumber>,
|
internal_used: Vec<ChildNumber>,
|
||||||
internal_next: u32,
|
internal_next: u32,
|
||||||
external_path: Vec<ChildNumber>,
|
external_path: Vec<ChildNumber>,
|
||||||
external_used: Vec<ChildNumber>,
|
external_used: Vec<ChildNumber>,
|
||||||
external_next: u32
|
external_next: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Account {
|
impl Default for Account {
|
||||||
fn default() -> Account {
|
fn default() -> Account {
|
||||||
Account {
|
Account {
|
||||||
internal_path: vec![Hardened(0), Normal(1)],
|
internal_path: vec![Hardened(0), Normal(1)],
|
||||||
internal_used: vec![],
|
internal_used: vec![],
|
||||||
internal_next: 0,
|
internal_next: 0,
|
||||||
external_path: vec![Hardened(0), Normal(0)],
|
external_path: vec![Hardened(0), Normal(0)],
|
||||||
external_used: vec![],
|
external_used: vec![],
|
||||||
external_next: 0
|
external_next: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wallet
|
/// A wallet
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
master: ExtendedPrivKey,
|
master: ExtendedPrivKey,
|
||||||
accounts: HashMap<String, Account>,
|
accounts: HashMap<String, Account>,
|
||||||
index: Option<AddressIndex>
|
index: Option<AddressIndex>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Wallet {
|
impl Serialize for Wallet {
|
||||||
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
|
||||||
where S: Serializer {
|
where S: Serializer {
|
||||||
let len = self.accounts.len();
|
let len = self.accounts.len();
|
||||||
try!(self.master.serialize(s));
|
try!(self.master.serialize(s));
|
||||||
self.accounts.serialize(s)
|
self.accounts.serialize(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deserialize for Wallet {
|
impl Deserialize for Wallet {
|
||||||
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
|
fn deserialize<D>(d: &mut D) -> Result<Wallet, D::Error>
|
||||||
where D: Deserializer {
|
where D: Deserializer {
|
||||||
Ok(Wallet {
|
Ok(Wallet {
|
||||||
master: try!(Deserialize::deserialize(d)),
|
master: try!(Deserialize::deserialize(d)),
|
||||||
accounts: try!(Deserialize::deserialize(d)),
|
accounts: try!(Deserialize::deserialize(d)),
|
||||||
index: None
|
index: None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
/// Creates a new wallet from a BIP32 seed
|
/// Creates a new wallet from a BIP32 seed
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_seed(network: Network, seed: &[u8]) -> Result<Wallet, bip32::Error> {
|
pub fn from_seed(network: Network, seed: &[u8]) -> Result<Wallet, bip32::Error> {
|
||||||
let mut accounts = HashMap::new();
|
let mut accounts = HashMap::new();
|
||||||
accounts.insert(String::new(), Default::default());
|
accounts.insert(String::new(), Default::default());
|
||||||
|
|
||||||
Ok(Wallet {
|
Ok(Wallet {
|
||||||
master: try!(ExtendedPrivKey::new_master(network, seed)),
|
master: try!(ExtendedPrivKey::new_master(network, seed)),
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
index: None
|
index: None
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the address index
|
|
||||||
#[inline]
|
|
||||||
pub fn build_index(&mut self, utxo_set: &UtxoSet) {
|
|
||||||
let new = AddressIndex::new(utxo_set, self);
|
|
||||||
self.index = Some(new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Accessor for the wallet's address index
|
|
||||||
#[inline]
|
|
||||||
pub fn index<'a>(&'a self) -> Option<&'a AddressIndex> {
|
|
||||||
self.index.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutable accessor for the wallet's address index
|
|
||||||
#[inline]
|
|
||||||
pub fn index_mut<'a>(&'a mut self) -> Option<&'a mut AddressIndex> {
|
|
||||||
self.index.as_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds an account to a wallet
|
|
||||||
pub fn account_insert(&mut self, name: String)
|
|
||||||
-> Result<(), Error> {
|
|
||||||
if self.accounts.contains_key(&name) {
|
|
||||||
return Err(Error::DuplicateAccount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.accounts.len() as u32;
|
/// Creates the address index
|
||||||
self.accounts.insert(name, Account {
|
#[inline]
|
||||||
internal_path: vec![Hardened(idx), Normal(1)],
|
pub fn build_index(&mut self, utxo_set: &UtxoSet) {
|
||||||
internal_used: vec![],
|
let new = AddressIndex::new(utxo_set, self);
|
||||||
internal_next: 0,
|
self.index = Some(new);
|
||||||
external_path: vec![Hardened(idx), Normal(0)],
|
|
||||||
external_used: vec![],
|
|
||||||
external_next: 0
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Locates an account in a wallet
|
|
||||||
#[inline]
|
|
||||||
pub fn account_get<'a>(&'a self, name: &str) -> Option<&'a Account> {
|
|
||||||
self.accounts.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new address
|
|
||||||
pub fn new_address(&mut self,
|
|
||||||
account: &str,
|
|
||||||
chain: AccountChain)
|
|
||||||
-> Result<Address, Error> {
|
|
||||||
let account = self.accounts.get_mut(account);
|
|
||||||
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
|
||||||
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
|
|
||||||
|
|
||||||
let (mut i, master) = match chain {
|
|
||||||
Internal => (account.internal_next,
|
|
||||||
try!(ExtendedPrivKey::from_path(
|
|
||||||
&self.master,
|
|
||||||
account.internal_path.as_slice()).map_err(Error::Bip32Error))),
|
|
||||||
External => (account.external_next,
|
|
||||||
try!(ExtendedPrivKey::from_path(
|
|
||||||
&self.master,
|
|
||||||
account.external_path.as_slice()).map_err(Error::Bip32Error))),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Scan for next admissible address
|
|
||||||
let mut sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
|
|
||||||
let mut address = Address::from_key(
|
|
||||||
master.network,
|
|
||||||
&PublicKey::from_secret_key(&sk.secret_key, true));
|
|
||||||
while !index.admissible_address(&address) {
|
|
||||||
i += 1;
|
|
||||||
sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
|
|
||||||
address = Address::from_key(
|
|
||||||
master.network,
|
|
||||||
&PublicKey::from_secret_key(&sk.secret_key, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match chain {
|
/// Accessor for the wallet's address index
|
||||||
Internal => {
|
#[inline]
|
||||||
account.internal_used.push(Normal(i));
|
pub fn index<'a>(&'a self) -> Option<&'a AddressIndex> {
|
||||||
account.internal_next = i + 1;
|
self.index.as_ref()
|
||||||
}
|
|
||||||
External => {
|
|
||||||
account.external_used.push(Normal(i));
|
|
||||||
account.external_next = i + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(address)
|
/// Mutable accessor for the wallet's address index
|
||||||
}
|
#[inline]
|
||||||
|
pub fn index_mut<'a>(&'a mut self) -> Option<&'a mut AddressIndex> {
|
||||||
/// Returns the network of the wallet
|
self.index.as_mut()
|
||||||
#[inline]
|
|
||||||
pub fn network(&self) -> Network {
|
|
||||||
self.master.network
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a key suitable for keying hash functions for DoS protection
|
|
||||||
#[inline]
|
|
||||||
pub fn siphash_key(&self) -> (u64, u64) {
|
|
||||||
(LittleEndian::read_u64(&self.master.chain_code[0..8]),
|
|
||||||
LittleEndian::read_u64(&self.master.chain_code[8..16]))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Total balance
|
|
||||||
pub fn total_balance(&self) -> Result<u64, Error> {
|
|
||||||
let mut ret = 0;
|
|
||||||
for (_, account) in self.accounts.iter() {
|
|
||||||
ret += try!(self.account_balance(account));
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Account balance
|
|
||||||
pub fn balance(&self, account: &str) -> Result<u64, Error> {
|
|
||||||
let account = self.accounts.get(account);
|
|
||||||
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
|
||||||
self.account_balance(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn account_balance(&self, account: &Account) -> Result<u64, Error> {
|
|
||||||
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
|
|
||||||
|
|
||||||
let mut ret = 0;
|
|
||||||
|
|
||||||
// Sum internal balance
|
|
||||||
let master = try!(ExtendedPrivKey::from_path(
|
|
||||||
&self.master,
|
|
||||||
account.internal_path.as_slice()).map_err(Error::Bip32Error));
|
|
||||||
for &cnum in account.internal_used.iter() {
|
|
||||||
let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error));
|
|
||||||
let pk = ExtendedPubKey::from_private(&sk);
|
|
||||||
let addr = Address::from_key(pk.network, &pk.public_key);
|
|
||||||
for out in index.find_by_script(&addr.script_pubkey()).iter() {
|
|
||||||
ret += out.txo.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sum external balance
|
|
||||||
let master = try!(ExtendedPrivKey::from_path(
|
|
||||||
&self.master,
|
|
||||||
account.external_path.as_slice()).map_err(Error::Bip32Error));
|
|
||||||
for &cnum in account.external_used.iter() {
|
|
||||||
let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error));
|
|
||||||
let pk = ExtendedPubKey::from_private(&sk);
|
|
||||||
let addr = Address::from_key(pk.network, &pk.public_key);
|
|
||||||
for out in index.find_by_script(&addr.script_pubkey()).iter() {
|
|
||||||
ret += out.txo.value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ret)
|
/// Adds an account to a wallet
|
||||||
}
|
pub fn account_insert(&mut self, name: String)
|
||||||
|
-> Result<(), Error> {
|
||||||
|
if self.accounts.contains_key(&name) {
|
||||||
|
return Err(Error::DuplicateAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = self.accounts.len() as u32;
|
||||||
|
self.accounts.insert(name, Account {
|
||||||
|
internal_path: vec![Hardened(idx), Normal(1)],
|
||||||
|
internal_used: vec![],
|
||||||
|
internal_next: 0,
|
||||||
|
external_path: vec![Hardened(idx), Normal(0)],
|
||||||
|
external_used: vec![],
|
||||||
|
external_next: 0
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locates an account in a wallet
|
||||||
|
#[inline]
|
||||||
|
pub fn account_get<'a>(&'a self, name: &str) -> Option<&'a Account> {
|
||||||
|
self.accounts.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new address
|
||||||
|
pub fn new_address(&mut self,
|
||||||
|
account: &str,
|
||||||
|
chain: AccountChain)
|
||||||
|
-> Result<Address, Error> {
|
||||||
|
let account = self.accounts.get_mut(account);
|
||||||
|
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
||||||
|
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
|
||||||
|
|
||||||
|
let (mut i, master) = match chain {
|
||||||
|
Internal => (account.internal_next,
|
||||||
|
try!(ExtendedPrivKey::from_path(
|
||||||
|
&self.master,
|
||||||
|
account.internal_path.as_slice()).map_err(Error::Bip32Error))),
|
||||||
|
External => (account.external_next,
|
||||||
|
try!(ExtendedPrivKey::from_path(
|
||||||
|
&self.master,
|
||||||
|
account.external_path.as_slice()).map_err(Error::Bip32Error))),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Scan for next admissible address
|
||||||
|
let mut sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
|
||||||
|
let mut address = Address::from_key(
|
||||||
|
master.network,
|
||||||
|
&PublicKey::from_secret_key(&sk.secret_key, true));
|
||||||
|
while !index.admissible_address(&address) {
|
||||||
|
i += 1;
|
||||||
|
sk = try!(master.ckd_priv(Normal(i)).map_err(Error::Bip32Error));
|
||||||
|
address = Address::from_key(
|
||||||
|
master.network,
|
||||||
|
&PublicKey::from_secret_key(&sk.secret_key, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
match chain {
|
||||||
|
Internal => {
|
||||||
|
account.internal_used.push(Normal(i));
|
||||||
|
account.internal_next = i + 1;
|
||||||
|
}
|
||||||
|
External => {
|
||||||
|
account.external_used.push(Normal(i));
|
||||||
|
account.external_next = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the network of the wallet
|
||||||
|
#[inline]
|
||||||
|
pub fn network(&self) -> Network {
|
||||||
|
self.master.network
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a key suitable for keying hash functions for DoS protection
|
||||||
|
#[inline]
|
||||||
|
pub fn siphash_key(&self) -> (u64, u64) {
|
||||||
|
(LittleEndian::read_u64(&self.master.chain_code[0..8]),
|
||||||
|
LittleEndian::read_u64(&self.master.chain_code[8..16]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Total balance
|
||||||
|
pub fn total_balance(&self) -> Result<u64, Error> {
|
||||||
|
let mut ret = 0;
|
||||||
|
for (_, account) in self.accounts.iter() {
|
||||||
|
ret += try!(self.account_balance(account));
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Account balance
|
||||||
|
pub fn balance(&self, account: &str) -> Result<u64, Error> {
|
||||||
|
let account = self.accounts.get(account);
|
||||||
|
let account = match account { Some(a) => a, None => return Err(Error::AccountNotFound) };
|
||||||
|
self.account_balance(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn account_balance(&self, account: &Account) -> Result<u64, Error> {
|
||||||
|
let index = match self.index { Some(ref i) => i, None => return Err(Error::NoAddressIndex) };
|
||||||
|
|
||||||
|
let mut ret = 0;
|
||||||
|
|
||||||
|
// Sum internal balance
|
||||||
|
let master = try!(ExtendedPrivKey::from_path(
|
||||||
|
&self.master,
|
||||||
|
account.internal_path.as_slice()).map_err(Error::Bip32Error));
|
||||||
|
for &cnum in account.internal_used.iter() {
|
||||||
|
let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error));
|
||||||
|
let pk = ExtendedPubKey::from_private(&sk);
|
||||||
|
let addr = Address::from_key(pk.network, &pk.public_key);
|
||||||
|
for out in index.find_by_script(&addr.script_pubkey()).iter() {
|
||||||
|
ret += out.txo.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sum external balance
|
||||||
|
let master = try!(ExtendedPrivKey::from_path(
|
||||||
|
&self.master,
|
||||||
|
account.external_path.as_slice()).map_err(Error::Bip32Error));
|
||||||
|
for &cnum in account.external_used.iter() {
|
||||||
|
let sk = try!(master.ckd_priv(cnum).map_err(Error::Bip32Error));
|
||||||
|
let pk = ExtendedPubKey::from_private(&sk);
|
||||||
|
let addr = Address::from_key(pk.network, &pk.public_key);
|
||||||
|
for out in index.find_by_script(&addr.script_pubkey()).iter() {
|
||||||
|
ret += out.txo.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue