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#[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 from_millis(ms: u64) -> Self {
182 Self(ms)
183 }
184
185 #[inline(always)]
187 pub const fn as_millis(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 + 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::<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: 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::<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: 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::<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: 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 From,
751 Into,
752 Add,
753 AddAssign,
754 Sub,
755 SubAssign,
756)]
757#[cfg_attr(
758 feature = "scale-codec",
759 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
760)]
761#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
762#[repr(C)]
763pub struct BlockWeight(u128);
764
765impl BlockWeight {
766 pub const SIZE: usize = size_of::<u128>();
768 pub const ZERO: BlockWeight = BlockWeight(0);
770 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
772
773 #[inline(always)]
775 pub const fn new(n: u128) -> Self {
776 Self(n)
777 }
778
779 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
781 Self::new((SolutionRange::MAX.as_u64() - solution_range.as_u64()) as u128)
782 }
783
784 #[inline(always)]
786 pub const fn as_u128(self) -> u128 {
787 self.0
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}