1use crate::SchedulingProof;
20use alloc::vec::Vec;
21use codec::{Decode, Encode};
22use sp_runtime::traits::Block as BlockT;
23use sp_trie::CompactProof;
24
25const VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX: &[u8] = b"VERSIONEDPBD";
28
29pub(crate) struct PrependBytesInput<'a, I> {
31 prepend: &'a [u8],
32 read: usize,
33 inner: &'a mut I,
34}
35
36impl<'a, I: codec::Input> codec::Input for PrependBytesInput<'a, I> {
37 fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
38 let remaining_compact = self.prepend.len().saturating_sub(self.read);
39 Ok(self.inner.remaining_len()?.map(|len| len.saturating_add(remaining_compact)))
40 }
41
42 fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
43 if into.is_empty() {
44 return Ok(());
45 }
46
47 let remaining_compact = self.prepend.len().saturating_sub(self.read);
48 if remaining_compact > 0 {
49 let to_read = into.len().min(remaining_compact);
50 into[..to_read].copy_from_slice(&self.prepend[self.read..][..to_read]);
51 self.read += to_read;
52
53 if to_read < into.len() {
54 self.inner.read(&mut into[to_read..])
56 } else {
57 Ok(())
59 }
60 } else {
61 self.inner.read(into)
63 }
64 }
65}
66
67#[derive(Clone)]
72pub enum ParachainBlockData<Block> {
73 V0 {
74 block: [Block; 1],
75 proof: CompactProof,
76 },
77 V1 {
78 blocks: Vec<Block>,
79 proof: CompactProof,
80 },
81 V2 {
83 blocks: Vec<Block>,
84 proof: CompactProof,
85 scheduling_proof: SchedulingProof,
86 },
87}
88
89impl<Block: Encode> Encode for ParachainBlockData<Block> {
90 fn encode(&self) -> Vec<u8> {
91 match self {
92 Self::V0 { block, proof } => (&block[0], &proof).encode(),
93 Self::V1 { blocks, proof } => {
94 let mut res = VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX.to_vec();
95 1u8.encode_to(&mut res);
96 blocks.encode_to(&mut res);
97 proof.encode_to(&mut res);
98 res
99 },
100 Self::V2 { blocks, proof, scheduling_proof } => {
101 let mut res = VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX.to_vec();
102 2u8.encode_to(&mut res);
103 blocks.encode_to(&mut res);
104 proof.encode_to(&mut res);
105 scheduling_proof.encode_to(&mut res);
106 res
107 },
108 }
109 }
110}
111
112impl<Block: Decode> Decode for ParachainBlockData<Block> {
113 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
114 let mut prefix = [0u8; VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX.len()];
115 input.read(&mut prefix)?;
116
117 if prefix == VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX {
118 match input.read_byte()? {
119 1 => {
120 let blocks = Vec::<Block>::decode(input)?;
121 let proof = CompactProof::decode(input)?;
122
123 Ok(Self::V1 { blocks, proof })
124 },
125 2 => {
126 let blocks = Vec::<Block>::decode(input)?;
127 let proof = CompactProof::decode(input)?;
128 let scheduling_proof = crate::SchedulingProof::decode(input)?;
129
130 Ok(Self::V2 { blocks, proof, scheduling_proof })
131 },
132 _ => Err("Unknown `ParachainBlockData` version".into()),
133 }
134 } else {
135 let mut input = PrependBytesInput { prepend: &prefix, read: 0, inner: input };
136 let block = Block::decode(&mut input)?;
137 let proof = CompactProof::decode(&mut input)?;
138
139 Ok(Self::V0 { block: [block], proof })
140 }
141 }
142}
143
144impl<Block> ParachainBlockData<Block> {
145 pub fn new(
147 blocks: Vec<Block>,
148 proof: CompactProof,
149 scheduling_proof: Option<SchedulingProof>,
150 ) -> Self {
151 match scheduling_proof {
152 Some(sp) => Self::V2 { blocks, proof, scheduling_proof: sp },
153 None => Self::V1 { blocks, proof },
154 }
155 }
156
157 pub fn blocks(&self) -> &[Block] {
159 match self {
160 Self::V0 { block, .. } => &block[..],
161 Self::V1 { blocks, .. } => &blocks,
162 Self::V2 { blocks, .. } => &blocks,
163 }
164 }
165
166 pub fn blocks_mut(&mut self) -> &mut [Block] {
168 match self {
169 Self::V0 { ref mut block, .. } => block,
170 Self::V1 { ref mut blocks, .. } => blocks,
171 Self::V2 { ref mut blocks, .. } => blocks,
172 }
173 }
174
175 pub fn into_blocks(self) -> Vec<Block> {
177 match self {
178 Self::V0 { block, .. } => block.into_iter().collect(),
179 Self::V1 { blocks, .. } => blocks,
180 Self::V2 { blocks, .. } => blocks,
181 }
182 }
183
184 pub fn proof(&self) -> &CompactProof {
186 match self {
187 Self::V0 { proof, .. } => &proof,
188 Self::V1 { proof, .. } => proof,
189 Self::V2 { proof, .. } => proof,
190 }
191 }
192
193 pub fn into_inner(self) -> (Vec<Block>, CompactProof) {
195 match self {
196 Self::V0 { block, proof } => (block.into_iter().collect(), proof),
197 Self::V1 { blocks, proof } => (blocks, proof),
198 Self::V2 { blocks, proof, .. } => (blocks, proof),
199 }
200 }
201
202 pub fn scheduling_proof(&self) -> Option<&crate::SchedulingProof> {
204 match self {
205 Self::V2 { scheduling_proof, .. } => Some(scheduling_proof),
206 _ => None,
207 }
208 }
209}
210
211impl<Block: BlockT> ParachainBlockData<Block> {
212 pub fn log_size_info(&self) {
214 tracing::info!(
215 target: "cumulus",
216 header_kb = %self.blocks().iter().map(|b| b.header().encoded_size()).sum::<usize>() as f64 / 1024f64,
217 extrinsics_kb = %self.blocks().iter().map(|b| b.extrinsics().encoded_size()).sum::<usize>() as f64 / 1024f64,
218 storage_proof_kb = %self.proof().encoded_size() as f64 / 1024f64,
219 "PoV size",
220 );
221 }
222
223 pub fn as_v0(&self) -> Option<Self> {
227 match self {
228 Self::V0 { .. } => Some(self.clone()),
229 Self::V1 { blocks, proof } => {
230 if blocks.len() != 1 {
231 return None;
232 }
233
234 blocks
235 .first()
236 .map(|block| Self::V0 { block: [block.clone()], proof: proof.clone() })
237 },
238 Self::V2 { blocks, proof, .. } => {
239 if blocks.len() != 1 {
240 return None;
241 }
242
243 blocks
244 .first()
245 .map(|block| Self::V0 { block: [block.clone()], proof: proof.clone() })
246 },
247 }
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use sp_runtime::testing::*;
255
256 #[derive(codec::Encode, codec::Decode, Clone, PartialEq, Debug)]
257 struct ParachainBlockDataV0<B: BlockT> {
258 pub header: B::Header,
260 pub extrinsics: alloc::vec::Vec<B::Extrinsic>,
262 pub storage_proof: sp_trie::CompactProof,
264 }
265
266 type TestExtrinsic = TestXt<MockCallU64, ()>;
267 type TestBlock = Block<TestExtrinsic>;
268
269 #[test]
270 fn decoding_encoding_v0_works() {
271 let v0 = ParachainBlockDataV0::<TestBlock> {
272 header: Header::new_from_number(10),
273 extrinsics: vec![
274 TestExtrinsic::new_bare(MockCallU64(10)),
275 TestExtrinsic::new_bare(MockCallU64(100)),
276 ],
277 storage_proof: CompactProof { encoded_nodes: vec![vec![10u8; 200], vec![20u8; 30]] },
278 };
279
280 let encoded = v0.encode();
281 let decoded = ParachainBlockData::<TestBlock>::decode(&mut &encoded[..]).unwrap();
282
283 match &decoded {
284 ParachainBlockData::V0 { block, proof } => {
285 assert_eq!(v0.header, block[0].header);
286 assert_eq!(v0.extrinsics, block[0].extrinsics);
287 assert_eq!(&v0.storage_proof, proof);
288 },
289 _ => panic!("Invalid decoding"),
290 }
291
292 let encoded = decoded.as_v0().unwrap().encode();
293
294 let decoded = ParachainBlockDataV0::<TestBlock>::decode(&mut &encoded[..]).unwrap();
295 assert_eq!(decoded, v0);
296 }
297
298 #[test]
299 fn decoding_encoding_v1_works() {
300 let v1 = ParachainBlockData::<TestBlock>::V1 {
301 blocks: vec![TestBlock::new(
302 Header::new_from_number(10),
303 vec![
304 TestExtrinsic::new_bare(MockCallU64(10)),
305 TestExtrinsic::new_bare(MockCallU64(100)),
306 ],
307 )],
308 proof: CompactProof { encoded_nodes: vec![vec![10u8; 200], vec![20u8; 30]] },
309 };
310
311 let encoded = v1.encode();
312 let decoded = ParachainBlockData::<TestBlock>::decode(&mut &encoded[..]).unwrap();
313
314 assert_eq!(v1.blocks(), decoded.blocks());
315 assert_eq!(v1.proof(), decoded.proof());
316 }
317
318 fn make_relay_header(number: u32) -> polkadot_primitives::Header {
320 use sp_runtime::traits::Header as _;
321 polkadot_primitives::Header::new(
322 number,
323 polkadot_core_primitives::Hash::repeat_byte(1),
324 polkadot_core_primitives::Hash::repeat_byte(2),
325 polkadot_core_primitives::Hash::repeat_byte(3),
326 Default::default(),
327 )
328 }
329
330 #[test]
331 fn decoding_encoding_v2_works() {
332 let scheduling_proof = crate::SchedulingProof {
333 header_chain: vec![make_relay_header(5)],
334 internal_scheduling_parent_header: make_relay_header(4),
335 signed_scheduling_info: None,
336 };
337
338 let v2 = ParachainBlockData::<TestBlock>::V2 {
339 blocks: vec![TestBlock::new(
340 Header::new_from_number(10),
341 vec![
342 TestExtrinsic::new_bare(MockCallU64(10)),
343 TestExtrinsic::new_bare(MockCallU64(100)),
344 ],
345 )],
346 proof: CompactProof { encoded_nodes: vec![vec![10u8; 200], vec![20u8; 30]] },
347 scheduling_proof: scheduling_proof.clone(),
348 };
349
350 let encoded = v2.encode();
351 let decoded = ParachainBlockData::<TestBlock>::decode(&mut &encoded[..]).unwrap();
352
353 assert_eq!(v2.blocks(), decoded.blocks());
354 assert_eq!(v2.proof(), decoded.proof());
355 assert_eq!(v2.scheduling_proof(), decoded.scheduling_proof());
356
357 assert!(decoded.scheduling_proof().is_some());
359 assert_eq!(decoded.scheduling_proof().unwrap().header_chain.len(), 1);
360 }
361
362 #[test]
363 fn v2_scheduling_proof_accessor_returns_none_for_v0_v1() {
364 let v0 = ParachainBlockData::<TestBlock>::V0 {
366 block: [TestBlock::new(Header::new_from_number(1), vec![])],
367 proof: CompactProof { encoded_nodes: vec![] },
368 };
369 assert!(v0.scheduling_proof().is_none());
370
371 let v1 = ParachainBlockData::<TestBlock>::V1 {
373 blocks: vec![TestBlock::new(Header::new_from_number(1), vec![])],
374 proof: CompactProof { encoded_nodes: vec![] },
375 };
376 assert!(v1.scheduling_proof().is_none());
377 }
378
379 #[test]
380 fn v2_into_inner_drops_scheduling_proof() {
381 let scheduling_proof = crate::SchedulingProof {
382 header_chain: vec![make_relay_header(5)],
383 internal_scheduling_parent_header: make_relay_header(4),
384 signed_scheduling_info: None,
385 };
386
387 let v2 = ParachainBlockData::<TestBlock>::V2 {
388 blocks: vec![TestBlock::new(Header::new_from_number(10), vec![])],
389 proof: CompactProof { encoded_nodes: vec![vec![1u8; 10]] },
390 scheduling_proof,
391 };
392
393 let (blocks, proof) = v2.into_inner();
394 assert_eq!(blocks.len(), 1);
395 assert_eq!(proof.encoded_nodes.len(), 1);
396 }
397
398 #[test]
399 fn v2_as_v0_works_with_single_block() {
400 let scheduling_proof = crate::SchedulingProof {
401 header_chain: vec![make_relay_header(5)],
402 internal_scheduling_parent_header: make_relay_header(4),
403 signed_scheduling_info: None,
404 };
405
406 let v2_single = ParachainBlockData::<TestBlock>::V2 {
408 blocks: vec![TestBlock::new(Header::new_from_number(10), vec![])],
409 proof: CompactProof { encoded_nodes: vec![] },
410 scheduling_proof: scheduling_proof.clone(),
411 };
412 assert!(v2_single.as_v0().is_some());
413
414 let v2_multi = ParachainBlockData::<TestBlock>::V2 {
416 blocks: vec![
417 TestBlock::new(Header::new_from_number(10), vec![]),
418 TestBlock::new(Header::new_from_number(11), vec![]),
419 ],
420 proof: CompactProof { encoded_nodes: vec![] },
421 scheduling_proof,
422 };
423 assert!(v2_multi.as_v0().is_none());
424 }
425}