partial merkle tree calc

This commit is contained in:
Svyatoslav Nikolsky 2016-11-23 17:35:19 +03:00
parent 01f895cc08
commit f527b023da
1 changed files with 101 additions and 1 deletions

View File

@ -1,7 +1,8 @@
use std::cmp::min;
use linked_hash_map::LinkedHashMap;
use bit_vec::BitVec;
use murmur3::murmur3_32;
use chain::{Transaction, OutPoint};
use chain::{Block, Transaction, OutPoint, merkle_root};
use ser::serialize;
use message::types;
use primitives::hash::H256;
@ -38,6 +39,20 @@ struct ConnectionBloom {
tweak: u32,
}
/// Service structure to construct `merkleblock` message.
pub struct PartialMerkleTree {
/// All transactions length.
all_len: usize,
/// All transactions hashes.
all_hashes: Vec<H256>,
/// Match flags for all transactions.
all_matches: BitVec,
/// Partial hashes.
hashes: Vec<H256>,
/// Partial match flags.
matches: BitVec,
}
impl Default for ConnectionFilter {
fn default() -> Self {
ConnectionFilter {
@ -182,6 +197,32 @@ impl ConnectionFilter {
pub fn clear(&mut self) {
self.bloom = None;
}
/// Convert `Block` to `MerkleBlock` using this filter
pub fn make_merkle_block(&mut self, block: &Block) -> Option<types::MerkleBlock> {
if self.bloom.is_none() {
return None;
}
// calculate hashes && match flags for all transactions
let all_len = block.transactions.len();
let (all_hashes, all_flags) = block.transactions.iter()
.fold((Vec::<H256>::with_capacity(all_len), BitVec::with_capacity(all_len)), |(mut all_hashes, mut all_flags), t| {
let hash = t.hash();
all_flags.push(self.filter_transaction(&hash, t));
all_hashes.push(hash);
(all_hashes, all_flags)
});
// build partial merkle tree
let (hashes, flags) = PartialMerkleTree::build(all_hashes, all_flags);
Some(types::MerkleBlock {
block_header: block.block_header.clone(),
total_transactions: all_len as u32,
hashes: hashes,
flags: flags.to_bytes().into(),
})
}
}
impl ConnectionBloom {
@ -216,6 +257,65 @@ impl ConnectionBloom {
}
}
impl PartialMerkleTree {
/// Build partial merkle tree as described here:
/// https://bitcoin.org/en/developer-reference#creating-a-merkleblock-message
pub fn build(all_hashes: Vec<H256>, all_matches: BitVec) -> (Vec<H256>, BitVec) {
let mut partial_merkle_tree = PartialMerkleTree {
all_len: all_hashes.len(),
all_hashes: all_hashes,
all_matches: all_matches,
hashes: Vec::new(),
matches: BitVec::new(),
};
partial_merkle_tree.build_tree();
(partial_merkle_tree.hashes, partial_merkle_tree.matches)
}
fn build_tree(&mut self) {
let tree_height = {
let mut height = 0usize;
while self.level_width(height) > 1 {
height += 1;
}
height
};
self.build_branch(tree_height, 0)
}
fn build_branch(&mut self, pos: usize, height: usize) {
// determine whether this node is the parent of at least one matched txid
let transactions_begin = pos << height;
let transactions_end = min(self.all_len, (pos + 1) << height);
let flag = (transactions_begin..transactions_end).any(|idx| self.all_matches[idx]);
// remember flag
self.matches.push(flag);
// proceeed with descendants
if height == 0 || !flag {
// we're at the leaf level || there is no match
let hash = self.merkle_hash(height, pos);
self.hashes.push(hash);
} else {
// proceed with left child
self.build_branch(pos << 1, height - 1);
// proceed with right child if any
if (pos << 1) + 1 < self.level_width(height - 1) {
self.build_branch((pos << 1) + 1, height - 1);
}
}
}
fn level_width(&self, height: usize) -> usize {
(self.all_len + (1 << height) - 1) >> height
}
fn merkle_hash(&self, height: usize, pos: usize) -> H256 {
let transactions_begin = pos << height;
let transactions_end = min(self.all_len, (pos + 1) << height);
merkle_root(&self.all_hashes[transactions_begin..transactions_end])
}
}
#[cfg(test)]
pub mod tests {
use std::iter::{Iterator, repeat};