use super::transaction::{
deserialize_transaction_with_witness, serialize_transaction, serialize_transaction_with_witness,
};
use super::varint::{decode_varint, encode_varint};
use crate::error::{ConsensusError, Result};
use crate::types::*;
use std::borrow::Cow;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlockParseError {
InsufficientBytes,
InvalidVersion,
InvalidTimestamp,
InvalidBits,
InvalidNonce,
InvalidTransactionCount,
InvalidWitnessMarker,
}
impl std::fmt::Display for BlockParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BlockParseError::InsufficientBytes => {
write!(f, "Insufficient bytes to parse block header")
}
BlockParseError::InvalidVersion => write!(f, "Invalid block version"),
BlockParseError::InvalidTimestamp => write!(f, "Invalid block timestamp"),
BlockParseError::InvalidBits => write!(f, "Invalid block bits"),
BlockParseError::InvalidNonce => write!(f, "Invalid block nonce"),
BlockParseError::InvalidTransactionCount => write!(f, "Invalid transaction count"),
BlockParseError::InvalidWitnessMarker => write!(f, "Invalid witness marker"),
}
}
}
impl std::error::Error for BlockParseError {}
pub fn serialize_block_header(header: &BlockHeader) -> Vec<u8> {
let mut result = Vec::with_capacity(80);
result.extend_from_slice(&(header.version as i32).to_le_bytes());
result.extend_from_slice(&header.prev_block_hash);
result.extend_from_slice(&header.merkle_root);
result.extend_from_slice(&(header.timestamp as u32).to_le_bytes());
result.extend_from_slice(&(header.bits as u32).to_le_bytes());
result.extend_from_slice(&(header.nonce as u32).to_le_bytes());
assert_eq!(result.len(), 80);
result
}
pub fn deserialize_block_header(data: &[u8]) -> Result<BlockHeader> {
if data.len() < 80 {
return Err(ConsensusError::Serialization(Cow::Owned(
BlockParseError::InsufficientBytes.to_string(),
)));
}
let mut offset = 0;
let version = i32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as i64;
offset += 4;
let mut prev_block_hash = [0u8; 32];
prev_block_hash.copy_from_slice(&data[offset..offset + 32]);
offset += 32;
let mut merkle_root = [0u8; 32];
merkle_root.copy_from_slice(&data[offset..offset + 32]);
offset += 32;
let timestamp = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64;
offset += 4;
let bits = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64;
offset += 4;
let nonce = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64;
Ok(BlockHeader {
version,
prev_block_hash,
merkle_root,
timestamp,
bits,
nonce,
})
}
pub fn deserialize_block_with_witnesses(data: &[u8]) -> Result<(Block, Vec<Vec<Witness>>)> {
if data.len() < 80 {
return Err(ConsensusError::Serialization(Cow::Owned(
BlockParseError::InsufficientBytes.to_string(),
)));
}
let mut offset = 0;
let header = deserialize_block_header(&data[offset..offset + 80])?;
offset += 80;
let (tx_count, varint_len) = decode_varint(&data[offset..])?;
offset += varint_len;
if tx_count == 0 {
return Err(ConsensusError::Serialization(Cow::Owned(
BlockParseError::InvalidTransactionCount.to_string(),
)));
}
let mut transactions = Vec::new();
let mut all_witnesses: Vec<Vec<Witness>> = Vec::new();
for _ in 0..tx_count {
let (tx, input_witnesses, bytes_consumed) =
deserialize_transaction_with_witness(&data[offset..])?;
offset += bytes_consumed;
transactions.push(tx);
all_witnesses.push(input_witnesses);
}
while all_witnesses.len() < transactions.len() {
all_witnesses.push(Vec::new());
}
Ok((
Block::from_parts(header, transactions.into_boxed_slice()),
all_witnesses,
))
}
pub fn output_script_pubkey_slices_from_wire_block(data: &[u8]) -> Result<Vec<&[u8]>> {
if data.len() < 80 {
return Err(ConsensusError::Serialization(Cow::Owned(
BlockParseError::InsufficientBytes.to_string(),
)));
}
let mut offset = 80;
let (tx_count, varint_len) = decode_varint(&data[offset..])?;
offset += varint_len;
if tx_count == 0 {
return Err(ConsensusError::Serialization(Cow::Owned(
BlockParseError::InvalidTransactionCount.to_string(),
)));
}
let mut scripts = Vec::new();
for _ in 0..tx_count {
let (tx_scripts, consumed) =
super::transaction::output_script_pubkey_slices_from_wire_tx(&data[offset..])?;
offset += consumed;
scripts.extend(tx_scripts);
}
Ok(scripts)
}
pub fn serialize_block_with_witnesses(
block: &Block,
witnesses: &[Vec<Witness>],
include_witness: bool,
) -> Vec<u8> {
let mut result = Vec::new();
result.extend_from_slice(&serialize_block_header(&block.header));
result.extend_from_slice(&encode_varint(block.transactions.len() as u64));
for (i, tx) in block.transactions.iter().enumerate() {
let tx_witnesses = witnesses.get(i);
let use_witness = include_witness
&& tx_witnesses.is_some_and(|stacks| {
stacks.len() == tx.inputs.len() && stacks.iter().any(|stack| !stack.is_empty())
});
if use_witness {
result.extend_from_slice(
&serialize_transaction_with_witness(tx, tx_witnesses.unwrap())
.expect("witness count matches inputs"),
);
} else {
result.extend_from_slice(&serialize_transaction(tx));
}
}
result
}
pub fn serialize_block(block: &Block) -> Vec<u8> {
let witnesses: Vec<Vec<Witness>> = block.transactions.iter().map(|_| Vec::new()).collect();
serialize_block_with_witnesses(block, &witnesses, false)
}
pub fn validate_block_serialized_size(
block: &Block,
witnesses: &[Vec<Witness>],
include_witness: bool,
provided_size: usize,
) -> bool {
let serialized = serialize_block_with_witnesses(block, witnesses, include_witness);
serialized.len() == provided_size
}
#[cfg(test)]
mod output_script_tests {
use super::*;
use crate::types::{BlockHeader, OutPoint, Transaction, TransactionInput, TransactionOutput};
#[test]
fn wire_output_scripts_match_the_decoded_block() {
let legacy = Transaction {
version: 1,
inputs: crate::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [1; 32],
index: 0,
},
script_sig: vec![0x51, 0x52],
sequence: 0xffff_ffff,
}],
outputs: crate::tx_outputs![
TransactionOutput {
value: 50,
script_pubkey: vec![0x76, 0xa9],
},
TransactionOutput {
value: 1,
script_pubkey: vec![],
},
],
lock_time: 0,
};
let segwit = Transaction {
version: 2,
inputs: crate::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [2; 32],
index: 1,
},
script_sig: vec![],
sequence: 0xffff_fffe,
}],
outputs: crate::tx_outputs![TransactionOutput {
value: 25,
script_pubkey: vec![0x00, 0x14, 0xaa],
}],
lock_time: 7,
};
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [3; 32],
timestamp: 1_231_006_505,
bits: 0x1d00_ffff,
nonce: 9,
};
let block =
crate::types::Block::from_parts(header, vec![legacy, segwit].into_boxed_slice());
let witnesses = vec![vec![Vec::new()], vec![vec![vec![0x30, 0x01]]]];
let bytes = serialize_block_with_witnesses(&block, &witnesses, true);
let scripts = output_script_pubkey_slices_from_wire_block(&bytes).unwrap();
let (decoded, _) = deserialize_block_with_witnesses(&bytes).unwrap();
let expected: Vec<Vec<u8>> = decoded
.transactions
.iter()
.flat_map(|tx| tx.outputs.iter().map(|output| output.script_pubkey.clone()))
.collect();
assert_eq!(
scripts,
expected.iter().map(Vec::as_slice).collect::<Vec<_>>()
);
assert_eq!(
scripts,
vec![&[0x76, 0xa9][..], &[][..], &[0x00, 0x14, 0xaa][..]]
);
}
}