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]
|
||||
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 }
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
|
||||
use util;
|
||||
use util::Error::{BlockBadTarget, BlockBadProofOfWork};
|
||||
use util::hash::{BitcoinHash, MerkleRooted, bitcoin_merkle_root};
|
||||
use hashes::{Hash, sha256d, HashEngine};
|
||||
use hash_types::{Txid, Wtxid, BlockHash, TxMerkleRoot, WitnessMerkleRoot, WitnessCommitment};
|
||||
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;
|
||||
|
@ -40,7 +40,7 @@ pub struct BlockHeader {
|
|||
/// Reference to the previous block in the chain
|
||||
pub prev_blockhash: BlockHash,
|
||||
/// 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
|
||||
pub time: u32,
|
||||
/// The target value below which the blockhash must lie, encoded as a
|
||||
|
@ -93,8 +93,14 @@ 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: &WitnessMerkleRoot, witness_reserved_value: &[u8]) -> WitnessCommitment {
|
||||
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);
|
||||
|
@ -102,17 +108,16 @@ impl Block {
|
|||
}
|
||||
|
||||
/// Merkle root of transactions hashed for witness
|
||||
pub fn witness_root(&self) -> WitnessMerkleRoot {
|
||||
let mut txhashes = vec!(Wtxid::default());
|
||||
txhashes.extend(self.txdata.iter().skip(1).map(|t|t.wtxid()));
|
||||
let hash_value: sha256d::Hash = bitcoin_merkle_root(txhashes).into();
|
||||
hash_value.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl MerkleRooted for Block {
|
||||
fn merkle_root(&self) -> TxMerkleRoot {
|
||||
bitcoin_merkle_root::<Txid>(self.txdata.iter().map(|obj| obj.txid().into()).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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,7 +217,6 @@ mod tests {
|
|||
|
||||
use blockdata::block::{Block, BlockHeader};
|
||||
use consensus::encode::{deserialize, serialize};
|
||||
use util::hash::MerkleRooted;
|
||||
|
||||
#[test]
|
||||
fn block_test() {
|
||||
|
|
|
@ -694,7 +694,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction() {
|
||||
fn test_nonsegwit_transaction() {
|
||||
let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||
let tx: Result<Transaction, _> = deserialize(&hex_tx);
|
||||
assert!(tx.is_ok());
|
||||
|
@ -718,6 +718,37 @@ mod tests {
|
|||
assert_eq!(realtx.get_weight(), 193*4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_segwit_transaction() {
|
||||
let hex_tx = Vec::<u8>::from_hex(
|
||||
"02000000000101595895ea20179de87052b4046dfe6fd515860505d6511a9004cf12a1f93cac7c01000000\
|
||||
00ffffffff01deb807000000000017a9140f3444e271620c736808aa7b33e370bd87cb5a078702483045022\
|
||||
100fb60dad8df4af2841adc0346638c16d0b8035f5e3f3753b88db122e70c79f9370220756e6633b17fd271\
|
||||
0e626347d28d60b0a2d6cbb41de51740644b9fb3ba7751040121028fa937ca8cba2197a37c007176ed89410\
|
||||
55d3bcb8627d085e94553e62f057dcc00000000"
|
||||
).unwrap();
|
||||
let tx: Result<Transaction, _> = deserialize(&hex_tx);
|
||||
assert!(tx.is_ok());
|
||||
let realtx = tx.unwrap();
|
||||
// All these tests aren't really needed because if they fail, the hash check at the end
|
||||
// will also fail. But these will show you where the failure is so I'll leave them in.
|
||||
assert_eq!(realtx.version, 2);
|
||||
assert_eq!(realtx.input.len(), 1);
|
||||
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
|
||||
// as little-endian 256-bit numbers rather than as data strings.
|
||||
assert_eq!(format!("{:x}", realtx.input[0].previous_output.txid),
|
||||
"7cac3cf9a112cf04901a51d605058615d56ffe6d04b45270e89d1720ea955859".to_string());
|
||||
assert_eq!(realtx.input[0].previous_output.vout, 1);
|
||||
assert_eq!(realtx.output.len(), 1);
|
||||
assert_eq!(realtx.lock_time, 0);
|
||||
|
||||
assert_eq!(format!("{:x}", realtx.txid()),
|
||||
"f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string());
|
||||
assert_eq!(format!("{:x}", realtx.wtxid()),
|
||||
"80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string());
|
||||
assert_eq!(realtx.get_weight(), 442);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_no_input_deserialization() {
|
||||
let hex_tx = Vec::<u8>::from_hex(
|
||||
|
|
|
@ -34,8 +34,8 @@ use std::borrow::Cow;
|
|||
use std::io::{Cursor, Read, Write};
|
||||
use hashes::hex::ToHex;
|
||||
|
||||
use hashes::{sha256d, Hash as HashTrait};
|
||||
use hash_types::{FilterHash, TxMerkleBranch};
|
||||
use hashes::{sha256d, Hash};
|
||||
use hash_types::{BlockHash, FilterHash, TxMerkleNode};
|
||||
|
||||
use util::endian;
|
||||
use util::psbt;
|
||||
|
@ -72,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
|
||||
|
@ -82,6 +82,8 @@ pub enum Error {
|
|||
UnsupportedSegwitFlag(u8),
|
||||
/// Unrecognized network command
|
||||
UnrecognizedNetworkCommand(String),
|
||||
/// Invalid Inventory type
|
||||
UnknownInventoryType(u32),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
|
@ -95,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -591,8 +595,9 @@ macro_rules! impl_vec {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl_vec!(BlockHash);
|
||||
impl_vec!(FilterHash);
|
||||
impl_vec!(TxMerkleBranch);
|
||||
impl_vec!(TxMerkleNode);
|
||||
impl_vec!(Transaction);
|
||||
impl_vec!(TxOut);
|
||||
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
|
||||
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]]
|
||||
}
|
||||
|
||||
|
@ -732,8 +737,7 @@ impl Encodable for sha256d::Hash {
|
|||
|
||||
impl Decodable for sha256d::Hash {
|
||||
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
||||
let inner = <[u8; 32]>::consensus_decode(d)?;
|
||||
Ok(sha256d::Hash::from_slice(&inner).unwrap())
|
||||
Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(d)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@ macro_rules! impl_hashencode {
|
|||
|
||||
impl Decodable for $hashtype {
|
||||
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
||||
let inner = <<$hashtype as Hash>::Inner>::consensus_decode(d)?;
|
||||
Ok(Self::from_slice(&inner).unwrap())
|
||||
Ok(Self::from_inner(<<$hashtype as Hash>::Inner>::consensus_decode(d)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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!(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!(TxMerkleBranch, sha256d::Hash, 32, doc="A hash of the Merkle tree branch for transactions");
|
||||
hash_newtype!(WitnessMerkleRoot, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for witness data");
|
||||
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.");
|
||||
|
||||
|
@ -62,13 +60,6 @@ impl_hashencode!(Txid);
|
|||
impl_hashencode!(Wtxid);
|
||||
impl_hashencode!(SigHash);
|
||||
impl_hashencode!(BlockHash);
|
||||
impl_hashencode!(TxMerkleRoot);
|
||||
impl_hashencode!(TxMerkleBranch);
|
||||
impl_hashencode!(WitnessMerkleRoot);
|
||||
impl_hashencode!(TxMerkleNode);
|
||||
impl_hashencode!(WitnessMerkleNode);
|
||||
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 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,9 +362,9 @@ 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]).into()}]),
|
||||
NetworkMessage::GetData(vec![Inventory{inv_type: InvType::Transaction, hash: hash([45u8; 32]).into()}]),
|
||||
NetworkMessage::NotFound(vec![Inventory{inv_type: InvType::Error, hash: hash([45u8; 32]).into()}]),
|
||||
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,
|
||||
|
|
|
@ -18,25 +18,64 @@
|
|||
//! Bitcoin data (blocks and transactions) around.
|
||||
//!
|
||||
|
||||
use network::constants;
|
||||
use consensus::encode::{self, Decodable, Encodable};
|
||||
use hash_types::FilterHash;
|
||||
|
||||
use std::io;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
|
||||
/// The type of an inventory object
|
||||
pub enum InvType {
|
||||
use hashes::sha256d;
|
||||
|
||||
use network::constants;
|
||||
use consensus::encode::{self, Decodable, Encodable};
|
||||
use hash_types::{BlockHash, Txid, Wtxid};
|
||||
|
||||
/// An inventory item.
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
|
||||
pub enum Inventory {
|
||||
/// Error --- these inventories can be ignored
|
||||
Error,
|
||||
/// Transaction
|
||||
Transaction,
|
||||
Transaction(Txid),
|
||||
/// Block
|
||||
Block,
|
||||
/// Witness Block
|
||||
WitnessBlock,
|
||||
Block(BlockHash),
|
||||
/// Witness Transaction
|
||||
WitnessTransaction
|
||||
WitnessTransaction(Wtxid),
|
||||
/// Witness Block
|
||||
WitnessBlock(BlockHash),
|
||||
}
|
||||
|
||||
impl Encodable for Inventory {
|
||||
#[inline]
|
||||
fn consensus_encode<S: io::Write>(
|
||||
&self,
|
||||
mut s: S,
|
||||
) -> Result<usize, encode::Error> {
|
||||
macro_rules! encode_inv {
|
||||
($code:expr, $item:expr) => {
|
||||
u32::consensus_encode(&$code, &mut s)? +
|
||||
$item.consensus_encode(&mut s)?
|
||||
}
|
||||
}
|
||||
Ok(match *self {
|
||||
Inventory::Error => encode_inv!(0, sha256d::Hash::default()),
|
||||
Inventory::Transaction(ref t) => encode_inv!(1, t),
|
||||
Inventory::Block(ref b) => encode_inv!(2, b),
|
||||
Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t),
|
||||
Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Inventory {
|
||||
#[inline]
|
||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||
let inv_type: u32 = Decodable::consensus_decode(&mut d)?;
|
||||
Ok(match inv_type {
|
||||
0 => Inventory::Error,
|
||||
1 => Inventory::Transaction(Decodable::consensus_decode(&mut d)?),
|
||||
2 => Inventory::Block(Decodable::consensus_decode(&mut d)?),
|
||||
0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?),
|
||||
0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?),
|
||||
tp => return Err(encode::Error::UnknownInventoryType(tp)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Some simple messages
|
||||
|
@ -49,9 +88,9 @@ pub struct GetBlocksMessage {
|
|||
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||
/// reply with its longest known chain, starting from a locator hash
|
||||
/// if possible and block 1 otherwise.
|
||||
pub locator_hashes: Vec<FilterHash>,
|
||||
pub locator_hashes: Vec<BlockHash>,
|
||||
/// 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
|
||||
|
@ -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<FilterHash>,
|
||||
pub locator_hashes: Vec<BlockHash>,
|
||||
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||
pub stop_hash: FilterHash
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
pub stop_hash: BlockHash
|
||||
}
|
||||
|
||||
impl GetBlocksMessage {
|
||||
/// 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 {
|
||||
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<FilterHash>, stop_hash: FilterHash) -> GetHeadersMessage {
|
||||
pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
|
||||
GetHeadersMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
locator_hashes: locator_hashes,
|
||||
|
@ -108,40 +132,6 @@ impl GetHeadersMessage {
|
|||
|
||||
impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
|
||||
|
||||
impl Encodable for Inventory {
|
||||
#[inline]
|
||||
fn consensus_encode<S: io::Write>(
|
||||
&self,
|
||||
mut s: S,
|
||||
) -> Result<usize, encode::Error> {
|
||||
let inv_len = match self.inv_type {
|
||||
InvType::Error => 0u32,
|
||||
InvType::Transaction => 1,
|
||||
InvType::Block => 2,
|
||||
InvType::WitnessBlock => 0x40000002,
|
||||
InvType::WitnessTransaction => 0x40000001
|
||||
}.consensus_encode(&mut s)?;
|
||||
Ok(inv_len + self.hash.consensus_encode(&mut s)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Inventory {
|
||||
#[inline]
|
||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||
let int_type: u32 = Decodable::consensus_decode(&mut d)?;
|
||||
Ok(Inventory {
|
||||
inv_type: match int_type {
|
||||
0 => InvType::Error,
|
||||
1 => InvType::Transaction,
|
||||
2 => InvType::Block,
|
||||
// TODO do not fail here
|
||||
_ => { panic!("bad inventory type field") }
|
||||
},
|
||||
hash: Decodable::consensus_decode(d)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{GetHeadersMessage, GetBlocksMessage};
|
||||
|
|
|
@ -36,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: FilterHash,
|
||||
pub stop_hash: BlockHash,
|
||||
}
|
||||
impl_consensus_encoding!(GetCFHeaders, filter_type, start_height, stop_hash);
|
||||
|
||||
|
@ -46,7 +46,7 @@ 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: FilterHash,
|
||||
pub stop_hash: BlockHash,
|
||||
/// The filter header preceding the first block in the requested range
|
||||
pub previous_filter: FilterHash,
|
||||
/// The filter hashes for each block in the requested range
|
||||
|
@ -60,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: FilterHash,
|
||||
pub stop_hash: BlockHash,
|
||||
}
|
||||
impl_consensus_encoding!(GetCFCheckpt, filter_type, stop_hash);
|
||||
|
||||
|
@ -70,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: FilterHash,
|
||||
pub stop_hash: BlockHash,
|
||||
/// The filter headers at intervals of 1,000
|
||||
pub filter_headers: Vec<FilterHash>,
|
||||
}
|
||||
impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers);
|
||||
impl_consensus_encoding!(CFCheckpt, filter_type, stop_hash, filter_headers);
|
||||
|
|
|
@ -16,39 +16,64 @@
|
|||
//! Utility functions related to hashing data, including merkleization
|
||||
|
||||
use std::cmp::min;
|
||||
use std::default::Default;
|
||||
use std::io;
|
||||
|
||||
use hashes::Hash;
|
||||
use hash_types::{Txid, TxidType, TxMerkleRoot};
|
||||
use consensus::encode::Encodable;
|
||||
|
||||
|
||||
/// Any collection of objects for which a merkle root makes sense to calculate
|
||||
pub trait MerkleRooted {
|
||||
/// 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) -> TxMerkleRoot;
|
||||
}
|
||||
|
||||
/// Calculates the merkle root of a list of txids hashes directly
|
||||
pub fn bitcoin_merkle_root<T: TxidType>(data: Vec<T>) -> TxMerkleRoot {
|
||||
/// Calculates the merkle root of a list of hashes inline
|
||||
/// into the allocated slice.
|
||||
///
|
||||
/// In most cases, you'll want to use [bitcoin_merkle_root] instead.
|
||||
pub fn bitcoin_merkle_root_inline<T>(data: &mut [T]) -> T
|
||||
where T: Hash + Encodable,
|
||||
<T as Hash>::Engine: io::Write,
|
||||
{
|
||||
// Base case
|
||||
if data.len() < 1 {
|
||||
return Default::default();
|
||||
}
|
||||
if data.len() < 2 {
|
||||
return TxMerkleRoot::from_inner(data[0].into_inner());
|
||||
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 = TxMerkleRoot::engine();
|
||||
let mut encoder = T::engine();
|
||||
data[idx1].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
|
||||
|
|
|
@ -59,7 +59,7 @@ use std::collections::HashSet;
|
|||
use std::io;
|
||||
|
||||
use hashes::Hash;
|
||||
use hash_types::{Txid, TxMerkleRoot, TxMerkleBranch};
|
||||
use hash_types::{Txid, TxMerkleNode};
|
||||
|
||||
use blockdata::transaction::Transaction;
|
||||
use blockdata::constants::{MAX_BLOCK_WEIGHT, MIN_TRANSACTION_WEIGHT};
|
||||
|
@ -120,7 +120,7 @@ pub struct PartialMerkleTree {
|
|||
/// node-is-parent-of-matched-txid bits
|
||||
bits: Vec<bool>,
|
||||
/// Transaction ids and internal hashes
|
||||
hashes: Vec<TxMerkleBranch>,
|
||||
hashes: Vec<TxMerkleNode>,
|
||||
}
|
||||
|
||||
impl PartialMerkleTree {
|
||||
|
@ -181,7 +181,7 @@ impl PartialMerkleTree {
|
|||
&self,
|
||||
matches: &mut Vec<Txid>,
|
||||
indexes: &mut Vec<u32>,
|
||||
) -> Result<TxMerkleRoot, MerkleBlockError> {
|
||||
) -> Result<TxMerkleNode, MerkleBlockError> {
|
||||
matches.clear();
|
||||
indexes.clear();
|
||||
// An empty set will not work
|
||||
|
@ -221,7 +221,7 @@ impl PartialMerkleTree {
|
|||
if hash_used != self.hashes.len() as u32 {
|
||||
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
|
||||
|
@ -232,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: &[Txid]) -> TxMerkleBranch {
|
||||
fn calc_hash(&self, height: u32, pos: u32, txids: &[Txid]) -> TxMerkleNode {
|
||||
if height == 0 {
|
||||
// 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 {
|
||||
// Calculate left hash
|
||||
let left = self.calc_hash(height - 1, pos * 2, txids);
|
||||
|
@ -291,7 +291,7 @@ impl PartialMerkleTree {
|
|||
hash_used: &mut u32,
|
||||
matches: &mut Vec<Txid>,
|
||||
indexes: &mut Vec<u32>,
|
||||
) -> Result<TxMerkleBranch, MerkleBlockError> {
|
||||
) -> Result<TxMerkleNode, MerkleBlockError> {
|
||||
if *bits_used as usize >= self.bits.len() {
|
||||
return Err(BadFormat("Overflowed the bits array".to_owned()));
|
||||
}
|
||||
|
@ -344,11 +344,11 @@ impl PartialMerkleTree {
|
|||
}
|
||||
|
||||
/// Helper method to produce SHA256D(left + right)
|
||||
fn parent_hash(left: TxMerkleBranch, right: TxMerkleBranch) -> TxMerkleBranch {
|
||||
let mut encoder = TxMerkleBranch::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();
|
||||
TxMerkleBranch::from_engine(encoder)
|
||||
TxMerkleNode::from_engine(encoder)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ impl Encodable for PartialMerkleTree {
|
|||
impl Decodable for PartialMerkleTree {
|
||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||
let num_transactions: u32 = Decodable::consensus_decode(&mut d)?;
|
||||
let hashes: Vec<TxMerkleBranch> = Decodable::consensus_decode(&mut d)?;
|
||||
let hashes: Vec<TxMerkleNode> = Decodable::consensus_decode(&mut d)?;
|
||||
|
||||
let bytes: Vec<u8> = Decodable::consensus_decode(d)?;
|
||||
let mut bits: Vec<bool> = vec![false; bytes.len() * 8];
|
||||
|
@ -498,7 +498,7 @@ mod tests {
|
|||
|
||||
use hashes::Hash;
|
||||
use hashes::hex::{FromHex, ToHex};
|
||||
use hash_types::{Txid, TxMerkleRoot, TxMerkleBranch};
|
||||
use hash_types::{Txid, TxMerkleNode};
|
||||
use secp256k1::rand::prelude::*;
|
||||
|
||||
use consensus::encode::{deserialize, serialize};
|
||||
|
@ -518,7 +518,8 @@ mod tests {
|
|||
.collect::<Vec<_>>();
|
||||
|
||||
// Calculate the merkle root and height
|
||||
let merkle_root_1 = bitcoin_merkle_root(txids.clone());
|
||||
let hashes = txids.iter().map(|t| t.as_hash());
|
||||
let merkle_root_1: TxMerkleNode = bitcoin_merkle_root(hashes).into();
|
||||
let mut height = 1;
|
||||
let mut ntx = num_tx;
|
||||
while ntx > 1 {
|
||||
|
@ -565,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, TxMerkleRoot::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);
|
||||
|
@ -701,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] = TxMerkleBranch::from_slice(&hash).unwrap();
|
||||
hashes[n] = TxMerkleNode::from_slice(&hash).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue