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}