Skip to content
This repository was archived by the owner on Dec 4, 2024. It is now read-only.

Commit 243d4cc

Browse files
friedgerjcnelson
andauthored
Replace rs_merkle with new implementation (#218)
* chore: add test for block with odd number of txs * fix: implement Bitcoin txid Merkle tree and proof generator, which works with blocks with an odd number of transactions * fix: clippy * fix: fmt * fix: fmt * chore: remove unused var * chore: add test for empty merkle tree * chore: fmt --------- Co-authored-by: Jude Nelson <judecn@gmail.com>
1 parent 4a17e4f commit 243d4cc

1 file changed

Lines changed: 161 additions & 25 deletions

File tree

romeo/src/proof_data.rs

Lines changed: 161 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use bdk::bitcoin::{Block, BlockHeader, Transaction, Txid as BitcoinTxId};
33
use blockstack_lib::vm::types::{
44
ListData, ListTypeData, SequenceData, Value, BUFF_32,
55
};
6-
use rs_merkle::{Hasher, MerkleTree};
6+
use rs_merkle::Hasher;
77
use stacks_core::crypto::{sha256::DoubleSha256Hasher, Hashing};
88
/// The double sha256 algorithm used for bitcoin
99
#[derive(Clone)]
@@ -55,30 +55,142 @@ pub struct ProofDataClarityValues {
5555
pub merkle_path: Value,
5656
}
5757

58+
/// Merkle tree for Bitcoin block transactions
59+
pub struct BitcoinMerkleTree {
60+
data: Vec<Vec<[u8; 32]>>,
61+
}
62+
63+
impl BitcoinMerkleTree {
64+
/// Make a new Merkle tree out of the given Bitcoin txids
65+
pub fn new(txs: &[BitcoinTxId]) -> Self {
66+
if txs.is_empty() {
67+
return Self { data: vec![] };
68+
}
69+
70+
let mut tree = vec![];
71+
72+
// fill in leaf hashes
73+
let mut leaf_hashes = vec![];
74+
for tx in txs {
75+
let mut hash_slice = [0u8; 32];
76+
hash_slice.copy_from_slice(tx);
77+
leaf_hashes.push(hash_slice);
78+
}
79+
// must have an even number of hashes
80+
if txs.len() % 2 == 1 {
81+
let last_hash_slice = leaf_hashes
82+
.last()
83+
.expect(
84+
"FATAL: unreachable: non-empty vec does not have `last()`",
85+
)
86+
.to_owned();
87+
leaf_hashes.push(last_hash_slice);
88+
}
89+
90+
tree.push(leaf_hashes);
91+
92+
// calculate parent hashes until we reach the root
93+
let mut last_row_len =
94+
tree.last().expect("FATAL: unreachable: empty tree").len();
95+
loop {
96+
let mut next_row = vec![];
97+
let last_row = tree.last().expect("FATAL: unreachable: empty tree");
98+
for i in 0..(last_row_len / 2) {
99+
let mut intermediate_preimage = [0u8; 64];
100+
intermediate_preimage[0..32].copy_from_slice(&last_row[2 * i]);
101+
intermediate_preimage[32..64]
102+
.copy_from_slice(&last_row[2 * i + 1]);
103+
104+
let intermediate_hash =
105+
DoubleSha256Algorithm::hash(&intermediate_preimage);
106+
next_row.push(intermediate_hash);
107+
}
108+
109+
// reached the root
110+
if next_row.len() == 1 {
111+
tree.push(next_row);
112+
break;
113+
}
114+
115+
// have more to go -- this row must have an even number of nodes
116+
if next_row.len() % 2 == 1 {
117+
let last_hash_slice = next_row
118+
.last()
119+
.expect("FATAL: unreachable: next_row is empty")
120+
.to_owned();
121+
next_row.push(last_hash_slice);
122+
}
123+
124+
last_row_len = next_row.len();
125+
tree.push(next_row);
126+
}
127+
128+
Self { data: tree }
129+
}
130+
131+
/// Get the Merkle root.
132+
/// It will be None if the tree is empty
133+
pub fn root(&self) -> Option<[u8; 32]> {
134+
self.data.last().map(|root_row| root_row[0])
135+
}
136+
137+
/// Calculate a merkle proof for a transaction, given its index.
138+
/// This algorithm uses the following insight: the ith bit in the index
139+
/// tells us which sibling to use (left or right) at the ith level of the
140+
/// Merkle tree.
141+
/// * if ith bit of `index` is 0, then use the _right_ sibling at height i
142+
/// * if ith bit of `index` is 1, then use the _left_ sibling at height i
143+
pub fn proof(&self, mut index: usize) -> Option<Vec<[u8; 32]>> {
144+
if self.data.is_empty() {
145+
// empty tree
146+
return None;
147+
}
148+
if index >= self.data[0].len() {
149+
// off the end of the leaf row
150+
return None;
151+
}
152+
153+
let mut proof = vec![];
154+
for i in 0..(self.data.len() - 1) {
155+
let sibling = if index % 2 == 0 {
156+
assert!(
157+
index + 1 < self.data[i].len(),
158+
"BUG: {} + 1 >= data[{}].len() ({})",
159+
index,
160+
i,
161+
self.data[i].len()
162+
);
163+
self.data[i][index + 1]
164+
} else {
165+
assert!(index > 0, "BUG: index == 0");
166+
self.data[i][index - 1]
167+
};
168+
proof.push(sibling);
169+
index >>= 1;
170+
}
171+
172+
Some(proof)
173+
}
174+
175+
/// Calculate the tree depth, including leaves.
176+
/// This value is always greater than 0, unless the tree is empty.
177+
pub fn depth(&self) -> usize {
178+
self.data.len()
179+
}
180+
}
181+
58182
impl ProofData {
59183
/// Create a new proof from a bitcoin transaction and a block
60184
pub fn from_block_and_index(block: &Block, index: usize) -> Self {
61185
let tx: &Transaction =
62186
block.txdata.get(index).expect("Invalid tx index");
63-
let mut merkle_tree = MerkleTree::<DoubleSha256Algorithm>::new();
64-
for tx in &block.txdata {
65-
merkle_tree.insert(tx.txid().to_vec().try_into().unwrap());
66-
}
67-
// append last tx id if number of leaves is odd
68-
if block.txdata.len() % 2 == 1 {
69-
merkle_tree.insert(
70-
block
71-
.txdata
72-
.last()
73-
.unwrap()
74-
.txid()
75-
.to_vec()
76-
.try_into()
77-
.unwrap(),
78-
);
79-
}
80-
merkle_tree.commit();
81-
let merkle_path = merkle_tree.proof(&[index]);
187+
188+
let txids: Vec<BitcoinTxId> =
189+
block.txdata.iter().map(|tx| tx.txid()).collect();
190+
let merkle_tree = BitcoinMerkleTree::new(&txids);
191+
let merkle_path = merkle_tree
192+
.proof(index)
193+
.expect("FATAL: index is out-of-bounds");
82194

83195
Self {
84196
reversed_txid: tx.txid(),
@@ -87,11 +199,7 @@ impl ProofData {
87199
.bip34_block_height()
88200
.expect("Failed to get block height"),
89201
block_header: block.header,
90-
merkle_path: merkle_path
91-
.proof_hashes()
92-
.iter()
93-
.map(|h| h.to_vec())
94-
.collect(),
202+
merkle_path: merkle_path.into_iter().map(|h| h.to_vec()).collect(),
95203
merkle_root: hex::encode(merkle_tree.root().unwrap()),
96204
}
97205
}
@@ -219,4 +327,32 @@ mod tests {
219327
"0xd564f1a4e53e7bad92f67c9a05b748e504ac1b8155db4c2d9b4ed12afd32139f"
220328
)
221329
}
330+
331+
// test from_block_and_index returns correct proof
332+
// block taken from testnet, failed burn tx
333+
#[test]
334+
fn should_create_correct_merkle_proof() {
335+
let block_hex = "00002020b8a796757a3e087dfdbb0d68d7b74a632579561d5be646f015010000000000003b576e83c8e964e5a56fb443e5b8b10a001e9641328144a28f223ac45acee665802e1d6530b2031a4ddc3ff009020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230366982604802e1d654d65726d61696465722046545721010002a5bd9a080000000000ffffffff02dc6e130000000000160014c035e789d9efffa10aa92e93f48f29b8cfb224c20000000000000000266a24aa21a9ed2e42ffd390d39224c48c334444e06a7a83ae954b699bc36ce21b1103ec4959f901200000000000000000000000000000000000000000000000000000000000000000000000000200000001e790e0351515d924bacf2baafec27e6ae51622e3d423be3dfb3df00a3f1f43a4010000006a473044022036de7ebb625c475e1320f44c940e7c25e18abffe18d7b92593505fac3154b7cf02202a165962f6235d45de9f5c1d3a02dd31d2f17919ffd9c4feaac8f4612aad9f0d0121022c7f4e04dea8be8ffc76587c34676b0fa0d3f266dde875d0431c7996e3462695fdffffff027dbc1300000000001976a914546582e3af948c9065d39f00d2bf56ff998b91e288ac1b826e1d010000001976a9145b3c1c6518afdac084750c98b9ccda8520e2c4f088ac65982600010000000182e15c6b31e4871d530ed58c2ed8ac24c2ed9280bca800100106a95bcaee1ada020000006a47304402204e8ae4d5c246e37c95c1806419a9fb3260eaf49790378c4df7f16c55aacef336022059733743e9ac9bc78919bd5459b93528cc3feefebdee4c57c784b13d641ba9690121032f20eae43e911857fdb914fd40806a783a19b05607107c2e514e0b72b24477e2ffffffff01582b0200000000008c21032f20eae43e911857fdb914fd40806a783a19b05607107c2e514e0b72b24477e2ad512102f8dc94efa5016af7cde4f5433d9e46f9ebfc1cfafae2cc949bd2a369b8993da22102605350338e279a0e163b9581c43cccf822dbf45e5affe16ff81cb660a5b1f9372102d53f9790b9d03e7fd65507447db5c0f81b796b58763cb0febf91eed1e4b25f7253ae0000000001000000017ced464f994e79fe75ac19e50980e1f8335fb5a286cd624b0cfb43ba9acacf87030000006a47304402204dbe45d743d027f5362e3d7d53178d70aa9c24594241c407d7067ac7b6f37949022058576523f36b895186dfa971848d2af05110b2923824f4f2be3f4d48f49a69e60121037435c194e9b01b3d7f7a2802d6684a3af68d05bbf4ec8f17021980d777691f1dfdffffff040000000000000000536a4c5054325b76eaa00b1829bcf11d22b8b08747960f8c892c75b76641dc81fb74e7f0f42e0215a88d449445b54513aee65fbc3e71262534434e8853687e665bc5ca1e1356e4002698630002002694df00024910270000000000001976a914000000000000000000000000000000000000000088ac10270000000000001976a914000000000000000000000000000000000000000088ac2dc75801000000001976a914ba27f99e007c7f605a8305e318c1abde3cd220ac88ac000000000200000000010199c60618b12177ef73f14ee1a1d6531884344e7b18bacf3dc2fe8456a26367d90100000017160014ab85a42e84f1734dfcc50321decb751009e3ea3afdffffff0226200000000000002251206b0a1b2a5a618a9abdbd2f2f454a4b412d705290bd950e0fd4d23a523b1c4545df101b000000000017a914be42fa1629963ecab0e2ff1d8bb94273544632ef8702483045022100d25f5e6c4d410166ba170c08ec875448dea19576c8c45f0fcda49bd23683b6e10220202262b92543f73f6d7440a85e6f2e2b7a91077233ea599d21483bb6817b8cea012103c453710ff8121a8e01be0096404077ffab916d545f69adc196e9a8fa723312010000000002000000000101ab72c53b49545d8ada45ea1544e00bc161297bd9cf348546e828368b2505bc5d0200000000ffffffff0300000000000000004f6a4c4c54323e00000000000003e800a5075604a3d6efa3d15ddd1a3ab6db8b57ac037fc1a2207fe5fd6d1e29c772047b9318b30a3f6f4b208bbd84a9521316c8eaf72c0ee91d6f3495e0bb98ba4ecff401000000000000160014764ad6983a6455cca54cd6a4f7b0da71ba6a0baba5caf50500000000160014764ad6983a6455cca54cd6a4f7b0da71ba6a0bab02483045022100966e347c5673df63f78fd316aac2ed0a7e4b8f77e226b55bc5422a955abb65da02207a6509b852079cb4b2ae623d8ae7f0e5b20526c136f5b090fdb1ab522778f9d7012103968e761cb836bfc6711748cf05d093c80621144b1482fea29553492538887e6a0000000001000000000101e57e57dea1958ded04ca010d566ef2bdd791360320914dbb2ee640c2bac975a70100000000ffffffff02e7230000000000001976a9149c4b12bb5a2e7e4b2721a25d8abebd6a8144d41288acd4a1e81100000000160014c783068b2593c7138d8744956f9d048032c580800247304402204d68dfed915eed93158f0221b6bf8ec7778bf93286d6709f74ca9eb718c016aa022026780192ae7bdeee8053cc84d1124aa0a4049972c223ba34eee127b43593e770012103f500418025ba3babca935e9f7617c438210ab72ae3ece0b25e5dff579c31ddd10000000002000000000101006280955059670da318c1811713b9c1398687d15227ee91c0210279c0d8b2ec0100000000fdffffff02401f000000000000160014c67d2be99415528a01d7c8c13000d4ca0eb963fedc0d0e0000000000160014be9257af0584f100e7f16c8a1cf55f32a5aae47602473044022035147e241be86217240618be72b982f31e6873c8f4c8c1824bcea78b1c91238a02204f48e4a7b8022009726f4af42a3ce7ec7c83f0bb7609f5ffa2781defcd7ee2ab012102fc3bd735a715499b5ffa7d96d08f42f5eea78aed455de5bd095606cebdd4594e6598260001000000000101550dae167d4568d1d53e201eb9481348e90fa3086867aaa9a9f293af48d0df9d0100000000ffffffff02e80300000000000016001463c7dec8d97feed8f9e003eca65c8ca26152bea874661100000000001600142481f3daab15b06eeb768af20eb9b64c275dc65c02483045022100937cdd969a1b000a8bacf6549382b7ab8fb7c59dd23332139a03e1d2cfe446af02200569a1a3885058a358ba2f69df31951a1db5000e8f8c3ec407caf165f74da36e0121039a66476dd5fa7a668dc8f540a8fdfa63405baf2491ce907f055137460d0cc2ae65982600";
336+
let block: Block =
337+
deserialize(&Vec::<u8>::from_hex(block_hex).unwrap()).unwrap();
338+
let txindex: usize = 4;
339+
let proof_data = ProofData::from_block_and_index(&block, txindex);
340+
let values = proof_data.to_values();
341+
assert_eq!(
342+
values.txid.to_string(),
343+
"0x07268a427a3e0a0618fe94dcf434cd976c0cd29f2b0d645315ec56c4b04393a4"
344+
);
345+
assert_eq!(values.block_header.to_string(), "0x00002020b8a796757a3e087dfdbb0d68d7b74a632579561d5be646f015010000000000003b576e83c8e964e5a56fb443e5b8b10a001e9641328144a28f223ac45acee665802e1d6530b2031a4ddc3ff0");
346+
assert_eq!(values.block_height.to_string(), "u2529382");
347+
assert_eq!(values.merkle_path.to_string(), "(0xa9db8b2c0b4de3ee6945db550541adcc18852acef9148dc59747a31c9fbf8327 0xde7c38d3e809bcb86fa94695de178e1b27d8d9b6d25a5683b598c36deca50580 0x02f0523e28df15bf268ab52b9a3826d7f933467ea2708c0d7e7d7cd5b2e44892 0x7f37d80a06a9c7d9db4cf14d63e826ecf136b59df3583cb2b94e0a438d3ae506)");
348+
}
349+
350+
// test empty merkle tree
351+
#[test]
352+
fn should_create_merkle_trees_correctly() {
353+
let txids0 = vec![];
354+
let merkle_tree = BitcoinMerkleTree::new(&txids0);
355+
assert_eq!(merkle_tree.root(), None);
356+
assert_eq!(merkle_tree.proof(0), None);
357+
}
222358
}

0 commit comments

Comments
 (0)