Multiple fixes for hash types and their computing
Unit test for wtxid and SegWit transactions
This commit is contained in:
parent
0abe15b1f6
commit
5fc24dea33
|
@ -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 }
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 { }
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue