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::ShardKind;
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#[cfg(feature = "scale-codec")]
31use scale_info::TypeInfo;
32
33#[derive(
35 Debug,
36 Display,
37 Default,
38 Copy,
39 Clone,
40 Ord,
41 PartialOrd,
42 Eq,
43 PartialEq,
44 Hash,
45 From,
46 Into,
47 Add,
48 AddAssign,
49 Sub,
50 SubAssign,
51 TrivialType,
52)]
53#[cfg_attr(
54 feature = "scale-codec",
55 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
56)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[cfg_attr(feature = "serde", serde(transparent))]
59#[repr(C)]
60pub struct BlockNumber(u64);
61
62impl Step for BlockNumber {
63 #[inline(always)]
64 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
65 u64::steps_between(&start.0, &end.0)
66 }
67
68 #[inline(always)]
69 fn forward_checked(start: Self, count: usize) -> Option<Self> {
70 u64::forward_checked(start.0, count).map(Self)
71 }
72
73 #[inline(always)]
74 fn backward_checked(start: Self, count: usize) -> Option<Self> {
75 u64::backward_checked(start.0, count).map(Self)
76 }
77}
78
79impl BlockNumber {
80 pub const SIZE: usize = size_of::<u64>();
82 pub const ZERO: BlockNumber = BlockNumber(0);
84 pub const ONE: BlockNumber = BlockNumber(1);
86 pub const MAX: BlockNumber = BlockNumber(u64::MAX);
88
89 #[inline(always)]
91 pub const fn new(n: u64) -> Self {
92 Self(n)
93 }
94
95 #[inline(always)]
97 pub const fn as_u64(self) -> u64 {
98 self.0
99 }
100
101 #[inline(always)]
103 pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
104 Self(u64::from_le_bytes(bytes))
105 }
106
107 #[inline(always)]
109 pub const fn to_bytes(self) -> [u8; Self::SIZE] {
110 self.0.to_le_bytes()
111 }
112
113 #[inline(always)]
115 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
116 if let Some(n) = self.0.checked_add(rhs.0) {
117 Some(Self(n))
118 } else {
119 None
120 }
121 }
122
123 #[inline(always)]
125 pub const fn saturating_add(self, rhs: Self) -> Self {
126 Self(self.0.saturating_add(rhs.0))
127 }
128
129 #[inline(always)]
131 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
132 if let Some(n) = self.0.checked_sub(rhs.0) {
133 Some(Self(n))
134 } else {
135 None
136 }
137 }
138
139 #[inline(always)]
141 pub const fn saturating_sub(self, rhs: Self) -> Self {
142 Self(self.0.saturating_sub(rhs.0))
143 }
144}
145
146#[derive(
148 Debug,
149 Display,
150 Default,
151 Copy,
152 Clone,
153 Ord,
154 PartialOrd,
155 Eq,
156 PartialEq,
157 Hash,
158 From,
159 Into,
160 Add,
161 AddAssign,
162 Sub,
163 SubAssign,
164 TrivialType,
165)]
166#[cfg_attr(
167 feature = "scale-codec",
168 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
169)]
170#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
171#[cfg_attr(feature = "serde", serde(transparent))]
172#[repr(C)]
173pub struct BlockTimestamp(u64);
174
175impl BlockTimestamp {
176 pub const SIZE: usize = size_of::<u64>();
178
179 #[inline(always)]
181 pub const fn new(ms: u64) -> Self {
182 Self(ms)
183 }
184
185 #[inline(always)]
187 pub const fn as_ms(self) -> u64 {
188 self.0
189 }
190
191 #[inline(always)]
193 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
194 if let Some(n) = self.0.checked_add(rhs.0) {
195 Some(Self(n))
196 } else {
197 None
198 }
199 }
200
201 #[inline(always)]
203 pub const fn saturating_add(self, rhs: Self) -> Self {
204 Self(self.0.saturating_add(rhs.0))
205 }
206
207 #[inline(always)]
209 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
210 if let Some(n) = self.0.checked_sub(rhs.0) {
211 Some(Self(n))
212 } else {
213 None
214 }
215 }
216
217 #[inline(always)]
219 pub const fn saturating_sub(self, rhs: Self) -> Self {
220 Self(self.0.saturating_sub(rhs.0))
221 }
222}
223
224#[derive(
229 Debug,
230 Display,
231 Default,
232 Copy,
233 Clone,
234 Eq,
235 PartialEq,
236 Ord,
237 PartialOrd,
238 Hash,
239 From,
240 Into,
241 AsRef,
242 AsMut,
243 Deref,
244 DerefMut,
245 TrivialType,
246)]
247#[cfg_attr(
248 feature = "scale-codec",
249 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
250)]
251#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
252#[cfg_attr(feature = "serde", serde(transparent))]
253#[repr(C)]
254pub struct BlockRoot(Blake3Hash);
255
256impl AsRef<[u8]> for BlockRoot {
257 #[inline(always)]
258 fn as_ref(&self) -> &[u8] {
259 self.0.as_ref()
260 }
261}
262
263impl AsMut<[u8]> for BlockRoot {
264 #[inline(always)]
265 fn as_mut(&mut self) -> &mut [u8] {
266 self.0.as_mut()
267 }
268}
269
270impl BlockRoot {
271 pub const SIZE: usize = Blake3Hash::SIZE;
273
274 #[inline(always)]
276 pub const fn new(hash: Blake3Hash) -> Self {
277 Self(hash)
278 }
279
280 #[inline(always)]
282 pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
283 let value = Blake3Hash::slice_from_repr(value);
284 unsafe { mem::transmute(value) }
286 }
287
288 #[inline(always)]
290 pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
291 let value = unsafe { mem::transmute::<&[Self], &[Blake3Hash]>(value) };
293 Blake3Hash::repr_from_slice(value)
294 }
295}
296
297pub trait GenericBlock<'a>
299where
300 Self: Clone + fmt::Debug,
301{
302 const SHARD_KIND: ShardKind;
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::<u128>(remainder)?;
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::<u128>(remainder)?;
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: ShardKind = ShardKind::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::<u128>(remainder)?;
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::<u128>(remainder)?;
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: ShardKind = ShardKind::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::<u128>(remainder)?;
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::<u128>(remainder)?;
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: ShardKind = ShardKind::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: ShardKind) -> Option<(Self, &'a [u8])> {
668 match shard_kind {
669 ShardKind::BeaconChain => {
670 let (block_header, remainder) = BeaconChainBlock::try_from_bytes(bytes)?;
671 Some((Self::BeaconChain(block_header), remainder))
672 }
673 ShardKind::IntermediateShard => {
674 let (block_header, remainder) = IntermediateShardBlock::try_from_bytes(bytes)?;
675 Some((Self::IntermediateShard(block_header), remainder))
676 }
677 ShardKind::LeafShard => {
678 let (block_header, remainder) = LeafShardBlock::try_from_bytes(bytes)?;
679 Some((Self::LeafShard(block_header), remainder))
680 }
681 ShardKind::Phantom | ShardKind::Invalid => {
682 None
684 }
685 }
686 }
687
688 #[inline]
696 pub fn is_internally_consistent(&self) -> bool {
697 match self {
698 Self::BeaconChain(block) => block.is_internally_consistent(),
699 Self::IntermediateShard(block) => block.is_internally_consistent(),
700 Self::LeafShard(block) => block.is_internally_consistent(),
701 }
702 }
703
704 #[inline]
707 pub fn try_from_bytes_unchecked(
708 bytes: &'a [u8],
709 shard_kind: ShardKind,
710 ) -> Option<(Self, &'a [u8])> {
711 match shard_kind {
712 ShardKind::BeaconChain => {
713 let (block_header, remainder) = BeaconChainBlock::try_from_bytes_unchecked(bytes)?;
714 Some((Self::BeaconChain(block_header), remainder))
715 }
716 ShardKind::IntermediateShard => {
717 let (block_header, remainder) =
718 IntermediateShardBlock::try_from_bytes_unchecked(bytes)?;
719 Some((Self::IntermediateShard(block_header), remainder))
720 }
721 ShardKind::LeafShard => {
722 let (block_header, remainder) = LeafShardBlock::try_from_bytes_unchecked(bytes)?;
723 Some((Self::LeafShard(block_header), remainder))
724 }
725 ShardKind::Phantom | ShardKind::Invalid => {
726 None
728 }
729 }
730 }
731
732 #[cfg(feature = "alloc")]
734 #[inline(always)]
735 pub fn to_owned(self) -> OwnedBlock {
736 match self {
737 Self::BeaconChain(block) => block.to_owned().into(),
738 Self::IntermediateShard(block) => block.to_owned().into(),
739 Self::LeafShard(block) => block.to_owned().into(),
740 }
741 }
742}
743
744#[derive(
748 Debug,
749 Display,
750 Default,
751 Copy,
752 Clone,
753 Ord,
754 PartialOrd,
755 Eq,
756 PartialEq,
757 Hash,
758 From,
759 Into,
760 Add,
761 AddAssign,
762 Sub,
763 SubAssign,
764)]
765#[cfg_attr(
766 feature = "scale-codec",
767 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
768)]
769#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
770#[repr(C)]
771pub struct BlockWeight(u128);
772
773impl BlockWeight {
774 pub const SIZE: usize = size_of::<u128>();
776 pub const ZERO: BlockWeight = BlockWeight(0);
778 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
780
781 #[inline(always)]
783 pub const fn new(n: u128) -> Self {
784 Self(n)
785 }
786
787 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
789 Self::new((SolutionRange::MAX.as_u64() - solution_range.as_u64()) as u128)
790 }
791
792 #[inline(always)]
794 pub const fn as_u128(self) -> u128 {
795 self.0
796 }
797}
798
799fn align_to_and_ensure_zero_padding<T>(bytes: &[u8]) -> Option<&[u8]> {
801 let padding = unsafe { bytes.align_to::<T>() }.0;
803
804 if padding.iter().any(|&byte| byte != 0) {
806 return None;
807 }
808
809 Some(&bytes[padding.len()..])
810}