use super::len::scan_len;
use crate::bsl::{BlockHeader, Transaction};
use crate::{ParseResult, SResult, Visit, Visitor};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Block<'a> {
slice: &'a [u8],
header: BlockHeader<'a>,
total_txs: usize,
}
impl<'a> Visit<'a> for Block<'a> {
#[inline(always)]
fn visit<'b, V: Visitor>(slice: &'a [u8], visit: &'b mut V) -> SResult<'a, Self> {
let header = BlockHeader::visit(slice, visit)?;
let mut consumed = 0;
let total_txs = scan_len(header.remaining(), &mut consumed)? as usize;
consumed += 80;
visit.visit_block_begin(total_txs);
for _ in 0..total_txs {
let tx = Transaction::visit(&slice[consumed..], visit)?;
consumed += tx.consumed();
}
let (slice, remaining) = slice.split_at(consumed);
let parsed = Block {
slice,
header: header.parsed_owned(),
total_txs,
};
Ok(ParseResult::new(remaining, parsed))
}
}
impl<'a> Block<'a> {
#[cfg(feature = "bitcoin_hashes")]
pub fn block_hash(&self) -> crate::bitcoin_hashes::sha256d::Hash {
self.header.block_hash()
}
#[cfg(feature = "sha2")]
pub fn block_hash_sha2(
&self,
) -> crate::sha2::digest::generic_array::GenericArray<u8, crate::sha2::digest::typenum::U32>
{
self.header.block_hash_sha2()
}
pub fn total_transactions(&self) -> usize {
self.total_txs
}
pub fn header(&self) -> &BlockHeader {
&self.header
}
}
impl<'a> AsRef<[u8]> for Block<'a> {
fn as_ref(&self) -> &[u8] {
self.slice
}
}
#[cfg(all(feature = "bitcoin", feature = "sha2"))]
pub mod visitor {
use core::ops::ControlFlow;
use bitcoin::consensus::Decodable;
use bitcoin::hashes::Hash;
pub struct FindTransaction {
to_find: bitcoin::Txid,
tx_found: Option<bitcoin::Transaction>,
}
impl FindTransaction {
pub fn new(to_find: bitcoin::Txid) -> Self {
Self {
to_find,
tx_found: None,
}
}
pub fn tx_found(self) -> Option<bitcoin::Transaction> {
self.tx_found
}
}
impl crate::Visitor for FindTransaction {
fn visit_transaction(&mut self, tx: &crate::bsl::Transaction) -> ControlFlow<()> {
let current = bitcoin::Txid::from_slice(tx.txid_sha2().as_slice()).expect("32");
if self.to_find == current {
let tx_found = bitcoin::Transaction::consensus_decode(&mut tx.as_ref())
.expect("slice validated");
self.tx_found = Some(tx_found);
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
}
#[cfg(test)]
mod test {
use bitcoin_test_data::blocks::mainnet_702861;
use crate::{
bsl::{Block, BlockHeader},
test_common::GENESIS_BLOCK,
Parse,
};
const FUZZ_DATA: [u8; 132] = [
255, 255, 255, 255, 1, 0, 0, 0, 255, 255, 255, 255, 255, 2, 0, 0, 0, 0, 65, 0, 0, 0, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 182, 182, 182,
255, 255, 182, 182, 182, 182, 182, 182, 182, 182, 255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251,
251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 0, 0, 0, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 253, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10,
];
#[test]
fn parse_block() {
let block_header = BlockHeader::parse(&GENESIS_BLOCK).unwrap();
let block = Block::parse(&GENESIS_BLOCK).unwrap();
assert_eq!(block.remaining(), &[][..]);
assert_eq!(
block.parsed(),
&Block {
slice: &GENESIS_BLOCK,
header: block_header.parsed_owned(),
total_txs: 1
}
);
assert_eq!(block.consumed(), 285);
let block = Block::parse(mainnet_702861()).unwrap();
assert_eq!(block.remaining(), &[][..]);
assert_eq!(
block.parsed(),
&Block {
slice: mainnet_702861(),
header: BlockHeader::parse(mainnet_702861()).unwrap().parsed_owned(),
total_txs: 2500,
}
);
assert_eq!(block.consumed(), 1381836);
let block = Block::parse(&FUZZ_DATA).unwrap_err();
assert_eq!(block, crate::Error::MoreBytesNeeded);
}
#[cfg(all(feature = "bitcoin", feature = "sha2"))]
#[test]
fn find_tx() {
use crate::Visit;
use bitcoin_test_data::blocks::mainnet_702861;
use core::str::FromStr;
let txid = bitcoin::Txid::from_str(
"416a5f96cb63e7649f6f272e7f82a43a97bcf6cfc46184c733344de96ff1e433",
)
.unwrap();
let mut visitor = crate::bsl::FindTransaction::new(txid.clone());
let _ = Block::visit(&mainnet_702861(), &mut visitor);
let tx = visitor.tx_found().unwrap();
assert_eq!(tx.compute_txid(), txid);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn size_of() {
use core::ops::ControlFlow;
assert_eq!(std::mem::size_of::<Block>(), 56);
assert_eq!(std::mem::size_of::<ControlFlow<()>>(), 1);
}
}