tycho_types/models/
global_version.rs

1//! Global version and capabilities.
2
3use crate::cell::{Load, Store};
4use crate::error::ParseGlobalCapabilityError;
5
6macro_rules! decl_global_capability {
7    ($(#[doc = $doc:expr])* $vis:vis enum $ident:ident {$(
8        $(#[doc = $var_doc:expr])*
9        $field:ident = $descr:literal
10    ),*$(,)?}) => {
11        $(#[doc = $doc])*
12        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
13        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14        #[repr(u64)]
15        $vis enum $ident {$(
16            $(#[doc = $var_doc])*
17            $field = 1u64 << $descr
18        ),*,}
19
20        impl GlobalCapability {
21            const fn from_bit_offset(bit_offset: u32) -> Option<Self> {
22                Some(match bit_offset {
23                    $($descr => Self::$field),*,
24                    _ => return None,
25                })
26            }
27        }
28
29        impl std::fmt::Display for GlobalCapability {
30            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31                f.write_str(match self {
32                    $(Self::$field => stringify!($field),)*
33                })
34            }
35        }
36
37        impl std::str::FromStr for GlobalCapability {
38            type Err = ParseGlobalCapabilityError;
39
40            fn from_str(s: &str) -> Result<Self, Self::Err> {
41                Ok(match s {
42                    $(stringify!($field) => Self::$field,)*
43                    _ => return Err(ParseGlobalCapabilityError::UnknownCapability),
44                })
45            }
46        }
47    };
48}
49
50decl_global_capability! {
51    /// Node software capabilities.
52    pub enum GlobalCapability {
53        /// Instant Hypercube Routing.
54        ///
55        /// Mask: `0x0000001`.
56        CapIhrEnabled = 0,
57
58        /// Tracking of block collation stats.
59        ///
60        /// Mask: `0x0000002`.
61        CapCreateStatsEnabled = 1,
62
63        /// Body (at most 256 bits) in bounced messages.
64        ///
65        /// Mask: `0x0000004`.
66        CapBounceMsgBody = 2,
67
68        /// Supported software version and capabilities as field in [`BlockInfo`].
69        ///
70        /// Mask: `0x0000008`.
71        ///
72        /// [`BlockInfo`]: crate::models::block::BlockInfo
73        CapReportVersion = 3,
74
75        /// Special transactions on split or merge.
76        ///
77        /// Mask: `0x0000010`.
78        CapSplitMergeTransactions = 4,
79
80        /// Short output messages queue.
81        ///
82        /// Mask: `0x0000020`.
83        CapShortDequeue = 5,
84
85        /// _unknown_ (possibly just a stub).
86        ///
87        /// Mask: `0x0000040`.
88        CapMbppEnabled = 6,
89
90        /// Precompute storage stats for cells and use this info for storage phase.
91        /// NOTE: changes behavior for storage phase, computing stats for non-unique cells.
92        ///
93        /// Mask: `0x0000080`
94        CapFastStorageStat = 7,
95
96        /// Store init code hash in account state.
97        ///
98        /// Mask: `0x0000100`.
99        CapInitCodeHash = 8,
100
101        /// Disable hypercube for message routing.
102        ///
103        /// Mask: `0x0000200`.
104        CapOffHypercube = 9,
105
106        /// `MYCODE` TVM opcode.
107        ///
108        /// Mask: `0x0000400`.
109        CapMyCode = 10,
110
111        /// `CHANGELIB` and `SETLIBCODE` TVM opcodes.
112        ///
113        /// Mask: `0x0000800`.
114        CapSetLibCode = 11,
115
116        /// Fix in `SETINDEX*` TVM opcodes.
117        ///
118        /// Mask: `0x0001000`.
119        CapFixTupleIndexBug = 12,
120
121        /// Reliable External Messaging Protocol.
122        ///
123        /// Mask: `0x0002000`.
124        CapRemp = 13,
125
126        /// Support for decentralized elections.
127        ///
128        /// Mask: `0x0004000`.
129        CapDelections = 14,
130
131        // Capability 15 is reserved (?)
132        /// Full message body in bounced messages (in the first child cell).
133        ///
134        /// Mask: `0x0010000`.
135        CapFullBodyInBounced = 16,
136
137        /// `STORAGEFEE` TVM opcode.
138        ///
139        /// Mask: `0x0020000`.
140        CapStorageFeeToTvm = 17,
141
142        /// Support for copyleft messages.
143        ///
144        /// Mask: `0x0040000`.
145        CapCopyleft = 18,
146
147        /// `FIND_BY_*` TVM opcodes.
148        ///
149        /// Mask: `0x0080000`.
150        CapIndexAccounts = 19,
151
152        /// `DIFF*`, `ZIP`, `UNZIP` TVM opcodes.
153        ///
154        /// Mask: `0x0100000`.
155        CapDiff = 20,
156
157        /// Cumulative patches to TVM and cells (popsave, exception handler, loops).
158        ///
159        /// Mask: `0x0200000`.
160        CapsTvmBugfixes2022 = 21,
161
162        /// Support for message queues between workchains.
163        ///
164        /// Mask: `0x0400000`.
165        CapWorkchains = 22,
166
167        /// New continuation serialization format.
168        ///
169        /// Mask: `0x0800000`.
170        CapStcontNewFormat = 23,
171
172        /// Use fast stats for `*DATASIZE*` TVM opcodes.
173        ///
174        /// Mask: `0x1000000`.
175        CapFastStorageStatBugfix = 24,
176
177        /// Add support for transparent loading of merkle cells.
178        ///
179        /// Mask: `0x2000000`.
180        CapResolveMerkleCell = 25,
181
182        /// Prepend signature with `global_id` for TVM.
183        ///
184        /// Mask: `0x4000000`.
185        CapSignatureWithId = 26,
186
187        /// Execute bounce phase even after failed action phase.
188        ///
189        /// Mask: `0x8000000`.
190        CapBounceAfterFailedAction = 27,
191
192        /// Groth16 support in TVM.
193        ///
194        /// Mask: `0x10000000`
195        CapGroth16 = 28,
196
197        /// Makes all fees in config in gas units.
198        ///
199        /// Mask: `0x20000000`
200        CapFeeInGasUnits = 29,
201
202        /// Big cells support.
203        ///
204        /// Mask: `0x40000000`
205        CapBigCells = 30,
206
207        /// Suspend addresses using a config param.
208        ///
209        /// Mask: `0x80000000`
210        CapSuspendedList = 31,
211
212        /// Adds intershard communication between master blocks.
213        ///
214        /// Mask: `0x100000000`
215        CapFastFinality = 32,
216    }
217}
218
219impl std::ops::BitOr<GlobalCapability> for GlobalCapability {
220    type Output = GlobalCapabilities;
221
222    #[inline]
223    fn bitor(self, rhs: GlobalCapability) -> Self::Output {
224        GlobalCapabilities(self as u64 | rhs as u64)
225    }
226}
227
228impl std::ops::BitOr<GlobalCapability> for u64 {
229    type Output = GlobalCapabilities;
230
231    #[inline]
232    fn bitor(self, rhs: GlobalCapability) -> Self::Output {
233        GlobalCapabilities(self | rhs as u64)
234    }
235}
236
237impl std::ops::BitOrAssign<GlobalCapability> for u64 {
238    #[inline]
239    fn bitor_assign(&mut self, rhs: GlobalCapability) {
240        *self = (*self | rhs).0;
241    }
242}
243
244impl std::ops::BitOr<u64> for GlobalCapability {
245    type Output = GlobalCapabilities;
246
247    #[inline]
248    fn bitor(self, rhs: u64) -> Self::Output {
249        GlobalCapabilities(self as u64 | rhs)
250    }
251}
252
253impl std::ops::BitOr<GlobalCapability> for GlobalCapabilities {
254    type Output = GlobalCapabilities;
255
256    #[inline]
257    fn bitor(self, rhs: GlobalCapability) -> Self::Output {
258        GlobalCapabilities(self.0 | rhs as u64)
259    }
260}
261
262impl std::ops::BitOr<GlobalCapabilities> for GlobalCapability {
263    type Output = GlobalCapabilities;
264
265    #[inline]
266    fn bitor(self, rhs: GlobalCapabilities) -> Self::Output {
267        GlobalCapabilities(self as u64 | rhs.0)
268    }
269}
270
271impl std::ops::BitOrAssign<u64> for GlobalCapabilities {
272    #[inline]
273    fn bitor_assign(&mut self, rhs: u64) {
274        *self = GlobalCapabilities(self.0 | rhs);
275    }
276}
277
278impl std::ops::BitOrAssign<GlobalCapability> for GlobalCapabilities {
279    #[inline]
280    fn bitor_assign(&mut self, rhs: GlobalCapability) {
281        *self = *self | rhs;
282    }
283}
284
285/// Software info.
286#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Store, Load)]
287#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
288#[tlb(tag = "#c4")]
289pub struct GlobalVersion {
290    /// Software version.
291    pub version: u32,
292    /// Software capability flags.
293    pub capabilities: GlobalCapabilities,
294}
295
296/// A set of enabled capabilities.
297///
298/// See [`GlobalCapability`].
299#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Store, Load)]
300#[repr(transparent)]
301pub struct GlobalCapabilities(u64);
302
303impl GlobalCapabilities {
304    /// Creates a new capabilities set from the inner value.
305    #[inline]
306    pub const fn new(inner: u64) -> Self {
307        Self(inner)
308    }
309
310    /// Returns `true` if the set contains no enabled capabilities.
311    #[inline]
312    pub const fn is_empty(&self) -> bool {
313        self.0 == 0
314    }
315
316    /// Returns the number of enabled capabilities.
317    pub const fn len(&self) -> usize {
318        self.0.count_ones() as usize
319    }
320
321    /// Returns `true` if the specified capability is enabled.
322    #[inline]
323    pub const fn contains(&self, capability: GlobalCapability) -> bool {
324        (self.0 & (capability as u64)) != 0
325    }
326
327    /// Converts this wrapper into an underlying type.
328    #[inline]
329    pub const fn into_inner(self) -> u64 {
330        self.0
331    }
332
333    /// Gets an iterator over the enabled capabilities.
334    #[inline]
335    pub fn iter(&self) -> GlobalCapabilitiesIter {
336        GlobalCapabilitiesIter(self.0)
337    }
338}
339
340impl From<u64> for GlobalCapabilities {
341    #[inline]
342    fn from(value: u64) -> Self {
343        Self(value)
344    }
345}
346
347impl From<GlobalCapabilities> for u64 {
348    #[inline]
349    fn from(value: GlobalCapabilities) -> Self {
350        value.0
351    }
352}
353
354impl PartialEq<u64> for GlobalCapabilities {
355    #[inline]
356    fn eq(&self, other: &u64) -> bool {
357        self.0 == *other
358    }
359}
360
361impl IntoIterator for GlobalCapabilities {
362    type Item = GlobalCapability;
363    type IntoIter = GlobalCapabilitiesIter;
364
365    #[inline]
366    fn into_iter(self) -> Self::IntoIter {
367        GlobalCapabilitiesIter(self.0)
368    }
369}
370
371impl FromIterator<GlobalCapability> for GlobalCapabilities {
372    fn from_iter<T: IntoIterator<Item = GlobalCapability>>(iter: T) -> Self {
373        let mut res = GlobalCapabilities::default();
374        for item in iter {
375            res |= item;
376        }
377        res
378    }
379}
380
381impl<const N: usize> From<[GlobalCapability; N]> for GlobalCapabilities {
382    fn from(value: [GlobalCapability; N]) -> Self {
383        let mut res = GlobalCapabilities::default();
384        for item in value.iter() {
385            res |= *item;
386        }
387        res
388    }
389}
390
391#[cfg(feature = "serde")]
392impl serde::Serialize for GlobalCapabilities {
393    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
394    where
395        S: serde::Serializer,
396    {
397        use serde::ser::SerializeSeq;
398
399        if serializer.is_human_readable() {
400            let mut seq = serializer.serialize_seq(Some(self.len()))?;
401            for capability in self.iter() {
402                seq.serialize_element(&capability)?;
403            }
404            seq.end()
405        } else {
406            serializer.serialize_u64(self.0)
407        }
408    }
409}
410
411#[cfg(feature = "serde")]
412impl<'de> serde::Deserialize<'de> for GlobalCapabilities {
413    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
414    where
415        D: serde::Deserializer<'de>,
416    {
417        use serde::de::Visitor;
418
419        struct GlobalCapabilitiesVisitor;
420
421        impl<'de> Visitor<'de> for GlobalCapabilitiesVisitor {
422            type Value = GlobalCapabilities;
423
424            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
425                f.write_str("a list of global capabilities")
426            }
427
428            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
429            where
430                A: serde::de::SeqAccess<'de>,
431            {
432                let mut res = GlobalCapabilities::default();
433                while let Some(capacility) = ok!(seq.next_element::<GlobalCapability>()) {
434                    res |= capacility;
435                }
436                Ok(res)
437            }
438        }
439
440        if deserializer.is_human_readable() {
441            deserializer.deserialize_seq(GlobalCapabilitiesVisitor)
442        } else {
443            u64::deserialize(deserializer).map(Self)
444        }
445    }
446}
447
448/// An iterator over the enabled capabilities of [`GlobalCapabilities`].
449///
450/// This struct is created by the [`iter`] method on [`GlobalCapabilities`].
451/// See its documentation for more.
452///
453/// [`iter`]: GlobalCapabilities::iter
454#[derive(Clone)]
455pub struct GlobalCapabilitiesIter(u64);
456
457impl Iterator for GlobalCapabilitiesIter {
458    type Item = GlobalCapability;
459
460    fn next(&mut self) -> Option<Self::Item> {
461        while self.0 != 0 {
462            //  10100 - 1     = 10011
463            // !10011         = 01100
464            //  10100 & 01100 = 00100
465            let mask = self.0 & !(self.0 - 1);
466
467            // 10100 & !00100 -> 10000
468            self.0 &= !mask;
469
470            if let Some(item) = GlobalCapability::from_bit_offset(mask.trailing_zeros()) {
471                return Some(item);
472            }
473        }
474
475        None
476    }
477
478    fn size_hint(&self) -> (usize, Option<usize>) {
479        let len = self.0.count_ones() as usize;
480        (len, Some(len))
481    }
482}
483
484#[cfg(test)]
485mod tests {
486    use super::*;
487
488    #[test]
489    fn capabilities_iter() {
490        let capabilities = GlobalCapability::CapCreateStatsEnabled
491            | GlobalCapability::CapBounceMsgBody
492            | GlobalCapability::CapReportVersion
493            | GlobalCapability::CapShortDequeue
494            | GlobalCapability::CapFastStorageStat
495            | GlobalCapability::CapOffHypercube
496            | GlobalCapability::CapMyCode
497            | GlobalCapability::CapFixTupleIndexBug;
498
499        let capabilities = capabilities.into_iter().collect::<Vec<_>>();
500        assert_eq!(capabilities, [
501            GlobalCapability::CapCreateStatsEnabled,
502            GlobalCapability::CapBounceMsgBody,
503            GlobalCapability::CapReportVersion,
504            GlobalCapability::CapShortDequeue,
505            GlobalCapability::CapFastStorageStat,
506            GlobalCapability::CapOffHypercube,
507            GlobalCapability::CapMyCode,
508            GlobalCapability::CapFixTupleIndexBug
509        ]);
510    }
511}