1use crate::utils::codec::{Decode, Encode};
7use crate::{
8 Hashable, PrimitiveError, PrimitiveResult, Validate,
9 crypto::hashing::{Blake3Hasher, Hasher},
10 header::Header,
11 transaction::Transaction,
12 types::{AccountId, BlockNumber, Gas, Hash, ServiceId, Signature, TimeSlot, Timestamp, Weight},
13};
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
18pub struct Block {
19 pub header: Header,
21 pub body: BlockBody,
23}
24
25impl Block {
26 pub fn new(header: Header, body: BlockBody) -> Self {
28 Self { header, body }
29 }
30
31 pub fn number(&self) -> BlockNumber {
33 self.header.number
34 }
35
36 pub fn hash(&self) -> Hash {
38 self.header.hash()
39 }
40
41 pub fn parent_hash(&self) -> Hash {
43 self.header.parent_hash
44 }
45
46 pub fn timestamp(&self) -> Timestamp {
48 self.header.timestamp
49 }
50
51 pub fn weight(&self) -> Weight {
53 self.body.weight()
54 }
55
56 pub fn is_genesis(&self) -> bool {
58 self.header.number == BlockNumber(0)
59 }
60
61 pub fn transactions(&self) -> &[Transaction] {
63 &self.body.transactions
64 }
65
66 pub fn size(&self) -> usize {
68 self.header.encoded_size() + self.body.encoded_size()
69 }
70}
71
72impl Validate for Block {
73 fn validate(&self) -> PrimitiveResult<()> {
74 self.header.validate()?;
76
77 self.body.validate()?;
79
80 if self.header.extrinsics_root != self.body.hash() {
82 return Err(PrimitiveError::InvalidBlock(
83 "Header extrinsics root doesn't match computed body hash".to_string(),
84 ));
85 }
86
87 if self.size() > crate::constants::MAX_BLOCK_SIZE as usize {
89 return Err(PrimitiveError::SizeLimit(
90 "Block exceeds maximum size limit".to_string(),
91 ));
92 }
93
94 Ok(())
95 }
96}
97
98impl Hashable for Block {
99 fn hash(&self) -> Hash {
100 self.header.hash()
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
106pub struct BlockBody {
107 pub transactions: Vec<Transaction>,
109 pub availability: AvailabilityData,
111 pub work_reports: Vec<WorkReport>,
113 pub guarantees: Vec<Guarantee>,
115 pub preimages: Vec<Preimage>,
117}
118
119impl BlockBody {
120 pub fn new() -> Self {
122 Self {
123 transactions: Vec::new(),
124 availability: AvailabilityData::new(),
125 work_reports: Vec::new(),
126 guarantees: Vec::new(),
127 preimages: Vec::new(),
128 }
129 }
130
131 pub fn with_transactions(transactions: Vec<Transaction>) -> Self {
133 Self {
134 transactions,
135 availability: AvailabilityData::new(),
136 work_reports: Vec::new(),
137 guarantees: Vec::new(),
138 preimages: Vec::new(),
139 }
140 }
141
142 pub fn add_transaction(&mut self, transaction: Transaction) {
144 self.transactions.push(transaction);
145 }
146
147 pub fn weight(&self) -> Weight {
149 self.transactions
150 .iter()
151 .map(|tx| tx.weight())
152 .sum::<Weight>()
153 + Weight(self.work_reports.len() as u64) * 1000
154 + Weight(self.guarantees.len() as u64) * 500
155 + self
156 .preimages
157 .iter()
158 .map(|p| Weight(p.data.len() as u64))
159 .sum::<Weight>()
160 }
161
162 pub fn transaction_count(&self) -> usize {
164 self.transactions.len()
165 }
166
167 pub fn is_empty(&self) -> bool {
169 self.transactions.is_empty()
170 && self.work_reports.is_empty()
171 && self.guarantees.is_empty()
172 && self.preimages.is_empty()
173 }
174
175 pub fn encoded_size(&self) -> usize {
177 self.encode().len()
178 }
179}
180
181impl Default for BlockBody {
182 fn default() -> Self {
183 Self::new()
184 }
185}
186
187impl Validate for BlockBody {
188 fn validate(&self) -> PrimitiveResult<()> {
189 for (i, tx) in self.transactions.iter().enumerate() {
191 tx.validate().map_err(|e| {
192 PrimitiveError::InvalidBlock(format!("Invalid transaction at index {}: {}", i, e))
193 })?;
194 }
195
196 for (i, report) in self.work_reports.iter().enumerate() {
198 report.validate().map_err(|e| {
199 PrimitiveError::InvalidBlock(format!("Invalid work report at index {}: {}", i, e))
200 })?;
201 }
202
203 for (i, guarantee) in self.guarantees.iter().enumerate() {
205 guarantee.validate().map_err(|e| {
206 PrimitiveError::InvalidBlock(format!("Invalid guarantee at index {}: {}", i, e))
207 })?;
208 }
209
210 Ok(())
211 }
212}
213
214impl Hashable for BlockBody {
215 fn hash(&self) -> Hash {
216 let hasher = Blake3Hasher::new();
217 hasher.hash(&self.encode())
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
223pub struct AvailabilityData {
224 pub chunks: Vec<AvailabilityChunk>,
226}
227
228impl AvailabilityData {
229 pub fn new() -> Self {
231 Self { chunks: Vec::new() }
232 }
233}
234
235impl Default for AvailabilityData {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
243pub struct AvailabilityChunk {
244 pub index: u32,
246 pub data: Vec<u8>,
248 pub proof: Vec<u8>,
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
254pub struct WorkReport {
255 pub core_index: u16,
257 pub package_hash: Hash,
259 pub result: WorkResult,
261 pub context: WorkContext,
263 pub authorizer_signature: Signature,
265}
266
267impl WorkReport {
268 pub fn new(
270 core_index: u16,
271 package_hash: Hash,
272 result: WorkResult,
273 context: WorkContext,
274 authorizer_signature: Signature,
275 ) -> Self {
276 Self {
277 core_index,
278 package_hash,
279 result,
280 context,
281 authorizer_signature,
282 }
283 }
284}
285
286impl Validate for WorkReport {
287 fn validate(&self) -> PrimitiveResult<()> {
288 if self.core_index >= crate::constants::MAX_CORES as u16 {
290 return Err(PrimitiveError::InvalidBlock(
291 "Core index exceeds maximum".to_string(),
292 ));
293 }
294
295 self.result.validate()?;
297
298 Ok(())
299 }
300}
301
302#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
304pub struct WorkResult {
305 pub service_id: ServiceId,
307 pub payload_hash: Hash,
309 pub gas_used: Gas,
311 pub result_data: Vec<u8>,
313}
314
315impl Validate for WorkResult {
316 fn validate(&self) -> PrimitiveResult<()> {
317 if self.result_data.len() > crate::constants::MAX_ACCUMULATION_SIZE as usize {
319 return Err(PrimitiveError::SizeLimit(
320 "Work result data exceeds maximum size".to_string(),
321 ));
322 }
323
324 Ok(())
325 }
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
330pub struct WorkContext {
331 pub anchor: Hash,
333 pub state_root: Hash,
335 pub lookup_anchor: Hash,
337 pub prerequisite: Option<Hash>,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
343pub struct Guarantee {
344 pub work_report: Hash,
346 pub timeslot: TimeSlot,
348 pub signatures: Vec<(AccountId, Signature)>,
350}
351
352impl Validate for Guarantee {
353 fn validate(&self) -> PrimitiveResult<()> {
354 if self.signatures.is_empty() {
356 return Err(PrimitiveError::InvalidBlock(
357 "Guarantee must have at least one signature".to_string(),
358 ));
359 }
360
361 let mut signers = std::collections::HashSet::new();
363 for (signer, _) in &self.signatures {
364 if !signers.insert(signer) {
365 return Err(PrimitiveError::InvalidBlock(
366 "Duplicate signer in guarantee".to_string(),
367 ));
368 }
369 }
370
371 Ok(())
372 }
373}
374
375#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
377pub struct Preimage {
378 pub hash: Hash,
380 pub data: Vec<u8>,
382}
383
384impl Preimage {
385 pub fn new(data: Vec<u8>) -> Self {
387 let hasher = Blake3Hasher::new();
388 let hash = hasher.hash(&data);
389
390 Self { hash, data }
391 }
392
393 pub fn verify(&self) -> bool {
395 let hasher = Blake3Hasher::new();
396 hasher.hash(&self.data) == self.hash
397 }
398}