From ec92a056827a39a4d9f01a8936959524d71a7e39 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 9 Dec 2019 23:37:52 +0100 Subject: [PATCH] New HashTypes defined according to #284 (WIP), Txid is completed --- src/blockdata/block.rs | 2 +- src/blockdata/constants.rs | 11 ++++---- src/blockdata/transaction.rs | 21 ++++++++------- src/hash_types.rs | 51 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +++- src/util/psbt/mod.rs | 14 +++++----- 6 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 src/hash_types.rs diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index 9d4a40b3..d4c7d8c3 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -112,7 +112,7 @@ impl Block { impl MerkleRoot for Block { fn merkle_root(&self) -> sha256d::Hash { - bitcoin_merkle_root(self.txdata.iter().map(|obj| obj.txid()).collect()) + bitcoin_merkle_root(self.txdata.iter().map(|obj| obj.txid().into()).collect()) } } diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index 9fc7562f..0700303b 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -96,14 +96,15 @@ 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 merkle_root = txdata[0].txid().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 +113,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 +126,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 diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 57027eb2..3fce302c 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -34,13 +34,14 @@ 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 +50,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 +176,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..])?, }) } @@ -293,13 +294,13 @@ impl Transaction { /// to the output of `BitcoinHash::bitcoin_hash()`, but for segwit transactions, /// this will give the correct txid (not including witnesses) while `bitcoin_hash` /// will also hash witnesses. - pub fn txid(&self) -> sha256d::Hash { + pub fn txid(&self) -> Txid { let mut enc = sha256d::Hash::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 a signature hash for a given input index with a given sighash flag. @@ -630,6 +631,8 @@ mod tests { use hashes::{sha256d, Hash}; use hashes::hex::FromHex; + use hash_types::*; + #[test] fn test_outpoint() { assert_eq!(OutPoint::from_str("i don't care"), @@ -645,20 +648,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, })); } diff --git a/src/hash_types.rs b/src/hash_types.rs new file mode 100644 index 00000000..3c1c1e2e --- /dev/null +++ b/src/hash_types.rs @@ -0,0 +1,51 @@ +// 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 { + let inner = <<$hashtype as Hash>::Inner>::consensus_decode(d)?; + Ok(Self::from_slice(&inner).unwrap()) + } + } + } +} + +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!(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!(XpubIdentifier, hash160::Hash, 20, doc="XpubIdentifier as defined in BIP-32."); + +impl_hashencode!(Txid); diff --git a/src/lib.rs b/src/lib.rs index cabf24e6..fdd6e5c2 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,10 @@ pub mod network; pub mod blockdata; pub mod util; pub mod consensus; +#[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/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() );