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, which doesn't have child shards
49 LeafShard,
50 /// TODO
51 Phantom,
52}
53
54impl ShardKind {
55 /// Try to convert to real shard kind.
56 ///
57 /// Returns `None` for phantom shard.
58 #[inline(always)]
59 pub fn to_real(self) -> Option<RealShardKind> {
60 match self {
61 ShardKind::BeaconChain => Some(RealShardKind::BeaconChain),
62 ShardKind::IntermediateShard => Some(RealShardKind::IntermediateShard),
63 ShardKind::LeafShard => Some(RealShardKind::LeafShard),
64 ShardKind::Phantom => None,
65 }
66 }
67}
68
69/// Real shard kind for which a block may exist, see [`ShardKind`] for more details
70#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
71pub enum RealShardKind {
72 /// Beacon chain shard
73 BeaconChain,
74 /// Intermediate shard directly below the beacon chain that has child shards
75 IntermediateShard,
76 /// Leaf shard, which doesn't have child shards
77 LeafShard,
78}
79
80impl From<RealShardKind> for ShardKind {
81 #[inline(always)]
82 fn from(shard_kind: RealShardKind) -> Self {
83 match shard_kind {
84 RealShardKind::BeaconChain => ShardKind::BeaconChain,
85 RealShardKind::IntermediateShard => ShardKind::IntermediateShard,
86 RealShardKind::LeafShard => ShardKind::LeafShard,
87 }
88 }
89}
90
91/// Shard index
92#[derive(Debug, Display, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, TrivialType)]
93#[cfg_attr(
94 feature = "scale-codec",
95 derive(Encode, Decode, TypeInfo, MaxEncodedLen)
96)]
97#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
98#[repr(C)]
99pub struct ShardIndex(u32);
100
101impl ShardIndex {
102 /// Beacon chain
103 pub const BEACON_CHAIN: Self = Self(0);
104 /// Max possible shard index
105 pub const MAX_SHARD_INDEX: u32 = Self::MAX_SHARDS.get() - 1;
106 /// Max possible number of shards
107 pub const MAX_SHARDS: NonZeroU32 = NonZeroU32::new(2u32.pow(20)).expect("Not zero; qed");
108 /// Max possible number of addresses per shard
109 pub const MAX_ADDRESSES_PER_SHARD: NonZeroU128 =
110 NonZeroU128::new(2u128.pow(108)).expect("Not zero; qed");
111
112 // TODO: Remove once traits work in const environment and `From` could be used
113 /// Create shard index from `u32`.
114 ///
115 /// Returns `None` if `shard_index > ShardIndex::MAX_SHARD_INDEX`
116 ///
117 /// This is typically only necessary for low-level code.
118 #[inline(always)]
119 pub const fn new(shard_index: u32) -> Option<Self> {
120 if shard_index > Self::MAX_SHARD_INDEX {
121 return None;
122 }
123
124 Some(Self(shard_index))
125 }
126
127 // TODO: Remove once traits work in const environment and `From` could be used
128 /// Convert shard index to `u32`.
129 ///
130 /// This is typically only necessary for low-level code.
131 #[inline(always)]
132 pub const fn as_u32(self) -> u32 {
133 self.0
134 }
135
136 /// Whether the shard index corresponds to the beacon chain
137 #[inline(always)]
138 pub const fn is_beacon_chain(&self) -> bool {
139 self.0 == Self::BEACON_CHAIN.0
140 }
141
142 /// Whether the shard index corresponds to an intermediate shard
143 #[inline(always)]
144 pub const fn is_intermediate_shard(&self) -> bool {
145 self.0 >= 1 && self.0 <= 1023
146 }
147
148 /// Whether the shard index corresponds to an intermediate shard
149 #[inline(always)]
150 pub const fn is_leaf_shard(&self) -> bool {
151 if self.0 <= 1023 || self.0 > Self::MAX_SHARD_INDEX {
152 return false;
153 }
154
155 self.0 & 0b11_1111_1111 != 0
156 }
157
158 /// Whether the shard index corresponds to a real shard
159 #[inline(always)]
160 pub const fn is_real(&self) -> bool {
161 !self.is_phantom_shard()
162 }
163
164 /// Whether the shard index corresponds to a phantom shard
165 #[inline(always)]
166 pub const fn is_phantom_shard(&self) -> bool {
167 if self.0 <= 1023 || self.0 > Self::MAX_SHARD_INDEX {
168 return false;
169 }
170
171 self.0 & 0b11_1111_1111 == 0
172 }
173
174 /// Check if this shard is a child shard of `parent`
175 #[inline]
176 pub const fn is_child_of(self, parent: Self) -> bool {
177 match self.shard_kind() {
178 Some(ShardKind::BeaconChain) => false,
179 Some(ShardKind::IntermediateShard | ShardKind::Phantom) => parent.is_beacon_chain(),
180 Some(ShardKind::LeafShard) => {
181 // Check that the least significant bits match
182 self.0 & 0b11_1111_1111 == parent.0 & 0b11_1111_1111
183 }
184 None => false,
185 }
186 }
187
188 /// Get shard kind
189 #[inline(always)]
190 pub const fn shard_kind(&self) -> Option<ShardKind> {
191 Some(match self.0 {
192 0 => ShardKind::BeaconChain,
193 1..=1023 => ShardKind::IntermediateShard,
194 shard_index => {
195 if shard_index > Self::MAX_SHARD_INDEX {
196 return None;
197 }
198
199 // Check if the least significant bits correspond to the beacon chain
200 if shard_index & 0b11_1111_1111 == 0 {
201 ShardKind::Phantom
202 } else {
203 ShardKind::LeafShard
204 }
205 }
206 })
207 }
208}