Skip to content

Commit d16cb17

Browse files
committed
Add fast_merkle_root implementation
Based on Elements' ComputeFastMerkleRoot.
1 parent 9dfa540 commit d16cb17

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/fast_merkle_root.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Rust Elements Library
2+
// Written in 2019 by
3+
// The Elements developers
4+
//
5+
// To the extent possible under law, the author(s) have dedicated all
6+
// copyright and related and neighboring rights to this software to
7+
// the public domain worldwide. This software is distributed without
8+
// any warranty.
9+
//
10+
// You should have received a copy of the CC0 Public Domain Dedication
11+
// along with this software.
12+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13+
//
14+
15+
use bitcoin::hashes::{sha256, Hash, HashEngine};
16+
17+
/// Calculate a single sha256 midstate hash of the given left and right leaves.
18+
#[inline]
19+
fn sha256midstate(left: &[u8], right: &[u8]) -> sha256::Midstate {
20+
let mut engine = sha256::Hash::engine();
21+
engine.input(left);
22+
engine.input(right);
23+
engine.midstate()
24+
}
25+
26+
/// Compute the Merkle root of the give hashes using mid-state only.
27+
/// The inputs must be byte slices of length 32.
28+
/// Note that the merkle root calculated with this method is not the same as the
29+
/// one computed by a normal SHA256(d) merkle root.
30+
pub fn fast_merkle_root(leaves: &[[u8; 32]]) -> sha256::Midstate {
31+
let mut result_hash = Default::default();
32+
// Implementation based on ComputeFastMerkleRoot method in Elements Core.
33+
if leaves.is_empty() {
34+
return result_hash;
35+
}
36+
37+
// inner is an array of eagerly computed subtree hashes, indexed by tree
38+
// level (0 being the leaves).
39+
// For example, when count is 25 (11001 in binary), inner[4] is the hash of
40+
// the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
41+
// the last leaf. The other inner entries are undefined.
42+
//
43+
// First process all leaves into 'inner' values.
44+
let mut inner: [sha256::Midstate; 32] = Default::default();
45+
let mut count: u32 = 0;
46+
while (count as usize) < leaves.len() {
47+
let mut temp_hash = sha256::Midstate::from_inner(leaves[count as usize]);
48+
count += 1;
49+
// For each of the lower bits in count that are 0, do 1 step. Each
50+
// corresponds to an inner value that existed before processing the
51+
// current leaf, and each needs a hash to combine it.
52+
let mut level = 0;
53+
while count & (1u32 << level) == 0 {
54+
temp_hash = sha256midstate(&inner[level][..], &temp_hash[..]);
55+
level += 1;
56+
}
57+
// Store the resulting hash at inner position level.
58+
inner[level] = temp_hash;
59+
}
60+
61+
// Do a final 'sweep' over the rightmost branch of the tree to process
62+
// odd levels, and reduce everything to a single top value.
63+
// Level is the level (counted from the bottom) up to which we've sweeped.
64+
//
65+
// As long as bit number level in count is zero, skip it. It means there
66+
// is nothing left at this level.
67+
let mut level = 0;
68+
while count & (1u32 << level) == 0 {
69+
level += 1;
70+
}
71+
result_hash = inner[level];
72+
73+
while count != (1u32 << level) {
74+
// If we reach this point, hash is an inner value that is not the top.
75+
// We combine it with itself (Bitcoin's special rule for odd levels in
76+
// the tree) to produce a higher level one.
77+
78+
// Increment count to the value it would have if two entries at this
79+
// level had existed and propagate the result upwards accordingly.
80+
count += 1 << level;
81+
level += 1;
82+
while count & (1u32 << level) == 0 {
83+
result_hash = sha256midstate(&inner[level][..], &result_hash[..]);
84+
level += 1;
85+
}
86+
}
87+
// Return result.
88+
result_hash
89+
}
90+
91+
#[cfg(test)]
92+
mod tests {
93+
use super::fast_merkle_root;
94+
use bitcoin::hashes::hex::FromHex;
95+
use bitcoin::hashes::sha256;
96+
97+
#[test]
98+
fn test_fast_merkle_root() {
99+
// unit test vectors from Elements Core
100+
let test_leaves = [
101+
"b66b041650db0f297b53f8d93c0e8706925bf3323f8c59c14a6fac37bfdcd06f",
102+
"99cb2fa68b2294ae133550a9f765fc755d71baa7b24389fed67d1ef3e5cb0255",
103+
"257e1b2fa49dd15724c67bac4df7911d44f6689860aa9f65a881ae0a2f40a303",
104+
"b67b0b9f093fa83d5e44b707ab962502b7ac58630e556951136196e65483bb80",
105+
];
106+
107+
let test_roots = [
108+
"0000000000000000000000000000000000000000000000000000000000000000",
109+
"b66b041650db0f297b53f8d93c0e8706925bf3323f8c59c14a6fac37bfdcd06f",
110+
"f752938da0cb71c051aabdd5a86658e8d0b7ac00e1c2074202d8d2a79d8a6cf6",
111+
"245d364a28e9ad20d522c4a25ffc6a7369ab182f884e1c7dcd01aa3d32896bd3",
112+
"317d6498574b6ca75ee0368ec3faec75e096e245bdd5f36e8726fa693f775dfc",
113+
];
114+
115+
let mut leaves = vec![];
116+
for i in 0..4 {
117+
let root = fast_merkle_root(&leaves);
118+
assert_eq!(root, FromHex::from_hex(&test_roots[i]).unwrap(), "root #{}", i);
119+
leaves.push(sha256::Midstate::from_hex(&test_leaves[i]).unwrap().into_inner());
120+
}
121+
assert_eq!(fast_merkle_root(&leaves), FromHex::from_hex(test_roots[4]).unwrap());
122+
}
123+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ mod block;
3838
pub mod confidential;
3939
pub mod dynafed;
4040
pub mod encode;
41+
mod fast_merkle_root;
4142
mod transaction;
4243

4344
// export everything at the top level so it can be used as `elements::Transaction` etc.
@@ -46,4 +47,5 @@ pub use transaction::{OutPoint, PeginData, PegoutData, TxIn, TxOut, TxInWitness,
4647
pub use block::{BlockHeader, Block};
4748
pub use block::ExtData as BlockExtData;
4849
pub use ::bitcoin::consensus::encode::VarInt;
50+
pub use fast_merkle_root::fast_merkle_root;
4951

0 commit comments

Comments
 (0)