1pub mod body;
4pub mod header;
5#[cfg(feature = "alloc")]
6pub mod owned;
7
8use crate::block::body::{
9 BeaconChainBody, BlockBody, GenericBlockBody, IntermediateShardBody, LeafShardBody,
10};
11use crate::block::header::{
12 BeaconChainHeader, BlockHeader, GenericBlockHeader, IntermediateShardHeader, LeafShardHeader,
13};
14#[cfg(feature = "alloc")]
15use crate::block::owned::{
16 GenericOwnedBlock, OwnedBeaconChainBlock, OwnedBeaconChainBlockError, OwnedBlock,
17 OwnedBlockError, OwnedIntermediateShardBlock, OwnedIntermediateShardBlockError,
18 OwnedLeafShardBlock, OwnedLeafShardBlockError,
19};
20use crate::hashes::Blake3Hash;
21use crate::shard::ShardKind;
22use crate::solutions::SolutionRange;
23#[cfg(feature = "serde")]
24use ::serde::{Deserialize, Serialize};
25use ab_io_type::trivial_type::TrivialType;
26use core::iter::Step;
27use core::{fmt, mem};
28use derive_more::{
29 Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, From, Into, Sub, SubAssign,
30};
31#[cfg(feature = "scale-codec")]
32use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
33#[cfg(feature = "scale-codec")]
34use scale_info::TypeInfo;
35
36#[derive(
38 Debug,
39 Display,
40 Default,
41 Copy,
42 Clone,
43 Ord,
44 PartialOrd,
45 Eq,
46 PartialEq,
47 Hash,
48 From,
49 Into,
50 Add,
51 AddAssign,
52 Sub,
53 SubAssign,
54 TrivialType,
55)]
56#[cfg_attr(
57 feature = "scale-codec",
58 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
59)]
60#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
61#[cfg_attr(feature = "serde", serde(transparent))]
62#[repr(C)]
63pub struct BlockNumber(u64);
64
65impl Step for BlockNumber {
66 #[inline(always)]
67 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
68 u64::steps_between(&start.0, &end.0)
69 }
70
71 #[inline(always)]
72 fn forward_checked(start: Self, count: usize) -> Option<Self> {
73 u64::forward_checked(start.0, count).map(Self)
74 }
75
76 #[inline(always)]
77 fn backward_checked(start: Self, count: usize) -> Option<Self> {
78 u64::backward_checked(start.0, count).map(Self)
79 }
80}
81
82impl BlockNumber {
83 pub const SIZE: usize = size_of::<u64>();
85 pub const ZERO: BlockNumber = BlockNumber(0);
87 pub const ONE: BlockNumber = BlockNumber(1);
89 pub const MAX: BlockNumber = BlockNumber(u64::MAX);
91
92 #[inline(always)]
94 pub const fn new(n: u64) -> Self {
95 Self(n)
96 }
97
98 #[inline(always)]
100 pub const fn as_u64(self) -> u64 {
101 self.0
102 }
103
104 #[inline(always)]
106 pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
107 Self(u64::from_le_bytes(bytes))
108 }
109
110 #[inline(always)]
112 pub const fn to_bytes(self) -> [u8; Self::SIZE] {
113 self.0.to_le_bytes()
114 }
115
116 #[inline(always)]
118 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
119 if let Some(n) = self.0.checked_sub(rhs.0) {
120 Some(Self(n))
121 } else {
122 None
123 }
124 }
125
126 #[inline(always)]
128 pub const fn saturating_sub(self, rhs: Self) -> Self {
129 Self(self.0.saturating_sub(rhs.0))
130 }
131}
132
133#[derive(
138 Debug,
139 Display,
140 Default,
141 Copy,
142 Clone,
143 Eq,
144 PartialEq,
145 Ord,
146 PartialOrd,
147 Hash,
148 From,
149 Into,
150 AsRef,
151 AsMut,
152 Deref,
153 DerefMut,
154 TrivialType,
155)]
156#[cfg_attr(
157 feature = "scale-codec",
158 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
159)]
160#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
161#[cfg_attr(feature = "serde", serde(transparent))]
162#[repr(C)]
163pub struct BlockRoot(Blake3Hash);
164
165impl AsRef<[u8]> for BlockRoot {
166 #[inline(always)]
167 fn as_ref(&self) -> &[u8] {
168 self.0.as_ref()
169 }
170}
171
172impl AsMut<[u8]> for BlockRoot {
173 #[inline(always)]
174 fn as_mut(&mut self) -> &mut [u8] {
175 self.0.as_mut()
176 }
177}
178
179impl BlockRoot {
180 pub const SIZE: usize = Blake3Hash::SIZE;
182
183 #[inline(always)]
185 pub const fn new(hash: Blake3Hash) -> Self {
186 Self(hash)
187 }
188
189 #[inline(always)]
191 pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
192 let value = Blake3Hash::slice_from_repr(value);
193 unsafe { mem::transmute(value) }
195 }
196
197 #[inline(always)]
199 pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
200 let value = unsafe { mem::transmute::<&[Self], &[Blake3Hash]>(value) };
202 Blake3Hash::repr_from_slice(value)
203 }
204}
205
206pub trait GenericBlock<'a>
208where
209 Self: Copy + fmt::Debug,
210{
211 type Header: GenericBlockHeader<'a>;
213 type Body: GenericBlockBody<'a>;
215 #[cfg(feature = "alloc")]
217 type Owned: GenericOwnedBlock<Block<'a> = Self>
218 where
219 Self: 'a;
220
221 fn header(&self) -> Self::Header;
223
224 fn body(&self) -> Self::Body;
226
227 #[cfg(feature = "alloc")]
229 fn try_to_owned(self) -> Option<Self::Owned>;
230}
231
232#[derive(Debug, Copy, Clone)]
234pub struct BeaconChainBlock<'a> {
235 pub header: BeaconChainHeader<'a>,
237 pub body: BeaconChainBody<'a>,
239}
240
241impl<'a> BeaconChainBlock<'a> {
242 #[inline]
252 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
253 let (header, remainder) = BeaconChainHeader::try_from_bytes(bytes)?;
254 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
255 let (body, remainder) = BeaconChainBody::try_from_bytes(remainder)?;
256
257 let block = Self { header, body };
258
259 if !block.is_internally_consistent() {
261 return None;
262 }
263
264 Some((block, remainder))
265 }
266
267 #[inline]
272 pub fn is_internally_consistent(&self) -> bool {
273 self.body.root() == self.header.result.body_root
274 && self.header.child_shard_blocks.len() == self.body.intermediate_shard_blocks.len()
275 && self
276 .header
277 .child_shard_blocks
278 .iter()
279 .zip(self.body.intermediate_shard_blocks.iter())
280 .all(|(child_shard_block_root, intermediate_shard_block)| {
281 child_shard_block_root == &intermediate_shard_block.header.root()
282 && intermediate_shard_block
283 .header
284 .prefix
285 .shard_index
286 .is_child_of(self.header.prefix.shard_index)
287 })
288 }
289
290 #[inline]
293 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
294 let (header, remainder) = BeaconChainHeader::try_from_bytes_unchecked(bytes)?;
295 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
296 let (body, remainder) = BeaconChainBody::try_from_bytes_unchecked(remainder)?;
297
298 Some((Self { header, body }, remainder))
299 }
300
301 #[cfg(feature = "alloc")]
303 #[inline(always)]
304 pub fn to_owned(self) -> Result<OwnedBeaconChainBlock, OwnedBeaconChainBlockError> {
305 OwnedBeaconChainBlock::from_block(self)
306 }
307}
308
309impl<'a> GenericBlock<'a> for BeaconChainBlock<'a> {
310 type Header = BeaconChainHeader<'a>;
311 type Body = BeaconChainBody<'a>;
312 #[cfg(feature = "alloc")]
313 type Owned = OwnedBeaconChainBlock;
314
315 #[inline(always)]
316 fn header(&self) -> Self::Header {
317 self.header
318 }
319
320 #[inline(always)]
321 fn body(&self) -> Self::Body {
322 self.body
323 }
324
325 #[cfg(feature = "alloc")]
326 #[inline(always)]
327 fn try_to_owned(self) -> Option<Self::Owned> {
328 self.to_owned().ok()
329 }
330}
331
332#[derive(Debug, Copy, Clone)]
334pub struct IntermediateShardBlock<'a> {
335 pub header: IntermediateShardHeader<'a>,
337 pub body: IntermediateShardBody<'a>,
339}
340
341impl<'a> IntermediateShardBlock<'a> {
342 #[inline]
352 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
353 let (header, remainder) = IntermediateShardHeader::try_from_bytes(bytes)?;
354 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
355 let (body, remainder) = IntermediateShardBody::try_from_bytes(remainder)?;
356
357 let block = Self { header, body };
358
359 if !block.is_internally_consistent() {
361 return None;
362 }
363
364 Some((block, remainder))
365 }
366
367 #[inline]
372 pub fn is_internally_consistent(&self) -> bool {
373 self.body.root() == self.header.result.body_root
374 && self.header.child_shard_blocks.len() == self.body.leaf_shard_blocks.len()
375 && self
376 .header
377 .child_shard_blocks
378 .iter()
379 .zip(self.body.leaf_shard_blocks.iter())
380 .all(|(child_shard_block_root, leaf_shard_block)| {
381 child_shard_block_root == &leaf_shard_block.header.root()
382 && leaf_shard_block
383 .header
384 .prefix
385 .shard_index
386 .is_child_of(self.header.prefix.shard_index)
387 })
388 }
389
390 #[inline]
393 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
394 let (header, remainder) = IntermediateShardHeader::try_from_bytes_unchecked(bytes)?;
395 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
396 let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(remainder)?;
397
398 Some((Self { header, body }, remainder))
399 }
400
401 #[cfg(feature = "alloc")]
403 #[inline(always)]
404 pub fn to_owned(self) -> Result<OwnedIntermediateShardBlock, OwnedIntermediateShardBlockError> {
405 OwnedIntermediateShardBlock::from_block(self)
406 }
407}
408
409impl<'a> GenericBlock<'a> for IntermediateShardBlock<'a> {
410 type Header = IntermediateShardHeader<'a>;
411 type Body = IntermediateShardBody<'a>;
412 #[cfg(feature = "alloc")]
413 type Owned = OwnedIntermediateShardBlock;
414
415 #[inline(always)]
416 fn header(&self) -> Self::Header {
417 self.header
418 }
419
420 #[inline(always)]
421 fn body(&self) -> Self::Body {
422 self.body
423 }
424
425 #[cfg(feature = "alloc")]
426 #[inline(always)]
427 fn try_to_owned(self) -> Option<Self::Owned> {
428 self.to_owned().ok()
429 }
430}
431
432#[derive(Debug, Copy, Clone)]
434pub struct LeafShardBlock<'a> {
435 pub header: LeafShardHeader<'a>,
437 pub body: LeafShardBody<'a>,
439}
440
441impl<'a> LeafShardBlock<'a> {
442 #[inline]
452 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
453 let (header, remainder) = LeafShardHeader::try_from_bytes(bytes)?;
454 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
455 let (body, remainder) = LeafShardBody::try_from_bytes(remainder)?;
456
457 let block = Self { header, body };
458
459 if !block.is_internally_consistent() {
461 return None;
462 }
463
464 Some((block, remainder))
465 }
466
467 #[inline]
472 pub fn is_internally_consistent(&self) -> bool {
473 self.body.root() == self.header.result.body_root
474 }
475
476 #[inline]
479 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
480 let (header, remainder) = LeafShardHeader::try_from_bytes_unchecked(bytes)?;
481 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
482 let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(remainder)?;
483
484 Some((Self { header, body }, remainder))
485 }
486
487 #[cfg(feature = "alloc")]
489 #[inline(always)]
490 pub fn to_owned(self) -> Result<OwnedLeafShardBlock, OwnedLeafShardBlockError> {
491 OwnedLeafShardBlock::from_block(self)
492 }
493}
494
495impl<'a> GenericBlock<'a> for LeafShardBlock<'a> {
496 type Header = LeafShardHeader<'a>;
497 type Body = LeafShardBody<'a>;
498 #[cfg(feature = "alloc")]
499 type Owned = OwnedLeafShardBlock;
500
501 #[inline(always)]
502 fn header(&self) -> Self::Header {
503 self.header
504 }
505
506 #[inline(always)]
507 fn body(&self) -> Self::Body {
508 self.body
509 }
510
511 #[cfg(feature = "alloc")]
512 #[inline(always)]
513 fn try_to_owned(self) -> Option<Self::Owned> {
514 self.to_owned().ok()
515 }
516}
517
518#[derive(Debug, Copy, Clone, From)]
520pub enum Block<'a> {
521 BeaconChain(BeaconChainBlock<'a>),
523 IntermediateShard(IntermediateShardBlock<'a>),
525 LeafShard(LeafShardBlock<'a>),
527}
528
529impl<'a> GenericBlock<'a> for Block<'a> {
530 type Header = BlockHeader<'a>;
531 type Body = BlockBody<'a>;
532 #[cfg(feature = "alloc")]
533 type Owned = OwnedBlock;
534
535 #[inline(always)]
536 fn header(&self) -> Self::Header {
537 match self {
538 Self::BeaconChain(block) => BlockHeader::BeaconChain(block.header()),
539 Self::IntermediateShard(block) => BlockHeader::IntermediateShard(block.header()),
540 Self::LeafShard(block) => BlockHeader::LeafShard(block.header()),
541 }
542 }
543
544 #[inline(always)]
545 fn body(&self) -> Self::Body {
546 match self {
547 Self::BeaconChain(block) => BlockBody::BeaconChain(block.body()),
548 Self::IntermediateShard(block) => BlockBody::IntermediateShard(block.body()),
549 Self::LeafShard(block) => BlockBody::LeafShard(block.body()),
550 }
551 }
552
553 #[cfg(feature = "alloc")]
554 #[inline(always)]
555 fn try_to_owned(self) -> Option<Self::Owned> {
556 self.to_owned().ok()
557 }
558}
559
560impl<'a> Block<'a> {
561 #[inline]
571 pub fn try_from_bytes(bytes: &'a [u8], shard_kind: ShardKind) -> Option<(Self, &'a [u8])> {
572 match shard_kind {
573 ShardKind::BeaconChain => {
574 let (block_header, remainder) = BeaconChainBlock::try_from_bytes(bytes)?;
575 Some((Self::BeaconChain(block_header), remainder))
576 }
577 ShardKind::IntermediateShard => {
578 let (block_header, remainder) = IntermediateShardBlock::try_from_bytes(bytes)?;
579 Some((Self::IntermediateShard(block_header), remainder))
580 }
581 ShardKind::LeafShard => {
582 let (block_header, remainder) = LeafShardBlock::try_from_bytes(bytes)?;
583 Some((Self::LeafShard(block_header), remainder))
584 }
585 ShardKind::Phantom | ShardKind::Invalid => {
586 None
588 }
589 }
590 }
591
592 #[inline]
597 pub fn is_internally_consistent(&self) -> bool {
598 match self {
599 Self::BeaconChain(body) => body.is_internally_consistent(),
600 Self::IntermediateShard(body) => body.is_internally_consistent(),
601 Self::LeafShard(body) => body.is_internally_consistent(),
602 }
603 }
604
605 #[inline]
608 pub fn try_from_bytes_unchecked(
609 bytes: &'a [u8],
610 shard_kind: ShardKind,
611 ) -> Option<(Self, &'a [u8])> {
612 match shard_kind {
613 ShardKind::BeaconChain => {
614 let (block_header, remainder) = BeaconChainBlock::try_from_bytes_unchecked(bytes)?;
615 Some((Self::BeaconChain(block_header), remainder))
616 }
617 ShardKind::IntermediateShard => {
618 let (block_header, remainder) =
619 IntermediateShardBlock::try_from_bytes_unchecked(bytes)?;
620 Some((Self::IntermediateShard(block_header), remainder))
621 }
622 ShardKind::LeafShard => {
623 let (block_header, remainder) = LeafShardBlock::try_from_bytes_unchecked(bytes)?;
624 Some((Self::LeafShard(block_header), remainder))
625 }
626 ShardKind::Phantom | ShardKind::Invalid => {
627 None
629 }
630 }
631 }
632
633 #[cfg(feature = "alloc")]
635 #[inline(always)]
636 pub fn to_owned(self) -> Result<OwnedBlock, OwnedBlockError> {
637 OwnedBlock::from_block(self)
638 }
639}
640
641#[derive(
645 Debug,
646 Display,
647 Default,
648 Copy,
649 Clone,
650 Ord,
651 PartialOrd,
652 Eq,
653 PartialEq,
654 Hash,
655 From,
656 Into,
657 Add,
658 AddAssign,
659 Sub,
660 SubAssign,
661)]
662#[cfg_attr(
663 feature = "scale-codec",
664 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
665)]
666#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
667#[repr(C)]
668pub struct BlockWeight(u128);
669
670impl BlockWeight {
671 pub const SIZE: usize = size_of::<u128>();
673 pub const ZERO: BlockWeight = BlockWeight(0);
675 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
677
678 #[inline(always)]
680 pub const fn new(n: u128) -> Self {
681 Self(n)
682 }
683
684 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
686 Self::new((SolutionRange::MAX.as_u64() - solution_range.as_u64()) as u128)
687 }
688
689 #[inline(always)]
691 pub const fn as_u128(self) -> u128 {
692 self.0
693 }
694}
695
696fn align_to_and_ensure_zero_padding<T>(bytes: &[u8]) -> Option<&[u8]> {
698 let padding = unsafe { bytes.align_to::<T>() }.0;
700
701 if padding.iter().any(|&byte| byte != 0) {
703 return None;
704 }
705
706 Some(&bytes[padding.len()..])
707}