ab_core_primitives/shard.rs
1//! Shard-related primitives
2
3use ab_io_type::trivial_type::TrivialType;
4use core::num::{NonZeroU32, NonZeroU128};
5use derive_more::Display;
6#[cfg(feature = "scale-codec")]
7use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
8#[cfg(feature = "scale-codec")]
9use scale_info::TypeInfo;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13/// A kind of shard.
14///
15/// Schematically, the hierarchy of shards is as follows:
16/// ```text
17/// Beacon chain
18/// / \
19/// Intermediate shard 1 Intermediate shard 2
20/// / \ / \
21/// Leaf shard 11 Leaf shard 12 Leaf shard 22 Leaf shard 22
22/// ```
23///
24/// Beacon chain has index 0, intermediate shards indices 1..=1023. Leaf shards share the same least
25/// significant 10 bits as their respective intermediate shards, so leaf shards of intermediate
26/// shard 1 have indices like 1025,2049,3097,etc.
27///
28/// If represented as least significant bits first (as it will be in the formatted address):
29/// ```text
30/// Intermediate shard bits
31/// \ /
32/// 1000_0000_0001_0000_0000
33/// / \
34/// Leaf shard bits
35/// ```
36///
37/// Note that shards that have 10 least significant bits set to 0 (corresponds to the beacon chain)
38/// are not leaf shards, in fact, they are not even physical shards that have nodes in general. The
39/// meaning of these shards is TBD, currently they are called "phantom" shards and may end up
40/// containing some system contracts with special meaning, but no blocks will ever exist for such
41/// shards.
42#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
43pub enum ShardKind {
44 /// Beacon chain shard
45 BeaconChain,
46 /// Intermediate shard directly below the beacon chain that has child shards
47 IntermediateShard,
48 /// Leaf shard, doesn't have child shards
49 LeafShard,
50 /// TODO
51 Phantom,
52 /// Invalid shard kind (if decoded from invalid bit pattern)
53 Invalid,
54}
55
56/// Shard index
57#[derive(Debug, Display, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, TrivialType)]
58#[cfg_attr(
59 feature = "scale-codec",
60 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
61)]
62#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
63#[repr(C)]
64pub struct ShardIndex(u32);
65
66impl ShardIndex {
67 /// Beacon chain
68 pub const BEACON_CHAIN: Self = Self(0);
69 /// Max possible shard index
70 pub const MAX_SHARD_INDEX: u32 = Self::MAX_SHARDS.get() - 1;
71 /// Max possible number of shards
72 pub const MAX_SHARDS: NonZeroU32 = NonZeroU32::new(2u32.pow(20)).expect("Not zero; qed");
73 /// Max possible number of addresses per shard
74 pub const MAX_ADDRESSES_PER_SHARD: NonZeroU128 =
75 NonZeroU128::new(2u128.pow(108)).expect("Not zero; qed");
76
77 // TODO: Remove once traits work in const environment and `From` could be used
78 /// Create shard index from `u32`.
79 ///
80 /// Returns `None` if `shard_index > ShardIndex::MAX_SHARD_INDEX`
81 ///
82 /// This is typically only necessary for low-level code.
83 #[inline(always)]
84 pub const fn new(shard_index: u32) -> Option<Self> {
85 if shard_index > Self::MAX_SHARD_INDEX {
86 return None;
87 }
88
89 Some(Self(shard_index))
90 }
91
92 // TODO: Remove once traits work in const environment and `From` could be used
93 /// Convert shard index to `u32`.
94 ///
95 /// This is typically only necessary for low-level code.
96 #[inline(always)]
97 pub const fn as_u32(self) -> u32 {
98 self.0
99 }
100
101 /// Whether the shard index corresponds to the beacon chain
102 #[inline(always)]
103 pub const fn is_beacon_chain(&self) -> bool {
104 self.0 == Self::BEACON_CHAIN.0
105 }
106
107 /// Whether the shard index corresponds to an intermediate shard
108 #[inline(always)]
109 pub const fn is_intermediate_shard(&self) -> bool {
110 self.0 >= 1 && self.0 <= 1023
111 }
112
113 /// Whether the shard index corresponds to an intermediate shard
114 #[inline(always)]
115 pub const fn is_leaf_shard(&self) -> bool {
116 if self.0 <= 1023 || self.0 > Self::MAX_SHARD_INDEX {
117 return false;
118 }
119
120 self.0 & 0b11_1111_1111 != 0
121 }
122
123 /// Whether the shard index corresponds to a phantom shard
124 #[inline(always)]
125 pub const fn is_phantom_shard(&self) -> bool {
126 if self.0 <= 1023 || self.0 > Self::MAX_SHARD_INDEX {
127 return false;
128 }
129
130 self.0 & 0b11_1111_1111 == 0
131 }
132
133 /// Check if this shard is a child shard of `parent`
134 #[inline]
135 pub const fn is_child_of(self, parent: Self) -> bool {
136 match self.shard_kind() {
137 ShardKind::BeaconChain => false,
138 ShardKind::IntermediateShard | ShardKind::Phantom => parent.is_beacon_chain(),
139 ShardKind::LeafShard => {
140 // Check that least significant bits match
141 self.0 & 0b11_1111_1111 == parent.0 & 0b11_1111_1111
142 }
143 ShardKind::Invalid => false,
144 }
145 }
146
147 /// Get shard kind
148 #[inline(always)]
149 pub const fn shard_kind(&self) -> ShardKind {
150 match self.0 {
151 0 => ShardKind::BeaconChain,
152 1..=1023 => ShardKind::IntermediateShard,
153 shard_index => {
154 if shard_index > Self::MAX_SHARD_INDEX {
155 return ShardKind::Invalid;
156 }
157
158 // Check if least significant bits correspond to the beacon chain
159 if shard_index & 0b11_1111_1111 == 0 {
160 ShardKind::Phantom
161 } else {
162 ShardKind::LeafShard
163 }
164 }
165 }
166 }
167}