use core::fmt;
use hashes::Hash;
use io::{Read, Write};
use self::MerkleBlockError::*;
use crate::blockdata::block::{self, Block, TxMerkleNode};
use crate::blockdata::transaction::{Transaction, Txid};
use crate::blockdata::weight::Weight;
use crate::consensus::encode::{self, Decodable, Encodable, MAX_VEC_SIZE};
use crate::prelude::*;
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct MerkleBlock {
pub header: block::Header,
pub txn: PartialMerkleTree,
}
impl MerkleBlock {
pub fn from_block_with_predicate<F>(block: &Block, match_txids: F) -> Self
where
F: Fn(&Txid) -> bool,
{
let block_txids: Vec<_> = block.txdata.iter().map(Transaction::compute_txid).collect();
Self::from_header_txids_with_predicate(&block.header, &block_txids, match_txids)
}
pub fn from_header_txids_with_predicate<F>(
header: &block::Header,
block_txids: &[Txid],
match_txids: F,
) -> Self
where
F: Fn(&Txid) -> bool,
{
let matches: Vec<bool> = block_txids.iter().map(match_txids).collect();
let pmt = PartialMerkleTree::from_txids(block_txids, &matches);
MerkleBlock { header: *header, txn: pmt }
}
pub fn extract_matches(
&self,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<(), MerkleBlockError> {
let merkle_root = self.txn.extract_matches(matches, indexes)?;
if merkle_root.eq(&self.header.merkle_root) {
Ok(())
} else {
Err(MerkleRootMismatch)
}
}
}
impl Encodable for MerkleBlock {
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let len = self.header.consensus_encode(w)? + self.txn.consensus_encode(w)?;
Ok(len)
}
}
impl Decodable for MerkleBlock {
fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Ok(MerkleBlock {
header: Decodable::consensus_decode(r)?,
txn: Decodable::consensus_decode(r)?,
})
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct PartialMerkleTree {
num_transactions: u32,
bits: Vec<bool>,
hashes: Vec<TxMerkleNode>,
}
impl PartialMerkleTree {
pub fn num_transactions(&self) -> u32 { self.num_transactions }
pub fn bits(&self) -> &Vec<bool> { &self.bits }
pub fn hashes(&self) -> &Vec<TxMerkleNode> { &self.hashes }
pub fn from_txids(txids: &[Txid], matches: &[bool]) -> Self {
assert_ne!(txids.len(), 0);
assert_eq!(txids.len(), matches.len());
let mut pmt = PartialMerkleTree {
num_transactions: txids.len() as u32,
bits: Vec::with_capacity(txids.len()),
hashes: vec![],
};
let height = pmt.calc_tree_height();
pmt.traverse_and_build(height, 0, txids, matches);
pmt
}
pub fn extract_matches(
&self,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<TxMerkleNode, MerkleBlockError> {
matches.clear();
indexes.clear();
if self.num_transactions == 0 {
return Err(NoTransactions);
};
if self.num_transactions as u64 > Weight::MAX_BLOCK / Weight::MIN_TRANSACTION {
return Err(TooManyTransactions);
}
if self.hashes.len() as u32 > self.num_transactions {
return Err(TooManyHashes);
};
if self.bits.len() < self.hashes.len() {
return Err(NotEnoughBits);
};
let height = self.calc_tree_height();
let mut bits_used = 0u32;
let mut hash_used = 0u32;
let hash_merkle_root =
self.traverse_and_extract(height, 0, &mut bits_used, &mut hash_used, matches, indexes)?;
if (bits_used + 7) / 8 != (self.bits.len() as u32 + 7) / 8 {
return Err(NotAllBitsConsumed);
}
if hash_used != self.hashes.len() as u32 {
return Err(NotAllHashesConsumed);
}
Ok(TxMerkleNode::from_byte_array(hash_merkle_root.to_byte_array()))
}
fn calc_tree_height(&self) -> u32 {
let mut height = 0;
while self.calc_tree_width(height) > 1 {
height += 1;
}
height
}
#[inline]
fn calc_tree_width(&self, height: u32) -> u32 {
(self.num_transactions + (1 << height) - 1) >> height
}
fn calc_hash(&self, height: u32, pos: u32, txids: &[Txid]) -> TxMerkleNode {
if height == 0 {
TxMerkleNode::from_byte_array(txids[pos as usize].to_byte_array())
} else {
let left = self.calc_hash(height - 1, pos * 2, txids);
let right = if pos * 2 + 1 < self.calc_tree_width(height - 1) {
self.calc_hash(height - 1, pos * 2 + 1, txids)
} else {
left
};
PartialMerkleTree::parent_hash(left, right)
}
}
fn traverse_and_build(&mut self, height: u32, pos: u32, txids: &[Txid], matches: &[bool]) {
let mut parent_of_match = false;
let mut p = pos << height;
while p < (pos + 1) << height && p < self.num_transactions {
parent_of_match |= matches[p as usize];
p += 1;
}
self.bits.push(parent_of_match);
if height == 0 || !parent_of_match {
let hash = self.calc_hash(height, pos, txids);
self.hashes.push(hash);
} else {
self.traverse_and_build(height - 1, pos * 2, txids, matches);
if pos * 2 + 1 < self.calc_tree_width(height - 1) {
self.traverse_and_build(height - 1, pos * 2 + 1, txids, matches);
}
}
}
fn traverse_and_extract(
&self,
height: u32,
pos: u32,
bits_used: &mut u32,
hash_used: &mut u32,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<TxMerkleNode, MerkleBlockError> {
if *bits_used as usize >= self.bits.len() {
return Err(BitsArrayOverflow);
}
let parent_of_match = self.bits[*bits_used as usize];
*bits_used += 1;
if height == 0 || !parent_of_match {
if *hash_used as usize >= self.hashes.len() {
return Err(HashesArrayOverflow);
}
let hash = self.hashes[*hash_used as usize];
*hash_used += 1;
if height == 0 && parent_of_match {
matches.push(Txid::from_byte_array(hash.to_byte_array()));
indexes.push(pos);
}
Ok(hash)
} else {
let left = self.traverse_and_extract(
height - 1,
pos * 2,
bits_used,
hash_used,
matches,
indexes,
)?;
let right;
if pos * 2 + 1 < self.calc_tree_width(height - 1) {
right = self.traverse_and_extract(
height - 1,
pos * 2 + 1,
bits_used,
hash_used,
matches,
indexes,
)?;
if right == left {
return Err(IdenticalHashesFound);
}
} else {
right = left;
}
Ok(PartialMerkleTree::parent_hash(left, right))
}
}
fn parent_hash(left: TxMerkleNode, right: TxMerkleNode) -> TxMerkleNode {
let mut encoder = TxMerkleNode::engine();
left.consensus_encode(&mut encoder).expect("engines don't error");
right.consensus_encode(&mut encoder).expect("engines don't error");
TxMerkleNode::from_engine(encoder)
}
}
impl Encodable for PartialMerkleTree {
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let mut ret = self.num_transactions.consensus_encode(w)?;
ret += self.hashes.consensus_encode(w)?;
let nb_bytes_for_bits = (self.bits.len() + 7) / 8;
ret += encode::VarInt::from(nb_bytes_for_bits).consensus_encode(w)?;
for chunk in self.bits.chunks(8) {
let mut byte = 0u8;
for (i, bit) in chunk.iter().enumerate() {
byte |= (*bit as u8) << i;
}
ret += byte.consensus_encode(w)?;
}
Ok(ret)
}
}
impl Decodable for PartialMerkleTree {
fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
r: &mut R,
) -> Result<Self, encode::Error> {
let num_transactions: u32 = Decodable::consensus_decode(r)?;
let hashes: Vec<TxMerkleNode> = Decodable::consensus_decode(r)?;
let nb_bytes_for_bits = encode::VarInt::consensus_decode(r)?.0 as usize;
if nb_bytes_for_bits > MAX_VEC_SIZE {
return Err(encode::Error::OversizedVectorAllocation {
requested: nb_bytes_for_bits,
max: MAX_VEC_SIZE,
});
}
let mut bits = vec![false; nb_bytes_for_bits * 8];
for chunk in bits.chunks_mut(8) {
let byte = u8::consensus_decode(r)?;
for (i, bit) in chunk.iter_mut().enumerate() {
*bit = (byte & (1 << i)) != 0;
}
}
Ok(PartialMerkleTree { num_transactions, hashes, bits })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum MerkleBlockError {
MerkleRootMismatch,
NoTransactions,
TooManyTransactions,
TooManyHashes,
NotEnoughBits,
NotAllBitsConsumed,
NotAllHashesConsumed,
BitsArrayOverflow,
HashesArrayOverflow,
IdenticalHashesFound,
}
internals::impl_from_infallible!(MerkleBlockError);
impl fmt::Display for MerkleBlockError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use MerkleBlockError::*;
match *self {
MerkleRootMismatch => write!(f, "merkle header root doesn't match to the root calculated from the partial merkle tree"),
NoTransactions => write!(f, "partial merkle tree contains no transactions"),
TooManyTransactions => write!(f, "too many transactions"),
TooManyHashes => write!(f, "proof contains more hashes than transactions"),
NotEnoughBits => write!(f, "proof contains less bits than hashes"),
NotAllBitsConsumed => write!(f, "not all bit were consumed"),
NotAllHashesConsumed => write!(f, "not all hashes were consumed"),
BitsArrayOverflow => write!(f, "overflowed the bits array"),
HashesArrayOverflow => write!(f, "overflowed the hashes array"),
IdenticalHashesFound => write!(f, "found identical transaction hashes"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for MerkleBlockError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use MerkleBlockError::*;
match *self {
MerkleRootMismatch | NoTransactions | TooManyTransactions | TooManyHashes
| NotEnoughBits | NotAllBitsConsumed | NotAllHashesConsumed | BitsArrayOverflow
| HashesArrayOverflow | IdenticalHashesFound => None,
}
}
}
#[cfg(test)]
mod tests {
use hex::test_hex_unwrap as hex;
#[cfg(feature = "rand-std")]
use secp256k1::rand::prelude::*;
use super::*;
use crate::consensus::encode::{deserialize, serialize};
#[cfg(feature = "rand-std")]
macro_rules! pmt_tests {
($($name:ident),* $(,)?) => {
$(
#[test]
fn $name() {
pmt_test_from_name(stringify!($name));
}
)*
}
}
#[cfg(feature = "rand-std")]
pmt_tests!(
pmt_test_1,
pmt_test_4,
pmt_test_7,
pmt_test_17,
pmt_test_56,
pmt_test_100,
pmt_test_127,
pmt_test_256,
pmt_test_312,
pmt_test_513,
pmt_test_1000,
pmt_test_4095
);
#[cfg(feature = "rand-std")]
fn pmt_test_from_name(name: &str) { pmt_test(name[9..].parse().unwrap()) }
#[cfg(feature = "rand-std")]
fn pmt_test(tx_count: usize) {
use core::cmp::min;
use crate::merkle_tree;
let mut rng = thread_rng();
let tx_ids = (1..=tx_count)
.map(|i| format!("{:064x}", i).parse::<Txid>().unwrap())
.collect::<Vec<_>>();
let hashes = tx_ids.iter().map(|t| t.to_raw_hash());
let merkle_root_1: TxMerkleNode =
merkle_tree::calculate_root(hashes).expect("hashes is not empty").into();
let mut height = 1;
let mut ntx = tx_count;
while ntx > 1 {
ntx = (ntx + 1) / 2;
height += 1;
}
for att in 1..15 {
let mut matches = vec![false; tx_count];
let mut match_txid1 = vec![];
for j in 0..tx_count {
let rand_bits = match att / 2 {
0 => 0,
bits => rng.gen::<u64>() >> (64 - bits),
};
let include = rand_bits == 0;
matches[j] = include;
if include {
match_txid1.push(tx_ids[j]);
};
}
let pmt1 = PartialMerkleTree::from_txids(&tx_ids, &matches);
let serialized = serialize(&pmt1);
let n = min(tx_count, 1 + match_txid1.len() * height);
assert!(serialized.len() <= 10 + (258 * n + 7) / 8);
let pmt2: PartialMerkleTree =
deserialize(&serialized).expect("Could not deserialize own data");
let mut match_txid2: Vec<Txid> = vec![];
let mut indexes = vec![];
let merkle_root_2 = pmt2
.extract_matches(&mut match_txid2, &mut indexes)
.expect("Could not extract matches");
assert_eq!(merkle_root_1, merkle_root_2);
assert_ne!(merkle_root_2, TxMerkleNode::all_zeros());
assert_eq!(match_txid1, match_txid2);
for _ in 0..4 {
let mut pmt3: PartialMerkleTree = deserialize(&serialized).unwrap();
pmt3.damage(&mut rng);
let mut match_txid3 = vec![];
let merkle_root_3 = pmt3.extract_matches(&mut match_txid3, &mut indexes).unwrap();
assert_ne!(merkle_root_3, merkle_root_1);
}
}
}
#[test]
fn pmt_malleability() {
let txids: Vec<Txid> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 10]
.iter()
.map(|i| format!("{:064x}", i).parse::<Txid>().unwrap())
.collect();
let matches =
vec![false, false, false, false, false, false, false, false, false, true, true, false];
let tree = PartialMerkleTree::from_txids(&txids, &matches);
let result = tree.extract_matches(&mut vec![], &mut vec![]);
assert!(result.is_err());
}
#[test]
fn merkleblock_serialization() {
let mb_hex = include_str!("../../tests/data/merkle_block.hex");
let mb: MerkleBlock = deserialize(&hex!(mb_hex)).unwrap();
assert_eq!(get_block_13b8a().block_hash(), mb.header.block_hash());
assert_eq!(
mb.header.merkle_root,
mb.txn.extract_matches(&mut vec![], &mut vec![]).unwrap()
);
assert_eq!(mb_hex, serialize(&mb).to_lower_hex_string().as_str());
}
#[test]
fn merkleblock_construct_from_txids_found() {
let block = get_block_13b8a();
let txids: Vec<Txid> = [
"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20",
"f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07",
]
.iter()
.map(|hex| hex.parse::<Txid>().unwrap())
.collect();
let txid1 = txids[0];
let txid2 = txids[1];
let txids = [txid1, txid2];
let merkle_block = MerkleBlock::from_block_with_predicate(&block, |t| txids.contains(t));
assert_eq!(merkle_block.header.block_hash(), block.block_hash());
let mut matches: Vec<Txid> = vec![];
let mut index: Vec<u32> = vec![];
assert_eq!(
merkle_block.txn.extract_matches(&mut matches, &mut index).unwrap(),
block.header.merkle_root
);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0], txid2);
assert_eq!(index[0], 1);
assert_eq!(matches[1], txid1);
assert_eq!(index[1], 8);
}
#[test]
fn merkleblock_construct_from_txids_not_found() {
let block = get_block_13b8a();
let txids: Vec<Txid> = ["c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"]
.iter()
.map(|hex| hex.parse::<Txid>().unwrap())
.collect();
let merkle_block = MerkleBlock::from_block_with_predicate(&block, |t| txids.contains(t));
assert_eq!(merkle_block.header.block_hash(), block.block_hash());
let mut matches: Vec<Txid> = vec![];
let mut index: Vec<u32> = vec![];
assert_eq!(
merkle_block.txn.extract_matches(&mut matches, &mut index).unwrap(),
block.header.merkle_root
);
assert_eq!(matches.len(), 0);
assert_eq!(index.len(), 0);
}
#[cfg(feature = "rand-std")]
impl PartialMerkleTree {
fn damage(&mut self, rng: &mut ThreadRng) {
let n = rng.gen_range(0..self.hashes.len());
let bit = rng.gen::<u8>();
let hashes = &mut self.hashes;
let mut hash = hashes[n].to_byte_array();
hash[(bit >> 3) as usize] ^= 1 << (bit & 7);
hashes[n] = TxMerkleNode::from_slice(&hash).unwrap();
}
}
fn get_block_13b8a() -> Block {
use hex::FromHex;
let block_hex = include_str!("../../tests/data/block_13b8a.hex");
deserialize(&Vec::from_hex(block_hex).unwrap()).unwrap()
}
macro_rules! check_calc_tree_width {
($($test_name:ident, $num_transactions:literal, $height:literal, $expected_width:literal);* $(;)?) => {
$(
#[test]
fn $test_name() {
let pmt = PartialMerkleTree {
num_transactions: $num_transactions,
bits: vec![],
hashes: vec![],
};
let got = pmt.calc_tree_width($height);
assert_eq!(got, $expected_width)
}
)*
}
}
check_calc_tree_width! {
tree_width_01, 1, 0, 1;
tree_width_02, 2, 0, 2;
tree_width_03, 2, 1, 1;
tree_width_04, 3, 0, 3;
tree_width_05, 3, 1, 2;
tree_width_06, 3, 2, 1;
tree_width_07, 4, 0, 4;
tree_width_08, 4, 1, 2;
tree_width_09, 4, 2, 1;
tree_width_10, 5, 0, 5;
tree_width_11, 5, 1, 3;
tree_width_12, 5, 2, 2;
tree_width_13, 5, 3, 1;
tree_width_14, 6, 0, 6;
tree_width_15, 6, 1, 3;
tree_width_16, 6, 2, 2;
tree_width_17, 6, 3, 1;
tree_width_18, 7, 0, 7;
tree_width_19, 7, 1, 4;
tree_width_20, 7, 2, 2;
tree_width_21, 7, 3, 1;
}
#[test]
fn regression_2606() {
let bytes = hex!(
"000006000000000000000004ee00000004c7f1ccb1000000ffff000000010000\
0000ffffffffff1f000000000400000000000002000000000500000000000000\
000000000300000000000003000000000200000000ff00000000c7f1ccb10407\
00000000000000ccb100c76538b100000004bfa9c251681b1b00040000000025\
00000004bfaac251681b1b25\
"
);
let deser = crate::consensus::deserialize::<MerkleBlock>(&bytes);
assert!(deser.is_err());
}
}