use core::fmt;
use hashes::Hash;
use self::MerkleBlockError::*;
use crate::blockdata::block::{self, Block};
use crate::blockdata::transaction::Transaction;
use crate::blockdata::weight::Weight;
use crate::consensus::encode::{self, Decodable, Encodable, MAX_VEC_SIZE};
use crate::hash_types::{TxMerkleNode, Txid};
use crate::io;
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::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.clone(), 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: io::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: io::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: io::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: io::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,
}
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,
}
}
}