#![forbid(unsafe_code)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![warn(rustdoc::broken_intra_doc_links)]
#![warn(missing_docs)]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#[cfg(not(any(feature = "std", feature = "no-std")))]
compile_error!("at least one of the `std` or `no-std` features must be enabled");
use bitcoin::absolute::LockTime;
use bitcoin::hash_types::TxMerkleNode;
use merkle::IncrementalHasher;
use bitcoin::{io, Amount};
extern crate alloc;
extern crate core;
pub mod merkle;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use core::mem;
use core::ops::{Deref, DerefMut};
use bitcoin::consensus::{encode, Decodable, Encodable};
use bitcoin::hashes::{sha256::HashEngine, Hash, HashEngine as _};
use bitcoin::{
OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid,
VarInt,
};
use bitcoin::blockdata::block::Header as BlockHeader;
use log::*;
#[derive(Debug)]
pub enum Error {
IncompleteData,
ParseError,
TrailingData,
}
impl From<encode::Error> for Error {
fn from(e: encode::Error) -> Self {
debug!("parse error: {}", e);
Error::ParseError
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
debug!("IO error: {}", e);
Error::ParseError
}
}
pub trait Listener {
fn on_block_start(&mut self, header: &BlockHeader);
fn on_transaction_start(&mut self, version: i32);
fn on_transaction_input(&mut self, txin: &TxIn);
fn on_transaction_output(&mut self, txout: &TxOut);
fn on_transaction_end(&mut self, locktime: LockTime, txid: Txid);
fn on_block_end(&mut self);
}
#[derive(Debug, PartialEq)]
enum ParserState {
BeforeHeader,
ReadingTransactionHeader,
ReadingInputs(usize),
ReadingInputScript(usize, OutPoint, usize),
ReadingWitnesses(usize, usize, usize),
BeforeOutputs,
ReadingOutputs(usize),
ReadingOutputScript(usize, u64, usize),
ReadingLockTime,
FinishedBlock,
}
struct Buffer(VecDeque<u8>);
impl Buffer {
fn with_capacity(capacity: usize) -> Self {
Self(VecDeque::with_capacity(capacity))
}
}
impl Deref for Buffer {
type Target = VecDeque<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Buffer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl io::Read for Buffer {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let (ref mut front, _) = self.as_slices();
let n = io::Read::read(front, buf)?;
self.drain(..n);
Ok(n)
}
}
pub struct BlockDecoder {
buffer: Buffer,
buffer_capacity: usize,
max_script_size: usize,
parser_state: ParserState,
script: Option<Vec<u8>>,
remaining_txs: usize,
segwit_inputs: Option<usize>,
hasher: HashEngine,
merkle: IncrementalHasher,
merkle_root: Option<TxMerkleNode>,
}
impl BlockDecoder {
pub fn new() -> Self {
Self::new_with_capacity(100, 100)
}
pub fn new_with_capacity(buffer_capacity: usize, max_script_size: usize) -> Self {
assert!(buffer_capacity >= 100);
let hasher = Txid::engine();
Self {
buffer: Buffer::with_capacity(buffer_capacity),
buffer_capacity,
max_script_size,
parser_state: ParserState::BeforeHeader,
script: None,
remaining_txs: 0,
segwit_inputs: None,
hasher,
merkle: IncrementalHasher::new(),
merkle_root: None,
}
}
pub fn decode_next<L: Listener>(
&mut self,
mut data: &[u8],
listener: &mut L,
) -> Result<(), Error> {
while !data.is_empty() {
let bytes_to_copy = usize::min(data.len(), self.buffer_capacity - self.buffer.len());
trace!("copying {} bytes", bytes_to_copy);
self.buffer.extend(&data[..bytes_to_copy]);
data = &data[bytes_to_copy..];
if !self.parse_step(listener)? {
break;
}
}
trace!("data is empty");
self.parse_step(listener)?;
trace!(
"no progress possible at state {:?} len {}",
self.parser_state,
self.buffer.len()
);
Ok(())
}
pub fn finish(self) -> Result<(), Error> {
assert_eq!(
self.merkle_root,
Some(self.merkle.finish()),
"merkle root mismatch"
);
if self.parser_state != ParserState::FinishedBlock {
Err(Error::IncompleteData)
} else {
Ok(())
}
}
fn parse_step<L: Listener>(&mut self, listener: &mut L) -> Result<bool, Error> {
let initial_buffer_len = self.buffer.len();
loop {
trace!("state is {:?} len {}", self.parser_state, self.buffer.len());
trace!("buffer {}", hex::encode(self.buffer.make_contiguous()));
match self.parser_state {
ParserState::BeforeHeader =>
{
if self.buffer.len() >= 80 + 5 {
let header = BlockHeader::consensus_decode(&mut self.buffer)?;
listener.on_block_start(&header);
self.merkle_root = Some(header.merkle_root);
let tx_count = VarInt::consensus_decode(&mut self.buffer)?;
self.remaining_txs = tx_count.0 as usize;
if self.remaining_txs == 0 {
return Err(Error::IncompleteData);
}
self.parser_state = ParserState::ReadingTransactionHeader;
} else {
break;
}
}
ParserState::ReadingTransactionHeader => {
if self.buffer.len() >= 4 + 5 {
let version = i32::consensus_decode(&mut self.buffer)?;
version.consensus_encode(&mut self.hasher)?;
let mut input_count = VarInt::consensus_decode(&mut self.buffer)?;
if input_count.0 == 0 {
let expected_one = VarInt::consensus_decode(&mut self.buffer)?;
if expected_one.0 != 1 {
return Err(Error::ParseError);
}
input_count = VarInt::consensus_decode(&mut self.buffer)?;
self.segwit_inputs = Some(input_count.0 as usize);
} else {
self.segwit_inputs = None;
}
input_count.consensus_encode(&mut self.hasher)?;
listener.on_transaction_start(version);
if input_count.0 > 0 {
self.parser_state = ParserState::ReadingInputs(input_count.0 as usize);
} else {
self.parser_state = ParserState::BeforeOutputs;
}
} else {
break;
}
}
ParserState::ReadingInputs(remaining_inputs) => {
if self.buffer.len() >= 36 + 3 {
let outpoint = OutPoint::consensus_decode(&mut self.buffer)?;
outpoint.consensus_encode(&mut self.hasher)?;
let script_len = VarInt::consensus_decode(&mut self.buffer)?;
script_len.consensus_encode(&mut self.hasher)?;
self.script = if script_len.0 > self.max_script_size as u64 {
None
} else {
Some(Vec::<u8>::with_capacity(script_len.0 as usize))
};
self.parser_state = ParserState::ReadingInputScript(
remaining_inputs,
outpoint,
script_len.0 as usize,
);
} else {
break;
}
}
ParserState::ReadingInputScript(
mut remaining_inputs,
outpoint,
mut remaining_script_len,
) => {
if self.buffer.is_empty() {
break;
}
let to_read = usize::min(remaining_script_len, self.buffer.len());
if let Some(ref mut script) = self.script {
script.extend(self.buffer.range(..to_read));
}
for byte in self.buffer.drain(..to_read) {
self.hasher.input(&[byte]);
}
remaining_script_len -= to_read;
self.parser_state = ParserState::ReadingInputScript(
remaining_inputs,
outpoint,
remaining_script_len,
);
if remaining_script_len == 0 {
if self.buffer.len() >= 4 {
let sequence = Sequence(u32::consensus_decode(&mut self.buffer)?);
sequence.consensus_encode(&mut self.hasher)?;
let script = self.script.take().unwrap_or(Vec::new());
let txin = TxIn {
previous_output: outpoint,
script_sig: ScriptBuf::from(script),
sequence,
witness: Default::default(),
};
listener.on_transaction_input(&txin);
remaining_inputs -= 1;
if remaining_inputs == 0 {
self.parser_state = ParserState::BeforeOutputs;
} else {
self.parser_state = ParserState::ReadingInputs(remaining_inputs);
}
} else {
break;
}
}
}
ParserState::ReadingWitnesses(
mut remaining_inputs,
mut remaining_witnesses,
mut remaining_witness_len,
) => {
if remaining_witness_len > 0 {
if self.buffer.is_empty() {
break;
}
let to_read = usize::min(remaining_witness_len, self.buffer.len());
self.buffer.drain(..to_read);
remaining_witness_len -= to_read;
self.parser_state = ParserState::ReadingWitnesses(
remaining_inputs,
remaining_witnesses,
remaining_witness_len,
);
} else if remaining_witnesses > 0 {
if self.buffer.len() < 5 {
break;
}
let witness_length = VarInt::consensus_decode(&mut self.buffer)?.0 as usize;
remaining_witnesses -= 1;
self.parser_state = ParserState::ReadingWitnesses(
remaining_inputs,
remaining_witnesses,
witness_length,
);
} else if remaining_inputs > 0 {
if self.buffer.len() < 5 {
break;
}
let witnesses = VarInt::consensus_decode(&mut self.buffer)?.0 as usize;
remaining_inputs -= 1;
self.parser_state =
ParserState::ReadingWitnesses(remaining_inputs, witnesses, 0);
} else {
self.parser_state = ParserState::ReadingLockTime;
}
}
ParserState::BeforeOutputs => {
if self.buffer.len() >= 5 {
let output_count = VarInt::consensus_decode(&mut self.buffer)?;
output_count.consensus_encode(&mut self.hasher)?;
self.parser_state = ParserState::ReadingOutputs(output_count.0 as usize);
} else {
break;
}
}
ParserState::ReadingOutputs(remaining_outputs) => {
if self.buffer.len() >= 8 + 3 {
let value = u64::consensus_decode(&mut self.buffer)?;
value.consensus_encode(&mut self.hasher)?;
let script_len = VarInt::consensus_decode(&mut self.buffer)?;
script_len.consensus_encode(&mut self.hasher)?;
self.script = if script_len.0 > self.max_script_size as u64 {
None
} else {
Some(Vec::with_capacity(script_len.0 as usize))
};
self.parser_state = ParserState::ReadingOutputScript(
remaining_outputs,
value,
script_len.0 as usize,
);
} else {
break;
}
}
ParserState::ReadingOutputScript(
mut remaining_outputs,
value,
mut remaining_script_len,
) => {
if self.buffer.is_empty() {
break;
}
let to_read = usize::min(remaining_script_len, self.buffer.len());
if let Some(ref mut script) = self.script {
script.extend(self.buffer.range(..to_read));
}
for byte in self.buffer.drain(..to_read) {
self.hasher.input(&[byte]);
}
remaining_script_len -= to_read;
if remaining_script_len > 0 {
self.parser_state = ParserState::ReadingOutputScript(
remaining_outputs,
value,
remaining_script_len,
);
} else {
let script = self.script.take().unwrap_or(Vec::new());
let txout = TxOut {
value: Amount::from_sat(value),
script_pubkey: ScriptBuf::from(script),
};
listener.on_transaction_output(&txout);
remaining_outputs -= 1;
if remaining_outputs == 0 {
if let Some(segwit_inputs) = self.segwit_inputs {
self.parser_state =
ParserState::ReadingWitnesses(segwit_inputs, 0, 0);
} else {
self.parser_state = ParserState::ReadingLockTime;
}
} else {
self.parser_state = ParserState::ReadingOutputs(remaining_outputs);
}
}
}
ParserState::ReadingLockTime => {
if self.buffer.len() >= 4 {
let locktime = LockTime::consensus_decode(&mut self.buffer)?;
locktime.consensus_encode(&mut self.hasher)?;
let engine = mem::take(&mut self.hasher);
let txid = Txid::from_engine(engine);
let txid_hash = txid.as_raw_hash();
self.merkle.add(TxMerkleNode::from_raw_hash(txid_hash.clone()));
listener.on_transaction_end(locktime, txid);
self.remaining_txs -= 1;
if self.remaining_txs == 0 {
self.parser_state = ParserState::FinishedBlock;
listener.on_block_end();
} else {
self.parser_state = ParserState::ReadingTransactionHeader;
}
} else {
break;
}
}
ParserState::FinishedBlock => {
if self.buffer.is_empty() {
break;
} else {
return Err(Error::TrailingData);
}
}
}
}
Ok(self.buffer.len() != initial_buffer_len)
}
}
#[cfg(feature = "std")]
#[allow(missing_docs)]
pub mod test_util {
use super::*;
pub struct MockListener {
pub block_headers: Vec<BlockHeader>,
pub transaction_versions: Vec<i32>,
pub transaction_inputs: Vec<TxIn>,
pub transaction_outputs: Vec<TxOut>,
pub transaction_locktimes: Vec<LockTime>,
pub transaction_ids: Vec<Txid>,
pub output_index: usize,
}
impl MockListener {
pub fn new() -> Self {
Self {
block_headers: Vec::new(),
transaction_versions: Vec::new(),
transaction_inputs: Vec::new(),
transaction_outputs: Vec::new(),
transaction_locktimes: Vec::new(),
transaction_ids: Vec::new(),
output_index: 0,
}
}
}
impl Listener for MockListener {
fn on_block_start(&mut self, header: &BlockHeader) {
self.block_headers.push(header.clone());
}
fn on_transaction_start(&mut self, version: i32) {
trace!("on_transaction_start({})", version);
self.output_index = 0;
self.transaction_versions.push(version);
}
fn on_transaction_input(&mut self, txin: &TxIn) {
trace!("on_transaction_input: {:?}", txin);
assert_eq!(txin.sequence, Sequence::default());
self.transaction_inputs.push(txin.clone());
}
fn on_transaction_output(&mut self, txout: &TxOut) {
trace!("on_transaction_output: {:?}", txout);
assert_eq!(txout.value.to_sat(), self.output_index as u64);
self.output_index += 1;
self.transaction_outputs.push(txout.clone());
}
fn on_transaction_end(&mut self, locktime: LockTime, txid: Txid) {
self.transaction_locktimes.push(locktime);
self.transaction_ids.push(txid);
}
fn on_block_end(&mut self) {}
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use bitcoin::block::Version as BlockVersion;
use bitcoin::transaction::Version;
use bitcoin::consensus::{serialize, Encodable};
use bitcoin::hashes::Hash;
use bitcoin::{Block, BlockHash, CompactTarget, Transaction, Witness};
use test_log::test;
use super::test_util::MockListener;
use super::*;
#[test]
fn test_decode_block_with_no_inputs() {
let mut listener = MockListener::new();
let mut decoder = BlockDecoder::new();
let mut block = Block {
header: BlockHeader {
version: BlockVersion::ONE,
prev_blockhash: BlockHash::all_zeros(),
merkle_root: TxMerkleNode::all_zeros(),
time: 0,
bits: CompactTarget::from_consensus(0),
nonce: 0,
},
txdata: vec![Transaction {
version: Version::ONE,
lock_time: LockTime::from_consensus(0),
input: vec![],
output: vec![TxOut {
value: Amount::ZERO,
script_pubkey: ScriptBuf::from(vec![0x33, 0x44]),
}],
}],
};
block.header.merkle_root = block.compute_merkle_root().unwrap();
let mut block_bytes = Vec::new();
block.consensus_encode(&mut block_bytes).unwrap();
trace!("block: {}\n{:#?}", hex::encode(block_bytes.clone()), block);
decoder.decode_next(&block_bytes, &mut listener).unwrap();
decoder.finish().unwrap();
assert_eq!(listener.transaction_ids.len(), 1);
assert_eq!(listener.transaction_inputs.len(), 0);
}
#[test]
fn test_decode_block() {
let mut listener = MockListener::new();
let mut decoder = BlockDecoder::new();
let mut block = Block {
header: BlockHeader {
version: BlockVersion::ONE,
prev_blockhash: BlockHash::all_zeros(),
merkle_root: TxMerkleNode::all_zeros(),
time: 0,
bits: CompactTarget::from_consensus(0),
nonce: 0,
},
txdata: vec![Transaction {
version: Version::ONE,
lock_time: LockTime::from_consensus(0),
input: vec![TxIn {
previous_output: OutPoint {
txid: Txid::from_slice(&[0x33u8; 32]).unwrap(),
vout: 0x44,
},
script_sig: ScriptBuf::from(vec![0x11, 0x22]),
sequence: Default::default(),
witness: Default::default(),
}],
output: vec![TxOut {
value: Amount::ZERO,
script_pubkey: ScriptBuf::from(vec![0x33, 0x44]),
}],
}],
};
block.header.merkle_root = block.compute_merkle_root().unwrap();
let mut block_bytes = Vec::new();
block.consensus_encode(&mut block_bytes).unwrap();
trace!("block: {}\n{:#?}", hex::encode(block_bytes.clone()), block);
decoder.decode_next(&block_bytes, &mut listener).unwrap();
decoder.finish().unwrap();
assert_eq!(listener.block_headers.len(), 1);
assert_eq!(listener.block_headers[0], block.header);
assert_eq!(listener.transaction_versions.len(), 1);
assert_eq!(listener.transaction_inputs.len(), 1);
assert_eq!(
listener.transaction_inputs[0].script_sig.as_bytes(),
&[0x11, 0x22]
);
assert_eq!(listener.transaction_outputs.len(), 1);
assert_eq!(
listener.transaction_outputs[0].script_pubkey.as_bytes(),
&[0x33, 0x44]
);
assert_eq!(listener.transaction_locktimes.len(), 1);
assert_eq!(listener.transaction_ids.len(), 1);
assert_eq!(listener.transaction_ids[0], block.txdata[0].compute_txid());
let mut listener = MockListener::new();
let mut decoder = BlockDecoder::new();
block.txdata[0].input[0].witness =
Witness::from_slice(&vec![vec![0x11, 0x22], vec![], vec![0x33, 0x44]]);
let mut block_bytes = Vec::new();
block.consensus_encode(&mut block_bytes).unwrap();
trace!("block: {}\n{:#?}", hex::encode(block_bytes.clone()), block);
let ser = serialize(&block.txdata[0]);
Transaction::consensus_decode(&mut &ser[..]).unwrap();
trace!("tx: {}\n{:#?}", hex::encode(ser), block.txdata[0]);
decoder.decode_next(&block_bytes, &mut listener).unwrap();
decoder.finish().unwrap();
assert_eq!(listener.transaction_ids.len(), 1);
assert_eq!(listener.transaction_ids[0], block.txdata[0].compute_txid());
}
}