diff --git a/Cargo.toml b/Cargo.toml index 3b9ae51b..4a828c47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index 9d4a40b3..f67e8586 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -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) - } -} - -impl MerkleRoot for Block { - fn merkle_root(&self) -> sha256d::Hash { - bitcoin_merkle_root(self.txdata.iter().map(|obj| obj.txid()).collect()) + 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() + } + ); + bitcoin_merkle_root(hashes).into() } } @@ -188,15 +193,15 @@ impl BlockHeader { } } -impl BitcoinHash for BlockHeader { - fn bitcoin_hash(&self) -> sha256d::Hash { +impl BitcoinHash 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 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() { diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index 9fc7562f..23622918 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -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()); } diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 0d51be5e..508a91da 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -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() } diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 57027eb2..7beac7f5 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -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::::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap(); let tx: Result = 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::::from_hex( + "02000000000101595895ea20179de87052b4046dfe6fd515860505d6511a9004cf12a1f93cac7c01000000\ + 00ffffffff01deb807000000000017a9140f3444e271620c736808aa7b33e370bd87cb5a078702483045022\ + 100fb60dad8df4af2841adc0346638c16d0b8035f5e3f3753b88db122e70c79f9370220756e6633b17fd271\ + 0e626347d28d60b0a2d6cbb41de51740644b9fb3ba7751040121028fa937ca8cba2197a37c007176ed89410\ + 55d3bcb8627d085e94553e62f057dcc00000000" + ).unwrap(); + let tx: Result = 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::::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::::from_hex(script).unwrap()); let mut raw_expected = Vec::::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); diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs index a7fa71b7..402a4b51 100644 --- a/src/consensus/encode.rs +++ b/src/consensus/encode.rs @@ -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; @@ -71,8 +72,8 @@ pub enum Error { /// The invalid checksum actual: [u8; 4], }, - /// VarInt was encoded in a non-minimal way - NonMinimalVarInt, + /// VarInt was encoded in a non-minimal way + NonMinimalVarInt, /// Network magic was unknown UnknownNetworkMagic(u32), /// Parsing error @@ -81,6 +82,8 @@ pub enum Error { UnsupportedSegwitFlag(u8), /// Unrecognized network command UnrecognizedNetworkCommand(String), + /// Invalid Inventory type + UnknownInventoryType(u32), } impl fmt::Display for Error { @@ -94,13 +97,14 @@ impl fmt::Display for Error { "allocation of oversized vector: requested {}, maximum {}", r, m), Error::InvalidChecksum { expected: ref e, actual: ref a } => write!(f, "invalid checksum: expected {}, actual {}", e.to_hex(), a.to_hex()), - Error::NonMinimalVarInt => write!(f, "non-minimal varint"), + Error::NonMinimalVarInt => write!(f, "non-minimal varint"), Error::UnknownNetworkMagic(ref m) => write!(f, "unknown network magic: {}", m), Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e), Error::UnsupportedSegwitFlag(ref swflag) => write!(f, "unsupported segwit version: {}", swflag), Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f, "unrecognized network command: {}", nwcmd), + Error::UnknownInventoryType(ref tp) => write!(f, "Unknown Inventory type: {}", tp), } } } @@ -113,11 +117,12 @@ impl error::Error for Error { Error::UnexpectedNetworkMagic { .. } | Error::OversizedVectorAllocation { .. } | Error::InvalidChecksum { .. } - | Error::NonMinimalVarInt + | Error::NonMinimalVarInt | 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 = ::hash(data); + let checksum = ::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: D) -> Result { - let inner = <[u8; 32]>::consensus_decode(d)?; - Ok(sha256d::Hash::from_slice(&inner).unwrap()) + Ok(Self::from_inner(<::Inner>::consensus_decode(d)?)) } } diff --git a/src/hash_types.rs b/src/hash_types.rs new file mode 100644 index 00000000..514c5309 --- /dev/null +++ b/src/hash_types.rs @@ -0,0 +1,65 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// 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 . +// + +//! 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(&self, s: S) -> Result { + self.0.consensus_encode(s) + } + } + + impl Decodable for $hashtype { + fn consensus_decode(d: D) -> Result { + 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); diff --git a/src/internal_macros.rs b/src/internal_macros.rs index ebf2dbbe..362a2290 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -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),*) => ( diff --git a/src/lib.rs b/src/lib.rs index cabf24e6..6ba26f92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/network/message.rs b/src/network/message.rs index 88187495..82c74060 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -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])}), ]; diff --git a/src/network/message_blockdata.rs b/src/network/message_blockdata.rs index bbf43a74..a758c3c4 100644 --- a/src/network/message_blockdata.rs +++ b/src/network/message_blockdata.rs @@ -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( + &self, + mut s: S, + ) -> Result { + 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(mut d: D) -> Result { + 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, + pub locator_hashes: Vec, /// 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, + pub locator_hashes: Vec, /// 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(&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, stop_hash: sha256d::Hash) -> GetBlocksMessage { + pub fn new(locator_hashes: Vec, 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, stop_hash: sha256d::Hash) -> GetHeadersMessage { + pub fn new(locator_hashes: Vec, 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( - &self, - mut s: S, - ) -> Result { - 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(mut d: D) -> Result { - 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}; diff --git a/src/network/message_filter.rs b/src/network/message_filter.rs index 20076c8a..17d3318c 100644 --- a/src/network/message_filter.rs +++ b/src/network/message_filter.rs @@ -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, } @@ -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, + pub filter_hashes: Vec, } 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, + pub filter_headers: Vec, } -impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers); \ No newline at end of file +impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers); diff --git a/src/util/address.rs b/src/util/address.rs index 9f519891..7ad2d32b 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -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 { 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!( diff --git a/src/util/bip143.rs b/src/util/bip143.rs index 397084fa..742a53f2 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -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") ); } } diff --git a/src/util/bip158.rs b/src/util/bip158.rs index bc2504ff..1d71d01d 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -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) -> Result { + pub fn match_any(&self, block_hash: &BlockHash, query: &mut Iterator) -> Result { 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) -> Result { + pub fn match_all(&self, block_hash: &BlockHash, query: &mut Iterator) -> Result { 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::(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 { diff --git a/src/util/bip32.rs b/src/util/bip32.rs index 60d10457..6228dd24 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -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(&self, secp: &Secp256k1) -> hash160::Hash { + pub fn identifier(&self, secp: &Secp256k1) -> 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 diff --git a/src/util/contracthash.rs b/src/util/contracthash.rs index 6d80454f..62691915 100644 --- a/src/util/contracthash.rs +++ b/src/util/contracthash.rs @@ -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(secp: &Secp256k1, Ok(address::Address { network: network, payload: address::Payload::ScriptHash( - hash160::Hash::hash(&script[..]) + ScriptHash::hash(&script[..]) ) }) } diff --git a/src/util/hash.rs b/src/util/hash.rs index 64bdbd2d..0de4731f 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -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 { +/// 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(data: &mut [T]) -> T + where T: Hash + Encodable, + ::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(mut iter: I) -> T + where T: Hash + Encodable, + ::Engine: io::Write, + I: ExactSizeIterator, +{ + // 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 { /// Produces a Sha256dHash which can be used to refer to the object - fn bitcoin_hash(&self) -> sha256d::Hash; + fn bitcoin_hash(&self) -> T; } diff --git a/src/util/merkleblock.rs b/src/util/merkleblock.rs index 9f56c8f0..3dca69d8 100644 --- a/src/util/merkleblock.rs +++ b/src/util/merkleblock.rs @@ -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 = vec![]; +//! let mut matches: Vec = vec![]; //! let mut index: Vec = 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, /// Transaction ids and internal hashes - hashes: Vec, + hashes: Vec, } 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 = [ + /// let txids: Vec = [ /// "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, + matches: &mut Vec, indexes: &mut Vec, - ) -> Result { + ) -> Result { 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, + matches: &mut Vec, indexes: &mut Vec, - ) -> Result { + ) -> Result { 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(mut d: D) -> Result { let num_transactions: u32 = Decodable::consensus_decode(&mut d)?; - let hashes: Vec = Decodable::consensus_decode(&mut d)?; + let hashes: Vec = Decodable::consensus_decode(&mut d)?; let bytes: Vec = Decodable::consensus_decode(d)?; let mut bits: Vec = 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 = vec![]; + /// let mut matches: Vec = vec![]; /// let mut index: Vec = 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) -> Self { + pub fn from_block(block: &Block, match_txids: &HashSet) -> Self { let header = block.header; let mut matches: Vec = Vec::with_capacity(block.txdata.len()); - let mut hashes: Vec = Vec::with_capacity(block.txdata.len()); + let mut hashes: Vec = 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, + matches: &mut Vec, indexes: &mut Vec, ) -> 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::>(); // 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 = 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 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 10] + let txids: Vec = [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 = [ + let txids: Vec = [ "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 = vec![]; + let mut matches: Vec = vec![]; let mut index: Vec = 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 = vec![]; + let mut matches: Vec = vec![]; let mut index: Vec = 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(); } } diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 6e631719..48a8e240 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -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() );