1pub mod body;
4pub mod header;
5#[cfg(feature = "alloc")]
6pub mod owned;
7
8use crate::block::body::{BeaconChainBody, GenericBlockBody, IntermediateShardBody, LeafShardBody};
9use crate::block::header::{
10 BeaconChainHeader, GenericBlockHeader, IntermediateShardHeader, LeafShardHeader,
11};
12#[cfg(feature = "alloc")]
13use crate::block::owned::{
14 GenericOwnedBlock, OwnedBeaconChainBlock, OwnedBlock, OwnedIntermediateShardBlock,
15 OwnedLeafShardBlock,
16};
17use crate::hashes::Blake3Hash;
18use crate::shard::RealShardKind;
19use crate::solutions::SolutionRange;
20#[cfg(feature = "serde")]
21use ::serde::{Deserialize, Serialize};
22use ab_io_type::alignment::Alignment;
23use ab_io_type::trivial_type::TrivialType;
24use core::fmt;
25use core::iter::Step;
26use derive_more::{
27 Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, From, Into, Sub, SubAssign,
28};
29#[cfg(feature = "scale-codec")]
30use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
31use transparent_wrapper::TransparentWrapper;
32
33#[derive(
35 Debug,
36 Display,
37 Default,
38 Copy,
39 Clone,
40 Ord,
41 PartialOrd,
42 Eq,
43 PartialEq,
44 Hash,
45 Add,
46 AddAssign,
47 Sub,
48 SubAssign,
49 TrivialType,
50)]
51#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53#[cfg_attr(feature = "serde", serde(transparent))]
54#[repr(C)]
55pub struct BlockNumber(u64);
56
57impl Step for BlockNumber {
58 #[inline(always)]
59 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
60 u64::steps_between(&start.0, &end.0)
61 }
62
63 #[inline(always)]
64 fn forward_checked(start: Self, count: usize) -> Option<Self> {
65 u64::forward_checked(start.0, count).map(Self)
66 }
67
68 #[inline(always)]
69 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
70 let (n, overflowing) = u64::forward_overflowing(start.0, count);
71 (Self(n), overflowing)
72 }
73
74 #[inline(always)]
75 fn backward_checked(start: Self, count: usize) -> Option<Self> {
76 u64::backward_checked(start.0, count).map(Self)
77 }
78
79 #[inline(always)]
80 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
81 let (n, overflowing) = u64::backward_overflowing(start.0, count);
82 (Self(n), overflowing)
83 }
84}
85
86const impl From<u64> for BlockNumber {
87 #[inline(always)]
88 fn from(value: u64) -> Self {
89 Self(value)
90 }
91}
92
93const impl From<BlockNumber> for u64 {
94 #[inline(always)]
95 fn from(value: BlockNumber) -> Self {
96 value.0
97 }
98}
99
100impl BlockNumber {
101 pub const SIZE: usize = size_of::<u64>();
103 pub const ZERO: BlockNumber = BlockNumber(0);
105 pub const ONE: BlockNumber = BlockNumber(1);
107 pub const MAX: BlockNumber = BlockNumber(u64::MAX);
109
110 #[inline(always)]
112 pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
113 Self(u64::from_le_bytes(bytes))
114 }
115
116 #[inline(always)]
118 pub const fn to_bytes(self) -> [u8; Self::SIZE] {
119 self.0.to_le_bytes()
120 }
121
122 #[inline(always)]
124 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
125 if let Some(n) = self.0.checked_add(rhs.0) {
126 Some(Self(n))
127 } else {
128 None
129 }
130 }
131
132 #[inline(always)]
134 pub const fn saturating_add(self, rhs: Self) -> Self {
135 Self(self.0.saturating_add(rhs.0))
136 }
137
138 #[inline(always)]
140 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
141 if let Some(n) = self.0.checked_sub(rhs.0) {
142 Some(Self(n))
143 } else {
144 None
145 }
146 }
147
148 #[inline(always)]
150 pub const fn saturating_sub(self, rhs: Self) -> Self {
151 Self(self.0.saturating_sub(rhs.0))
152 }
153}
154
155#[derive(
157 Debug,
158 Display,
159 Default,
160 Copy,
161 Clone,
162 Ord,
163 PartialOrd,
164 Eq,
165 PartialEq,
166 Hash,
167 From,
168 Into,
169 Add,
170 AddAssign,
171 Sub,
172 SubAssign,
173 TrivialType,
174)]
175#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177#[cfg_attr(feature = "serde", serde(transparent))]
178#[repr(C)]
179pub struct BlockTimestamp(u64);
180
181impl BlockTimestamp {
182 pub const SIZE: usize = size_of::<u64>();
184
185 #[inline(always)]
187 pub const fn from_millis(ms: u64) -> Self {
188 Self(ms)
189 }
190
191 #[inline(always)]
193 pub const fn as_millis(self) -> u64 {
194 self.0
195 }
196
197 #[inline(always)]
199 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
200 if let Some(n) = self.0.checked_add(rhs.0) {
201 Some(Self(n))
202 } else {
203 None
204 }
205 }
206
207 #[inline(always)]
209 pub const fn saturating_add(self, rhs: Self) -> Self {
210 Self(self.0.saturating_add(rhs.0))
211 }
212
213 #[inline(always)]
215 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
216 if let Some(n) = self.0.checked_sub(rhs.0) {
217 Some(Self(n))
218 } else {
219 None
220 }
221 }
222
223 #[inline(always)]
225 pub const fn saturating_sub(self, rhs: Self) -> Self {
226 Self(self.0.saturating_sub(rhs.0))
227 }
228}
229
230#[derive(
235 Debug,
236 Display,
237 Default,
238 Copy,
239 Clone,
240 Eq,
241 PartialEq,
242 Ord,
243 PartialOrd,
244 Hash,
245 From,
246 Into,
247 AsRef,
248 AsMut,
249 Deref,
250 DerefMut,
251 TrivialType,
252 TransparentWrapper,
253)]
254#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
255#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
256#[cfg_attr(feature = "serde", serde(transparent))]
257#[repr(C)]
258pub struct BlockRoot(Blake3Hash);
259
260impl AsRef<[u8]> for BlockRoot {
261 #[inline(always)]
262 fn as_ref(&self) -> &[u8] {
263 self.0.as_ref()
264 }
265}
266
267impl AsMut<[u8]> for BlockRoot {
268 #[inline(always)]
269 fn as_mut(&mut self) -> &mut [u8] {
270 self.0.as_mut()
271 }
272}
273
274impl BlockRoot {
275 pub const SIZE: usize = Blake3Hash::SIZE;
277
278 #[inline(always)]
280 pub const fn new(hash: Blake3Hash) -> Self {
281 Self(hash)
282 }
283
284 #[inline(always)]
286 pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
287 Self::wrap_slice(Blake3Hash::slice_from_repr(value))
288 }
289
290 #[inline(always)]
292 pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
293 Blake3Hash::repr_from_slice(Self::peel_slice(value))
294 }
295}
296
297pub trait GenericBlock<'a>
299where
300 Self: Clone + fmt::Debug + Send + Sync,
301{
302 const SHARD_KIND: RealShardKind;
304
305 type Header: GenericBlockHeader<'a>;
307 type Body: GenericBlockBody<'a>;
309 #[cfg(feature = "alloc")]
311 type Owned: GenericOwnedBlock<Block<'a> = Self>
312 where
313 Self: 'a;
314
315 fn header(&self) -> &Self::Header;
317
318 fn body(&self) -> &Self::Body;
320
321 #[cfg(feature = "alloc")]
323 fn to_owned(self) -> Self::Owned;
324}
325
326#[derive(Debug, Clone)]
328#[non_exhaustive]
330pub struct BeaconChainBlock<'a> {
331 header: BeaconChainHeader<'a>,
333 body: BeaconChainBody<'a>,
335}
336
337impl<'a> BeaconChainBlock<'a> {
338 #[inline]
348 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
349 let (header, remainder) = BeaconChainHeader::try_from_bytes(bytes)?;
350 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
351 let (body, remainder) = BeaconChainBody::try_from_bytes(remainder)?;
352
353 let block = Self { header, body };
354
355 if !block.is_internally_consistent() {
357 return None;
358 }
359
360 Some((block, remainder))
361 }
362
363 #[inline]
371 pub fn is_internally_consistent(&self) -> bool {
372 self.body.root() == self.header.result.body_root
373 && self.header.child_shard_blocks().len() == self.body.intermediate_shard_blocks().len()
374 && self
375 .header
376 .child_shard_blocks()
377 .iter()
378 .zip(self.body.intermediate_shard_blocks().iter())
379 .all(|(child_shard_block_root, intermediate_shard_block)| {
380 child_shard_block_root == &*intermediate_shard_block.header.root()
381 && intermediate_shard_block
382 .header
383 .prefix
384 .shard_index
385 .is_child_of(self.header.prefix.shard_index)
386 })
387 }
388
389 #[inline]
392 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
393 let (header, remainder) = BeaconChainHeader::try_from_bytes_unchecked(bytes)?;
394 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
395 let (body, remainder) = BeaconChainBody::try_from_bytes_unchecked(remainder)?;
396
397 Some((Self { header, body }, remainder))
398 }
399
400 #[cfg(feature = "alloc")]
402 #[inline(always)]
403 pub fn to_owned(self) -> OwnedBeaconChainBlock {
404 OwnedBeaconChainBlock {
405 header: self.header.to_owned(),
406 body: self.body.to_owned(),
407 }
408 }
409}
410
411impl<'a> GenericBlock<'a> for BeaconChainBlock<'a> {
412 const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
413
414 type Header = BeaconChainHeader<'a>;
415 type Body = BeaconChainBody<'a>;
416 #[cfg(feature = "alloc")]
417 type Owned = OwnedBeaconChainBlock;
418
419 #[inline(always)]
420 fn header(&self) -> &Self::Header {
421 &self.header
422 }
423
424 #[inline(always)]
425 fn body(&self) -> &Self::Body {
426 &self.body
427 }
428
429 #[cfg(feature = "alloc")]
430 #[inline(always)]
431 fn to_owned(self) -> Self::Owned {
432 self.to_owned()
433 }
434}
435
436#[derive(Debug, Clone)]
438#[non_exhaustive]
440pub struct IntermediateShardBlock<'a> {
441 header: IntermediateShardHeader<'a>,
443 body: IntermediateShardBody<'a>,
445}
446
447impl<'a> IntermediateShardBlock<'a> {
448 #[inline]
458 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
459 let (header, remainder) = IntermediateShardHeader::try_from_bytes(bytes)?;
460 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
461 let (body, remainder) = IntermediateShardBody::try_from_bytes(remainder)?;
462
463 let block = Self { header, body };
464
465 if !block.is_internally_consistent() {
467 return None;
468 }
469
470 Some((block, remainder))
471 }
472
473 #[inline]
481 pub fn is_internally_consistent(&self) -> bool {
482 self.body.root() == self.header.result.body_root
483 && self.header.child_shard_blocks().len() == self.body.leaf_shard_blocks().len()
484 && self
485 .header
486 .child_shard_blocks()
487 .iter()
488 .zip(self.body.leaf_shard_blocks().iter())
489 .all(|(child_shard_block_root, leaf_shard_block)| {
490 child_shard_block_root == &*leaf_shard_block.header.root()
491 && leaf_shard_block
492 .header
493 .prefix
494 .shard_index
495 .is_child_of(self.header.prefix.shard_index)
496 })
497 }
498
499 #[inline]
502 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
503 let (header, remainder) = IntermediateShardHeader::try_from_bytes_unchecked(bytes)?;
504 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
505 let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(remainder)?;
506
507 Some((Self { header, body }, remainder))
508 }
509
510 #[cfg(feature = "alloc")]
512 #[inline(always)]
513 pub fn to_owned(self) -> OwnedIntermediateShardBlock {
514 OwnedIntermediateShardBlock {
515 header: self.header.to_owned(),
516 body: self.body.to_owned(),
517 }
518 }
519}
520
521impl<'a> GenericBlock<'a> for IntermediateShardBlock<'a> {
522 const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
523
524 type Header = IntermediateShardHeader<'a>;
525 type Body = IntermediateShardBody<'a>;
526 #[cfg(feature = "alloc")]
527 type Owned = OwnedIntermediateShardBlock;
528
529 #[inline(always)]
530 fn header(&self) -> &Self::Header {
531 &self.header
532 }
533
534 #[inline(always)]
535 fn body(&self) -> &Self::Body {
536 &self.body
537 }
538
539 #[cfg(feature = "alloc")]
540 #[inline(always)]
541 fn to_owned(self) -> Self::Owned {
542 self.to_owned()
543 }
544}
545
546#[derive(Debug, Clone)]
548#[non_exhaustive]
550pub struct LeafShardBlock<'a> {
551 header: LeafShardHeader<'a>,
553 body: LeafShardBody<'a>,
555}
556
557impl<'a> LeafShardBlock<'a> {
558 #[inline]
568 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
569 let (header, remainder) = LeafShardHeader::try_from_bytes(bytes)?;
570 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
571 let (body, remainder) = LeafShardBody::try_from_bytes(remainder)?;
572
573 let block = Self { header, body };
574
575 if !block.is_internally_consistent() {
577 return None;
578 }
579
580 Some((block, remainder))
581 }
582
583 #[inline]
591 pub fn is_internally_consistent(&self) -> bool {
592 self.body.root() == self.header.result.body_root
593 }
594
595 #[inline]
598 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
599 let (header, remainder) = LeafShardHeader::try_from_bytes_unchecked(bytes)?;
600 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
601 let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(remainder)?;
602
603 Some((Self { header, body }, remainder))
604 }
605
606 #[cfg(feature = "alloc")]
608 #[inline(always)]
609 pub fn to_owned(self) -> OwnedLeafShardBlock {
610 OwnedLeafShardBlock {
611 header: self.header.to_owned(),
612 body: self.body.to_owned(),
613 }
614 }
615}
616
617impl<'a> GenericBlock<'a> for LeafShardBlock<'a> {
618 const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
619
620 type Header = LeafShardHeader<'a>;
621 type Body = LeafShardBody<'a>;
622 #[cfg(feature = "alloc")]
623 type Owned = OwnedLeafShardBlock;
624
625 #[inline(always)]
626 fn header(&self) -> &Self::Header {
627 &self.header
628 }
629
630 #[inline(always)]
631 fn body(&self) -> &Self::Body {
632 &self.body
633 }
634
635 #[cfg(feature = "alloc")]
636 #[inline(always)]
637 fn to_owned(self) -> Self::Owned {
638 self.to_owned()
639 }
640}
641
642#[derive(Debug, Clone, From)]
647pub enum Block<'a> {
648 BeaconChain(BeaconChainBlock<'a>),
650 IntermediateShard(IntermediateShardBlock<'a>),
652 LeafShard(LeafShardBlock<'a>),
654}
655
656impl<'a> Block<'a> {
657 #[inline]
667 pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
668 match shard_kind {
669 RealShardKind::BeaconChain => {
670 let (block_header, remainder) = BeaconChainBlock::try_from_bytes(bytes)?;
671 Some((Self::BeaconChain(block_header), remainder))
672 }
673 RealShardKind::IntermediateShard => {
674 let (block_header, remainder) = IntermediateShardBlock::try_from_bytes(bytes)?;
675 Some((Self::IntermediateShard(block_header), remainder))
676 }
677 RealShardKind::LeafShard => {
678 let (block_header, remainder) = LeafShardBlock::try_from_bytes(bytes)?;
679 Some((Self::LeafShard(block_header), remainder))
680 }
681 }
682 }
683
684 #[inline]
692 pub fn is_internally_consistent(&self) -> bool {
693 match self {
694 Self::BeaconChain(block) => block.is_internally_consistent(),
695 Self::IntermediateShard(block) => block.is_internally_consistent(),
696 Self::LeafShard(block) => block.is_internally_consistent(),
697 }
698 }
699
700 #[inline]
703 pub fn try_from_bytes_unchecked(
704 bytes: &'a [u8],
705 shard_kind: RealShardKind,
706 ) -> Option<(Self, &'a [u8])> {
707 match shard_kind {
708 RealShardKind::BeaconChain => {
709 let (block_header, remainder) = BeaconChainBlock::try_from_bytes_unchecked(bytes)?;
710 Some((Self::BeaconChain(block_header), remainder))
711 }
712 RealShardKind::IntermediateShard => {
713 let (block_header, remainder) =
714 IntermediateShardBlock::try_from_bytes_unchecked(bytes)?;
715 Some((Self::IntermediateShard(block_header), remainder))
716 }
717 RealShardKind::LeafShard => {
718 let (block_header, remainder) = LeafShardBlock::try_from_bytes_unchecked(bytes)?;
719 Some((Self::LeafShard(block_header), remainder))
720 }
721 }
722 }
723
724 #[cfg(feature = "alloc")]
726 #[inline(always)]
727 pub fn to_owned(self) -> OwnedBlock {
728 match self {
729 Self::BeaconChain(block) => block.to_owned().into(),
730 Self::IntermediateShard(block) => block.to_owned().into(),
731 Self::LeafShard(block) => block.to_owned().into(),
732 }
733 }
734}
735
736#[derive(
740 Debug,
741 Display,
742 Default,
743 Copy,
744 Clone,
745 Ord,
746 PartialOrd,
747 Eq,
748 PartialEq,
749 Hash,
750 Add,
751 AddAssign,
752 Sub,
753 SubAssign,
754)]
755#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
756#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
757#[repr(C)]
758pub struct BlockWeight(u128);
759
760const impl From<u128> for BlockWeight {
761 #[inline(always)]
762 fn from(value: u128) -> Self {
763 Self(value)
764 }
765}
766
767const impl From<BlockWeight> for u128 {
768 #[inline(always)]
769 fn from(value: BlockWeight) -> Self {
770 value.0
771 }
772}
773
774impl BlockWeight {
775 pub const SIZE: usize = size_of::<u128>();
777 pub const ZERO: BlockWeight = BlockWeight(0);
779 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
781
782 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
784 Self::from(u128::from(
785 u64::from(SolutionRange::MAX) - u64::from(solution_range),
786 ))
787 }
788}
789
790fn align_to_and_ensure_zero_padding(bytes: &[u8], alignment: Alignment) -> Option<&[u8]> {
792 let padding_len = bytes
793 .as_ptr()
794 .align_offset(usize::from(alignment.get()))
795 .min(bytes.len());
796 let (padding, remainder) = bytes.split_at(padding_len);
797
798 if padding.iter().any(|&byte| byte != 0) {
800 return None;
801 }
802
803 Some(remainder)
804}