blvm_primitives/serialization/
block.rs1use super::transaction::{
7 deserialize_transaction_with_witness, serialize_transaction, serialize_transaction_with_witness,
8};
9use super::varint::{decode_varint, encode_varint};
10use crate::error::{ConsensusError, Result};
11use crate::types::*;
12use std::borrow::Cow;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum BlockParseError {
17 InsufficientBytes,
18 InvalidVersion,
19 InvalidTimestamp,
20 InvalidBits,
21 InvalidNonce,
22 InvalidTransactionCount,
23 InvalidWitnessMarker,
24}
25
26impl std::fmt::Display for BlockParseError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 BlockParseError::InsufficientBytes => {
30 write!(f, "Insufficient bytes to parse block header")
31 }
32 BlockParseError::InvalidVersion => write!(f, "Invalid block version"),
33 BlockParseError::InvalidTimestamp => write!(f, "Invalid block timestamp"),
34 BlockParseError::InvalidBits => write!(f, "Invalid block bits"),
35 BlockParseError::InvalidNonce => write!(f, "Invalid block nonce"),
36 BlockParseError::InvalidTransactionCount => write!(f, "Invalid transaction count"),
37 BlockParseError::InvalidWitnessMarker => write!(f, "Invalid witness marker"),
38 }
39 }
40}
41
42impl std::error::Error for BlockParseError {}
43
44pub fn serialize_block_header(header: &BlockHeader) -> Vec<u8> {
46 let mut result = Vec::with_capacity(80);
47 result.extend_from_slice(&(header.version as i32).to_le_bytes());
48 result.extend_from_slice(&header.prev_block_hash);
49 result.extend_from_slice(&header.merkle_root);
50 result.extend_from_slice(&(header.timestamp as u32).to_le_bytes());
51 result.extend_from_slice(&(header.bits as u32).to_le_bytes());
52 result.extend_from_slice(&(header.nonce as u32).to_le_bytes());
53 assert_eq!(result.len(), 80);
54 result
55}
56
57pub fn deserialize_block_header(data: &[u8]) -> Result<BlockHeader> {
59 if data.len() < 80 {
60 return Err(ConsensusError::Serialization(Cow::Owned(
61 BlockParseError::InsufficientBytes.to_string(),
62 )));
63 }
64
65 let mut offset = 0;
66
67 let version = i32::from_le_bytes([
68 data[offset],
69 data[offset + 1],
70 data[offset + 2],
71 data[offset + 3],
72 ]) as i64;
73 offset += 4;
74
75 let mut prev_block_hash = [0u8; 32];
76 prev_block_hash.copy_from_slice(&data[offset..offset + 32]);
77 offset += 32;
78
79 let mut merkle_root = [0u8; 32];
80 merkle_root.copy_from_slice(&data[offset..offset + 32]);
81 offset += 32;
82
83 let timestamp = u32::from_le_bytes([
84 data[offset],
85 data[offset + 1],
86 data[offset + 2],
87 data[offset + 3],
88 ]) as u64;
89 offset += 4;
90
91 let bits = u32::from_le_bytes([
92 data[offset],
93 data[offset + 1],
94 data[offset + 2],
95 data[offset + 3],
96 ]) as u64;
97 offset += 4;
98
99 let nonce = u32::from_le_bytes([
100 data[offset],
101 data[offset + 1],
102 data[offset + 2],
103 data[offset + 3],
104 ]) as u64;
105
106 Ok(BlockHeader {
107 version,
108 prev_block_hash,
109 merkle_root,
110 timestamp,
111 bits,
112 nonce,
113 })
114}
115
116pub fn deserialize_block_with_witnesses(data: &[u8]) -> Result<(Block, Vec<Vec<Witness>>)> {
118 if data.len() < 80 {
119 return Err(ConsensusError::Serialization(Cow::Owned(
120 BlockParseError::InsufficientBytes.to_string(),
121 )));
122 }
123
124 let mut offset = 0;
125
126 let header = deserialize_block_header(&data[offset..offset + 80])?;
127 offset += 80;
128
129 let (tx_count, varint_len) = decode_varint(&data[offset..])?;
130 offset += varint_len;
131
132 if tx_count == 0 {
133 return Err(ConsensusError::Serialization(Cow::Owned(
134 BlockParseError::InvalidTransactionCount.to_string(),
135 )));
136 }
137
138 let mut transactions = Vec::new();
139 let mut all_witnesses: Vec<Vec<Witness>> = Vec::new();
140
141 for _ in 0..tx_count {
142 let (tx, input_witnesses, bytes_consumed) =
143 deserialize_transaction_with_witness(&data[offset..])?;
144 offset += bytes_consumed;
145 transactions.push(tx);
146 all_witnesses.push(input_witnesses);
147 }
148
149 while all_witnesses.len() < transactions.len() {
150 all_witnesses.push(Vec::new());
151 }
152
153 Ok((
154 Block::from_parts(header, transactions.into_boxed_slice()),
155 all_witnesses,
156 ))
157}
158
159pub fn output_script_pubkey_slices_from_wire_block(data: &[u8]) -> Result<Vec<&[u8]>> {
161 if data.len() < 80 {
162 return Err(ConsensusError::Serialization(Cow::Owned(
163 BlockParseError::InsufficientBytes.to_string(),
164 )));
165 }
166 let mut offset = 80;
167 let (tx_count, varint_len) = decode_varint(&data[offset..])?;
168 offset += varint_len;
169 if tx_count == 0 {
170 return Err(ConsensusError::Serialization(Cow::Owned(
171 BlockParseError::InvalidTransactionCount.to_string(),
172 )));
173 }
174 let mut scripts = Vec::new();
175 for _ in 0..tx_count {
176 let (tx_scripts, consumed) =
177 super::transaction::output_script_pubkey_slices_from_wire_tx(&data[offset..])?;
178 offset += consumed;
179 scripts.extend(tx_scripts);
180 }
181 Ok(scripts)
182}
183
184pub fn serialize_block_with_witnesses(
186 block: &Block,
187 witnesses: &[Vec<Witness>],
188 include_witness: bool,
189) -> Vec<u8> {
190 let mut result = Vec::new();
191
192 result.extend_from_slice(&serialize_block_header(&block.header));
193 result.extend_from_slice(&encode_varint(block.transactions.len() as u64));
194
195 for (i, tx) in block.transactions.iter().enumerate() {
198 let tx_witnesses = witnesses.get(i);
199 let use_witness = include_witness
200 && tx_witnesses.is_some_and(|stacks| {
201 stacks.len() == tx.inputs.len() && stacks.iter().any(|stack| !stack.is_empty())
202 });
203 if use_witness {
204 result.extend_from_slice(
205 &serialize_transaction_with_witness(tx, tx_witnesses.unwrap())
206 .expect("witness count matches inputs"),
207 );
208 } else {
209 result.extend_from_slice(&serialize_transaction(tx));
210 }
211 }
212
213 result
214}
215
216pub fn serialize_block(block: &Block) -> Vec<u8> {
218 let witnesses: Vec<Vec<Witness>> = block.transactions.iter().map(|_| Vec::new()).collect();
219 serialize_block_with_witnesses(block, &witnesses, false)
220}
221
222pub fn validate_block_serialized_size(
224 block: &Block,
225 witnesses: &[Vec<Witness>],
226 include_witness: bool,
227 provided_size: usize,
228) -> bool {
229 let serialized = serialize_block_with_witnesses(block, witnesses, include_witness);
230 serialized.len() == provided_size
231}
232
233#[cfg(test)]
234mod output_script_tests {
235 use super::*;
236 use crate::types::{BlockHeader, OutPoint, Transaction, TransactionInput, TransactionOutput};
237
238 #[test]
239 fn wire_output_scripts_match_the_decoded_block() {
240 let legacy = Transaction {
241 version: 1,
242 inputs: crate::tx_inputs![TransactionInput {
243 prevout: OutPoint {
244 hash: [1; 32],
245 index: 0,
246 },
247 script_sig: vec![0x51, 0x52],
248 sequence: 0xffff_ffff,
249 }],
250 outputs: crate::tx_outputs![
251 TransactionOutput {
252 value: 50,
253 script_pubkey: vec![0x76, 0xa9],
254 },
255 TransactionOutput {
256 value: 1,
257 script_pubkey: vec![],
258 },
259 ],
260 lock_time: 0,
261 };
262 let segwit = Transaction {
263 version: 2,
264 inputs: crate::tx_inputs![TransactionInput {
265 prevout: OutPoint {
266 hash: [2; 32],
267 index: 1,
268 },
269 script_sig: vec![],
270 sequence: 0xffff_fffe,
271 }],
272 outputs: crate::tx_outputs![TransactionOutput {
273 value: 25,
274 script_pubkey: vec![0x00, 0x14, 0xaa],
275 }],
276 lock_time: 7,
277 };
278 let header = BlockHeader {
279 version: 1,
280 prev_block_hash: [0; 32],
281 merkle_root: [3; 32],
282 timestamp: 1_231_006_505,
283 bits: 0x1d00_ffff,
284 nonce: 9,
285 };
286 let block =
287 crate::types::Block::from_parts(header, vec![legacy, segwit].into_boxed_slice());
288 let witnesses = vec![vec![Vec::new()], vec![vec![vec![0x30, 0x01]]]];
289 let bytes = serialize_block_with_witnesses(&block, &witnesses, true);
290 let scripts = output_script_pubkey_slices_from_wire_block(&bytes).unwrap();
291 let (decoded, _) = deserialize_block_with_witnesses(&bytes).unwrap();
292 let expected: Vec<Vec<u8>> = decoded
293 .transactions
294 .iter()
295 .flat_map(|tx| tx.outputs.iter().map(|output| output.script_pubkey.clone()))
296 .collect();
297 assert_eq!(
298 scripts,
299 expected.iter().map(Vec::as_slice).collect::<Vec<_>>()
300 );
301 assert_eq!(
302 scripts,
303 vec![&[0x76, 0xa9][..], &[][..], &[0x00, 0x14, 0xaa][..]]
304 );
305 }
306}