|
| 1 | +//! Utilities helping to handle Electrum-related data. |
| 2 | +
|
| 3 | +use bitcoin::hash_types::TxMerkleNode; |
| 4 | +use bitcoin::hashes::sha256d::Hash as Sha256d; |
| 5 | +use bitcoin::hashes::Hash; |
| 6 | +use bitcoin::Txid; |
| 7 | +use types::GetMerkleRes; |
| 8 | + |
| 9 | +/// Verifies a Merkle inclusion proof as retrieved via [`transaction_get_merkle`] for a transaction with the |
| 10 | +/// given `txid` and `merkle_root` as included in the [`BlockHeader`]. |
| 11 | +/// |
| 12 | +/// Returns `true` if the transaction is included in the corresponding block, and `false` |
| 13 | +/// otherwise. |
| 14 | +/// |
| 15 | +/// [`transaction_get_merkle`]: crate::ElectrumApi::transaction_get_merkle |
| 16 | +/// [`BlockHeader`]: bitcoin::BlockHeader |
| 17 | +pub fn validate_merkle_proof( |
| 18 | + txid: &Txid, |
| 19 | + merkle_root: &TxMerkleNode, |
| 20 | + merkle_res: &GetMerkleRes, |
| 21 | +) -> bool { |
| 22 | + let mut index = merkle_res.pos; |
| 23 | + let mut cur = txid.to_raw_hash(); |
| 24 | + for bytes in &merkle_res.merkle { |
| 25 | + let mut reversed = [0u8; 32]; |
| 26 | + reversed.copy_from_slice(bytes); |
| 27 | + reversed.reverse(); |
| 28 | + // unwrap() safety: `reversed` has len 32 so `from_slice` can never fail. |
| 29 | + let next_hash = Sha256d::from_slice(&reversed).unwrap(); |
| 30 | + |
| 31 | + let (left, right) = if index % 2 == 0 { |
| 32 | + (cur, next_hash) |
| 33 | + } else { |
| 34 | + (next_hash, cur) |
| 35 | + }; |
| 36 | + |
| 37 | + let data = [&left[..], &right[..]].concat(); |
| 38 | + cur = Sha256d::hash(&data); |
| 39 | + index /= 2; |
| 40 | + } |
| 41 | + |
| 42 | + cur == merkle_root.to_raw_hash() |
| 43 | +} |
0 commit comments