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::trivial_type::TrivialType;
23use core::iter::Step;
24use core::{fmt, mem};
25use derive_more::{
26 Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, From, Into, Sub, SubAssign,
27};
28#[cfg(feature = "scale-codec")]
29use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
30
31#[derive(
33 Debug,
34 Display,
35 Default,
36 Copy,
37 Clone,
38 Ord,
39 PartialOrd,
40 Eq,
41 PartialEq,
42 Hash,
43 Add,
44 AddAssign,
45 Sub,
46 SubAssign,
47 TrivialType,
48)]
49#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51#[cfg_attr(feature = "serde", serde(transparent))]
52#[repr(C)]
53pub struct BlockNumber(u64);
54
55impl Step for BlockNumber {
56 #[inline(always)]
57 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
58 u64::steps_between(&start.0, &end.0)
59 }
60
61 #[inline(always)]
62 fn forward_checked(start: Self, count: usize) -> Option<Self> {
63 u64::forward_checked(start.0, count).map(Self)
64 }
65
66 #[inline(always)]
67 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
68 let (n, overflowing) = u64::forward_overflowing(start.0, count);
69 (Self(n), overflowing)
70 }
71
72 #[inline(always)]
73 fn backward_checked(start: Self, count: usize) -> Option<Self> {
74 u64::backward_checked(start.0, count).map(Self)
75 }
76
77 #[inline(always)]
78 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
79 let (n, overflowing) = u64::backward_overflowing(start.0, count);
80 (Self(n), overflowing)
81 }
82}
83
84const impl From<u64> for BlockNumber {
85 #[inline(always)]
86 fn from(value: u64) -> Self {
87 Self(value)
88 }
89}
90
91const impl From<BlockNumber> for u64 {
92 #[inline(always)]
93 fn from(value: BlockNumber) -> Self {
94 value.0
95 }
96}
97
98impl BlockNumber {
99 pub const SIZE: usize = size_of::<u64>();
101 pub const ZERO: BlockNumber = BlockNumber(0);
103 pub const ONE: BlockNumber = BlockNumber(1);
105 pub const MAX: BlockNumber = BlockNumber(u64::MAX);
107
108 #[inline(always)]
110 pub const fn from_bytes(bytes: [u8; const { Self::SIZE }]) -> Self {
111 Self(u64::from_le_bytes(bytes))
112 }
113
114 #[inline(always)]
116 pub const fn to_bytes(self) -> [u8; const { Self::SIZE }] {
117 self.0.to_le_bytes()
118 }
119
120 #[inline(always)]
122 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
123 if let Some(n) = self.0.checked_add(rhs.0) {
124 Some(Self(n))
125 } else {
126 None
127 }
128 }
129
130 #[inline(always)]
132 pub const fn saturating_add(self, rhs: Self) -> Self {
133 Self(self.0.saturating_add(rhs.0))
134 }
135
136 #[inline(always)]
138 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
139 if let Some(n) = self.0.checked_sub(rhs.0) {
140 Some(Self(n))
141 } else {
142 None
143 }
144 }
145
146 #[inline(always)]
148 pub const fn saturating_sub(self, rhs: Self) -> Self {
149 Self(self.0.saturating_sub(rhs.0))
150 }
151}
152
153#[derive(
155 Debug,
156 Display,
157 Default,
158 Copy,
159 Clone,
160 Ord,
161 PartialOrd,
162 Eq,
163 PartialEq,
164 Hash,
165 From,
166 Into,
167 Add,
168 AddAssign,
169 Sub,
170 SubAssign,
171 TrivialType,
172)]
173#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
174#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
175#[cfg_attr(feature = "serde", serde(transparent))]
176#[repr(C)]
177pub struct BlockTimestamp(u64);
178
179impl BlockTimestamp {
180 pub const SIZE: usize = size_of::<u64>();
182
183 #[inline(always)]
185 pub const fn from_millis(ms: u64) -> Self {
186 Self(ms)
187 }
188
189 #[inline(always)]
191 pub const fn as_millis(self) -> u64 {
192 self.0
193 }
194
195 #[inline(always)]
197 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
198 if let Some(n) = self.0.checked_add(rhs.0) {
199 Some(Self(n))
200 } else {
201 None
202 }
203 }
204
205 #[inline(always)]
207 pub const fn saturating_add(self, rhs: Self) -> Self {
208 Self(self.0.saturating_add(rhs.0))
209 }
210
211 #[inline(always)]
213 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
214 if let Some(n) = self.0.checked_sub(rhs.0) {
215 Some(Self(n))
216 } else {
217 None
218 }
219 }
220
221 #[inline(always)]
223 pub const fn saturating_sub(self, rhs: Self) -> Self {
224 Self(self.0.saturating_sub(rhs.0))
225 }
226}
227
228#[derive(
233 Debug,
234 Display,
235 Default,
236 Copy,
237 Clone,
238 Eq,
239 PartialEq,
240 Ord,
241 PartialOrd,
242 Hash,
243 From,
244 Into,
245 AsRef,
246 AsMut,
247 Deref,
248 DerefMut,
249 TrivialType,
250)]
251#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
252#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
253#[cfg_attr(feature = "serde", serde(transparent))]
254#[repr(C)]
255pub struct BlockRoot(Blake3Hash);
256
257impl AsRef<[u8]> for BlockRoot {
258 #[inline(always)]
259 fn as_ref(&self) -> &[u8] {
260 self.0.as_ref()
261 }
262}
263
264impl AsMut<[u8]> for BlockRoot {
265 #[inline(always)]
266 fn as_mut(&mut self) -> &mut [u8] {
267 self.0.as_mut()
268 }
269}
270
271impl BlockRoot {
272 pub const SIZE: usize = Blake3Hash::SIZE;
274
275 #[inline(always)]
277 pub const fn new(hash: Blake3Hash) -> Self {
278 Self(hash)
279 }
280
281 #[inline(always)]
283 pub const fn slice_from_repr(value: &[[u8; const { Self::SIZE }]]) -> &[Self] {
284 let value = Blake3Hash::slice_from_repr(value);
285 unsafe { mem::transmute(value) }
287 }
288
289 #[inline(always)]
291 pub const fn repr_from_slice(value: &[Self]) -> &[[u8; const { Self::SIZE }]] {
292 let value = unsafe { mem::transmute::<&[Self], &[Blake3Hash]>(value) };
294 Blake3Hash::repr_from_slice(value)
295 }
296}
297
298pub trait GenericBlock<'a>
300where
301 Self: Clone + fmt::Debug + Send + Sync,
302{
303 const SHARD_KIND: RealShardKind;
305
306 type Header: GenericBlockHeader<'a>;
308 type Body: GenericBlockBody<'a>;
310 #[cfg(feature = "alloc")]
312 type Owned: GenericOwnedBlock<Block<'a> = Self>
313 where
314 Self: 'a;
315
316 fn header(&self) -> &Self::Header;
318
319 fn body(&self) -> &Self::Body;
321
322 #[cfg(feature = "alloc")]
324 fn to_owned(self) -> Self::Owned;
325}
326
327#[derive(Debug, Clone)]
329#[non_exhaustive]
331pub struct BeaconChainBlock<'a> {
332 header: BeaconChainHeader<'a>,
334 body: BeaconChainBody<'a>,
336}
337
338impl<'a> BeaconChainBlock<'a> {
339 #[inline]
349 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
350 let (header, remainder) = BeaconChainHeader::try_from_bytes(bytes)?;
351 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
352 let (body, remainder) = BeaconChainBody::try_from_bytes(remainder)?;
353
354 let block = Self { header, body };
355
356 if !block.is_internally_consistent() {
358 return None;
359 }
360
361 Some((block, remainder))
362 }
363
364 #[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.intermediate_shard_blocks().len()
375 && self
376 .header
377 .child_shard_blocks()
378 .iter()
379 .zip(self.body.intermediate_shard_blocks().iter())
380 .all(|(child_shard_block_root, intermediate_shard_block)| {
381 child_shard_block_root == &*intermediate_shard_block.header.root()
382 && intermediate_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) = BeaconChainHeader::try_from_bytes_unchecked(bytes)?;
395 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
396 let (body, remainder) = BeaconChainBody::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) -> OwnedBeaconChainBlock {
405 OwnedBeaconChainBlock {
406 header: self.header.to_owned(),
407 body: self.body.to_owned(),
408 }
409 }
410}
411
412impl<'a> GenericBlock<'a> for BeaconChainBlock<'a> {
413 const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
414
415 type Header = BeaconChainHeader<'a>;
416 type Body = BeaconChainBody<'a>;
417 #[cfg(feature = "alloc")]
418 type Owned = OwnedBeaconChainBlock;
419
420 #[inline(always)]
421 fn header(&self) -> &Self::Header {
422 &self.header
423 }
424
425 #[inline(always)]
426 fn body(&self) -> &Self::Body {
427 &self.body
428 }
429
430 #[cfg(feature = "alloc")]
431 #[inline(always)]
432 fn to_owned(self) -> Self::Owned {
433 self.to_owned()
434 }
435}
436
437#[derive(Debug, Clone)]
439#[non_exhaustive]
441pub struct IntermediateShardBlock<'a> {
442 header: IntermediateShardHeader<'a>,
444 body: IntermediateShardBody<'a>,
446}
447
448impl<'a> IntermediateShardBlock<'a> {
449 #[inline]
459 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
460 let (header, remainder) = IntermediateShardHeader::try_from_bytes(bytes)?;
461 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
462 let (body, remainder) = IntermediateShardBody::try_from_bytes(remainder)?;
463
464 let block = Self { header, body };
465
466 if !block.is_internally_consistent() {
468 return None;
469 }
470
471 Some((block, remainder))
472 }
473
474 #[inline]
482 pub fn is_internally_consistent(&self) -> bool {
483 self.body.root() == self.header.result.body_root
484 && self.header.child_shard_blocks().len() == self.body.leaf_shard_blocks().len()
485 && self
486 .header
487 .child_shard_blocks()
488 .iter()
489 .zip(self.body.leaf_shard_blocks().iter())
490 .all(|(child_shard_block_root, leaf_shard_block)| {
491 child_shard_block_root == &*leaf_shard_block.header.root()
492 && leaf_shard_block
493 .header
494 .prefix
495 .shard_index
496 .is_child_of(self.header.prefix.shard_index)
497 })
498 }
499
500 #[inline]
503 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
504 let (header, remainder) = IntermediateShardHeader::try_from_bytes_unchecked(bytes)?;
505 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
506 let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(remainder)?;
507
508 Some((Self { header, body }, remainder))
509 }
510
511 #[cfg(feature = "alloc")]
513 #[inline(always)]
514 pub fn to_owned(self) -> OwnedIntermediateShardBlock {
515 OwnedIntermediateShardBlock {
516 header: self.header.to_owned(),
517 body: self.body.to_owned(),
518 }
519 }
520}
521
522impl<'a> GenericBlock<'a> for IntermediateShardBlock<'a> {
523 const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
524
525 type Header = IntermediateShardHeader<'a>;
526 type Body = IntermediateShardBody<'a>;
527 #[cfg(feature = "alloc")]
528 type Owned = OwnedIntermediateShardBlock;
529
530 #[inline(always)]
531 fn header(&self) -> &Self::Header {
532 &self.header
533 }
534
535 #[inline(always)]
536 fn body(&self) -> &Self::Body {
537 &self.body
538 }
539
540 #[cfg(feature = "alloc")]
541 #[inline(always)]
542 fn to_owned(self) -> Self::Owned {
543 self.to_owned()
544 }
545}
546
547#[derive(Debug, Clone)]
549#[non_exhaustive]
551pub struct LeafShardBlock<'a> {
552 header: LeafShardHeader<'a>,
554 body: LeafShardBody<'a>,
556}
557
558impl<'a> LeafShardBlock<'a> {
559 #[inline]
569 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
570 let (header, remainder) = LeafShardHeader::try_from_bytes(bytes)?;
571 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
572 let (body, remainder) = LeafShardBody::try_from_bytes(remainder)?;
573
574 let block = Self { header, body };
575
576 if !block.is_internally_consistent() {
578 return None;
579 }
580
581 Some((block, remainder))
582 }
583
584 #[inline]
592 pub fn is_internally_consistent(&self) -> bool {
593 self.body.root() == self.header.result.body_root
594 }
595
596 #[inline]
599 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
600 let (header, remainder) = LeafShardHeader::try_from_bytes_unchecked(bytes)?;
601 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
602 let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(remainder)?;
603
604 Some((Self { header, body }, remainder))
605 }
606
607 #[cfg(feature = "alloc")]
609 #[inline(always)]
610 pub fn to_owned(self) -> OwnedLeafShardBlock {
611 OwnedLeafShardBlock {
612 header: self.header.to_owned(),
613 body: self.body.to_owned(),
614 }
615 }
616}
617
618impl<'a> GenericBlock<'a> for LeafShardBlock<'a> {
619 const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
620
621 type Header = LeafShardHeader<'a>;
622 type Body = LeafShardBody<'a>;
623 #[cfg(feature = "alloc")]
624 type Owned = OwnedLeafShardBlock;
625
626 #[inline(always)]
627 fn header(&self) -> &Self::Header {
628 &self.header
629 }
630
631 #[inline(always)]
632 fn body(&self) -> &Self::Body {
633 &self.body
634 }
635
636 #[cfg(feature = "alloc")]
637 #[inline(always)]
638 fn to_owned(self) -> Self::Owned {
639 self.to_owned()
640 }
641}
642
643#[derive(Debug, Clone, From)]
648pub enum Block<'a> {
649 BeaconChain(BeaconChainBlock<'a>),
651 IntermediateShard(IntermediateShardBlock<'a>),
653 LeafShard(LeafShardBlock<'a>),
655}
656
657impl<'a> Block<'a> {
658 #[inline]
668 pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
669 match shard_kind {
670 RealShardKind::BeaconChain => {
671 let (block_header, remainder) = BeaconChainBlock::try_from_bytes(bytes)?;
672 Some((Self::BeaconChain(block_header), remainder))
673 }
674 RealShardKind::IntermediateShard => {
675 let (block_header, remainder) = IntermediateShardBlock::try_from_bytes(bytes)?;
676 Some((Self::IntermediateShard(block_header), remainder))
677 }
678 RealShardKind::LeafShard => {
679 let (block_header, remainder) = LeafShardBlock::try_from_bytes(bytes)?;
680 Some((Self::LeafShard(block_header), remainder))
681 }
682 }
683 }
684
685 #[inline]
693 pub fn is_internally_consistent(&self) -> bool {
694 match self {
695 Self::BeaconChain(block) => block.is_internally_consistent(),
696 Self::IntermediateShard(block) => block.is_internally_consistent(),
697 Self::LeafShard(block) => block.is_internally_consistent(),
698 }
699 }
700
701 #[inline]
704 pub fn try_from_bytes_unchecked(
705 bytes: &'a [u8],
706 shard_kind: RealShardKind,
707 ) -> Option<(Self, &'a [u8])> {
708 match shard_kind {
709 RealShardKind::BeaconChain => {
710 let (block_header, remainder) = BeaconChainBlock::try_from_bytes_unchecked(bytes)?;
711 Some((Self::BeaconChain(block_header), remainder))
712 }
713 RealShardKind::IntermediateShard => {
714 let (block_header, remainder) =
715 IntermediateShardBlock::try_from_bytes_unchecked(bytes)?;
716 Some((Self::IntermediateShard(block_header), remainder))
717 }
718 RealShardKind::LeafShard => {
719 let (block_header, remainder) = LeafShardBlock::try_from_bytes_unchecked(bytes)?;
720 Some((Self::LeafShard(block_header), remainder))
721 }
722 }
723 }
724
725 #[cfg(feature = "alloc")]
727 #[inline(always)]
728 pub fn to_owned(self) -> OwnedBlock {
729 match self {
730 Self::BeaconChain(block) => block.to_owned().into(),
731 Self::IntermediateShard(block) => block.to_owned().into(),
732 Self::LeafShard(block) => block.to_owned().into(),
733 }
734 }
735}
736
737#[derive(
741 Debug,
742 Display,
743 Default,
744 Copy,
745 Clone,
746 Ord,
747 PartialOrd,
748 Eq,
749 PartialEq,
750 Hash,
751 Add,
752 AddAssign,
753 Sub,
754 SubAssign,
755)]
756#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
757#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
758#[repr(C)]
759pub struct BlockWeight(u128);
760
761const impl From<u128> for BlockWeight {
762 #[inline(always)]
763 fn from(value: u128) -> Self {
764 Self(value)
765 }
766}
767
768const impl From<BlockWeight> for u128 {
769 #[inline(always)]
770 fn from(value: BlockWeight) -> Self {
771 value.0
772 }
773}
774
775impl BlockWeight {
776 pub const SIZE: usize = size_of::<u128>();
778 pub const ZERO: BlockWeight = BlockWeight(0);
780 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
782
783 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
785 Self::from(u128::from(
786 u64::from(SolutionRange::MAX) - u64::from(solution_range),
787 ))
788 }
789}
790
791fn align_to_and_ensure_zero_padding<T>(bytes: &[u8]) -> Option<&[u8]> {
793 let padding = unsafe { bytes.align_to::<T>() }.0;
795
796 if padding.iter().any(|&byte| byte != 0) {
798 return None;
799 }
800
801 Some(&bytes[padding.len()..])
802}