electrs2 0.9.9

An efficient re-implementation of Electrum Server in Rust for Bitcoin II
Documentation
use bitcoin::{
    hashes::{hex::ToHex, Hash},
    TxMerkleNode, Txid,
};

pub(crate) struct Proof {
    proof: Vec<TxMerkleNode>,
    position: usize,
}

impl Proof {
    pub(crate) fn create(txids: &[Txid], position: usize) -> Self {
        assert!(position < txids.len());
        let mut offset = position;
        let mut hashes: Vec<TxMerkleNode> = txids
            .iter()
            .map(|txid| TxMerkleNode::from_hash(txid.as_hash()))
            .collect();

        let mut proof = vec![];
        while hashes.len() > 1 {
            if hashes.len() % 2 != 0 {
                let last = *hashes.last().unwrap();
                hashes.push(last);
            }
            offset = if offset % 2 == 0 {
                offset + 1
            } else {
                offset - 1
            };
            proof.push(hashes[offset]);
            offset /= 2;
            hashes = hashes
                .chunks(2)
                .map(|pair| {
                    let left = pair[0];
                    let right = pair[1];
                    let input = [&left[..], &right[..]].concat();
                    TxMerkleNode::hash(&input)
                })
                .collect()
        }
        Self { proof, position }
    }

    pub(crate) fn to_hex(&self) -> Vec<String> {
        self.proof.iter().map(|node| node.to_hex()).collect()
    }

    pub(crate) fn position(&self) -> usize {
        self.position
    }
}

#[cfg(test)]
mod tests {
    use bitcoin::{consensus::encode::deserialize, Block, Txid};
    use std::path::Path;

    use super::Proof;

    #[test]
    fn test_merkle() {
        let proof = Proof::create(
            &load_block_txids("000000003d0ecda04e04130209d3d17dd43c19ede60061a4a059a2315a6c9561"),
            157,
        );
        assert_eq!(
            proof.to_hex(),
            vec![
                "1975e29bfb3990e7d59cdb1059f8e0b0e5300f4908d4dd043d578bb267c9163b"
                
            ]
        );

        let proof = Proof::create(
            &load_block_txids("0000000017189e6944eb114bd9538b7b469b1a88a33542244a7de726d7a0245d"),
            6,
        );
        assert_eq!(
            proof.to_hex(),
            vec![
                "0174ba0fe3ea5370af33fd9f02ba28a3d166d3726e6ac208e083f83d8cfa4df0"
                
            ]
        );
    }

    fn load_block_txids(block_hash_hex: &str) -> Vec<Txid> {
        let path = Path::new("src")
            .join("tests")
            .join("blocks")
            .join(block_hash_hex);
        let data = std::fs::read(path).unwrap();
        let block: Block = deserialize(&data).unwrap();
        block.txdata.iter().map(|tx| tx.txid()).collect()
    }
}