1use alloc::vec::Vec;
20use codec::{Decode, Encode};
21use sp_runtime::traits::Block as BlockT;
22use sp_trie::CompactProof;
23
24const VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX: &[u8] = b"VERSIONEDPBD";
27
28pub(crate) struct PrependBytesInput<'a, I> {
30 prepend: &'a [u8],
31 read: usize,
32 inner: &'a mut I,
33}
34
35impl<'a, I: codec::Input> codec::Input for PrependBytesInput<'a, I> {
36 fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
37 let remaining_compact = self.prepend.len().saturating_sub(self.read);
38 Ok(self.inner.remaining_len()?.map(|len| len.saturating_add(remaining_compact)))
39 }
40
41 fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
42 if into.is_empty() {
43 return Ok(());
44 }
45
46 let remaining_compact = self.prepend.len().saturating_sub(self.read);
47 if remaining_compact > 0 {
48 let to_read = into.len().min(remaining_compact);
49 into[..to_read].copy_from_slice(&self.prepend[self.read..][..to_read]);
50 self.read += to_read;
51
52 if to_read < into.len() {
53 self.inner.read(&mut into[to_read..])
55 } else {
56 Ok(())
58 }
59 } else {
60 self.inner.read(into)
62 }
63 }
64}
65
66#[derive(Clone)]
71pub enum ParachainBlockData<Block: BlockT> {
72 V0 { block: [Block; 1], proof: CompactProof },
73 V1 { blocks: Vec<Block>, proof: CompactProof },
74}
75
76impl<Block: BlockT> Encode for ParachainBlockData<Block> {
77 fn encode(&self) -> Vec<u8> {
78 match self {
79 Self::V0 { block, proof } =>
80 (block[0].header(), block[0].extrinsics(), &proof).encode(),
81 Self::V1 { blocks, proof } => {
82 let mut res = VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX.to_vec();
83 1u8.encode_to(&mut res);
84 blocks.encode_to(&mut res);
85 proof.encode_to(&mut res);
86 res
87 },
88 }
89 }
90}
91
92impl<Block: BlockT> Decode for ParachainBlockData<Block> {
93 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
94 let mut prefix = [0u8; VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX.len()];
95 input.read(&mut prefix)?;
96
97 if prefix == VERSIONED_PARACHAIN_BLOCK_DATA_PREFIX {
98 match input.read_byte()? {
99 1 => {
100 let blocks = Vec::<Block>::decode(input)?;
101 let proof = CompactProof::decode(input)?;
102
103 Ok(Self::V1 { blocks, proof })
104 },
105 _ => Err("Unknown `ParachainBlockData` version".into()),
106 }
107 } else {
108 let mut input = PrependBytesInput { prepend: &prefix, read: 0, inner: input };
109 let header = Block::Header::decode(&mut input)?;
110 let extrinsics = Vec::<Block::Extrinsic>::decode(&mut input)?;
111 let proof = CompactProof::decode(&mut input)?;
112
113 Ok(Self::V0 { block: [Block::new(header, extrinsics)], proof })
114 }
115 }
116}
117
118impl<Block: BlockT> ParachainBlockData<Block> {
119 pub fn new(blocks: Vec<Block>, proof: CompactProof) -> Self {
121 Self::V1 { blocks, proof }
122 }
123
124 pub fn blocks(&self) -> &[Block] {
126 match self {
127 Self::V0 { block, .. } => &block[..],
128 Self::V1 { blocks, .. } => &blocks,
129 }
130 }
131
132 pub fn blocks_mut(&mut self) -> &mut [Block] {
134 match self {
135 Self::V0 { ref mut block, .. } => block,
136 Self::V1 { ref mut blocks, .. } => blocks,
137 }
138 }
139
140 pub fn into_blocks(self) -> Vec<Block> {
142 match self {
143 Self::V0 { block, .. } => block.into_iter().collect(),
144 Self::V1 { blocks, .. } => blocks,
145 }
146 }
147
148 pub fn proof(&self) -> &CompactProof {
150 match self {
151 Self::V0 { proof, .. } => &proof,
152 Self::V1 { proof, .. } => proof,
153 }
154 }
155
156 pub fn into_inner(self) -> (Vec<Block>, CompactProof) {
158 match self {
159 Self::V0 { block, proof } => (block.into_iter().collect(), proof),
160 Self::V1 { blocks, proof } => (blocks, proof),
161 }
162 }
163
164 pub fn log_size_info(&self) {
166 tracing::info!(
167 target: "cumulus",
168 header_kb = %self.blocks().iter().map(|b| b.header().encoded_size()).sum::<usize>() as f64 / 1024f64,
169 extrinsics_kb = %self.blocks().iter().map(|b| b.extrinsics().encoded_size()).sum::<usize>() as f64 / 1024f64,
170 storage_proof_kb = %self.proof().encoded_size() as f64 / 1024f64,
171 "PoV size",
172 );
173 }
174
175 pub fn as_v0(&self) -> Option<Self> {
179 match self {
180 Self::V0 { .. } => Some(self.clone()),
181 Self::V1 { blocks, proof } => {
182 if blocks.len() != 1 {
183 return None
184 }
185
186 blocks
187 .first()
188 .map(|block| Self::V0 { block: [block.clone()], proof: proof.clone() })
189 },
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use sp_runtime::testing::*;
198
199 #[derive(codec::Encode, codec::Decode, Clone, PartialEq, Debug)]
200 struct ParachainBlockDataV0<B: BlockT> {
201 pub header: B::Header,
203 pub extrinsics: alloc::vec::Vec<B::Extrinsic>,
205 pub storage_proof: sp_trie::CompactProof,
207 }
208
209 type TestExtrinsic = TestXt<MockCallU64, ()>;
210 type TestBlock = Block<TestExtrinsic>;
211
212 #[test]
213 fn decoding_encoding_v0_works() {
214 let v0 = ParachainBlockDataV0::<TestBlock> {
215 header: Header::new_from_number(10),
216 extrinsics: vec![
217 TestExtrinsic::new_bare(MockCallU64(10)),
218 TestExtrinsic::new_bare(MockCallU64(100)),
219 ],
220 storage_proof: CompactProof { encoded_nodes: vec![vec![10u8; 200], vec![20u8; 30]] },
221 };
222
223 let encoded = v0.encode();
224 let decoded = ParachainBlockData::<TestBlock>::decode(&mut &encoded[..]).unwrap();
225
226 match &decoded {
227 ParachainBlockData::V0 { block, proof } => {
228 assert_eq!(v0.header, block[0].header);
229 assert_eq!(v0.extrinsics, block[0].extrinsics);
230 assert_eq!(&v0.storage_proof, proof);
231 },
232 _ => panic!("Invalid decoding"),
233 }
234
235 let encoded = decoded.as_v0().unwrap().encode();
236
237 let decoded = ParachainBlockDataV0::<TestBlock>::decode(&mut &encoded[..]).unwrap();
238 assert_eq!(decoded, v0);
239 }
240
241 #[test]
242 fn decoding_encoding_v1_works() {
243 let v1 = ParachainBlockData::<TestBlock>::V1 {
244 blocks: vec![TestBlock::new(
245 Header::new_from_number(10),
246 vec![
247 TestExtrinsic::new_bare(MockCallU64(10)),
248 TestExtrinsic::new_bare(MockCallU64(100)),
249 ],
250 )],
251 proof: CompactProof { encoded_nodes: vec![vec![10u8; 200], vec![20u8; 30]] },
252 };
253
254 let encoded = v1.encode();
255 let decoded = ParachainBlockData::<TestBlock>::decode(&mut &encoded[..]).unwrap();
256
257 assert_eq!(v1.blocks(), decoded.blocks());
258 assert_eq!(v1.proof(), decoded.proof());
259 }
260}