Put the `MerkleBlock` struct at the top of the file

The `MerkleBlock` struct is the main type in this file, put it at the
top of the file. This leaves the next most important type,
`PartialMerkleTree` below that.

Refactor only, no logic changes.
This commit is contained in:
Tobin C. Harding 2023-02-10 11:35:19 +11:00
parent f0d968197a
commit 861fdd6ab1
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
1 changed files with 109 additions and 109 deletions

View File

@ -51,6 +51,115 @@ use crate::io;
use crate::prelude::*; use crate::prelude::*;
use self::MerkleBlockError::*; use self::MerkleBlockError::*;
/// Data structure that represents a block header paired to a partial merkle tree.
///
/// NOTE: This assumes that the given Block has *at least* 1 transaction. If the Block has 0 txs,
/// it will hit an assertion.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct MerkleBlock {
/// The block header
pub header: block::Header,
/// Transactions making up a partial merkle tree
pub txn: PartialMerkleTree,
}
impl MerkleBlock {
/// Create a MerkleBlock from a block, that contains proofs for specific txids.
///
/// The `block` is a full block containing the header and transactions and `match_txids` is a
/// function that returns true for the ids that should be included in the partial merkle tree.
///
/// # Examples
///
/// ```rust
/// use bitcoin::hash_types::Txid;
/// use bitcoin::hashes::hex::FromHex;
/// use bitcoin::{Block, MerkleBlock};
///
/// // Block 80000
/// let block_bytes = Vec::from_hex("01000000ba8b9cda965dd8e536670f9ddec10e53aab14b20bacad2\
/// 7b9137190000000000190760b278fe7b8565fda3b968b918d5fd997f993b23674c0af3b6fde300b38f33\
/// a5914ce6ed5b1b01e32f5702010000000100000000000000000000000000000000000000000000000000\
/// 00000000000000ffffffff0704e6ed5b1b014effffffff0100f2052a01000000434104b68a50eaa0287e\
/// ff855189f949c1c6e5f58b37c88231373d8a59809cbae83059cc6469d65c665ccfd1cfeb75c6e8e19413\
/// bba7fbff9bc762419a76d87b16086eac000000000100000001a6b97044d03da79c005b20ea9c0e1a6d9d\
/// c12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aee\
/// d3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d\
/// 5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b6\
/// 5d35549d88ac00000000").unwrap();
/// let block: Block = bitcoin::consensus::deserialize(&block_bytes).unwrap();
///
/// // Create a merkle block containing a single transaction
/// let txid = "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2".parse::<Txid>().unwrap();
/// let match_txids: Vec<Txid> = vec![txid].into_iter().collect();
/// let mb = MerkleBlock::from_block_with_predicate(&block, |t| match_txids.contains(t));
///
/// // Authenticate and extract matched transaction ids
/// let mut matches: Vec<Txid> = vec![];
/// let mut index: Vec<u32> = vec![];
/// assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
/// assert_eq!(txid, matches[0]);
/// ```
pub fn from_block_with_predicate<F>(block: &Block, match_txids: F) -> Self
where
F: Fn(&Txid) -> bool,
{
let block_txids: Vec<_> = block.txdata.iter().map(Transaction::txid).collect();
Self::from_header_txids_with_predicate(&block.header, &block_txids, match_txids)
}
/// Create a MerkleBlock from the block's header and txids, that contain proofs for specific txids.
///
/// The `header` is the block header, `block_txids` is the full list of txids included in the block and
/// `match_txids` is a function that returns true for the ids that should be included in the partial merkle tree.
pub fn from_header_txids_with_predicate<F>(
header: &block::Header,
block_txids: &[Txid],
match_txids: F,
) -> Self
where
F: Fn(&Txid) -> bool,
{
let matches: Vec<bool> = block_txids.iter().map(match_txids).collect();
let pmt = PartialMerkleTree::from_txids(block_txids, &matches);
MerkleBlock { header: *header, txn: pmt }
}
/// Extract the matching txid's represented by this partial merkle tree
/// and their respective indices within the partial tree.
/// returns Ok(()) on success, or error in case of failure
pub fn extract_matches(
&self,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<(), MerkleBlockError> {
let merkle_root = self.txn.extract_matches(matches, indexes)?;
if merkle_root.eq(&self.header.merkle_root) {
Ok(())
} else {
Err(MerkleRootMismatch)
}
}
}
impl Encodable for MerkleBlock {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let len = self.header.consensus_encode(w)? + self.txn.consensus_encode(w)?;
Ok(len)
}
}
impl Decodable for MerkleBlock {
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Ok(MerkleBlock {
header: Decodable::consensus_decode(r)?,
txn: Decodable::consensus_decode(r)?,
})
}
}
/// Data structure that represents a partial merkle tree. /// Data structure that represents a partial merkle tree.
/// ///
/// It represents a subset of the txid's of a known block, in a way that /// It represents a subset of the txid's of a known block, in a way that
@ -347,115 +456,6 @@ impl Decodable for PartialMerkleTree {
} }
} }
/// Data structure that represents a block header paired to a partial merkle tree.
///
/// NOTE: This assumes that the given Block has *at least* 1 transaction. If the Block has 0 txs,
/// it will hit an assertion.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct MerkleBlock {
/// The block header
pub header: block::Header,
/// Transactions making up a partial merkle tree
pub txn: PartialMerkleTree,
}
impl MerkleBlock {
/// Create a MerkleBlock from a block, that contains proofs for specific txids.
///
/// The `block` is a full block containing the header and transactions and `match_txids` is a
/// function that returns true for the ids that should be included in the partial merkle tree.
///
/// # Examples
///
/// ```rust
/// use bitcoin::hash_types::Txid;
/// use bitcoin::hashes::hex::FromHex;
/// use bitcoin::{Block, MerkleBlock};
///
/// // Block 80000
/// let block_bytes = Vec::from_hex("01000000ba8b9cda965dd8e536670f9ddec10e53aab14b20bacad2\
/// 7b9137190000000000190760b278fe7b8565fda3b968b918d5fd997f993b23674c0af3b6fde300b38f33\
/// a5914ce6ed5b1b01e32f5702010000000100000000000000000000000000000000000000000000000000\
/// 00000000000000ffffffff0704e6ed5b1b014effffffff0100f2052a01000000434104b68a50eaa0287e\
/// ff855189f949c1c6e5f58b37c88231373d8a59809cbae83059cc6469d65c665ccfd1cfeb75c6e8e19413\
/// bba7fbff9bc762419a76d87b16086eac000000000100000001a6b97044d03da79c005b20ea9c0e1a6d9d\
/// c12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aee\
/// d3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d\
/// 5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b6\
/// 5d35549d88ac00000000").unwrap();
/// let block: Block = bitcoin::consensus::deserialize(&block_bytes).unwrap();
///
/// // Create a merkle block containing a single transaction
/// let txid = "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2".parse::<Txid>().unwrap();
/// let match_txids: Vec<Txid> = vec![txid].into_iter().collect();
/// let mb = MerkleBlock::from_block_with_predicate(&block, |t| match_txids.contains(t));
///
/// // Authenticate and extract matched transaction ids
/// let mut matches: Vec<Txid> = vec![];
/// let mut index: Vec<u32> = vec![];
/// assert!(mb.extract_matches(&mut matches, &mut index).is_ok());
/// assert_eq!(txid, matches[0]);
/// ```
pub fn from_block_with_predicate<F>(block: &Block, match_txids: F) -> Self
where
F: Fn(&Txid) -> bool,
{
let block_txids: Vec<_> = block.txdata.iter().map(Transaction::txid).collect();
Self::from_header_txids_with_predicate(&block.header, &block_txids, match_txids)
}
/// Create a MerkleBlock from the block's header and txids, that contain proofs for specific txids.
///
/// The `header` is the block header, `block_txids` is the full list of txids included in the block and
/// `match_txids` is a function that returns true for the ids that should be included in the partial merkle tree.
pub fn from_header_txids_with_predicate<F>(
header: &block::Header,
block_txids: &[Txid],
match_txids: F,
) -> Self
where
F: Fn(&Txid) -> bool,
{
let matches: Vec<bool> = block_txids.iter().map(match_txids).collect();
let pmt = PartialMerkleTree::from_txids(block_txids, &matches);
MerkleBlock { header: *header, txn: pmt }
}
/// Extract the matching txid's represented by this partial merkle tree
/// and their respective indices within the partial tree.
/// returns Ok(()) on success, or error in case of failure
pub fn extract_matches(
&self,
matches: &mut Vec<Txid>,
indexes: &mut Vec<u32>,
) -> Result<(), MerkleBlockError> {
let merkle_root = self.txn.extract_matches(matches, indexes)?;
if merkle_root.eq(&self.header.merkle_root) {
Ok(())
} else {
Err(MerkleRootMismatch)
}
}
}
impl Encodable for MerkleBlock {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let len = self.header.consensus_encode(w)? + self.txn.consensus_encode(w)?;
Ok(len)
}
}
impl Decodable for MerkleBlock {
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Ok(MerkleBlock {
header: Decodable::consensus_decode(r)?,
txn: Decodable::consensus_decode(r)?,
})
}
}
/// An error when verifying the merkle block. /// An error when verifying the merkle block.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
#[non_exhaustive] #[non_exhaustive]