use bitcoin::block::Header;
use bitcoin::merkle_tree::PartialMerkleTree;
use bitcoin::Txid;
use bitcoin_hashes::Hash;
pub fn verify_merkle_proof_rust_bitcoin(
txid: &[u8; 32],
merkle_root: &[u8; 32],
merkle_branch: &[[u8; 32]],
_tx_index: u32,
) -> bool {
let txid = Txid::from_slice(txid).expect("valid txid");
let _mroot =
bitcoin::hashes::sha256d::Hash::from_slice(merkle_root).expect("valid merkle root");
if merkle_branch.is_empty() {
return txid.to_byte_array() == *merkle_root;
}
false
}
pub fn build_merkle_proof_from_txids(txids: &[Txid], matches: &[bool]) -> PartialMerkleTree {
PartialMerkleTree::from_txids(txids, matches)
}
pub fn serialize_merkle_proof(pmt: &PartialMerkleTree) -> Vec<u8> {
bitcoin::consensus::encode::serialize(pmt)
}
pub fn deserialize_merkle_proof(data: &[u8]) -> Option<(PartialMerkleTree, u32)> {
bitcoin::consensus::encode::deserialize(data)
.ok()
.map(|pmt: PartialMerkleTree| {
let total_txs = pmt.num_transactions();
(pmt, total_txs)
})
}
pub fn verify_full_spv_proof_rust_bitcoin(
header_data: &[u8],
merkle_proof: &[u8],
txid: &[u8; 32],
block_height: u64,
required_confirmations: u32,
current_height: u64,
) -> bool {
if header_data.len() != 80 {
return false;
}
let header: Header = match bitcoin::consensus::encode::deserialize(header_data) {
Ok(h) => h,
Err(_) => return false,
};
let (pmt, _total_txs) = match deserialize_merkle_proof(merkle_proof) {
Some(result) => result,
None => return false,
};
let txid = Txid::from_slice(txid).expect("valid txid");
let mut extracted_txids = Vec::new();
let mut indexes = Vec::new();
match pmt.extract_matches(&mut extracted_txids, &mut indexes) {
Ok(_merkle_root) => {}
Err(_) => return false,
};
if !extracted_txids.contains(&txid) {
return false;
}
if block_height + required_confirmations as u64 > current_height {
return false;
}
header.block_hash().to_byte_array() != [0u8; 32]
}
pub fn from_rust_bitcoin_merkle_proof(
pmt: &PartialMerkleTree,
block_hash: [u8; 32],
tx_index: u32,
block_height: u64,
) -> crate::types::BitcoinInclusionProof {
let merkle_branch = extract_merkle_branch_from_pmt(pmt);
crate::types::BitcoinInclusionProof::new(merkle_branch, block_hash, tx_index, block_height)
}
fn extract_merkle_branch_from_pmt(pmt: &PartialMerkleTree) -> Vec<[u8; 32]> {
let serialized = bitcoin::consensus::encode::serialize(pmt);
let mut branch = Vec::new();
for chunk in serialized.chunks(32) {
if chunk.len() == 32 {
let mut hash = [0u8; 32];
hash.copy_from_slice(chunk);
branch.push(hash);
}
}
branch
}
pub fn to_rust_bitcoin_merkle_proof(
_inclusion_proof: &crate::types::BitcoinInclusionProof,
_total_transactions: u32,
) -> Option<PartialMerkleTree> {
None
}
pub fn compute_merkle_root_rust_bitcoin(txids: &[Txid]) -> Option<[u8; 32]> {
if txids.is_empty() {
return None;
}
bitcoin::merkle_tree::calculate_root(txids.iter().copied()).map(|h| h.to_byte_array())
}
pub fn verify_block_merkle_root_rust_bitcoin(txids: &[Txid], expected_root: [u8; 32]) -> bool {
if txids.is_empty() {
return false;
}
let expected = bitcoin::hashes::sha256d::Hash::from_slice(&expected_root).expect("valid root");
let computed = match bitcoin::merkle_tree::calculate_root(txids.iter().copied()) {
Some(h) => h,
None => return false,
};
computed.to_byte_array() == expected.to_byte_array()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_verify_merkle_proof_single_tx() {
let txid = [1u8; 32];
let merkle_root = txid;
let branch = [];
assert!(verify_merkle_proof_rust_bitcoin(
&txid,
&merkle_root,
&branch,
0
));
}
#[test]
fn test_build_and_verify_merkle_proof() {
let txid1 = Txid::from_slice(&[1u8; 32]).unwrap();
let txid2 = Txid::from_slice(&[2u8; 32]).unwrap();
let txids = vec![txid1, txid2];
let matches = vec![true, false]; let pmt = build_merkle_proof_from_txids(&txids, &matches);
let _root = bitcoin::merkle_tree::calculate_root(txids.iter().copied()).unwrap();
let mut extracted = Vec::new();
let mut indexes = Vec::new();
let result = pmt.extract_matches(&mut extracted, &mut indexes);
assert!(result.is_ok());
assert!(extracted.contains(&txid1));
}
#[test]
fn test_verify_block_merkle_root() {
let txid1 = Txid::from_slice(&[1u8; 32]).unwrap();
let txid2 = Txid::from_slice(&[2u8; 32]).unwrap();
let txids = vec![txid1, txid2];
let root = bitcoin::merkle_tree::calculate_root(txids.iter().copied()).unwrap();
let root_bytes = root.to_byte_array();
assert!(verify_block_merkle_root_rust_bitcoin(&txids, root_bytes));
assert!(!verify_block_merkle_root_rust_bitcoin(&txids, [0u8; 32]));
}
#[test]
fn test_serialize_deserialize_merkle_proof() {
let txid1 = Txid::from_slice(&[1u8; 32]).unwrap();
let txid2 = Txid::from_slice(&[2u8; 32]).unwrap();
let txids = vec![txid1, txid2];
let matches = vec![true, false];
let pmt = build_merkle_proof_from_txids(&txids, &matches);
let serialized = serialize_merkle_proof(&pmt);
let (deserialized, total_txs) = deserialize_merkle_proof(&serialized).unwrap();
assert_eq!(total_txs, 2);
let mut extracted = Vec::new();
let mut indexes = Vec::new();
let result = deserialized.extract_matches(&mut extracted, &mut indexes);
assert!(result.is_ok());
assert!(extracted.contains(&txid1));
}
}