1use chia_sha2::Sha256;
2use chia_streamable_macro::streamable;
3
4use crate::Bytes;
5use crate::Bytes32;
6use crate::EndOfSubSlotBundle;
7use crate::Program;
8use crate::RewardChainBlockUnfinished;
9use crate::VDFProof;
10use crate::{Foliage, FoliageTransactionBlock, TransactionsInfo};
11use chia_traits::Streamable;
12use chia_traits::chia_error::{Error, Result};
13use std::io::Cursor;
14
15#[streamable(no_streamable)]
21pub struct UnfinishedBlock {
22 finished_sub_slots: Vec<EndOfSubSlotBundle>, reward_chain_block: RewardChainBlockUnfinished, challenge_chain_sp_proof: Option<VDFProof>, reward_chain_sp_proof: Option<VDFProof>, foliage: Foliage, foliage_transaction_block: Option<FoliageTransactionBlock>, transactions_info: Option<TransactionsInfo>, transactions_generator: Option<Program>, transactions_generator_ref_list: Vec<u32>, transactions_generator_buffer: Option<Vec<u8>>,
36
37 version: u8,
40}
41
42impl Streamable for UnfinishedBlock {
43 fn update_digest(&self, digest: &mut Sha256) {
44 self.finished_sub_slots.update_digest(digest);
45 self.reward_chain_block.update_digest(digest);
46 self.challenge_chain_sp_proof.update_digest(digest);
47 self.reward_chain_sp_proof.update_digest(digest);
48 self.foliage.update_digest(digest);
49 self.foliage_transaction_block.update_digest(digest);
50 self.transactions_info.update_digest(digest);
51
52 if self.version == 0 {
53 self.transactions_generator.update_digest(digest);
54 self.transactions_generator_ref_list.update_digest(digest);
55 } else if self.version == 1 {
56 match &self.transactions_generator_buffer {
57 None => {
58 0b10_u8.update_digest(digest);
59 }
60 Some(buf) => {
61 0b11_u8.update_digest(digest);
62 (buf.len() as u32).update_digest(digest);
63 digest.update(buf);
64 }
65 }
66 } else {
67 digest.update(b"invalid-unfinished-block-version");
68 }
69 }
70
71 fn stream(&self, out: &mut Vec<u8>) -> Result<()> {
72 self.finished_sub_slots.stream(out)?;
73 self.reward_chain_block.stream(out)?;
74 self.challenge_chain_sp_proof.stream(out)?;
75 self.reward_chain_sp_proof.stream(out)?;
76 self.foliage.stream(out)?;
77 self.foliage_transaction_block.stream(out)?;
78 self.transactions_info.stream(out)?;
79
80 if self.version == 0 {
81 self.transactions_generator.stream(out)?;
82 self.transactions_generator_ref_list.stream(out)?;
83 } else if self.version == 1 {
84 match &self.transactions_generator_buffer {
85 None => {
86 0b10_u8.stream(out)?;
87 }
88 Some(buf) => {
89 0b11_u8.stream(out)?;
90 (buf.len() as u32).stream(out)?;
91 out.extend_from_slice(buf);
92 }
93 }
94 } else {
95 return Err(Error::InvalidUnfinishedBlock);
96 }
97 Ok(())
98 }
99
100 fn parse<const TRUSTED: bool>(input: &mut Cursor<&[u8]>) -> Result<Self> {
101 let finished_sub_slots = <Vec<EndOfSubSlotBundle> as Streamable>::parse::<TRUSTED>(input)?;
102 let reward_chain_block =
103 <RewardChainBlockUnfinished as Streamable>::parse::<TRUSTED>(input)?;
104 let challenge_chain_sp_proof = <Option<VDFProof> as Streamable>::parse::<TRUSTED>(input)?;
105 let reward_chain_sp_proof = <Option<VDFProof> as Streamable>::parse::<TRUSTED>(input)?;
106 let foliage = <Foliage as Streamable>::parse::<TRUSTED>(input)?;
107 let foliage_transaction_block =
108 <Option<FoliageTransactionBlock> as Streamable>::parse::<TRUSTED>(input)?;
109 let transactions_info = <Option<TransactionsInfo> as Streamable>::parse::<TRUSTED>(input)?;
110
111 let prefix = <u8 as Streamable>::parse::<TRUSTED>(input)?;
112 let version = prefix >> 1;
113 let has_generator = (prefix & 1) != 0;
114
115 if version == 0 {
116 let transactions_generator = if has_generator {
117 Some(<Program as Streamable>::parse::<TRUSTED>(input)?)
118 } else {
119 None
120 };
121 let transactions_generator_ref_list =
122 <Vec<u32> as Streamable>::parse::<TRUSTED>(input)?;
123
124 Ok(UnfinishedBlock {
125 finished_sub_slots,
126 reward_chain_block,
127 challenge_chain_sp_proof,
128 reward_chain_sp_proof,
129 foliage,
130 foliage_transaction_block,
131 transactions_info,
132 transactions_generator,
133 transactions_generator_ref_list,
134 transactions_generator_buffer: None,
135 version,
136 })
137 } else if version == 1 {
138 let transactions_generator_buffer = if has_generator {
139 let bytes = <Bytes as Streamable>::parse::<TRUSTED>(input)?;
140 Some(bytes.into_inner())
141 } else {
142 None
143 };
144
145 Ok(UnfinishedBlock {
146 finished_sub_slots,
147 reward_chain_block,
148 challenge_chain_sp_proof,
149 reward_chain_sp_proof,
150 foliage,
151 foliage_transaction_block,
152 transactions_info,
153 transactions_generator: None,
154 transactions_generator_ref_list: vec![],
155 transactions_generator_buffer,
156 version,
157 })
158 } else {
159 Err(Error::InvalidUnfinishedBlock)
160 }
161 }
162}
163
164impl UnfinishedBlock {
165 pub fn prev_header_hash(&self) -> Bytes32 {
166 self.foliage.prev_block_hash
167 }
168
169 pub fn partial_hash(&self) -> Bytes32 {
170 self.reward_chain_block.hash().into()
171 }
172
173 pub fn is_transaction_block(&self) -> bool {
174 self.foliage.foliage_transaction_block_hash.is_some()
175 }
176
177 pub fn total_iters(&self) -> u128 {
178 self.reward_chain_block.total_iters
179 }
180}
181
182#[cfg(feature = "py-bindings")]
183use chia_traits::ChiaToPython;
184#[cfg(feature = "py-bindings")]
185use pyo3::prelude::*;
186
187#[cfg(feature = "py-bindings")]
188#[pymethods]
189impl UnfinishedBlock {
190 #[getter]
191 #[pyo3(name = "prev_header_hash")]
192 fn py_prev_header_hash(&self) -> Bytes32 {
193 self.prev_header_hash()
194 }
195
196 #[getter]
197 #[pyo3(name = "partial_hash")]
198 fn py_partial_hash(&self) -> Bytes32 {
199 self.partial_hash()
200 }
201
202 #[pyo3(name = "is_transaction_block")]
203 fn py_is_transaction_block(&self) -> bool {
204 self.is_transaction_block()
205 }
206
207 #[getter]
208 #[pyo3(name = "total_iters")]
209 fn py_total_iters<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
210 ChiaToPython::to_python(&self.total_iters(), py)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::{FoliageBlockData, PoolTarget, ProofOfSpace};
218 use chia_bls::{G1Element, G2Element};
219 use rstest::rstest;
220
221 fn make_proof_of_space() -> ProofOfSpace {
222 ProofOfSpace::new(
223 Bytes32::default(),
224 Some(G1Element::default()),
225 None,
226 G1Element::default(),
227 0,
228 0,
229 0,
230 0,
231 32,
232 Bytes::from(vec![0x80]),
233 )
234 }
235
236 fn make_reward_chain_block_unfinished() -> RewardChainBlockUnfinished {
237 RewardChainBlockUnfinished::new(
238 1,
239 0,
240 Bytes32::default(),
241 make_proof_of_space(),
242 None,
243 G2Element::default(),
244 None,
245 G2Element::default(),
246 )
247 }
248
249 fn make_foliage() -> Foliage {
250 let pool_target = PoolTarget::new(Bytes32::default(), 0);
251 let foliage_block_data = FoliageBlockData::new(
252 Bytes32::default(),
253 pool_target,
254 Some(G2Element::default()),
255 Bytes32::default(),
256 Bytes32::default(),
257 );
258 Foliage::new(
259 Bytes32::default(),
260 Bytes32::default(),
261 foliage_block_data,
262 G2Element::default(),
263 None,
264 None,
265 )
266 }
267
268 fn make_v0_block(generator: Option<Program>, ref_list: Vec<u32>) -> UnfinishedBlock {
269 UnfinishedBlock::new(
270 vec![],
271 make_reward_chain_block_unfinished(),
272 None,
273 None,
274 make_foliage(),
275 None,
276 None,
277 generator,
278 ref_list,
279 None,
280 0,
281 )
282 }
283
284 fn make_v1_block(buffer: Option<Vec<u8>>) -> UnfinishedBlock {
285 UnfinishedBlock::new(
286 vec![],
287 make_reward_chain_block_unfinished(),
288 None,
289 None,
290 make_foliage(),
291 None,
292 None,
293 None,
294 vec![],
295 buffer,
296 1,
297 )
298 }
299
300 #[test]
301 fn v0_no_generator_roundtrip() {
302 let block = make_v0_block(None, vec![]);
303 let buf = block.to_bytes().unwrap();
304 let block2 = UnfinishedBlock::parse::<false>(&mut Cursor::new(&buf)).unwrap();
305
306 assert_eq!(block2.version, 0);
307 assert!(block2.transactions_generator.is_none());
308 assert!(block2.transactions_generator_ref_list.is_empty());
309 assert!(block2.transactions_generator_buffer.is_none());
310 assert_eq!(block2.to_bytes().unwrap(), buf);
311 }
312
313 #[test]
314 fn v0_with_generator_roundtrip() {
315 let generator = Program::from(vec![0xff, 0x01, 0x80]);
316 let block = make_v0_block(Some(generator.clone()), vec![100, 200]);
317 let buf = block.to_bytes().unwrap();
318 let block2 = UnfinishedBlock::parse::<false>(&mut Cursor::new(&buf)).unwrap();
319
320 assert_eq!(block2.version, 0);
321 assert_eq!(
322 block2.transactions_generator.as_ref().unwrap().as_ref(),
323 generator.as_ref()
324 );
325 assert_eq!(block2.transactions_generator_ref_list, vec![100, 200]);
326 assert!(block2.transactions_generator_buffer.is_none());
327 assert_eq!(block2.to_bytes().unwrap(), buf);
328 }
329
330 #[test]
331 fn v1_no_generator_roundtrip() {
332 let block = make_v1_block(None);
333 let buf = block.to_bytes().unwrap();
334 let block2 = UnfinishedBlock::parse::<false>(&mut Cursor::new(&buf)).unwrap();
335
336 assert_eq!(block2.version, 1);
337 assert!(block2.transactions_generator.is_none());
338 assert!(block2.transactions_generator_ref_list.is_empty());
339 assert!(block2.transactions_generator_buffer.is_none());
340 assert_eq!(block2.to_bytes().unwrap(), buf);
341 }
342
343 #[test]
344 fn v1_with_buffer_roundtrip() {
345 let raw = vec![0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe];
346 let block = make_v1_block(Some(raw.clone()));
347 let buf = block.to_bytes().unwrap();
348 let block2 = UnfinishedBlock::parse::<false>(&mut Cursor::new(&buf)).unwrap();
349
350 assert_eq!(block2.version, 1);
351 assert!(block2.transactions_generator.is_none());
352 assert!(block2.transactions_generator_ref_list.is_empty());
353 assert_eq!(block2.transactions_generator_buffer.as_ref().unwrap(), &raw);
354 assert_eq!(block2.to_bytes().unwrap(), buf);
355 }
356
357 #[rstest]
358 #[case::v0(0, 0b00, 0b01)]
359 #[case::v1(1, 0b10, 0b11)]
360 fn prefix_byte_encoding(
361 #[case] version: u8,
362 #[case] expected_none: u8,
363 #[case] expected_some: u8,
364 ) {
365 let (buf_none, buf_some) = if version == 0 {
366 (
367 make_v0_block(None, vec![]).to_bytes().unwrap(),
368 make_v0_block(Some(Program::from(vec![0x80])), vec![])
369 .to_bytes()
370 .unwrap(),
371 )
372 } else {
373 (
374 make_v1_block(None).to_bytes().unwrap(),
375 make_v1_block(Some(vec![0x80])).to_bytes().unwrap(),
376 )
377 };
378
379 let prefix_offset = buf_none
380 .iter()
381 .zip(buf_some.iter())
382 .position(|(a, b)| a != b)
383 .unwrap();
384
385 assert_eq!(buf_none[prefix_offset], expected_none);
386 assert_eq!(buf_some[prefix_offset], expected_some);
387 }
388
389 #[test]
390 fn v1_generator_has_length_prefix() {
391 let raw = vec![0xca, 0xfe, 0xba, 0xbe];
392 let block = make_v1_block(Some(raw.clone()));
393 let buf = block.to_bytes().unwrap();
394
395 let block_empty = make_v1_block(None);
396 let buf_empty = block_empty.to_bytes().unwrap();
397
398 let prefix_offset = buf
399 .iter()
400 .zip(buf_empty.iter())
401 .position(|(a, b)| a != b)
402 .unwrap();
403
404 assert_eq!(buf[prefix_offset], 0b11);
405 let len = u32::from_be_bytes(
406 buf[prefix_offset + 1..prefix_offset + 5]
407 .try_into()
408 .unwrap(),
409 );
410 assert_eq!(len as usize, raw.len());
411 assert_eq!(&buf[prefix_offset + 5..prefix_offset + 5 + raw.len()], &raw);
412 assert_eq!(prefix_offset + 5 + raw.len(), buf.len());
413 }
414
415 #[test]
416 fn v1_omits_ref_list() {
417 let block_v0 = make_v0_block(Some(Program::from(vec![0x80])), vec![42]);
418 let buf_v0 = block_v0.to_bytes().unwrap();
419
420 let block_v1 = make_v1_block(Some(vec![0x80]));
421 let buf_v1 = block_v1.to_bytes().unwrap();
422
423 assert!(buf_v1.len() < buf_v0.len());
424 }
425
426 #[test]
427 fn v1_unvalidated_buffer_roundtrips() {
428 let garbage = vec![0xff; 1000];
429 let block = make_v1_block(Some(garbage.clone()));
430 let buf = block.to_bytes().unwrap();
431 let block2 = UnfinishedBlock::parse::<false>(&mut Cursor::new(&buf)).unwrap();
432 assert_eq!(block2.transactions_generator_buffer.unwrap(), garbage);
433 }
434
435 #[test]
441 fn high_prefix_bits_rejected() {
442 let v0_none = make_v0_block(None, vec![]).to_bytes().unwrap();
443 let v0_some = make_v0_block(Some(Program::from(vec![0x80])), vec![])
444 .to_bytes()
445 .unwrap();
446 let offset = v0_none
447 .iter()
448 .zip(v0_some.iter())
449 .position(|(a, b)| a != b)
450 .unwrap();
451 assert_eq!(v0_none[offset], 0b00);
452
453 let v1_none = make_v1_block(None).to_bytes().unwrap();
454 assert_eq!(v1_none[offset], 0b10);
455
456 for valid in [&v0_none, &v1_none] {
457 for bit in 2..8u8 {
458 let mut buf = valid.clone();
459 buf[offset] |= 1 << bit;
460 let err = UnfinishedBlock::parse::<false>(&mut Cursor::new(&buf))
461 .expect_err("high prefix bit must be rejected");
462 assert_eq!(err, Error::InvalidUnfinishedBlock);
463 }
464 }
465 }
466}