Merge pull request #349 from pandoracore/hashtypes

Hash new types as specified in #284
This commit is contained in:
Andrew Poelstra 2020-01-05 00:46:45 +00:00 committed by GitHub
commit 50f3a60712
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 452 additions and 311 deletions

View File

@ -22,7 +22,7 @@ use-serde = ["hex", "serde", "bitcoin_hashes/serde", "secp256k1/serde"]
[dependencies]
bech32 = "0.7.1"
bitcoin_hashes = "0.7"
bitcoin_hashes = "0.7.3"
bitcoinconsensus = { version = "0.17", optional = true }
serde = { version = "1", optional = true }
hex = { version = "=0.3.2", optional = true }

View File

@ -20,17 +20,16 @@
//! these blocks and the blockchain.
//!
use hashes::{sha256d, Hash};
use util;
use util::Error::{BlockBadTarget, BlockBadProofOfWork};
use util::hash::{BitcoinHash, MerkleRoot, bitcoin_merkle_root};
use util::hash::{BitcoinHash, bitcoin_merkle_root};
use hashes::{Hash, HashEngine};
use hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
use util::uint::Uint256;
use consensus::encode::Encodable;
use network::constants::Network;
use blockdata::transaction::Transaction;
use blockdata::constants::max_target;
use hashes::HashEngine;
/// A block header, which contains all the block's information except
/// the actual transactions
@ -39,9 +38,9 @@ pub struct BlockHeader {
/// The protocol version. Should always be 1.
pub version: u32,
/// Reference to the previous block in the chain
pub prev_blockhash: sha256d::Hash,
pub prev_blockhash: BlockHash,
/// The root hash of the merkle tree of transactions in the block
pub merkle_root: sha256d::Hash,
pub merkle_root: TxMerkleNode,
/// The timestamp of the block, as claimed by the miner
pub time: u32,
/// The target value below which the blockhash must lie, encoded as a
@ -82,7 +81,7 @@ impl Block {
.rposition(|o| {
o.script_pubkey.len () >= 38 &&
o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) {
let commitment = sha256d::Hash::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
let commitment = WitnessCommitment::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
// witness reserved value is in coinbase input witness
if coinbase.input[0].witness.len() == 1 && coinbase.input[0].witness[0].len() == 32 {
let witness_root = self.witness_root();
@ -94,25 +93,31 @@ impl Block {
false
}
/// Calculate the transaction merkle root.
pub fn merkle_root(&self) -> TxMerkleNode {
let hashes = self.txdata.iter().map(|obj| obj.txid().as_hash());
bitcoin_merkle_root(hashes).into()
}
/// compute witness commitment for the transaction list
pub fn compute_witness_commitment (witness_root: &sha256d::Hash, witness_reserved_value: &[u8]) -> sha256d::Hash {
let mut encoder = sha256d::Hash::engine();
pub fn compute_witness_commitment (witness_root: &WitnessMerkleNode, witness_reserved_value: &[u8]) -> WitnessCommitment {
let mut encoder = WitnessCommitment::engine();
witness_root.consensus_encode(&mut encoder).unwrap();
encoder.input(witness_reserved_value);
sha256d::Hash::from_engine(encoder)
WitnessCommitment::from_engine(encoder)
}
/// Merkle root of transactions hashed for witness
pub fn witness_root(&self) -> sha256d::Hash {
let mut txhashes = vec!(sha256d::Hash::default());
txhashes.extend(self.txdata.iter().skip(1).map(|t|t.bitcoin_hash()));
bitcoin_merkle_root(txhashes)
pub fn witness_root(&self) -> WitnessMerkleNode {
let hashes = self.txdata.iter().enumerate().map(|(i, t)|
if i == 0 {
// Replace the first hash with zeroes.
Wtxid::default().as_hash()
} else {
t.wtxid().as_hash()
}
}
impl MerkleRoot for Block {
fn merkle_root(&self) -> sha256d::Hash {
bitcoin_merkle_root(self.txdata.iter().map(|obj| obj.txid()).collect())
);
bitcoin_merkle_root(hashes).into()
}
}
@ -188,15 +193,15 @@ impl BlockHeader {
}
}
impl BitcoinHash for BlockHeader {
fn bitcoin_hash(&self) -> sha256d::Hash {
impl BitcoinHash<BlockHash> for BlockHeader {
fn bitcoin_hash(&self) -> BlockHash {
use consensus::encode::serialize;
sha256d::Hash::hash(&serialize(self))
BlockHash::hash(&serialize(self))
}
}
impl BitcoinHash for Block {
fn bitcoin_hash(&self) -> sha256d::Hash {
impl BitcoinHash<BlockHash> for Block {
fn bitcoin_hash(&self) -> BlockHash {
self.header.bitcoin_hash()
}
}
@ -212,7 +217,6 @@ mod tests {
use blockdata::block::{Block, BlockHeader};
use consensus::encode::{deserialize, serialize};
use util::hash::MerkleRoot;
#[test]
fn block_test() {

View File

@ -22,7 +22,7 @@
use std::default::Default;
use hashes::hex::FromHex;
use hashes::sha256d;
use blockdata::opcodes;
use blockdata::script;
use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
@ -96,14 +96,16 @@ fn bitcoin_genesis_tx() -> Transaction {
/// Constructs and returns the genesis block
pub fn genesis_block(network: Network) -> Block {
let txdata = vec![bitcoin_genesis_tx()];
let hash: sha256d::Hash = txdata[0].txid().into();
let merkle_root = hash.into();
match network {
Network::Bitcoin => {
let txdata = vec![bitcoin_genesis_tx()];
Block {
header: BlockHeader {
version: 1,
prev_blockhash: Default::default(),
merkle_root: txdata[0].txid(),
merkle_root,
time: 1231006505,
bits: 0x1d00ffff,
nonce: 2083236893
@ -112,12 +114,11 @@ pub fn genesis_block(network: Network) -> Block {
}
}
Network::Testnet => {
let txdata = vec![bitcoin_genesis_tx()];
Block {
header: BlockHeader {
version: 1,
prev_blockhash: Default::default(),
merkle_root: txdata[0].txid(),
merkle_root,
time: 1296688602,
bits: 0x1d00ffff,
nonce: 414098458
@ -126,12 +127,11 @@ pub fn genesis_block(network: Network) -> Block {
}
}
Network::Regtest => {
let txdata = vec![bitcoin_genesis_tx()];
Block {
header: BlockHeader {
version: 1,
prev_blockhash: Default::default(),
merkle_root: txdata[0].txid(),
merkle_root,
time: 1296688602,
bits: 0x207fffff,
nonce: 2
@ -171,7 +171,7 @@ mod test {
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
assert_eq!(gen.lock_time, 0);
assert_eq!(format!("{:x}", gen.bitcoin_hash()),
assert_eq!(format!("{:x}", gen.wtxid()),
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
}

View File

@ -29,9 +29,10 @@ use std::{error, fmt, io};
#[cfg(feature = "serde")] use serde;
use hash_types::{ScriptHash, WScriptHash};
use blockdata::opcodes;
use consensus::{encode, Decodable, Encodable};
use hashes::{hash160, sha256, Hash};
use hashes::Hash;
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
#[cfg(feature="bitcoinconsensus")] use std::convert;
#[cfg(feature="bitcoinconsensus")] use OutPoint;
@ -233,7 +234,7 @@ impl Script {
/// Compute the P2SH output corresponding to this redeem script
pub fn to_p2sh(&self) -> Script {
Builder::new().push_opcode(opcodes::all::OP_HASH160)
.push_slice(&hash160::Hash::hash(&self.0)[..])
.push_slice(&ScriptHash::hash(&self.0)[..])
.push_opcode(opcodes::all::OP_EQUAL)
.into_script()
}
@ -242,7 +243,7 @@ impl Script {
/// script")
pub fn to_v0_p2wsh(&self) -> Script {
Builder::new().push_int(0)
.push_slice(&sha256::Hash::hash(&self.0)[..])
.push_slice(&WScriptHash::hash(&self.0)[..])
.into_script()
}

View File

@ -26,21 +26,21 @@
use std::default::Default;
use std::{fmt, io};
use hashes::{self, sha256d, Hash};
use hashes::{self, Hash, sha256d};
use hashes::hex::FromHex;
use util::endian;
use util::hash::BitcoinHash;
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
use blockdata::script::Script;
use consensus::{encode, serialize, Decodable, Encodable};
use hash_types::*;
use VarInt;
/// A reference to a transaction output
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct OutPoint {
/// The referenced transaction's txid
pub txid: sha256d::Hash,
pub txid: Txid,
/// The index of the referenced output in its transaction's vout
pub vout: u32,
}
@ -49,7 +49,7 @@ serde_struct_human_string_impl!(OutPoint, "an OutPoint", txid, vout);
impl OutPoint {
/// Create a new [OutPoint].
#[inline]
pub fn new(txid: sha256d::Hash, vout: u32) -> OutPoint {
pub fn new(txid: Txid, vout: u32) -> OutPoint {
OutPoint {
txid: txid,
vout: vout,
@ -175,7 +175,7 @@ impl ::std::str::FromStr for OutPoint {
return Err(ParseOutPointError::Format);
}
Ok(OutPoint {
txid: sha256d::Hash::from_hex(&s[..colon]).map_err(ParseOutPointError::Txid)?,
txid: Txid::from_hex(&s[..colon]).map_err(ParseOutPointError::Txid)?,
vout: parse_vout(&s[colon+1..])?,
})
}
@ -286,20 +286,29 @@ impl Transaction {
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), witness: vec![], .. *txin }).collect(),
output: self.output.clone(),
};
cloned_tx.bitcoin_hash()
cloned_tx.txid().into()
}
/// Computes the txid. For non-segwit transactions this will be identical
/// to the output of `BitcoinHash::bitcoin_hash()`, but for segwit transactions,
/// this will give the correct txid (not including witnesses) while `bitcoin_hash`
/// to the output of `wtxid()`, but for segwit transactions,
/// this will give the correct txid (not including witnesses) while `wtxid`
/// will also hash witnesses.
pub fn txid(&self) -> sha256d::Hash {
let mut enc = sha256d::Hash::engine();
pub fn txid(&self) -> Txid {
let mut enc = Txid::engine();
self.version.consensus_encode(&mut enc).unwrap();
self.input.consensus_encode(&mut enc).unwrap();
self.output.consensus_encode(&mut enc).unwrap();
self.lock_time.consensus_encode(&mut enc).unwrap();
sha256d::Hash::from_engine(enc)
Txid::from_engine(enc)
}
/// Computes SegWit-version of the transaction id (wtxid). For transaction with the witness
/// data this hash includes witness, for pre-witness transaction it is equal to the normal
/// value returned by txid() function.
pub fn wtxid(&self) -> Wtxid {
let mut enc = Wtxid::engine();
self.consensus_encode(&mut enc).unwrap();
Wtxid::from_engine(enc)
}
/// Computes a signature hash for a given input index with a given sighash flag.
@ -315,14 +324,14 @@ impl Transaction {
/// # Panics
/// Panics if `input_index` is greater than or equal to `self.input.len()`
///
pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighash_u32: u32) -> sha256d::Hash {
pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighash_u32: u32) -> SigHash {
assert!(input_index < self.input.len()); // Panic on OOB
let (sighash, anyone_can_pay) = SigHashType::from_u32(sighash_u32).split_anyonecanpay_flag();
// Special-case sighash_single bug because this is easy enough.
if sighash == SigHashType::Single && input_index >= self.output.len() {
return sha256d::Hash::from_slice(&[1, 0, 0, 0, 0, 0, 0, 0,
return SigHash::from_slice(&[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]).unwrap();
@ -370,7 +379,7 @@ impl Transaction {
// hash the result
let mut raw_vec = serialize(&tx);
raw_vec.extend_from_slice(&endian::u32_to_array_le(sighash_u32));
sha256d::Hash::hash(&raw_vec)
SigHash::hash(&raw_vec)
}
/// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty
@ -437,14 +446,6 @@ impl Transaction {
}
}
impl BitcoinHash for Transaction {
fn bitcoin_hash(&self) -> sha256d::Hash {
let mut enc = sha256d::Hash::engine();
self.consensus_encode(&mut enc).unwrap();
sha256d::Hash::from_engine(enc)
}
}
impl_consensus_encoding!(TxOut, value, script_pubkey);
impl Encodable for OutPoint {
@ -625,11 +626,12 @@ mod tests {
use blockdata::script::Script;
use consensus::encode::serialize;
use consensus::encode::deserialize;
use util::hash::BitcoinHash;
use hashes::{sha256d, Hash};
use hashes::Hash;
use hashes::hex::FromHex;
use hash_types::*;
#[test]
fn test_outpoint() {
assert_eq!(OutPoint::from_str("i don't care"),
@ -645,20 +647,20 @@ mod tests {
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:+42"),
Err(ParseOutPointError::VoutNotCanonical));
assert_eq!(OutPoint::from_str("i don't care:1"),
Err(ParseOutPointError::Txid(sha256d::Hash::from_hex("i don't care").unwrap_err())));
Err(ParseOutPointError::Txid(Txid::from_hex("i don't care").unwrap_err())));
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X:1"),
Err(ParseOutPointError::Txid(sha256d::Hash::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X").unwrap_err())));
Err(ParseOutPointError::Txid(Txid::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c945X").unwrap_err())));
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:lol"),
Err(ParseOutPointError::Vout(u32::from_str("lol").unwrap_err())));
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:42"),
Ok(OutPoint{
txid: sha256d::Hash::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
txid: Txid::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
vout: 42,
}));
assert_eq!(OutPoint::from_str("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0"),
Ok(OutPoint{
txid: sha256d::Hash::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
txid: Txid::from_hex("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap(),
vout: 0,
}));
}
@ -692,7 +694,7 @@ mod tests {
}
#[test]
fn test_transaction() {
fn test_nonsegwit_transaction() {
let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
let tx: Result<Transaction, _> = deserialize(&hex_tx);
assert!(tx.is_ok());
@ -709,11 +711,44 @@ mod tests {
assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, 0);
assert_eq!(format!("{:x}", realtx.bitcoin_hash()),
assert_eq!(format!("{:x}", realtx.txid()),
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
assert_eq!(format!("{:x}", realtx.wtxid()),
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
assert_eq!(realtx.get_weight(), 193*4);
}
#[test]
fn test_segwit_transaction() {
let hex_tx = Vec::<u8>::from_hex(
"02000000000101595895ea20179de87052b4046dfe6fd515860505d6511a9004cf12a1f93cac7c01000000\
00ffffffff01deb807000000000017a9140f3444e271620c736808aa7b33e370bd87cb5a078702483045022\
100fb60dad8df4af2841adc0346638c16d0b8035f5e3f3753b88db122e70c79f9370220756e6633b17fd271\
0e626347d28d60b0a2d6cbb41de51740644b9fb3ba7751040121028fa937ca8cba2197a37c007176ed89410\
55d3bcb8627d085e94553e62f057dcc00000000"
).unwrap();
let tx: Result<Transaction, _> = deserialize(&hex_tx);
assert!(tx.is_ok());
let realtx = tx.unwrap();
// All these tests aren't really needed because if they fail, the hash check at the end
// will also fail. But these will show you where the failure is so I'll leave them in.
assert_eq!(realtx.version, 2);
assert_eq!(realtx.input.len(), 1);
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
// as little-endian 256-bit numbers rather than as data strings.
assert_eq!(format!("{:x}", realtx.input[0].previous_output.txid),
"7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string());
assert_eq!(realtx.input[0].previous_output.vout, 1);
assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, 0);
assert_eq!(format!("{:x}", realtx.txid()),
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());
assert_eq!(format!("{:x}", realtx.wtxid()),
"80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string());
assert_eq!(realtx.get_weight(), 442);
}
#[test]
fn tx_no_input_deserialization() {
let hex_tx = Vec::<u8>::from_hex(
@ -778,7 +813,7 @@ mod tests {
).unwrap();
let tx: Transaction = deserialize(&hex_tx).unwrap();
assert_eq!(format!("{:x}", tx.bitcoin_hash()), "d6ac4a5e61657c4c604dcde855a1db74ec6b3e54f32695d72c5e11c7761ea1b4");
assert_eq!(format!("{:x}", tx.wtxid()), "d6ac4a5e61657c4c604dcde855a1db74ec6b3e54f32695d72c5e11c7761ea1b4");
assert_eq!(format!("{:x}", tx.txid()), "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec");
assert_eq!(tx.get_weight(), 2718);
@ -793,7 +828,7 @@ mod tests {
).unwrap();
let tx: Transaction = deserialize(&hex_tx).unwrap();
assert_eq!(format!("{:x}", tx.bitcoin_hash()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
assert_eq!(format!("{:x}", tx.wtxid()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
assert_eq!(format!("{:x}", tx.txid()), "971ed48a62c143bbd9c87f4bafa2ef213cfa106c6e140f111931d0be307468dd");
}
@ -810,7 +845,7 @@ mod tests {
let script = Script::from(Vec::<u8>::from_hex(script).unwrap());
let mut raw_expected = Vec::<u8>::from_hex(expected_result).unwrap();
raw_expected.reverse();
let expected_result = sha256d::Hash::from_slice(&raw_expected[..]).unwrap();
let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap();
let actual_result = tx.signature_hash(input_index, &script, hash_type as u32);
assert_eq!(actual_result, expected_result);

View File

@ -34,7 +34,8 @@ use std::borrow::Cow;
use std::io::{Cursor, Read, Write};
use hashes::hex::ToHex;
use hashes::{sha256d, Hash as HashTrait};
use hashes::{sha256d, Hash};
use hash_types::{BlockHash, FilterHash, TxMerkleNode};
use util::endian;
use util::psbt;
@ -81,6 +82,8 @@ pub enum Error {
UnsupportedSegwitFlag(u8),
/// Unrecognized network command
UnrecognizedNetworkCommand(String),
/// Invalid Inventory type
UnknownInventoryType(u32),
}
impl fmt::Display for Error {
@ -101,6 +104,7 @@ impl fmt::Display for Error {
"unsupported segwit version: {}", swflag),
Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f,
"unrecognized network command: {}", nwcmd),
Error::UnknownInventoryType(ref tp) => write!(f, "Unknown Inventory type: {}", tp),
}
}
}
@ -117,7 +121,8 @@ impl error::Error for Error {
| Error::UnknownNetworkMagic(..)
| Error::ParseFailed(..)
| Error::UnsupportedSegwitFlag(..)
| Error::UnrecognizedNetworkCommand(..) => None,
| Error::UnrecognizedNetworkCommand(..)
| Error::UnknownInventoryType(..) => None,
}
}
@ -590,7 +595,9 @@ macro_rules! impl_vec {
}
}
}
impl_vec!(sha256d::Hash);
impl_vec!(BlockHash);
impl_vec!(FilterHash);
impl_vec!(TxMerkleNode);
impl_vec!(Transaction);
impl_vec!(TxOut);
impl_vec!(TxIn);
@ -649,7 +656,7 @@ impl Decodable for Box<[u8]> {
/// Do a double-SHA256 on some data and return the first 4 bytes
fn sha2_checksum(data: &[u8]) -> [u8; 4] {
let checksum = <sha256d::Hash as HashTrait>::hash(data);
let checksum = <sha256d::Hash as Hash>::hash(data);
[checksum[0], checksum[1], checksum[2], checksum[3]]
}
@ -730,8 +737,7 @@ impl Encodable for sha256d::Hash {
impl Decodable for sha256d::Hash {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
let inner = <[u8; 32]>::consensus_decode(d)?;
Ok(sha256d::Hash::from_slice(&inner).unwrap())
Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(d)?))
}
}

65
src/hash_types.rs Normal file
View File

@ -0,0 +1,65 @@
// Rust Bitcoin Library
// Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! File defines types for hashes used throughout the library. These types are needed in order
//! to avoid mixing data of the same hash format (like SHA256d) but of different meaning
//! (transaction id, block hash etc).
use std::io;
use consensus::encode::{Encodable, Decodable, Error};
use hashes::{sha256, sha256d, hash160, Hash};
use hashes::hex::{ToHex, FromHex};
macro_rules! impl_hashencode {
($hashtype:ident) => {
impl Encodable for $hashtype {
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
self.0.consensus_encode(s)
}
}
impl Decodable for $hashtype {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
Ok(Self::from_inner(<<$hashtype as Hash>::Inner>::consensus_decode(d)?))
}
}
}
}
hash_newtype!(Txid, sha256d::Hash, 32, doc="A bitcoin transaction hash/transaction ID.");
hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID.");
hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash.");
hash_newtype!(SigHash, sha256d::Hash, 32, doc="Hash of the transaction according to the signature algorithm");
hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key.");
hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode.");
hash_newtype!(WPubkeyHash, hash160::Hash, 20, doc="SegWit version of a public key hash.");
hash_newtype!(WScriptHash, sha256::Hash, 32, doc="SegWit version of a Bitcoin Script bytecode hash.");
hash_newtype!(TxMerkleNode, sha256d::Hash, 32, doc="A hash of the Merkle tree branch or root for transactions");
hash_newtype!(WitnessMerkleNode, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for witness data");
hash_newtype!(WitnessCommitment, sha256d::Hash, 32, doc="A hash corresponding to the witness structure commitment in the coinbase transaction");
hash_newtype!(XpubIdentifier, hash160::Hash, 20, doc="XpubIdentifier as defined in BIP-32.");
hash_newtype!(FilterHash, sha256d::Hash, 32, doc="Bloom filter souble-SHA256 locator hash, as defined in BIP-168");
impl_hashencode!(Txid);
impl_hashencode!(Wtxid);
impl_hashencode!(SigHash);
impl_hashencode!(BlockHash);
impl_hashencode!(TxMerkleNode);
impl_hashencode!(WitnessMerkleNode);
impl_hashencode!(FilterHash);

View File

@ -227,7 +227,7 @@ macro_rules! display_from_debug {
macro_rules! hex_script (($s:expr) => (::blockdata::script::Script::from(::hex::decode($s).unwrap())));
#[cfg(test)]
macro_rules! hex_hash (($s:expr) => (::hashes::sha256d::Hash::from_slice(&::hex::decode($s).unwrap()).unwrap()));
macro_rules! hex_hash (($h:ident, $s:expr) => ($h::from_slice(&::hex::decode($s).unwrap()).unwrap()));
macro_rules! serde_struct_impl {
($name:ident, $($fe:ident),*) => (

View File

@ -52,7 +52,7 @@
#![allow(ellipsis_inclusive_range_patterns)]
// Re-exported dependencies.
pub extern crate bitcoin_hashes as hashes;
#[macro_use] pub extern crate bitcoin_hashes as hashes;
pub extern crate secp256k1;
pub extern crate bech32;
@ -77,7 +77,11 @@ pub mod network;
pub mod blockdata;
pub mod util;
pub mod consensus;
// Do not remove: required in order to get hash types implementation macros to work correctly
#[allow(unused_imports)]
pub mod hash_types;
pub use hash_types::*;
pub use blockdata::block::Block;
pub use blockdata::block::BlockHeader;
pub use blockdata::script::Script;

View File

@ -341,7 +341,7 @@ mod test {
use hashes::Hash as HashTrait;
use network::address::Address;
use super::message_network::{Reject, RejectReason, VersionMessage};
use network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage, InvType};
use network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage};
use blockdata::block::{Block, BlockHeader};
use network::message_filter::{GetCFilters, CFilter, GetCFHeaders, CFHeaders, GetCFCheckpt, CFCheckpt};
use blockdata::transaction::Transaction;
@ -362,11 +362,11 @@ mod test {
NetworkMessage::Version(version_msg),
NetworkMessage::Verack,
NetworkMessage::Addr(vec![(45, Address::new(&([123,255,000,100], 833).into(), ServiceFlags::NETWORK))]),
NetworkMessage::Inv(vec![Inventory{inv_type: InvType::Block, hash: hash([8u8; 32])}]),
NetworkMessage::GetData(vec![Inventory{inv_type: InvType::Transaction, hash: hash([45u8; 32])}]),
NetworkMessage::NotFound(vec![Inventory{inv_type: InvType::Error, hash: hash([45u8; 32])}]),
NetworkMessage::GetBlocks(GetBlocksMessage::new(vec![hash([1u8; 32]), hash([4u8; 32])], hash([5u8; 32]))),
NetworkMessage::GetHeaders(GetHeadersMessage::new(vec![hash([10u8; 32]), hash([40u8; 32])], hash([50u8; 32]))),
NetworkMessage::Inv(vec![Inventory::Block(hash([8u8; 32]).into())]),
NetworkMessage::GetData(vec![Inventory::Transaction(hash([45u8; 32]).into())]),
NetworkMessage::NotFound(vec![Inventory::Error]),
NetworkMessage::GetBlocks(GetBlocksMessage::new(vec![hash([1u8; 32]).into(), hash([4u8; 32]).into()], hash([5u8; 32]).into())),
NetworkMessage::GetHeaders(GetHeadersMessage::new(vec![hash([10u8; 32]).into(), hash([40u8; 32]).into()], hash([50u8; 32]).into())),
NetworkMessage::MemPool,
NetworkMessage::Tx(tx),
NetworkMessage::Block(block),
@ -375,12 +375,12 @@ mod test {
NetworkMessage::GetAddr,
NetworkMessage::Ping(15),
NetworkMessage::Pong(23),
NetworkMessage::GetCFilters(GetCFilters{filter_type: 2, start_height: 52, stop_hash: hash([42u8; 32])}),
NetworkMessage::CFilter(CFilter{filter_type: 7, block_hash: hash([25u8; 32]), filter: vec![1,2,3]}),
NetworkMessage::GetCFHeaders(GetCFHeaders{filter_type: 4, start_height: 102, stop_hash: hash([47u8; 32])}),
NetworkMessage::CFHeaders(CFHeaders{filter_type: 13, stop_hash: hash([53u8; 32]), previous_filter: hash([12u8; 32]), filter_hashes: vec![hash([4u8; 32]), hash([12u8; 32])]}),
NetworkMessage::GetCFCheckpt(GetCFCheckpt{filter_type: 17, stop_hash: hash([25u8; 32])}),
NetworkMessage::CFCheckpt(CFCheckpt{filter_type: 27, stop_hash: hash([77u8; 32]), filter_headers: vec![hash([3u8; 32]), hash([99u8; 32])]}),
NetworkMessage::GetCFilters(GetCFilters{filter_type: 2, start_height: 52, stop_hash: hash([42u8; 32]).into()}),
NetworkMessage::CFilter(CFilter{filter_type: 7, block_hash: hash([25u8; 32]).into(), filter: vec![1,2,3]}),
NetworkMessage::GetCFHeaders(GetCFHeaders{filter_type: 4, start_height: 102, stop_hash: hash([47u8; 32]).into()}),
NetworkMessage::CFHeaders(CFHeaders{filter_type: 13, stop_hash: hash([53u8; 32]).into(), previous_filter: hash([12u8; 32]).into(), filter_hashes: vec![hash([4u8; 32]).into(), hash([12u8; 32]).into()]}),
NetworkMessage::GetCFCheckpt(GetCFCheckpt{filter_type: 17, stop_hash: hash([25u8; 32]).into()}),
NetworkMessage::CFCheckpt(CFCheckpt{filter_type: 27, stop_hash: hash([77u8; 32]).into(), filter_headers: vec![hash([3u8; 32]).into(), hash([99u8; 32]).into()]}),
NetworkMessage::Alert(vec![45,66,3,2,6,8,9,12,3,130]),
NetworkMessage::Reject(Reject{message: "Test reject".into(), ccode: RejectReason::Duplicate, reason: "Cause".into(), hash: hash([255u8; 32])}),
];

View File

@ -18,25 +18,64 @@
//! Bitcoin data (blocks and transactions) around.
//!
use network::constants;
use consensus::encode::{self, Decodable, Encodable};
use hashes::sha256d;
use std::io;
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
/// The type of an inventory object
pub enum InvType {
use hashes::sha256d;
use network::constants;
use consensus::encode::{self, Decodable, Encodable};
use hash_types::{BlockHash, Txid, Wtxid};
/// An inventory item.
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
pub enum Inventory {
/// Error --- these inventories can be ignored
Error,
/// Transaction
Transaction,
Transaction(Txid),
/// Block
Block,
/// Witness Block
WitnessBlock,
Block(BlockHash),
/// Witness Transaction
WitnessTransaction
WitnessTransaction(Wtxid),
/// Witness Block
WitnessBlock(BlockHash),
}
impl Encodable for Inventory {
#[inline]
fn consensus_encode<S: io::Write>(
&self,
mut s: S,
) -> Result<usize, encode::Error> {
macro_rules! encode_inv {
($code:expr, $item:expr) => {
u32::consensus_encode(&$code, &mut s)? +
$item.consensus_encode(&mut s)?
}
}
Ok(match *self {
Inventory::Error => encode_inv!(0, sha256d::Hash::default()),
Inventory::Transaction(ref t) => encode_inv!(1, t),
Inventory::Block(ref b) => encode_inv!(2, b),
Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t),
Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b),
})
}
}
impl Decodable for Inventory {
#[inline]
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
let inv_type: u32 = Decodable::consensus_decode(&mut d)?;
Ok(match inv_type {
0 => Inventory::Error,
1 => Inventory::Transaction(Decodable::consensus_decode(&mut d)?),
2 => Inventory::Block(Decodable::consensus_decode(&mut d)?),
0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?),
0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?),
tp => return Err(encode::Error::UnknownInventoryType(tp)),
})
}
}
// Some simple messages
@ -49,9 +88,9 @@ pub struct GetBlocksMessage {
/// Locator hashes --- ordered newest to oldest. The remote peer will
/// reply with its longest known chain, starting from a locator hash
/// if possible and block 1 otherwise.
pub locator_hashes: Vec<sha256d::Hash>,
pub locator_hashes: Vec<BlockHash>,
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
pub stop_hash: sha256d::Hash,
pub stop_hash: BlockHash,
}
/// The `getheaders` message
@ -62,29 +101,14 @@ pub struct GetHeadersMessage {
/// Locator hashes --- ordered newest to oldest. The remote peer will
/// reply with its longest known chain, starting from a locator hash
/// if possible and block 1 otherwise.
pub locator_hashes: Vec<sha256d::Hash>,
pub locator_hashes: Vec<BlockHash>,
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
pub stop_hash: sha256d::Hash
}
/// An inventory object --- a reference to a Bitcoin object
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Inventory {
/// The type of object that is referenced
pub inv_type: InvType,
/// The object's hash
pub hash: sha256d::Hash
}
impl ::std::hash::Hash for Inventory {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
pub stop_hash: BlockHash
}
impl GetBlocksMessage {
/// Construct a new `getblocks` message
pub fn new(locator_hashes: Vec<sha256d::Hash>, stop_hash: sha256d::Hash) -> GetBlocksMessage {
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
GetBlocksMessage {
version: constants::PROTOCOL_VERSION,
locator_hashes: locator_hashes.clone(),
@ -97,7 +121,7 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
impl GetHeadersMessage {
/// Construct a new `getheaders` message
pub fn new(locator_hashes: Vec<sha256d::Hash>, stop_hash: sha256d::Hash) -> GetHeadersMessage {
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
GetHeadersMessage {
version: constants::PROTOCOL_VERSION,
locator_hashes: locator_hashes,
@ -108,40 +132,6 @@ impl GetHeadersMessage {
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
impl Encodable for Inventory {
#[inline]
fn consensus_encode<S: io::Write>(
&self,
mut s: S,
) -> Result<usize, encode::Error> {
let inv_len = match self.inv_type {
InvType::Error => 0u32,
InvType::Transaction => 1,
InvType::Block => 2,
InvType::WitnessBlock => 0x40000002,
InvType::WitnessTransaction => 0x40000001
}.consensus_encode(&mut s)?;
Ok(inv_len + self.hash.consensus_encode(&mut s)?)
}
}
impl Decodable for Inventory {
#[inline]
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
let int_type: u32 = Decodable::consensus_decode(&mut d)?;
Ok(Inventory {
inv_type: match int_type {
0 => InvType::Error,
1 => InvType::Transaction,
2 => InvType::Block,
// TODO do not fail here
_ => { panic!("bad inventory type field") }
},
hash: Decodable::consensus_decode(d)?
})
}
}
#[cfg(test)]
mod tests {
use super::{GetHeadersMessage, GetBlocksMessage};

View File

@ -1,7 +1,8 @@
//!
//! BIP157 Client Side Block Filtering network messages
//!
use hashes::sha256d;
use hash_types::{BlockHash, FilterHash};
#[derive(PartialEq, Eq, Clone, Debug)]
/// getcfilters message
@ -11,7 +12,7 @@ pub struct GetCFilters {
/// The height of the first block in the requested range
pub start_height: u32,
/// The hash of the last block in the requested range
pub stop_hash: sha256d::Hash,
pub stop_hash: BlockHash,
}
impl_consensus_encoding!(GetCFilters, filter_type, start_height, stop_hash);
@ -21,7 +22,7 @@ pub struct CFilter {
/// Byte identifying the type of filter being returned
pub filter_type: u8,
/// Block hash of the Bitcoin block for which the filter is being returned
pub block_hash: sha256d::Hash,
pub block_hash: BlockHash,
/// The serialized compact filter for this block
pub filter: Vec<u8>,
}
@ -35,7 +36,7 @@ pub struct GetCFHeaders {
/// The height of the first block in the requested range
pub start_height: u32,
/// The hash of the last block in the requested range
pub stop_hash: sha256d::Hash,
pub stop_hash: BlockHash,
}
impl_consensus_encoding!(GetCFHeaders, filter_type, start_height, stop_hash);
@ -45,11 +46,11 @@ pub struct CFHeaders {
/// Filter type for which headers are requested
pub filter_type: u8,
/// The hash of the last block in the requested range
pub stop_hash: sha256d::Hash,
pub stop_hash: BlockHash,
/// The filter header preceding the first block in the requested range
pub previous_filter: sha256d::Hash,
pub previous_filter: FilterHash,
/// The filter hashes for each block in the requested range
pub filter_hashes: Vec<sha256d::Hash>,
pub filter_hashes: Vec<FilterHash>,
}
impl_consensus_encoding!(CFHeaders, filter_type, stop_hash, previous_filter, filter_hashes);
@ -59,7 +60,7 @@ pub struct GetCFCheckpt {
/// Filter type for which headers are requested
pub filter_type: u8,
/// The hash of the last block in the requested range
pub stop_hash: sha256d::Hash,
pub stop_hash: BlockHash,
}
impl_consensus_encoding!(GetCFCheckpt, filter_type, stop_hash);
@ -69,8 +70,8 @@ pub struct CFCheckpt {
/// Filter type for which headers are requested
pub filter_type: u8,
/// The hash of the last block in the requested range
pub stop_hash: sha256d::Hash,
pub stop_hash: BlockHash,
/// The filter headers at intervals of 1,000
pub filter_headers: Vec<sha256d::Hash>,
pub filter_headers: Vec<FilterHash>,
}
impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers);

View File

@ -44,8 +44,9 @@ use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use bech32;
use hashes::{hash160, sha256, Hash};
use hashes::Hash;
use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
use blockdata::opcodes;
use blockdata::script;
use network::constants::Network;
@ -159,9 +160,9 @@ impl FromStr for AddressType {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Payload {
/// pay-to-pkhash address
PubkeyHash(hash160::Hash),
PubkeyHash(PubkeyHash),
/// P2SH address
ScriptHash(hash160::Hash),
ScriptHash(ScriptHash),
/// Segwit address
WitnessProgram {
/// The witness program version
@ -175,9 +176,9 @@ impl Payload {
/// Get a [Payload] from an output script (scriptPubkey).
pub fn from_script(script: &script::Script) -> Option<Payload> {
Some(if script.is_p2pkh() {
Payload::PubkeyHash(Hash::from_slice(&script.as_bytes()[3..23]).unwrap())
Payload::PubkeyHash(PubkeyHash::from_slice(&script.as_bytes()[3..23]).unwrap())
} else if script.is_p2sh() {
Payload::ScriptHash(Hash::from_slice(&script.as_bytes()[2..22]).unwrap())
Payload::ScriptHash(ScriptHash::from_slice(&script.as_bytes()[2..22]).unwrap())
} else if script.is_witness_program() {
// We can unwrap the u5 check and assume script length
// because [Script::is_witness_program] makes sure of this.
@ -242,12 +243,12 @@ impl Address {
/// This is the preferred non-witness type address
#[inline]
pub fn p2pkh(pk: &key::PublicKey, network: Network) -> Address {
let mut hash_engine = hash160::Hash::engine();
let mut hash_engine = PubkeyHash::engine();
pk.write_into(&mut hash_engine);
Address {
network: network,
payload: Payload::PubkeyHash(hash160::Hash::from_engine(hash_engine)),
payload: Payload::PubkeyHash(PubkeyHash::from_engine(hash_engine)),
}
}
@ -257,21 +258,21 @@ impl Address {
pub fn p2sh(script: &script::Script, network: Network) -> Address {
Address {
network: network,
payload: Payload::ScriptHash(hash160::Hash::hash(&script[..])),
payload: Payload::ScriptHash(ScriptHash::hash(&script[..])),
}
}
/// Create a witness pay to public key address from a public key
/// This is the native segwit address type for an output redeemable with a single signature
pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Address {
let mut hash_engine = hash160::Hash::engine();
let mut hash_engine = WPubkeyHash::engine();
pk.write_into(&mut hash_engine);
Address {
network: network,
payload: Payload::WitnessProgram {
version: bech32::u5::try_from_u8(0).expect("0<32"),
program: hash160::Hash::from_engine(hash_engine)[..].to_vec(),
program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
},
}
}
@ -279,16 +280,16 @@ impl Address {
/// Create a pay to script address that embeds a witness pay to public key
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients
pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Address {
let mut hash_engine = hash160::Hash::engine();
let mut hash_engine = WPubkeyHash::engine();
pk.write_into(&mut hash_engine);
let builder = script::Builder::new()
.push_int(0)
.push_slice(&hash160::Hash::from_engine(hash_engine)[..]);
.push_slice(&WPubkeyHash::from_engine(hash_engine)[..]);
Address {
network: network,
payload: Payload::ScriptHash(hash160::Hash::hash(builder.into_script().as_bytes())),
payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())),
}
}
@ -298,7 +299,7 @@ impl Address {
network: network,
payload: Payload::WitnessProgram {
version: bech32::u5::try_from_u8(0).expect("0<32"),
program: sha256::Hash::hash(&script[..])[..].to_vec(),
program: WScriptHash::hash(&script[..])[..].to_vec(),
},
}
}
@ -308,12 +309,12 @@ impl Address {
pub fn p2shwsh(script: &script::Script, network: Network) -> Address {
let ws = script::Builder::new()
.push_int(0)
.push_slice(&sha256::Hash::hash(&script[..])[..])
.push_slice(&WScriptHash::hash(&script[..])[..])
.into_script();
Address {
network: network,
payload: Payload::ScriptHash(hash160::Hash::hash(&ws[..])),
payload: Payload::ScriptHash(ScriptHash::hash(&ws[..])),
}
}
@ -470,19 +471,19 @@ impl FromStr for Address {
let (network, payload) = match data[0] {
0 => (
Network::Bitcoin,
Payload::PubkeyHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap()),
),
5 => (
Network::Bitcoin,
Payload::ScriptHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap()),
),
111 => (
Network::Testnet,
Payload::PubkeyHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
Payload::PubkeyHash(PubkeyHash::from_slice(&data[1..]).unwrap()),
),
196 => (
Network::Testnet,
Payload::ScriptHash(hash160::Hash::from_slice(&data[1..]).unwrap()),
Payload::ScriptHash(ScriptHash::from_slice(&data[1..]).unwrap()),
),
x => return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))),
};
@ -505,7 +506,7 @@ mod tests {
use std::str::FromStr;
use std::string::ToString;
use hashes::{hash160, Hash};
use hashes::Hash;
use hex::{decode as hex_decode, encode as hex_encode};
use blockdata::script::Script;
@ -517,7 +518,8 @@ mod tests {
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
macro_rules! hex_script (($hex:expr) => (Script::from(hex!($hex))));
macro_rules! hex_hash160 (($hex:expr) => (hash160::Hash::from_slice(&hex!($hex)).unwrap()));
macro_rules! hex_pubkeyhash (($hex:expr) => (PubkeyHash::from_slice(&hex!($hex)).unwrap()));
macro_rules! hex_scripthash (($hex:expr) => (ScriptHash::from_slice(&hex!($hex)).unwrap()));
fn roundtrips(addr: &Address) {
assert_eq!(
@ -539,7 +541,7 @@ mod tests {
fn test_p2pkh_address_58() {
let addr = Address {
network: Bitcoin,
payload: Payload::PubkeyHash(hex_hash160!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
payload: Payload::PubkeyHash(hex_pubkeyhash!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
};
assert_eq!(
@ -568,7 +570,7 @@ mod tests {
fn test_p2sh_address_58() {
let addr = Address {
network: Bitcoin,
payload: Payload::ScriptHash(hex_hash160!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
payload: Payload::ScriptHash(hex_scripthash!("162c5ea71c0b23f5b9022ef047c4a86470a5b070")),
};
assert_eq!(

View File

@ -19,8 +19,8 @@
//! signatures, which are placed in the scriptSig.
//!
use hashes::{sha256d, Hash};
use hashes::Hash;
use hash_types::SigHash;
use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxIn};
use consensus::encode::Encodable;
@ -32,11 +32,11 @@ pub struct SighashComponents {
tx_version: u32,
tx_locktime: u32,
/// Hash of all the previous outputs
pub hash_prevouts: sha256d::Hash,
pub hash_prevouts: SigHash,
/// Hash of all the input sequence nos
pub hash_sequence: sha256d::Hash,
pub hash_sequence: SigHash,
/// Hash of all the outputs in this transaction
pub hash_outputs: sha256d::Hash,
pub hash_outputs: SigHash,
}
impl SighashComponents {
@ -46,27 +46,27 @@ impl SighashComponents {
/// script_sig and witnesses.
pub fn new(tx: &Transaction) -> SighashComponents {
let hash_prevouts = {
let mut enc = sha256d::Hash::engine();
let mut enc = SigHash::engine();
for txin in &tx.input {
txin.previous_output.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
SigHash::from_engine(enc)
};
let hash_sequence = {
let mut enc = sha256d::Hash::engine();
let mut enc = SigHash::engine();
for txin in &tx.input {
txin.sequence.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
SigHash::from_engine(enc)
};
let hash_outputs = {
let mut enc = sha256d::Hash::engine();
let mut enc = SigHash::engine();
for txout in &tx.output {
txout.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
SigHash::from_engine(enc)
};
SighashComponents {
@ -80,8 +80,8 @@ impl SighashComponents {
/// Compute the BIP143 sighash for a `SIGHASH_ALL` signature for the given
/// input.
pub fn sighash_all(&self, txin: &TxIn, script_code: &Script, value: u64) -> sha256d::Hash {
let mut enc = sha256d::Hash::engine();
pub fn sighash_all(&self, txin: &TxIn, script_code: &Script, value: u64) -> SigHash {
let mut enc = SigHash::engine();
self.tx_version.consensus_encode(&mut enc).unwrap();
self.hash_prevouts.consensus_encode(&mut enc).unwrap();
self.hash_sequence.consensus_encode(&mut enc).unwrap();
@ -95,12 +95,13 @@ impl SighashComponents {
self.hash_outputs.consensus_encode(&mut enc).unwrap();
self.tx_locktime.consensus_encode(&mut enc).unwrap();
1u32.consensus_encode(&mut enc).unwrap(); // hashtype
sha256d::Hash::from_engine(enc)
SigHash::from_engine(enc)
}
}
#[cfg(test)]
mod tests {
use hash_types::SigHash;
use blockdata::script::Script;
use blockdata::transaction::Transaction;
use consensus::encode::deserialize;
@ -140,20 +141,20 @@ mod tests {
tx_version: 1,
tx_locktime: 17,
hash_prevouts: hex_hash!(
"96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
SigHash, "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
),
hash_sequence: hex_hash!(
"52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b"
SigHash, "52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b"
),
hash_outputs: hex_hash!(
"863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5"
SigHash, "863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5"
),
}
);
assert_eq!(
comp.sighash_all(&tx.input[1], &witness_script, value),
hex_hash!("c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670")
hex_hash!(SigHash, "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670")
);
}
@ -177,20 +178,20 @@ mod tests {
tx_version: 1,
tx_locktime: 1170,
hash_prevouts: hex_hash!(
"b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
SigHash, "b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a"
),
hash_sequence: hex_hash!(
"18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198"
SigHash, "18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198"
),
hash_outputs: hex_hash!(
"de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83"
SigHash, "de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83"
),
}
);
assert_eq!(
comp.sighash_all(&tx.input[0], &witness_script, value),
hex_hash!("64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6")
hex_hash!(SigHash, "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6")
);
}
@ -220,20 +221,20 @@ mod tests {
tx_version: 1,
tx_locktime: 0,
hash_prevouts: hex_hash!(
"74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
SigHash, "74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
),
hash_sequence: hex_hash!(
"3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044"
SigHash, "3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044"
),
hash_outputs: hex_hash!(
"bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc"
SigHash, "bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc"
),
}
);
assert_eq!(
comp.sighash_all(&tx.input[0], &witness_script, value),
hex_hash!("185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c")
hex_hash!(SigHash, "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c")
);
}
}

View File

@ -50,7 +50,8 @@ use std::error;
use std::fmt::{Display, Formatter};
use std::io::Cursor;
use hashes::{Hash, sha256d, siphash24};
use hashes::{Hash, siphash24};
use hash_types::{BlockHash, FilterHash};
use blockdata::block::Block;
use blockdata::script::Script;
@ -106,12 +107,12 @@ pub struct BlockFilter {
impl BlockFilter {
/// compute this filter's id in a chain of filters
pub fn filter_id(&self, previous_filter_id: &sha256d::Hash) -> sha256d::Hash {
let filter_hash = sha256d::Hash::hash(self.content.as_slice());
pub fn filter_id(&self, previous_filter_id: &FilterHash) -> FilterHash {
let filter_hash = FilterHash::hash(self.content.as_slice());
let mut header_data = [0u8; 64];
header_data[0..32].copy_from_slice(&filter_hash[..]);
header_data[32..64].copy_from_slice(&previous_filter_id[..]);
sha256d::Hash::hash(&header_data)
FilterHash::hash(&header_data)
}
/// create a new filter from pre-computed data
@ -133,13 +134,13 @@ impl BlockFilter {
}
/// match any query pattern
pub fn match_any(&self, block_hash: &sha256d::Hash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
pub fn match_any(&self, block_hash: &BlockHash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
let filter_reader = BlockFilterReader::new(block_hash);
filter_reader.match_any(&mut Cursor::new(self.content.as_slice()), query)
}
/// match all query pattern
pub fn match_all(&self, block_hash: &sha256d::Hash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
pub fn match_all(&self, block_hash: &BlockHash, query: &mut Iterator<Item=&[u8]>) -> Result<bool, Error> {
let filter_reader = BlockFilterReader::new(block_hash);
filter_reader.match_all(&mut Cursor::new(self.content.as_slice()), query)
}
@ -206,7 +207,7 @@ pub struct BlockFilterReader {
impl BlockFilterReader {
/// Create a block filter reader
pub fn new(block_hash: &sha256d::Hash) -> BlockFilterReader {
pub fn new(block_hash: &BlockHash) -> BlockFilterReader {
let block_hash_as_int = block_hash.into_inner();
let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]);
let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]);
@ -523,6 +524,7 @@ mod test {
use std::collections::{HashSet, HashMap};
use std::io::Cursor;
use hash_types::BlockHash;
use hashes::hex::FromHex;
use super::*;
@ -555,13 +557,13 @@ mod test {
let testdata = serde_json::from_str::<Value>(data).unwrap().as_array().unwrap().clone();
for t in testdata.iter().skip(1) {
let block_hash = sha256d::Hash::from_hex(&t.get(1).unwrap().as_str().unwrap()).unwrap();
let block_hash = BlockHash::from_hex(&t.get(1).unwrap().as_str().unwrap()).unwrap();
let block: Block = deserialize(hex::decode(&t.get(2).unwrap().as_str().unwrap().as_bytes()).unwrap().as_slice()).unwrap();
assert_eq!(block.bitcoin_hash(), block_hash);
let scripts = t.get(3).unwrap().as_array().unwrap();
let previous_filter_id = sha256d::Hash::from_hex(&t.get(4).unwrap().as_str().unwrap()).unwrap();
let previous_filter_id = FilterHash::from_hex(&t.get(4).unwrap().as_str().unwrap()).unwrap();
let filter_content = hex::decode(&t.get(5).unwrap().as_str().unwrap().as_bytes()).unwrap();
let filter_id = sha256d::Hash::from_hex(&t.get(6).unwrap().as_str().unwrap()).unwrap();
let filter_id = FilterHash::from_hex(&t.get(6).unwrap().as_str().unwrap()).unwrap();
let mut txmap = HashMap::new();
let mut si = scripts.iter();
@ -583,7 +585,7 @@ mod test {
assert_eq!(test_filter.content, filter.content);
let block_hash = &block.header.bitcoin_hash();
assert!(filter.match_all(&block_hash, &mut txmap.iter()
assert!(filter.match_all(block_hash, &mut txmap.iter()
.filter_map(|(_, s)| if !s.is_empty() { Some(s.as_bytes()) } else { None })).unwrap());
for (_, script) in &txmap {

View File

@ -21,7 +21,8 @@ use std::{error, fmt};
use std::str::FromStr;
#[cfg(feature = "serde")] use serde;
use hashes::{hex, hash160, sha512, Hash, HashEngine, Hmac, HmacEngine};
use hash_types::XpubIdentifier;
use hashes::{hex, sha512, Hash, HashEngine, Hmac, HmacEngine};
use secp256k1::{self, Secp256k1};
use network::constants::Network;
@ -478,7 +479,7 @@ impl ExtendedPrivKey {
}
/// Returns the HASH160 of the chaincode
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> hash160::Hash {
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XpubIdentifier {
ExtendedPubKey::from_private(secp, self).identifier()
}
@ -561,10 +562,10 @@ impl ExtendedPubKey {
}
/// Returns the HASH160 of the chaincode
pub fn identifier(&self) -> hash160::Hash {
let mut engine = hash160::Hash::engine();
pub fn identifier(&self) -> XpubIdentifier {
let mut engine = XpubIdentifier::engine();
self.public_key.write_into(&mut engine);
hash160::Hash::from_engine(engine)
XpubIdentifier::from_engine(engine)
}
/// Returns the first four bytes of the identifier

View File

@ -21,11 +21,12 @@
use secp256k1::{self, Secp256k1};
use PrivateKey;
use PublicKey;
use hashes::{hash160, sha256, Hash, HashEngine, Hmac, HmacEngine};
use hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
use blockdata::{opcodes, script};
use std::{error, fmt};
use hash_types::ScriptHash;
use network::constants::Network;
use util::address;
@ -208,7 +209,7 @@ pub fn create_address<C: secp256k1::Verification>(secp: &Secp256k1<C>,
Ok(address::Address {
network: network,
payload: address::Payload::ScriptHash(
hash160::Hash::hash(&script[..])
ScriptHash::hash(&script[..])
)
})
}

View File

@ -16,43 +16,68 @@
//! Utility functions related to hashing data, including merkleization
use std::cmp::min;
use std::default::Default;
use hashes::{sha256d, Hash};
use std::io;
use hashes::Hash;
use consensus::encode::Encodable;
/// Any collection of objects for which a merkle root makes sense to calculate
pub trait MerkleRoot {
/// Construct a merkle tree from a collection, with elements ordered as
/// they were in the original collection, and return the merkle root.
fn merkle_root(&self) -> sha256d::Hash;
}
/// Calculates the merkle root of a list of txids hashes directly
pub fn bitcoin_merkle_root(data: Vec<sha256d::Hash>) -> sha256d::Hash {
/// Calculates the merkle root of a list of hashes inline
/// into the allocated slice.
///
/// In most cases, you'll want to use [bitcoin_merkle_root] instead.
pub fn bitcoin_merkle_root_inline<T>(data: &mut [T]) -> T
where T: Hash + Encodable,
<T as Hash>::Engine: io::Write,
{
// Base case
if data.len() < 1 {
return Default::default();
}
if data.len() < 2 {
return data[0];
return T::from_inner(data[0].into_inner());
}
// Recursion
let mut next = vec![];
for idx in 0..((data.len() + 1) / 2) {
let idx1 = 2 * idx;
let idx2 = min(idx1 + 1, data.len() - 1);
let mut encoder = sha256d::Hash::engine();
let mut encoder = T::engine();
data[idx1].consensus_encode(&mut encoder).unwrap();
data[idx2].consensus_encode(&mut encoder).unwrap();
next.push(sha256d::Hash::from_engine(encoder));
data[idx] = T::from_engine(encoder);
}
bitcoin_merkle_root(next)
let half_len = data.len() / 2 + data.len() % 2;
bitcoin_merkle_root_inline(&mut data[0..half_len])
}
/// Calculates the merkle root of an iterator of hashes.
pub fn bitcoin_merkle_root<T, I>(mut iter: I) -> T
where T: Hash + Encodable,
<T as Hash>::Engine: io::Write,
I: ExactSizeIterator<Item = T>,
{
// Base case
if iter.len() == 0 {
return Default::default();
}
if iter.len() == 1 {
return T::from_inner(iter.nth(0).unwrap().into_inner());
}
// Recursion
let half_len = iter.len() / 2 + iter.len() % 2;
let mut alloc = Vec::with_capacity(half_len);
while let Some(hash1) = iter.next() {
// If the size is odd, use the last element twice.
let hash2 = iter.next().unwrap_or(hash1);
let mut encoder = T::engine();
hash1.consensus_encode(&mut encoder).unwrap();
hash2.consensus_encode(&mut encoder).unwrap();
alloc.push(T::from_engine(encoder));
}
bitcoin_merkle_root_inline(&mut alloc)
}
/// Objects which are referred to by hash
pub trait BitcoinHash {
pub trait BitcoinHash<T: Hash> {
/// Produces a Sha256dHash which can be used to refer to the object
fn bitcoin_hash(&self) -> sha256d::Hash;
fn bitcoin_hash(&self) -> T;
}

View File

@ -26,7 +26,7 @@
//!
//! ```rust
//! extern crate bitcoin;
//! use bitcoin::hashes::sha256d;
//! use bitcoin::hash_types::Txid;
//! use bitcoin::hashes::hex::FromHex;
//! use bitcoin::{Block, MerkleBlock};
//!
@ -41,12 +41,12 @@
//! let mb: MerkleBlock = bitcoin::consensus::deserialize(&mb_bytes).unwrap();
//!
//! // Authenticate and extract matched transaction ids
//! let mut matches: Vec<sha256d::Hash> = vec![];
//! let mut matches: Vec<Txid> = vec![];
//! let mut index: Vec<u32> = vec![];
//! assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
//! assert_eq!(1, matches.len());
//! assert_eq!(
//! sha256d::Hash::from_hex(
//! Txid::from_hex(
//! "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap(),
//! matches[0]
//! );
@ -58,11 +58,12 @@
use std::collections::HashSet;
use std::io;
use hashes::{sha256d, Hash};
use hashes::Hash;
use hash_types::{Txid, TxMerkleNode};
use blockdata::transaction::Transaction;
use blockdata::constants::{MAX_BLOCK_WEIGHT, MIN_TRANSACTION_WEIGHT};
use consensus::encode::{self, Decodable, Encodable};
use util::hash::BitcoinHash;
use util::merkleblock::MerkleBlockError::*;
use {Block, BlockHeader};
@ -119,7 +120,7 @@ pub struct PartialMerkleTree {
/// node-is-parent-of-matched-txid bits
bits: Vec<bool>,
/// Transaction ids and internal hashes
hashes: Vec<sha256d::Hash>,
hashes: Vec<TxMerkleNode>,
}
impl PartialMerkleTree {
@ -133,18 +134,18 @@ impl PartialMerkleTree {
///
/// ```rust
/// extern crate bitcoin;
/// use bitcoin::hashes::sha256d;
/// use bitcoin::hash_types::Txid;
/// use bitcoin::hashes::hex::FromHex;
/// use bitcoin::util::merkleblock::PartialMerkleTree;
///
/// # fn main() {
/// // Block 80000
/// let txids: Vec<sha256d::Hash> = [
/// let txids: Vec<Txid> = [
/// "c06fbab289f723c6261d3030ddb6be121f7d2508d77862bb1e484f5cd7f92b25",
/// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2",
/// ]
/// .iter()
/// .map(|hex| sha256d::Hash::from_hex(hex).unwrap())
/// .map(|hex| Txid::from_hex(hex).unwrap())
/// .collect();
///
/// // Select the second transaction
@ -153,7 +154,7 @@ impl PartialMerkleTree {
/// assert!(tree.extract_matches(&mut vec![], &mut vec![]).is_ok());
/// # }
/// ```
pub fn from_txids(txids: &[sha256d::Hash], matches: &[bool]) -> Self {
pub fn from_txids(txids: &[Txid], matches: &[bool]) -> Self {
// We can never have zero txs in a merkle block, we always need the coinbase tx
assert_ne!(txids.len(), 0);
assert_eq!(txids.len(), matches.len());
@ -178,9 +179,9 @@ impl PartialMerkleTree {
/// returns the merkle root, or error in case of failure
pub fn extract_matches(
&self,
matches: &mut Vec<sha256d::Hash>,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<sha256d::Hash, MerkleBlockError> {
) -> Result<TxMerkleNode, MerkleBlockError> {
matches.clear();
indexes.clear();
// An empty set will not work
@ -220,7 +221,7 @@ impl PartialMerkleTree {
if hash_used != self.hashes.len() as u32 {
return Err(BadFormat("Not all hashes were consumed".to_owned()));
}
Ok(hash_merkle_root)
Ok(TxMerkleNode::from_inner(hash_merkle_root.into_inner()))
}
/// Helper function to efficiently calculate the number of nodes at given height
@ -231,10 +232,10 @@ impl PartialMerkleTree {
}
/// Calculate the hash of a node in the merkle tree (at leaf level: the txid's themselves)
fn calc_hash(&self, height: u32, pos: u32, txids: &[sha256d::Hash]) -> sha256d::Hash {
fn calc_hash(&self, height: u32, pos: u32, txids: &[Txid]) -> TxMerkleNode {
if height == 0 {
// Hash at height 0 is the txid itself
txids[pos as usize]
TxMerkleNode::from_inner(txids[pos as usize].into_inner())
} else {
// Calculate left hash
let left = self.calc_hash(height - 1, pos * 2, txids);
@ -254,7 +255,7 @@ impl PartialMerkleTree {
&mut self,
height: u32,
pos: u32,
txids: &[sha256d::Hash],
txids: &[Txid],
matches: &[bool],
) {
// Determine whether this node is the parent of at least one matched txid
@ -270,7 +271,7 @@ impl PartialMerkleTree {
if height == 0 || !parent_of_match {
// If at height 0, or nothing interesting below, store hash and stop
let hash = self.calc_hash(height, pos, txids);
self.hashes.push(hash);
self.hashes.push(hash.into());
} else {
// Otherwise, don't store any hash, but descend into the subtrees
self.traverse_and_build(height - 1, pos * 2, txids, matches);
@ -288,9 +289,9 @@ impl PartialMerkleTree {
pos: u32,
bits_used: &mut u32,
hash_used: &mut u32,
matches: &mut Vec<sha256d::Hash>,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<sha256d::Hash, MerkleBlockError> {
) -> Result<TxMerkleNode, MerkleBlockError> {
if *bits_used as usize >= self.bits.len() {
return Err(BadFormat("Overflowed the bits array".to_owned()));
}
@ -305,7 +306,7 @@ impl PartialMerkleTree {
*hash_used += 1;
if height == 0 && parent_of_match {
// in case of height 0, we have a matched txid
matches.push(hash);
matches.push(Txid::from_inner(hash.into_inner()));
indexes.push(pos);
}
Ok(hash)
@ -343,11 +344,11 @@ impl PartialMerkleTree {
}
/// Helper method to produce SHA256D(left + right)
fn parent_hash(left: sha256d::Hash, right: sha256d::Hash) -> sha256d::Hash {
let mut encoder = sha256d::Hash::engine();
fn parent_hash(left: TxMerkleNode, right: TxMerkleNode) -> TxMerkleNode {
let mut encoder = TxMerkleNode::engine();
left.consensus_encode(&mut encoder).unwrap();
right.consensus_encode(&mut encoder).unwrap();
sha256d::Hash::from_engine(encoder)
TxMerkleNode::from_engine(encoder)
}
}
@ -369,7 +370,7 @@ impl Encodable for PartialMerkleTree {
impl Decodable for PartialMerkleTree {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
let num_transactions: u32 = Decodable::consensus_decode(&mut d)?;
let hashes: Vec<sha256d::Hash> = Decodable::consensus_decode(&mut d)?;
let hashes: Vec<TxMerkleNode> = Decodable::consensus_decode(&mut d)?;
let bytes: Vec<u8> = Decodable::consensus_decode(d)?;
let mut bits: Vec<bool> = vec![false; bytes.len() * 8];
@ -407,7 +408,7 @@ impl MerkleBlock {
///
/// ```rust
/// extern crate bitcoin;
/// use bitcoin::hashes::sha256d;
/// use bitcoin::hash_types::Txid;
/// use bitcoin::hashes::hex::FromHex;
/// use bitcoin::{Block, MerkleBlock};
///
@ -426,25 +427,25 @@ impl MerkleBlock {
/// let block: Block = bitcoin::consensus::deserialize(&block_bytes).unwrap();
///
/// // Create a merkle block containing a single transaction
/// let txid = sha256d::Hash::from_hex(
/// let txid = Txid::from_hex(
/// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap();
/// let match_txids = vec![txid].into_iter().collect();
/// let mb = MerkleBlock::from_block(&block, &match_txids);
///
/// // Authenticate and extract matched transaction ids
/// let mut matches: Vec<sha256d::Hash> = vec![];
/// let mut matches: Vec<Txid> = vec![];
/// let mut index: Vec<u32> = vec![];
/// assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
/// assert_eq!(txid, matches[0]);
/// # }
/// ```
pub fn from_block(block: &Block, match_txids: &HashSet<sha256d::Hash>) -> Self {
pub fn from_block(block: &Block, match_txids: &HashSet<Txid>) -> Self {
let header = block.header;
let mut matches: Vec<bool> = Vec::with_capacity(block.txdata.len());
let mut hashes: Vec<sha256d::Hash> = Vec::with_capacity(block.txdata.len());
let mut hashes: Vec<Txid> = Vec::with_capacity(block.txdata.len());
for hash in block.txdata.iter().map(BitcoinHash::bitcoin_hash) {
for hash in block.txdata.iter().map(Transaction::txid) {
matches.push(match_txids.contains(&hash));
hashes.push(hash);
}
@ -458,7 +459,7 @@ impl MerkleBlock {
/// returns Ok(()) on success, or error in case of failure
pub fn extract_matches(
&self,
matches: &mut Vec<sha256d::Hash>,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<(), MerkleBlockError> {
let merkle_root = self.txn.extract_matches(matches, indexes)?;
@ -495,8 +496,9 @@ impl Decodable for MerkleBlock {
mod tests {
use std::cmp::min;
use hashes::Hash;
use hashes::hex::{FromHex, ToHex};
use hashes::{sha256d, Hash};
use hash_types::{Txid, TxMerkleNode};
use secp256k1::rand::prelude::*;
use consensus::encode::{deserialize, serialize};
@ -512,11 +514,12 @@ mod tests {
for num_tx in tx_counts {
// Create some fake tx ids
let txids = (1..num_tx + 1) // change to `1..=num_tx` when min Rust >= 1.26.0
.map(|i| sha256d::Hash::from_hex(&format!("{:064x}", i)).unwrap())
.map(|i| Txid::from_hex(&format!("{:064x}", i)).unwrap())
.collect::<Vec<_>>();
// Calculate the merkle root and height
let merkle_root_1 = bitcoin_merkle_root(txids.clone());
let hashes = txids.iter().map(|t| t.as_hash());
let merkle_root_1: TxMerkleNode = bitcoin_merkle_root(hashes).into();
let mut height = 1;
let mut ntx = num_tx;
while ntx > 1 {
@ -555,7 +558,7 @@ mod tests {
deserialize(&serialized).expect("Could not deserialize own data");
// Extract merkle root and matched txids from copy
let mut match_txid2 = vec![];
let mut match_txid2: Vec<Txid> = vec![];
let mut indexes = vec![];
let merkle_root_2 = pmt2
.extract_matches(&mut match_txid2, &mut indexes)
@ -563,7 +566,7 @@ mod tests {
// Check that it has the same merkle root as the original, and a valid one
assert_eq!(merkle_root_1, merkle_root_2);
assert_ne!(merkle_root_2, sha256d::Hash::default());
assert_ne!(merkle_root_2, TxMerkleNode::default());
// check that it contains the matched transactions (in the same order!)
assert_eq!(match_txid1, match_txid2);
@ -585,9 +588,9 @@ mod tests {
#[test]
fn pmt_malleability() {
// Create some fake tx ids with the last 2 hashes repeating
let txids: Vec<sha256d::Hash> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 10]
let txids: Vec<Txid> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 10]
.iter()
.map(|i| sha256d::Hash::from_hex(&format!("{:064x}", i)).unwrap())
.map(|i| Txid::from_hex(&format!("{:064x}", i)).unwrap())
.collect();
let matches = vec![
@ -628,12 +631,12 @@ mod tests {
fn merkleblock_construct_from_txids_found() {
let block = get_block_13b8a();
let txids: Vec<sha256d::Hash> = [
let txids: Vec<Txid> = [
"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20",
"f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07",
]
.iter()
.map(|hex| sha256d::Hash::from_hex(hex).unwrap())
.map(|hex| Txid::from_hex(hex).unwrap())
.collect();
let txid1 = txids[0];
@ -644,7 +647,7 @@ mod tests {
assert_eq!(merkle_block.header.bitcoin_hash(), block.bitcoin_hash());
let mut matches: Vec<sha256d::Hash> = vec![];
let mut matches: Vec<Txid> = vec![];
let mut index: Vec<u32> = vec![];
assert_eq!(
@ -670,14 +673,14 @@ mod tests {
let block = get_block_13b8a();
let txids = ["c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"]
.iter()
.map(|hex| sha256d::Hash::from_hex(hex).unwrap())
.map(|hex| Txid::from_hex(hex).unwrap())
.collect();
let merkle_block = MerkleBlock::from_block(&block, &txids);
assert_eq!(merkle_block.header.bitcoin_hash(), block.bitcoin_hash());
let mut matches: Vec<sha256d::Hash> = vec![];
let mut matches: Vec<Txid> = vec![];
let mut index: Vec<u32> = vec![];
assert_eq!(
@ -699,7 +702,7 @@ mod tests {
let hashes = &mut self.hashes;
let mut hash = hashes[n].into_inner();
hash[(bit >> 3) as usize] ^= 1 << (bit & 7);
hashes[n] = sha256d::Hash::from_slice(&hash).unwrap();
hashes[n] = TxMerkleNode::from_slice(&hash).unwrap();
}
}

View File

@ -163,7 +163,7 @@ impl Decodable for PartiallySignedTransaction {
#[cfg(test)]
mod tests {
use hashes::hex::FromHex;
use hashes::sha256d;
use hash_types::Txid;
use std::collections::BTreeMap;
@ -255,7 +255,7 @@ mod tests {
lock_time: 1257139,
input: vec![TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
txid: Txid::from_hex(
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
).unwrap(),
vout: 0,
@ -315,7 +315,7 @@ mod tests {
use hex::decode as hex_decode;
use hashes::hex::FromHex;
use hashes::sha256d;
use hash_types::Txid;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
@ -363,7 +363,7 @@ mod tests {
lock_time: 1257139,
input: vec![TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
txid: Txid::from_hex(
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
).unwrap(),
vout: 0,
@ -391,7 +391,7 @@ mod tests {
lock_time: 0,
input: vec![TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
txid: Txid::from_hex(
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
).unwrap(),
vout: 1,
@ -405,7 +405,7 @@ mod tests {
},
TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
txid: Txid::from_hex(
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
).unwrap(),
vout: 1,
@ -548,7 +548,7 @@ mod tests {
let tx = &psbt.global.unsigned_tx;
assert_eq!(
tx.txid(),
sha256d::Hash::from_hex(
Txid::from_hex(
"75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115"
).unwrap()
);