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 From,
44 Into,
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 backward_checked(start: Self, count: usize) -> Option<Self> {
70 u64::backward_checked(start.0, count).map(Self)
71 }
72}
73
74impl BlockNumber {
75 pub const SIZE: usize = size_of::<u64>();
77 pub const ZERO: BlockNumber = BlockNumber(0);
79 pub const ONE: BlockNumber = BlockNumber(1);
81 pub const MAX: BlockNumber = BlockNumber(u64::MAX);
83
84 #[inline(always)]
86 pub const fn new(n: u64) -> Self {
87 Self(n)
88 }
89
90 #[inline(always)]
92 pub const fn as_u64(self) -> u64 {
93 self.0
94 }
95
96 #[inline(always)]
98 pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
99 Self(u64::from_le_bytes(bytes))
100 }
101
102 #[inline(always)]
104 pub const fn to_bytes(self) -> [u8; Self::SIZE] {
105 self.0.to_le_bytes()
106 }
107
108 #[inline(always)]
110 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
111 if let Some(n) = self.0.checked_add(rhs.0) {
112 Some(Self(n))
113 } else {
114 None
115 }
116 }
117
118 #[inline(always)]
120 pub const fn saturating_add(self, rhs: Self) -> Self {
121 Self(self.0.saturating_add(rhs.0))
122 }
123
124 #[inline(always)]
126 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
127 if let Some(n) = self.0.checked_sub(rhs.0) {
128 Some(Self(n))
129 } else {
130 None
131 }
132 }
133
134 #[inline(always)]
136 pub const fn saturating_sub(self, rhs: Self) -> Self {
137 Self(self.0.saturating_sub(rhs.0))
138 }
139}
140
141#[derive(
143 Debug,
144 Display,
145 Default,
146 Copy,
147 Clone,
148 Ord,
149 PartialOrd,
150 Eq,
151 PartialEq,
152 Hash,
153 From,
154 Into,
155 Add,
156 AddAssign,
157 Sub,
158 SubAssign,
159 TrivialType,
160)]
161#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
162#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
163#[cfg_attr(feature = "serde", serde(transparent))]
164#[repr(C)]
165pub struct BlockTimestamp(u64);
166
167impl BlockTimestamp {
168 pub const SIZE: usize = size_of::<u64>();
170
171 #[inline(always)]
173 pub const fn from_millis(ms: u64) -> Self {
174 Self(ms)
175 }
176
177 #[inline(always)]
179 pub const fn as_millis(self) -> u64 {
180 self.0
181 }
182
183 #[inline(always)]
185 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
186 if let Some(n) = self.0.checked_add(rhs.0) {
187 Some(Self(n))
188 } else {
189 None
190 }
191 }
192
193 #[inline(always)]
195 pub const fn saturating_add(self, rhs: Self) -> Self {
196 Self(self.0.saturating_add(rhs.0))
197 }
198
199 #[inline(always)]
201 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
202 if let Some(n) = self.0.checked_sub(rhs.0) {
203 Some(Self(n))
204 } else {
205 None
206 }
207 }
208
209 #[inline(always)]
211 pub const fn saturating_sub(self, rhs: Self) -> Self {
212 Self(self.0.saturating_sub(rhs.0))
213 }
214}
215
216#[derive(
221 Debug,
222 Display,
223 Default,
224 Copy,
225 Clone,
226 Eq,
227 PartialEq,
228 Ord,
229 PartialOrd,
230 Hash,
231 From,
232 Into,
233 AsRef,
234 AsMut,
235 Deref,
236 DerefMut,
237 TrivialType,
238)]
239#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
240#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
241#[cfg_attr(feature = "serde", serde(transparent))]
242#[repr(C)]
243pub struct BlockRoot(Blake3Hash);
244
245impl AsRef<[u8]> for BlockRoot {
246 #[inline(always)]
247 fn as_ref(&self) -> &[u8] {
248 self.0.as_ref()
249 }
250}
251
252impl AsMut<[u8]> for BlockRoot {
253 #[inline(always)]
254 fn as_mut(&mut self) -> &mut [u8] {
255 self.0.as_mut()
256 }
257}
258
259impl BlockRoot {
260 pub const SIZE: usize = Blake3Hash::SIZE;
262
263 #[inline(always)]
265 pub const fn new(hash: Blake3Hash) -> Self {
266 Self(hash)
267 }
268
269 #[inline(always)]
271 pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
272 let value = Blake3Hash::slice_from_repr(value);
273 unsafe { mem::transmute(value) }
275 }
276
277 #[inline(always)]
279 pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
280 let value = unsafe { mem::transmute::<&[Self], &[Blake3Hash]>(value) };
282 Blake3Hash::repr_from_slice(value)
283 }
284}
285
286pub trait GenericBlock<'a>
288where
289 Self: Clone + fmt::Debug + Send + Sync,
290{
291 const SHARD_KIND: RealShardKind;
293
294 type Header: GenericBlockHeader<'a>;
296 type Body: GenericBlockBody<'a>;
298 #[cfg(feature = "alloc")]
300 type Owned: GenericOwnedBlock<Block<'a> = Self>
301 where
302 Self: 'a;
303
304 fn header(&self) -> &Self::Header;
306
307 fn body(&self) -> &Self::Body;
309
310 #[cfg(feature = "alloc")]
312 fn to_owned(self) -> Self::Owned;
313}
314
315#[derive(Debug, Clone)]
317#[non_exhaustive]
319pub struct BeaconChainBlock<'a> {
320 header: BeaconChainHeader<'a>,
322 body: BeaconChainBody<'a>,
324}
325
326impl<'a> BeaconChainBlock<'a> {
327 #[inline]
337 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
338 let (header, remainder) = BeaconChainHeader::try_from_bytes(bytes)?;
339 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
340 let (body, remainder) = BeaconChainBody::try_from_bytes(remainder)?;
341
342 let block = Self { header, body };
343
344 if !block.is_internally_consistent() {
346 return None;
347 }
348
349 Some((block, remainder))
350 }
351
352 #[inline]
360 pub fn is_internally_consistent(&self) -> bool {
361 self.body.root() == self.header.result.body_root
362 && self.header.child_shard_blocks().len() == self.body.intermediate_shard_blocks().len()
363 && self
364 .header
365 .child_shard_blocks()
366 .iter()
367 .zip(self.body.intermediate_shard_blocks().iter())
368 .all(|(child_shard_block_root, intermediate_shard_block)| {
369 child_shard_block_root == &*intermediate_shard_block.header.root()
370 && intermediate_shard_block
371 .header
372 .prefix
373 .shard_index
374 .is_child_of(self.header.prefix.shard_index)
375 })
376 }
377
378 #[inline]
381 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
382 let (header, remainder) = BeaconChainHeader::try_from_bytes_unchecked(bytes)?;
383 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
384 let (body, remainder) = BeaconChainBody::try_from_bytes_unchecked(remainder)?;
385
386 Some((Self { header, body }, remainder))
387 }
388
389 #[cfg(feature = "alloc")]
391 #[inline(always)]
392 pub fn to_owned(self) -> OwnedBeaconChainBlock {
393 OwnedBeaconChainBlock {
394 header: self.header.to_owned(),
395 body: self.body.to_owned(),
396 }
397 }
398}
399
400impl<'a> GenericBlock<'a> for BeaconChainBlock<'a> {
401 const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
402
403 type Header = BeaconChainHeader<'a>;
404 type Body = BeaconChainBody<'a>;
405 #[cfg(feature = "alloc")]
406 type Owned = OwnedBeaconChainBlock;
407
408 #[inline(always)]
409 fn header(&self) -> &Self::Header {
410 &self.header
411 }
412
413 #[inline(always)]
414 fn body(&self) -> &Self::Body {
415 &self.body
416 }
417
418 #[cfg(feature = "alloc")]
419 #[inline(always)]
420 fn to_owned(self) -> Self::Owned {
421 self.to_owned()
422 }
423}
424
425#[derive(Debug, Clone)]
427#[non_exhaustive]
429pub struct IntermediateShardBlock<'a> {
430 header: IntermediateShardHeader<'a>,
432 body: IntermediateShardBody<'a>,
434}
435
436impl<'a> IntermediateShardBlock<'a> {
437 #[inline]
447 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
448 let (header, remainder) = IntermediateShardHeader::try_from_bytes(bytes)?;
449 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
450 let (body, remainder) = IntermediateShardBody::try_from_bytes(remainder)?;
451
452 let block = Self { header, body };
453
454 if !block.is_internally_consistent() {
456 return None;
457 }
458
459 Some((block, remainder))
460 }
461
462 #[inline]
470 pub fn is_internally_consistent(&self) -> bool {
471 self.body.root() == self.header.result.body_root
472 && self.header.child_shard_blocks().len() == self.body.leaf_shard_blocks().len()
473 && self
474 .header
475 .child_shard_blocks()
476 .iter()
477 .zip(self.body.leaf_shard_blocks().iter())
478 .all(|(child_shard_block_root, leaf_shard_block)| {
479 child_shard_block_root == &*leaf_shard_block.header.root()
480 && leaf_shard_block
481 .header
482 .prefix
483 .shard_index
484 .is_child_of(self.header.prefix.shard_index)
485 })
486 }
487
488 #[inline]
491 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
492 let (header, remainder) = IntermediateShardHeader::try_from_bytes_unchecked(bytes)?;
493 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
494 let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(remainder)?;
495
496 Some((Self { header, body }, remainder))
497 }
498
499 #[cfg(feature = "alloc")]
501 #[inline(always)]
502 pub fn to_owned(self) -> OwnedIntermediateShardBlock {
503 OwnedIntermediateShardBlock {
504 header: self.header.to_owned(),
505 body: self.body.to_owned(),
506 }
507 }
508}
509
510impl<'a> GenericBlock<'a> for IntermediateShardBlock<'a> {
511 const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
512
513 type Header = IntermediateShardHeader<'a>;
514 type Body = IntermediateShardBody<'a>;
515 #[cfg(feature = "alloc")]
516 type Owned = OwnedIntermediateShardBlock;
517
518 #[inline(always)]
519 fn header(&self) -> &Self::Header {
520 &self.header
521 }
522
523 #[inline(always)]
524 fn body(&self) -> &Self::Body {
525 &self.body
526 }
527
528 #[cfg(feature = "alloc")]
529 #[inline(always)]
530 fn to_owned(self) -> Self::Owned {
531 self.to_owned()
532 }
533}
534
535#[derive(Debug, Clone)]
537#[non_exhaustive]
539pub struct LeafShardBlock<'a> {
540 header: LeafShardHeader<'a>,
542 body: LeafShardBody<'a>,
544}
545
546impl<'a> LeafShardBlock<'a> {
547 #[inline]
557 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
558 let (header, remainder) = LeafShardHeader::try_from_bytes(bytes)?;
559 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
560 let (body, remainder) = LeafShardBody::try_from_bytes(remainder)?;
561
562 let block = Self { header, body };
563
564 if !block.is_internally_consistent() {
566 return None;
567 }
568
569 Some((block, remainder))
570 }
571
572 #[inline]
580 pub fn is_internally_consistent(&self) -> bool {
581 self.body.root() == self.header.result.body_root
582 }
583
584 #[inline]
587 pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
588 let (header, remainder) = LeafShardHeader::try_from_bytes_unchecked(bytes)?;
589 let remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
590 let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(remainder)?;
591
592 Some((Self { header, body }, remainder))
593 }
594
595 #[cfg(feature = "alloc")]
597 #[inline(always)]
598 pub fn to_owned(self) -> OwnedLeafShardBlock {
599 OwnedLeafShardBlock {
600 header: self.header.to_owned(),
601 body: self.body.to_owned(),
602 }
603 }
604}
605
606impl<'a> GenericBlock<'a> for LeafShardBlock<'a> {
607 const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
608
609 type Header = LeafShardHeader<'a>;
610 type Body = LeafShardBody<'a>;
611 #[cfg(feature = "alloc")]
612 type Owned = OwnedLeafShardBlock;
613
614 #[inline(always)]
615 fn header(&self) -> &Self::Header {
616 &self.header
617 }
618
619 #[inline(always)]
620 fn body(&self) -> &Self::Body {
621 &self.body
622 }
623
624 #[cfg(feature = "alloc")]
625 #[inline(always)]
626 fn to_owned(self) -> Self::Owned {
627 self.to_owned()
628 }
629}
630
631#[derive(Debug, Clone, From)]
636pub enum Block<'a> {
637 BeaconChain(BeaconChainBlock<'a>),
639 IntermediateShard(IntermediateShardBlock<'a>),
641 LeafShard(LeafShardBlock<'a>),
643}
644
645impl<'a> Block<'a> {
646 #[inline]
656 pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
657 match shard_kind {
658 RealShardKind::BeaconChain => {
659 let (block_header, remainder) = BeaconChainBlock::try_from_bytes(bytes)?;
660 Some((Self::BeaconChain(block_header), remainder))
661 }
662 RealShardKind::IntermediateShard => {
663 let (block_header, remainder) = IntermediateShardBlock::try_from_bytes(bytes)?;
664 Some((Self::IntermediateShard(block_header), remainder))
665 }
666 RealShardKind::LeafShard => {
667 let (block_header, remainder) = LeafShardBlock::try_from_bytes(bytes)?;
668 Some((Self::LeafShard(block_header), remainder))
669 }
670 }
671 }
672
673 #[inline]
681 pub fn is_internally_consistent(&self) -> bool {
682 match self {
683 Self::BeaconChain(block) => block.is_internally_consistent(),
684 Self::IntermediateShard(block) => block.is_internally_consistent(),
685 Self::LeafShard(block) => block.is_internally_consistent(),
686 }
687 }
688
689 #[inline]
692 pub fn try_from_bytes_unchecked(
693 bytes: &'a [u8],
694 shard_kind: RealShardKind,
695 ) -> Option<(Self, &'a [u8])> {
696 match shard_kind {
697 RealShardKind::BeaconChain => {
698 let (block_header, remainder) = BeaconChainBlock::try_from_bytes_unchecked(bytes)?;
699 Some((Self::BeaconChain(block_header), remainder))
700 }
701 RealShardKind::IntermediateShard => {
702 let (block_header, remainder) =
703 IntermediateShardBlock::try_from_bytes_unchecked(bytes)?;
704 Some((Self::IntermediateShard(block_header), remainder))
705 }
706 RealShardKind::LeafShard => {
707 let (block_header, remainder) = LeafShardBlock::try_from_bytes_unchecked(bytes)?;
708 Some((Self::LeafShard(block_header), remainder))
709 }
710 }
711 }
712
713 #[cfg(feature = "alloc")]
715 #[inline(always)]
716 pub fn to_owned(self) -> OwnedBlock {
717 match self {
718 Self::BeaconChain(block) => block.to_owned().into(),
719 Self::IntermediateShard(block) => block.to_owned().into(),
720 Self::LeafShard(block) => block.to_owned().into(),
721 }
722 }
723}
724
725#[derive(
729 Debug,
730 Display,
731 Default,
732 Copy,
733 Clone,
734 Ord,
735 PartialOrd,
736 Eq,
737 PartialEq,
738 Hash,
739 From,
740 Into,
741 Add,
742 AddAssign,
743 Sub,
744 SubAssign,
745)]
746#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
747#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
748#[repr(C)]
749pub struct BlockWeight(u128);
750
751impl BlockWeight {
752 pub const SIZE: usize = size_of::<u128>();
754 pub const ZERO: BlockWeight = BlockWeight(0);
756 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
758
759 #[inline(always)]
761 pub const fn new(n: u128) -> Self {
762 Self(n)
763 }
764
765 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
767 Self::new((SolutionRange::MAX.as_u64() - solution_range.as_u64()) as u128)
768 }
769
770 #[inline(always)]
772 pub const fn as_u128(self) -> u128 {
773 self.0
774 }
775}
776
777fn align_to_and_ensure_zero_padding<T>(bytes: &[u8]) -> Option<&[u8]> {
779 let padding = unsafe { bytes.align_to::<T>() }.0;
781
782 if padding.iter().any(|&byte| byte != 0) {
784 return None;
785 }
786
787 Some(&bytes[padding.len()..])
788}