Multiple fixes for hash types and their computing

Unit test for wtxid and SegWit transactions
This commit is contained in:
Dr Maxim Orlovsky 2019-12-18 12:40:46 +01:00
parent 0abe15b1f6
commit 5fc24dea33
10 changed files with 198 additions and 152 deletions

View File

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

View File

@ -22,9 +22,9 @@
use util; use util;
use util::Error::{BlockBadTarget, BlockBadProofOfWork}; use util::Error::{BlockBadTarget, BlockBadProofOfWork};
use util::hash::{BitcoinHash, MerkleRooted, bitcoin_merkle_root}; use util::hash::{BitcoinHash, bitcoin_merkle_root};
use hashes::{Hash, sha256d, HashEngine}; use hashes::{Hash, HashEngine};
use hash_types::{Txid, Wtxid, BlockHash, TxMerkleRoot, WitnessMerkleRoot, WitnessCommitment}; use hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
use util::uint::Uint256; use util::uint::Uint256;
use consensus::encode::Encodable; use consensus::encode::Encodable;
use network::constants::Network; use network::constants::Network;
@ -40,7 +40,7 @@ pub struct BlockHeader {
/// Reference to the previous block in the chain /// Reference to the previous block in the chain
pub prev_blockhash: BlockHash, pub prev_blockhash: BlockHash,
/// The root hash of the merkle tree of transactions in the block /// The root hash of the merkle tree of transactions in the block
pub merkle_root: TxMerkleRoot, pub merkle_root: TxMerkleNode,
/// The timestamp of the block, as claimed by the miner /// The timestamp of the block, as claimed by the miner
pub time: u32, pub time: u32,
/// The target value below which the blockhash must lie, encoded as a /// The target value below which the blockhash must lie, encoded as a
@ -93,8 +93,14 @@ impl Block {
false 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 /// compute witness commitment for the transaction list
pub fn compute_witness_commitment (witness_root: &WitnessMerkleRoot, witness_reserved_value: &[u8]) -> WitnessCommitment { pub fn compute_witness_commitment (witness_root: &WitnessMerkleNode, witness_reserved_value: &[u8]) -> WitnessCommitment {
let mut encoder = WitnessCommitment::engine(); let mut encoder = WitnessCommitment::engine();
witness_root.consensus_encode(&mut encoder).unwrap(); witness_root.consensus_encode(&mut encoder).unwrap();
encoder.input(witness_reserved_value); encoder.input(witness_reserved_value);
@ -102,17 +108,16 @@ impl Block {
} }
/// Merkle root of transactions hashed for witness /// Merkle root of transactions hashed for witness
pub fn witness_root(&self) -> WitnessMerkleRoot { pub fn witness_root(&self) -> WitnessMerkleNode {
let mut txhashes = vec!(Wtxid::default()); let hashes = self.txdata.iter().enumerate().map(|(i, t)|
txhashes.extend(self.txdata.iter().skip(1).map(|t|t.wtxid())); if i == 0 {
let hash_value: sha256d::Hash = bitcoin_merkle_root(txhashes).into(); // Replace the first hash with zeroes.
hash_value.into() Wtxid::default().as_hash()
} } else {
} t.wtxid().as_hash()
}
impl MerkleRooted for Block { );
fn merkle_root(&self) -> TxMerkleRoot { bitcoin_merkle_root(hashes).into()
bitcoin_merkle_root::<Txid>(self.txdata.iter().map(|obj| obj.txid().into()).collect())
} }
} }
@ -212,7 +217,6 @@ mod tests {
use blockdata::block::{Block, BlockHeader}; use blockdata::block::{Block, BlockHeader};
use consensus::encode::{deserialize, serialize}; use consensus::encode::{deserialize, serialize};
use util::hash::MerkleRooted;
#[test] #[test]
fn block_test() { fn block_test() {

View File

@ -694,7 +694,7 @@ mod tests {
} }
#[test] #[test]
fn test_transaction() { fn test_nonsegwit_transaction() {
let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap(); let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
let tx: Result<Transaction, _> = deserialize(&hex_tx); let tx: Result<Transaction, _> = deserialize(&hex_tx);
assert!(tx.is_ok()); assert!(tx.is_ok());
@ -718,6 +718,37 @@ mod tests {
assert_eq!(realtx.get_weight(), 193*4); 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] #[test]
fn tx_no_input_deserialization() { fn tx_no_input_deserialization() {
let hex_tx = Vec::<u8>::from_hex( let hex_tx = Vec::<u8>::from_hex(

View File

@ -34,8 +34,8 @@ use std::borrow::Cow;
use std::io::{Cursor, Read, Write}; use std::io::{Cursor, Read, Write};
use hashes::hex::ToHex; use hashes::hex::ToHex;
use hashes::{sha256d, Hash as HashTrait}; use hashes::{sha256d, Hash};
use hash_types::{FilterHash, TxMerkleBranch}; use hash_types::{BlockHash, FilterHash, TxMerkleNode};
use util::endian; use util::endian;
use util::psbt; use util::psbt;
@ -72,8 +72,8 @@ pub enum Error {
/// The invalid checksum /// The invalid checksum
actual: [u8; 4], actual: [u8; 4],
}, },
/// VarInt was encoded in a non-minimal way /// VarInt was encoded in a non-minimal way
NonMinimalVarInt, NonMinimalVarInt,
/// Network magic was unknown /// Network magic was unknown
UnknownNetworkMagic(u32), UnknownNetworkMagic(u32),
/// Parsing error /// Parsing error
@ -82,6 +82,8 @@ pub enum Error {
UnsupportedSegwitFlag(u8), UnsupportedSegwitFlag(u8),
/// Unrecognized network command /// Unrecognized network command
UnrecognizedNetworkCommand(String), UnrecognizedNetworkCommand(String),
/// Invalid Inventory type
UnknownInventoryType(u32),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -95,13 +97,14 @@ impl fmt::Display for Error {
"allocation of oversized vector: requested {}, maximum {}", r, m), "allocation of oversized vector: requested {}, maximum {}", r, m),
Error::InvalidChecksum { expected: ref e, actual: ref a } => write!(f, Error::InvalidChecksum { expected: ref e, actual: ref a } => write!(f,
"invalid checksum: expected {}, actual {}", e.to_hex(), a.to_hex()), "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::UnknownNetworkMagic(ref m) => write!(f, "unknown network magic: {}", m),
Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e), Error::ParseFailed(ref e) => write!(f, "parse failed: {}", e),
Error::UnsupportedSegwitFlag(ref swflag) => write!(f, Error::UnsupportedSegwitFlag(ref swflag) => write!(f,
"unsupported segwit version: {}", swflag), "unsupported segwit version: {}", swflag),
Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f, Error::UnrecognizedNetworkCommand(ref nwcmd) => write!(f,
"unrecognized network command: {}", nwcmd), "unrecognized network command: {}", nwcmd),
Error::UnknownInventoryType(ref tp) => write!(f, "Unknown Inventory type: {}", tp),
} }
} }
} }
@ -114,11 +117,12 @@ impl error::Error for Error {
Error::UnexpectedNetworkMagic { .. } Error::UnexpectedNetworkMagic { .. }
| Error::OversizedVectorAllocation { .. } | Error::OversizedVectorAllocation { .. }
| Error::InvalidChecksum { .. } | Error::InvalidChecksum { .. }
| Error::NonMinimalVarInt | Error::NonMinimalVarInt
| Error::UnknownNetworkMagic(..) | Error::UnknownNetworkMagic(..)
| Error::ParseFailed(..) | Error::ParseFailed(..)
| Error::UnsupportedSegwitFlag(..) | Error::UnsupportedSegwitFlag(..)
| Error::UnrecognizedNetworkCommand(..) => None, | Error::UnrecognizedNetworkCommand(..)
| Error::UnknownInventoryType(..) => None,
} }
} }
@ -591,8 +595,9 @@ macro_rules! impl_vec {
} }
} }
} }
impl_vec!(BlockHash);
impl_vec!(FilterHash); impl_vec!(FilterHash);
impl_vec!(TxMerkleBranch); impl_vec!(TxMerkleNode);
impl_vec!(Transaction); impl_vec!(Transaction);
impl_vec!(TxOut); impl_vec!(TxOut);
impl_vec!(TxIn); impl_vec!(TxIn);
@ -651,7 +656,7 @@ impl Decodable for Box<[u8]> {
/// Do a double-SHA256 on some data and return the first 4 bytes /// Do a double-SHA256 on some data and return the first 4 bytes
fn sha2_checksum(data: &[u8]) -> [u8; 4] { 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]] [checksum[0], checksum[1], checksum[2], checksum[3]]
} }
@ -732,8 +737,7 @@ impl Encodable for sha256d::Hash {
impl Decodable for sha256d::Hash { impl Decodable for sha256d::Hash {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> { fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
let inner = <[u8; 32]>::consensus_decode(d)?; Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(d)?))
Ok(sha256d::Hash::from_slice(&inner).unwrap())
} }
} }

View File

@ -32,8 +32,7 @@ macro_rules! impl_hashencode {
impl Decodable for $hashtype { impl Decodable for $hashtype {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> { fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
let inner = <<$hashtype as Hash>::Inner>::consensus_decode(d)?; Ok(Self::from_inner(<<$hashtype as Hash>::Inner>::consensus_decode(d)?))
Ok(Self::from_slice(&inner).unwrap())
} }
} }
} }
@ -49,9 +48,8 @@ hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytec
hash_newtype!(WPubkeyHash, hash160::Hash, 20, doc="SegWit version of a public key hash."); 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!(WScriptHash, sha256::Hash, 32, doc="SegWit version of a Bitcoin Script bytecode hash.");
hash_newtype!(TxMerkleRoot, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for transactions"); hash_newtype!(TxMerkleNode, sha256d::Hash, 32, doc="A hash of the Merkle tree branch or root for transactions");
hash_newtype!(TxMerkleBranch, sha256d::Hash, 32, doc="A hash of the Merkle tree branch for transactions"); hash_newtype!(WitnessMerkleNode, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for witness data");
hash_newtype!(WitnessMerkleRoot, 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!(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!(XpubIdentifier, hash160::Hash, 20, doc="XpubIdentifier as defined in BIP-32.");
@ -62,13 +60,6 @@ impl_hashencode!(Txid);
impl_hashencode!(Wtxid); impl_hashencode!(Wtxid);
impl_hashencode!(SigHash); impl_hashencode!(SigHash);
impl_hashencode!(BlockHash); impl_hashencode!(BlockHash);
impl_hashencode!(TxMerkleRoot); impl_hashencode!(TxMerkleNode);
impl_hashencode!(TxMerkleBranch); impl_hashencode!(WitnessMerkleNode);
impl_hashencode!(WitnessMerkleRoot);
impl_hashencode!(FilterHash); impl_hashencode!(FilterHash);
/// Generic trait for functions which may either take a Txid type of Wtxid type as an imput.
/// For instance, used for Merklization procedure
pub trait TxidType: Hash<Inner = [u8; 32]> + Encodable + Decodable { }
impl TxidType for Txid { }
impl TxidType for Wtxid { }

View File

@ -341,7 +341,7 @@ mod test {
use hashes::Hash as HashTrait; use hashes::Hash as HashTrait;
use network::address::Address; use network::address::Address;
use super::message_network::{Reject, RejectReason, VersionMessage}; 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 blockdata::block::{Block, BlockHeader};
use network::message_filter::{GetCFilters, CFilter, GetCFHeaders, CFHeaders, GetCFCheckpt, CFCheckpt}; use network::message_filter::{GetCFilters, CFilter, GetCFHeaders, CFHeaders, GetCFCheckpt, CFCheckpt};
use blockdata::transaction::Transaction; use blockdata::transaction::Transaction;
@ -362,9 +362,9 @@ mod test {
NetworkMessage::Version(version_msg), NetworkMessage::Version(version_msg),
NetworkMessage::Verack, NetworkMessage::Verack,
NetworkMessage::Addr(vec![(45, Address::new(&([123,255,000,100], 833).into(), ServiceFlags::NETWORK))]), 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]).into()}]), NetworkMessage::Inv(vec![Inventory::Block(hash([8u8; 32]).into())]),
NetworkMessage::GetData(vec![Inventory{inv_type: InvType::Transaction, hash: hash([45u8; 32]).into()}]), NetworkMessage::GetData(vec![Inventory::Transaction(hash([45u8; 32]).into())]),
NetworkMessage::NotFound(vec![Inventory{inv_type: InvType::Error, hash: 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::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::GetHeaders(GetHeadersMessage::new(vec![hash([10u8; 32]).into(), hash([40u8; 32]).into()], hash([50u8; 32]).into())),
NetworkMessage::MemPool, NetworkMessage::MemPool,

View File

@ -18,25 +18,64 @@
//! Bitcoin data (blocks and transactions) around. //! Bitcoin data (blocks and transactions) around.
//! //!
use network::constants;
use consensus::encode::{self, Decodable, Encodable};
use hash_types::FilterHash;
use std::io; use std::io;
#[derive(PartialEq, Eq, Clone, Debug, Copy)] use hashes::sha256d;
/// The type of an inventory object
pub enum InvType { 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 --- these inventories can be ignored
Error, Error,
/// Transaction /// Transaction
Transaction, Transaction(Txid),
/// Block /// Block
Block, Block(BlockHash),
/// Witness Block
WitnessBlock,
/// Witness Transaction /// 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 // Some simple messages
@ -49,9 +88,9 @@ pub struct GetBlocksMessage {
/// Locator hashes --- ordered newest to oldest. The remote peer will /// Locator hashes --- ordered newest to oldest. The remote peer will
/// reply with its longest known chain, starting from a locator hash /// reply with its longest known chain, starting from a locator hash
/// if possible and block 1 otherwise. /// if possible and block 1 otherwise.
pub locator_hashes: Vec<FilterHash>, pub locator_hashes: Vec<BlockHash>,
/// References the block to stop at, or zero to just fetch the maximum 500 blocks /// References the block to stop at, or zero to just fetch the maximum 500 blocks
pub stop_hash: FilterHash, pub stop_hash: BlockHash,
} }
/// The `getheaders` message /// The `getheaders` message
@ -62,29 +101,14 @@ pub struct GetHeadersMessage {
/// Locator hashes --- ordered newest to oldest. The remote peer will /// Locator hashes --- ordered newest to oldest. The remote peer will
/// reply with its longest known chain, starting from a locator hash /// reply with its longest known chain, starting from a locator hash
/// if possible and block 1 otherwise. /// if possible and block 1 otherwise.
pub locator_hashes: Vec<FilterHash>, pub locator_hashes: Vec<BlockHash>,
/// References the header to stop at, or zero to just fetch the maximum 2000 headers /// References the header to stop at, or zero to just fetch the maximum 2000 headers
pub stop_hash: FilterHash pub stop_hash: BlockHash
}
/// 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: FilterHash
}
impl ::std::hash::Hash for Inventory {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
} }
impl GetBlocksMessage { impl GetBlocksMessage {
/// Construct a new `getblocks` message /// Construct a new `getblocks` message
pub fn new(locator_hashes: Vec<FilterHash>, stop_hash: FilterHash) -> GetBlocksMessage { pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
GetBlocksMessage { GetBlocksMessage {
version: constants::PROTOCOL_VERSION, version: constants::PROTOCOL_VERSION,
locator_hashes: locator_hashes.clone(), locator_hashes: locator_hashes.clone(),
@ -97,7 +121,7 @@ impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
impl GetHeadersMessage { impl GetHeadersMessage {
/// Construct a new `getheaders` message /// Construct a new `getheaders` message
pub fn new(locator_hashes: Vec<FilterHash>, stop_hash: FilterHash) -> GetHeadersMessage { pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
GetHeadersMessage { GetHeadersMessage {
version: constants::PROTOCOL_VERSION, version: constants::PROTOCOL_VERSION,
locator_hashes: locator_hashes, locator_hashes: locator_hashes,
@ -108,40 +132,6 @@ impl GetHeadersMessage {
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash); 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)] #[cfg(test)]
mod tests { mod tests {
use super::{GetHeadersMessage, GetBlocksMessage}; use super::{GetHeadersMessage, GetBlocksMessage};

View File

@ -36,7 +36,7 @@ pub struct GetCFHeaders {
/// The height of the first block in the requested range /// The height of the first block in the requested range
pub start_height: u32, pub start_height: u32,
/// The hash of the last block in the requested range /// The hash of the last block in the requested range
pub stop_hash: FilterHash, pub stop_hash: BlockHash,
} }
impl_consensus_encoding!(GetCFHeaders, filter_type, start_height, stop_hash); impl_consensus_encoding!(GetCFHeaders, filter_type, start_height, stop_hash);
@ -46,7 +46,7 @@ pub struct CFHeaders {
/// Filter type for which headers are requested /// Filter type for which headers are requested
pub filter_type: u8, pub filter_type: u8,
/// The hash of the last block in the requested range /// The hash of the last block in the requested range
pub stop_hash: FilterHash, pub stop_hash: BlockHash,
/// The filter header preceding the first block in the requested range /// The filter header preceding the first block in the requested range
pub previous_filter: FilterHash, pub previous_filter: FilterHash,
/// The filter hashes for each block in the requested range /// The filter hashes for each block in the requested range
@ -60,7 +60,7 @@ pub struct GetCFCheckpt {
/// Filter type for which headers are requested /// Filter type for which headers are requested
pub filter_type: u8, pub filter_type: u8,
/// The hash of the last block in the requested range /// The hash of the last block in the requested range
pub stop_hash: FilterHash, pub stop_hash: BlockHash,
} }
impl_consensus_encoding!(GetCFCheckpt, filter_type, stop_hash); impl_consensus_encoding!(GetCFCheckpt, filter_type, stop_hash);
@ -70,7 +70,7 @@ pub struct CFCheckpt {
/// Filter type for which headers are requested /// Filter type for which headers are requested
pub filter_type: u8, pub filter_type: u8,
/// The hash of the last block in the requested range /// The hash of the last block in the requested range
pub stop_hash: FilterHash, pub stop_hash: BlockHash,
/// The filter headers at intervals of 1,000 /// The filter headers at intervals of 1,000
pub filter_headers: Vec<FilterHash>, pub filter_headers: Vec<FilterHash>,
} }

View File

@ -16,39 +16,64 @@
//! Utility functions related to hashing data, including merkleization //! Utility functions related to hashing data, including merkleization
use std::cmp::min; use std::cmp::min;
use std::default::Default; use std::io;
use hashes::Hash; use hashes::Hash;
use hash_types::{Txid, TxidType, TxMerkleRoot}; use consensus::encode::Encodable;
/// Calculates the merkle root of a list of hashes inline
/// Any collection of objects for which a merkle root makes sense to calculate /// into the allocated slice.
pub trait MerkleRooted { ///
/// Construct a merkle tree from a collection, with elements ordered as /// In most cases, you'll want to use [bitcoin_merkle_root] instead.
/// they were in the original collection, and return the merkle root. pub fn bitcoin_merkle_root_inline<T>(data: &mut [T]) -> T
fn merkle_root(&self) -> TxMerkleRoot; where T: Hash + Encodable,
} <T as Hash>::Engine: io::Write,
{
/// Calculates the merkle root of a list of txids hashes directly
pub fn bitcoin_merkle_root<T: TxidType>(data: Vec<T>) -> TxMerkleRoot {
// Base case // Base case
if data.len() < 1 { if data.len() < 1 {
return Default::default(); return Default::default();
} }
if data.len() < 2 { if data.len() < 2 {
return TxMerkleRoot::from_inner(data[0].into_inner()); return T::from_inner(data[0].into_inner());
} }
// Recursion // Recursion
let mut next = vec![];
for idx in 0..((data.len() + 1) / 2) { for idx in 0..((data.len() + 1) / 2) {
let idx1 = 2 * idx; let idx1 = 2 * idx;
let idx2 = min(idx1 + 1, data.len() - 1); let idx2 = min(idx1 + 1, data.len() - 1);
let mut encoder = TxMerkleRoot::engine(); let mut encoder = T::engine();
data[idx1].consensus_encode(&mut encoder).unwrap(); data[idx1].consensus_encode(&mut encoder).unwrap();
data[idx2].consensus_encode(&mut encoder).unwrap(); data[idx2].consensus_encode(&mut encoder).unwrap();
next.push(Txid::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 /// Objects which are referred to by hash

View File

@ -59,7 +59,7 @@ use std::collections::HashSet;
use std::io; use std::io;
use hashes::Hash; use hashes::Hash;
use hash_types::{Txid, TxMerkleRoot, TxMerkleBranch}; use hash_types::{Txid, TxMerkleNode};
use blockdata::transaction::Transaction; use blockdata::transaction::Transaction;
use blockdata::constants::{MAX_BLOCK_WEIGHT, MIN_TRANSACTION_WEIGHT}; use blockdata::constants::{MAX_BLOCK_WEIGHT, MIN_TRANSACTION_WEIGHT};
@ -120,7 +120,7 @@ pub struct PartialMerkleTree {
/// node-is-parent-of-matched-txid bits /// node-is-parent-of-matched-txid bits
bits: Vec<bool>, bits: Vec<bool>,
/// Transaction ids and internal hashes /// Transaction ids and internal hashes
hashes: Vec<TxMerkleBranch>, hashes: Vec<TxMerkleNode>,
} }
impl PartialMerkleTree { impl PartialMerkleTree {
@ -181,7 +181,7 @@ impl PartialMerkleTree {
&self, &self,
matches: &mut Vec<Txid>, matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>, indexes: &mut Vec<u32>,
) -> Result<TxMerkleRoot, MerkleBlockError> { ) -> Result<TxMerkleNode, MerkleBlockError> {
matches.clear(); matches.clear();
indexes.clear(); indexes.clear();
// An empty set will not work // An empty set will not work
@ -221,7 +221,7 @@ impl PartialMerkleTree {
if hash_used != self.hashes.len() as u32 { if hash_used != self.hashes.len() as u32 {
return Err(BadFormat("Not all hashes were consumed".to_owned())); return Err(BadFormat("Not all hashes were consumed".to_owned()));
} }
Ok(TxMerkleRoot::from_inner(hash_merkle_root.into_inner())) Ok(TxMerkleNode::from_inner(hash_merkle_root.into_inner()))
} }
/// Helper function to efficiently calculate the number of nodes at given height /// Helper function to efficiently calculate the number of nodes at given height
@ -232,10 +232,10 @@ impl PartialMerkleTree {
} }
/// Calculate the hash of a node in the merkle tree (at leaf level: the txid's themselves) /// 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: &[Txid]) -> TxMerkleBranch { fn calc_hash(&self, height: u32, pos: u32, txids: &[Txid]) -> TxMerkleNode {
if height == 0 { if height == 0 {
// Hash at height 0 is the txid itself // Hash at height 0 is the txid itself
TxMerkleBranch::from_inner(txids[pos as usize].into_inner()) TxMerkleNode::from_inner(txids[pos as usize].into_inner())
} else { } else {
// Calculate left hash // Calculate left hash
let left = self.calc_hash(height - 1, pos * 2, txids); let left = self.calc_hash(height - 1, pos * 2, txids);
@ -291,7 +291,7 @@ impl PartialMerkleTree {
hash_used: &mut u32, hash_used: &mut u32,
matches: &mut Vec<Txid>, matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>, indexes: &mut Vec<u32>,
) -> Result<TxMerkleBranch, MerkleBlockError> { ) -> Result<TxMerkleNode, MerkleBlockError> {
if *bits_used as usize >= self.bits.len() { if *bits_used as usize >= self.bits.len() {
return Err(BadFormat("Overflowed the bits array".to_owned())); return Err(BadFormat("Overflowed the bits array".to_owned()));
} }
@ -344,11 +344,11 @@ impl PartialMerkleTree {
} }
/// Helper method to produce SHA256D(left + right) /// Helper method to produce SHA256D(left + right)
fn parent_hash(left: TxMerkleBranch, right: TxMerkleBranch) -> TxMerkleBranch { fn parent_hash(left: TxMerkleNode, right: TxMerkleNode) -> TxMerkleNode {
let mut encoder = TxMerkleBranch::engine(); let mut encoder = TxMerkleNode::engine();
left.consensus_encode(&mut encoder).unwrap(); left.consensus_encode(&mut encoder).unwrap();
right.consensus_encode(&mut encoder).unwrap(); right.consensus_encode(&mut encoder).unwrap();
TxMerkleBranch::from_engine(encoder) TxMerkleNode::from_engine(encoder)
} }
} }
@ -370,7 +370,7 @@ impl Encodable for PartialMerkleTree {
impl Decodable for PartialMerkleTree { impl Decodable for PartialMerkleTree {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> { fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
let num_transactions: u32 = Decodable::consensus_decode(&mut d)?; let num_transactions: u32 = Decodable::consensus_decode(&mut d)?;
let hashes: Vec<TxMerkleBranch> = Decodable::consensus_decode(&mut d)?; let hashes: Vec<TxMerkleNode> = Decodable::consensus_decode(&mut d)?;
let bytes: Vec<u8> = Decodable::consensus_decode(d)?; let bytes: Vec<u8> = Decodable::consensus_decode(d)?;
let mut bits: Vec<bool> = vec![false; bytes.len() * 8]; let mut bits: Vec<bool> = vec![false; bytes.len() * 8];
@ -498,7 +498,7 @@ mod tests {
use hashes::Hash; use hashes::Hash;
use hashes::hex::{FromHex, ToHex}; use hashes::hex::{FromHex, ToHex};
use hash_types::{Txid, TxMerkleRoot, TxMerkleBranch}; use hash_types::{Txid, TxMerkleNode};
use secp256k1::rand::prelude::*; use secp256k1::rand::prelude::*;
use consensus::encode::{deserialize, serialize}; use consensus::encode::{deserialize, serialize};
@ -518,7 +518,8 @@ mod tests {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Calculate the merkle root and height // 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 height = 1;
let mut ntx = num_tx; let mut ntx = num_tx;
while ntx > 1 { while ntx > 1 {
@ -565,7 +566,7 @@ mod tests {
// Check that it has the same merkle root as the original, and a valid one // Check that it has the same merkle root as the original, and a valid one
assert_eq!(merkle_root_1, merkle_root_2); assert_eq!(merkle_root_1, merkle_root_2);
assert_ne!(merkle_root_2, TxMerkleRoot::default()); assert_ne!(merkle_root_2, TxMerkleNode::default());
// check that it contains the matched transactions (in the same order!) // check that it contains the matched transactions (in the same order!)
assert_eq!(match_txid1, match_txid2); assert_eq!(match_txid1, match_txid2);
@ -701,7 +702,7 @@ mod tests {
let hashes = &mut self.hashes; let hashes = &mut self.hashes;
let mut hash = hashes[n].into_inner(); let mut hash = hashes[n].into_inner();
hash[(bit >> 3) as usize] ^= 1 << (bit & 7); hash[(bit >> 3) as usize] ^= 1 << (bit & 7);
hashes[n] = TxMerkleBranch::from_slice(&hash).unwrap(); hashes[n] = TxMerkleNode::from_slice(&hash).unwrap();
} }
} }