1pub mod body;
4pub mod header;
5#[cfg(feature = "alloc")]
6pub mod owned;
7
8use crate::block::body::{
9 BeaconChainBody, GenericBlockBody, IntermediateShardBody, LeafShardBlocksInfo, LeafShardBody,
10};
11use crate::block::header::{
12 BeaconChainHeader, GenericBlockHeader, IntermediateShardHeader, LeafShardHeader,
13};
14#[cfg(feature = "alloc")]
15use crate::block::owned::{
16 GenericOwnedBlock, OwnedBeaconChainBlock, OwnedBlock, OwnedIntermediateShardBlock,
17 OwnedLeafShardBlock,
18};
19use crate::hashes::Blake3Hash;
20use crate::shard::{RealShardKind, ShardIndex};
21use crate::solutions::SolutionRange;
22#[cfg(feature = "serde")]
23use ::serde::{Deserialize, Serialize};
24use ab_io_type::alignment::Alignment;
25use ab_io_type::trivial_type::TrivialType;
26use core::fmt;
27use core::iter::Step;
28use derive_more::{
29 Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, From, Into, Sub, SubAssign,
30};
31#[cfg(feature = "scale-codec")]
32use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
33use transparent_wrapper::TransparentWrapper;
34
35#[derive(
37 Debug,
38 Display,
39 Default,
40 Copy,
41 Clone,
42 Ord,
43 PartialOrd,
44 Eq,
45 PartialEq,
46 Hash,
47 Add,
48 AddAssign,
49 Sub,
50 SubAssign,
51 TrivialType,
52)]
53#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55#[cfg_attr(feature = "serde", serde(transparent))]
56#[repr(C)]
57pub struct BlockNumber(u64);
58
59impl Step for BlockNumber {
60 #[inline(always)]
61 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
62 u64::steps_between(&start.0, &end.0)
63 }
64
65 #[inline(always)]
66 fn forward_checked(start: Self, count: usize) -> Option<Self> {
67 u64::forward_checked(start.0, count).map(Self)
68 }
69
70 #[inline(always)]
71 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
72 let (n, overflowing) = u64::forward_overflowing(start.0, count);
73 (Self(n), overflowing)
74 }
75
76 #[inline(always)]
77 fn backward_checked(start: Self, count: usize) -> Option<Self> {
78 u64::backward_checked(start.0, count).map(Self)
79 }
80
81 #[inline(always)]
82 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
83 let (n, overflowing) = u64::backward_overflowing(start.0, count);
84 (Self(n), overflowing)
85 }
86}
87
88const impl From<u64> for BlockNumber {
89 #[inline(always)]
90 fn from(value: u64) -> Self {
91 Self(value)
92 }
93}
94
95const impl From<BlockNumber> for u64 {
96 #[inline(always)]
97 fn from(value: BlockNumber) -> Self {
98 value.0
99 }
100}
101
102impl BlockNumber {
103 pub const SIZE: usize = size_of::<u64>();
105 pub const ZERO: BlockNumber = BlockNumber(0);
107 pub const ONE: BlockNumber = BlockNumber(1);
109 pub const MAX: BlockNumber = BlockNumber(u64::MAX);
111
112 #[inline(always)]
114 pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
115 Self(u64::from_le_bytes(bytes))
116 }
117
118 #[inline(always)]
120 pub const fn to_bytes(self) -> [u8; Self::SIZE] {
121 self.0.to_le_bytes()
122 }
123
124 #[inline(always)]
126 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
127 if let Some(n) = self.0.checked_add(rhs.0) {
128 Some(Self(n))
129 } else {
130 None
131 }
132 }
133
134 #[inline(always)]
136 pub const fn saturating_add(self, rhs: Self) -> Self {
137 Self(self.0.saturating_add(rhs.0))
138 }
139
140 #[inline(always)]
142 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
143 if let Some(n) = self.0.checked_sub(rhs.0) {
144 Some(Self(n))
145 } else {
146 None
147 }
148 }
149
150 #[inline(always)]
152 pub const fn saturating_sub(self, rhs: Self) -> Self {
153 Self(self.0.saturating_sub(rhs.0))
154 }
155}
156
157#[derive(
159 Debug,
160 Display,
161 Default,
162 Copy,
163 Clone,
164 Ord,
165 PartialOrd,
166 Eq,
167 PartialEq,
168 Hash,
169 From,
170 Into,
171 Add,
172 AddAssign,
173 Sub,
174 SubAssign,
175 TrivialType,
176)]
177#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
179#[cfg_attr(feature = "serde", serde(transparent))]
180#[repr(C)]
181pub struct BlockTimestamp(u64);
182
183impl BlockTimestamp {
184 pub const SIZE: usize = size_of::<u64>();
186
187 #[inline(always)]
189 pub const fn from_millis(ms: u64) -> Self {
190 Self(ms)
191 }
192
193 #[inline(always)]
195 pub const fn as_millis(self) -> u64 {
196 self.0
197 }
198
199 #[inline(always)]
201 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
202 if let Some(n) = self.0.checked_add(rhs.0) {
203 Some(Self(n))
204 } else {
205 None
206 }
207 }
208
209 #[inline(always)]
211 pub const fn saturating_add(self, rhs: Self) -> Self {
212 Self(self.0.saturating_add(rhs.0))
213 }
214
215 #[inline(always)]
217 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
218 if let Some(n) = self.0.checked_sub(rhs.0) {
219 Some(Self(n))
220 } else {
221 None
222 }
223 }
224
225 #[inline(always)]
227 pub const fn saturating_sub(self, rhs: Self) -> Self {
228 Self(self.0.saturating_sub(rhs.0))
229 }
230}
231
232#[derive(
237 Debug,
238 Display,
239 Default,
240 Copy,
241 Clone,
242 Eq,
243 PartialEq,
244 Ord,
245 PartialOrd,
246 Hash,
247 From,
248 Into,
249 AsRef,
250 AsMut,
251 Deref,
252 DerefMut,
253 TrivialType,
254 TransparentWrapper,
255)]
256#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
257#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
258#[cfg_attr(feature = "serde", serde(transparent))]
259#[repr(C)]
260pub struct BlockRoot(Blake3Hash);
261
262impl AsRef<[u8]> for BlockRoot {
263 #[inline(always)]
264 fn as_ref(&self) -> &[u8] {
265 self.0.as_ref()
266 }
267}
268
269impl AsMut<[u8]> for BlockRoot {
270 #[inline(always)]
271 fn as_mut(&mut self) -> &mut [u8] {
272 self.0.as_mut()
273 }
274}
275
276impl BlockRoot {
277 pub const SIZE: usize = Blake3Hash::SIZE;
279
280 #[inline(always)]
282 pub const fn new(hash: Blake3Hash) -> Self {
283 Self(hash)
284 }
285
286 #[inline(always)]
288 pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
289 Self::wrap_slice(Blake3Hash::slice_from_repr(value))
290 }
291
292 #[inline(always)]
294 pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
295 Blake3Hash::repr_from_slice(Self::peel_slice(value))
296 }
297}
298
299pub impl(self) trait GenericBlock<'a>
304where
305 Self: Clone + fmt::Debug + Send + Sync,
306{
307 const SHARD_KIND: RealShardKind;
309
310 type Header: GenericBlockHeader<'a>;
312 type Body: GenericBlockBody<'a>;
314 #[cfg(feature = "alloc")]
316 type Owned: GenericOwnedBlock<Block<'a> = Self>
317 where
318 Self: 'a;
319
320 fn header(&self) -> &Self::Header;
322
323 fn body(&self) -> &Self::Body;
325
326 #[cfg(feature = "alloc")]
328 fn to_owned(self) -> Self::Owned;
329}
330
331#[derive(Debug, Clone)]
336pub struct BeaconChainBlock<'a> {
337 pub mut(self) header: BeaconChainHeader<'a>,
339 pub mut(self) body: BeaconChainBody<'a>,
341}
342
343impl<'a> BeaconChainBlock<'a> {
344 #[inline]
354 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
355 if !Alignment::MAX.is_slice_aligned(bytes) {
356 return None;
357 }
358
359 let (header, remainder) = BeaconChainHeader::try_from_bytes(bytes)?;
360 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
361 let (body, remainder) = BeaconChainBody::try_from_bytes(remainder)?;
362
363 let block = Self { header, body };
364
365 if !block.is_internally_consistent() {
367 return None;
368 }
369
370 Some((block, remainder))
371 }
372
373 #[inline]
377 fn is_internally_consistent(&self) -> bool {
378 let intermediate_shard_blocks_root = self.body.intermediate_shard_blocks().headers_root();
381
382 self.body
385 .root_with_intermediate_shard_blocks_root(&intermediate_shard_blocks_root)
386 == self.header.result.body_root
387 && *self.header.intermediate_shard_blocks_root() == intermediate_shard_blocks_root
388 }
389
390 #[cfg(feature = "alloc")]
392 #[inline(always)]
393 pub fn to_owned(self) -> OwnedBeaconChainBlock {
394 OwnedBeaconChainBlock::from_block(self)
395 }
396}
397
398impl<'a> GenericBlock<'a> for BeaconChainBlock<'a> {
399 const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
400
401 type Header = BeaconChainHeader<'a>;
402 type Body = BeaconChainBody<'a>;
403 #[cfg(feature = "alloc")]
404 type Owned = OwnedBeaconChainBlock;
405
406 #[inline(always)]
407 fn header(&self) -> &Self::Header {
408 &self.header
409 }
410
411 #[inline(always)]
412 fn body(&self) -> &Self::Body {
413 &self.body
414 }
415
416 #[cfg(feature = "alloc")]
417 #[inline(always)]
418 fn to_owned(self) -> Self::Owned {
419 self.to_owned()
420 }
421}
422
423#[derive(Debug, Clone)]
428pub struct IntermediateShardBlock<'a> {
429 pub mut(self) header: IntermediateShardHeader<'a>,
431 pub mut(self) body: IntermediateShardBody<'a>,
433}
434
435impl<'a> IntermediateShardBlock<'a> {
436 #[inline]
446 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
447 if !Alignment::MAX.is_slice_aligned(bytes) {
448 return None;
449 }
450
451 let (header, remainder) = IntermediateShardHeader::try_from_bytes(bytes)?;
452 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
453 let (body, remainder) = IntermediateShardBody::try_from_bytes(remainder)?;
454
455 let block = Self { header, body };
456
457 if !block.is_internally_consistent() {
459 return None;
460 }
461
462 Some((block, remainder))
463 }
464
465 #[inline]
469 fn is_internally_consistent(&self) -> bool {
470 let leaf_shard_blocks_root = self.body.leaf_shard_blocks().headers_root();
473
474 self.body
475 .root_with_leaf_shard_blocks_root(&leaf_shard_blocks_root)
476 == self.header.result.body_root
477 && *self.header.leaf_shard_blocks_root() == leaf_shard_blocks_root
478 && Self::find_leaf_shard_block_of_another_shard(
479 self.body.leaf_shard_blocks(),
480 self.header.prefix.shard_index,
481 )
482 .is_none()
483 }
484
485 #[inline]
490 fn find_leaf_shard_block_of_another_shard(
491 leaf_shard_blocks: &LeafShardBlocksInfo<'_>,
492 shard_index: ShardIndex,
493 ) -> Option<ShardIndex> {
494 leaf_shard_blocks
495 .iter()
496 .map(|leaf_shard_block| leaf_shard_block.header.prefix.shard_index)
497 .find(|leaf_shard_index| !leaf_shard_index.is_child_of(shard_index))
498 }
499
500 #[cfg(feature = "alloc")]
502 #[inline(always)]
503 pub fn to_owned(self) -> OwnedIntermediateShardBlock {
504 OwnedIntermediateShardBlock::from_block(self)
505 }
506}
507
508impl<'a> GenericBlock<'a> for IntermediateShardBlock<'a> {
509 const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
510
511 type Header = IntermediateShardHeader<'a>;
512 type Body = IntermediateShardBody<'a>;
513 #[cfg(feature = "alloc")]
514 type Owned = OwnedIntermediateShardBlock;
515
516 #[inline(always)]
517 fn header(&self) -> &Self::Header {
518 &self.header
519 }
520
521 #[inline(always)]
522 fn body(&self) -> &Self::Body {
523 &self.body
524 }
525
526 #[cfg(feature = "alloc")]
527 #[inline(always)]
528 fn to_owned(self) -> Self::Owned {
529 self.to_owned()
530 }
531}
532
533#[derive(Debug, Clone)]
538pub struct LeafShardBlock<'a> {
539 pub mut(self) header: LeafShardHeader<'a>,
541 pub mut(self) body: LeafShardBody<'a>,
543}
544
545impl<'a> LeafShardBlock<'a> {
546 #[inline]
556 pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
557 if !Alignment::MAX.is_slice_aligned(bytes) {
558 return None;
559 }
560
561 let (header, remainder) = LeafShardHeader::try_from_bytes(bytes)?;
562 let remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
563 let (body, remainder) = LeafShardBody::try_from_bytes(remainder)?;
564
565 let block = Self { header, body };
566
567 if !block.is_internally_consistent() {
569 return None;
570 }
571
572 Some((block, remainder))
573 }
574
575 #[inline]
579 fn is_internally_consistent(&self) -> bool {
580 self.body.root() == self.header.result.body_root
581 }
582
583 #[cfg(feature = "alloc")]
585 #[inline(always)]
586 pub fn to_owned(self) -> OwnedLeafShardBlock {
587 OwnedLeafShardBlock::from_block(self)
588 }
589}
590
591impl<'a> GenericBlock<'a> for LeafShardBlock<'a> {
592 const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
593
594 type Header = LeafShardHeader<'a>;
595 type Body = LeafShardBody<'a>;
596 #[cfg(feature = "alloc")]
597 type Owned = OwnedLeafShardBlock;
598
599 #[inline(always)]
600 fn header(&self) -> &Self::Header {
601 &self.header
602 }
603
604 #[inline(always)]
605 fn body(&self) -> &Self::Body {
606 &self.body
607 }
608
609 #[cfg(feature = "alloc")]
610 #[inline(always)]
611 fn to_owned(self) -> Self::Owned {
612 self.to_owned()
613 }
614}
615
616#[derive(Debug, Clone, From)]
621pub enum Block<'a> {
622 BeaconChain(BeaconChainBlock<'a>),
624 IntermediateShard(IntermediateShardBlock<'a>),
626 LeafShard(LeafShardBlock<'a>),
628}
629
630impl<'a> Block<'a> {
631 #[inline]
641 pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
642 match shard_kind {
643 RealShardKind::BeaconChain => {
644 let (block_header, remainder) = BeaconChainBlock::try_from_bytes(bytes)?;
645 Some((Self::BeaconChain(block_header), remainder))
646 }
647 RealShardKind::IntermediateShard => {
648 let (block_header, remainder) = IntermediateShardBlock::try_from_bytes(bytes)?;
649 Some((Self::IntermediateShard(block_header), remainder))
650 }
651 RealShardKind::LeafShard => {
652 let (block_header, remainder) = LeafShardBlock::try_from_bytes(bytes)?;
653 Some((Self::LeafShard(block_header), remainder))
654 }
655 }
656 }
657
658 #[cfg(feature = "alloc")]
660 #[inline(always)]
661 pub fn to_owned(self) -> OwnedBlock {
662 match self {
663 Self::BeaconChain(block) => block.to_owned().into(),
664 Self::IntermediateShard(block) => block.to_owned().into(),
665 Self::LeafShard(block) => block.to_owned().into(),
666 }
667 }
668}
669
670#[derive(
674 Debug,
675 Display,
676 Default,
677 Copy,
678 Clone,
679 Ord,
680 PartialOrd,
681 Eq,
682 PartialEq,
683 Hash,
684 Add,
685 AddAssign,
686 Sub,
687 SubAssign,
688)]
689#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
690#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
691#[repr(C)]
692pub struct BlockWeight(u128);
693
694const impl From<u128> for BlockWeight {
695 #[inline(always)]
696 fn from(value: u128) -> Self {
697 Self(value)
698 }
699}
700
701const impl From<BlockWeight> for u128 {
702 #[inline(always)]
703 fn from(value: BlockWeight) -> Self {
704 value.0
705 }
706}
707
708impl BlockWeight {
709 pub const SIZE: usize = size_of::<u128>();
711 pub const ZERO: BlockWeight = BlockWeight(0);
713 pub const MAX: BlockWeight = BlockWeight(u128::MAX);
715
716 pub const fn from_solution_range(solution_range: SolutionRange) -> Self {
718 Self::from(u128::from(
719 u64::from(SolutionRange::MAX) - u64::from(solution_range),
720 ))
721 }
722}
723
724fn align_to_and_ensure_zero_padding(bytes: &[u8], alignment: Alignment) -> Option<&[u8]> {
728 let padding_len = bytes.as_ptr().align_offset(usize::from(alignment.get()));
729 let (padding, remainder) = bytes.split_at_checked(padding_len)?;
730
731 if padding.iter().any(|&byte| byte != 0) {
733 return None;
734 }
735
736 Some(remainder)
737}