iota_sdk_types/
version.rs1use std::ops::{Add, AddAssign, Sub, SubAssign};
6
7#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
8#[non_exhaustive]
9pub enum VersionError {
10 #[error("cannot increment Version: maximum valid Version has already been reached")]
11 InvalidIncrement,
12 #[error("cannot decrement Version: minimum valid Version has already been reached")]
13 InvalidDecrement,
14 #[error("Version arithmetic resulted in an overflow")]
15 Overflow,
16 #[error("not a version used for congested shared objects in the gas price feedback mechanism")]
17 InvalidCongestedVersion,
18}
19
20#[derive(
21 Clone,
22 Copy,
23 Debug,
24 Default,
25 derive_more::Add,
26 derive_more::AddAssign,
27 derive_more::Display,
28 derive_more::Div,
29 derive_more::DivAssign,
30 derive_more::From,
31 derive_more::FromStr,
32 derive_more::Mul,
33 derive_more::MulAssign,
34 derive_more::Rem,
35 derive_more::RemAssign,
36 derive_more::Sub,
37 derive_more::SubAssign,
38 derive_more::Sum,
39 Eq,
40 Hash,
41 Ord,
42 PartialEq,
43 PartialOrd,
44)]
45#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
46#[cfg_attr(
47 feature = "serde",
48 derive(serde::Deserialize, serde::Serialize),
49 serde(transparent)
50)]
51#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
52#[repr(transparent)]
53pub struct Version(
54 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] u64,
55);
56
57impl Version {
58 pub const MIN_VALID_INCL: Self = Self(u64::MIN);
63
64 pub const INITIAL_SHARED_VERSION: Self = Self(1);
66
67 pub const MAX_VALID_EXCL: Self = Self(0x7fff_ffff_ffff_ffff);
75
76 pub const CANCELED_READ: Self = Self(Self::MAX_VALID_EXCL.0 + 1);
79
80 pub const CONGESTED_PRIOR_TO_GAS_PRICE_FEEDBACK: Self = Self(Self::MAX_VALID_EXCL.0 + 2);
85
86 pub const RANDOMNESS_UNAVAILABLE: Self = Self(Self::MAX_VALID_EXCL.0 + 3);
87
88 const CONGESTED_BASE_OFFSET_FOR_GAS_PRICE_FEEDBACK: Self = Self(1_000);
108
109 const MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK: Self =
113 Self(Self::MAX_VALID_EXCL.0 + Self::CONGESTED_BASE_OFFSET_FOR_GAS_PRICE_FEEDBACK.0);
114
115 pub const OBJECT_START: Self = Self(1);
116
117 pub const fn from_u64(value: u64) -> Self {
119 Self(value)
120 }
121
122 pub const fn as_u64(&self) -> u64 {
124 self.0
125 }
126
127 pub fn next(mut self) -> Result<Self, VersionError> {
129 if !self.is_valid() {
130 return Err(VersionError::InvalidIncrement);
131 }
132 self.0 += 1;
133 Ok(self)
134 }
135
136 pub fn increment(&mut self) -> Result<(), VersionError> {
138 *self = self.next()?;
139 Ok(())
140 }
141
142 pub fn previous(mut self) -> Result<Self, VersionError> {
144 if self == Self::MIN_VALID_INCL {
145 return Err(VersionError::InvalidDecrement);
146 }
147 self.0 -= 1;
148 Ok(self)
149 }
150
151 pub fn decrement(&mut self) -> Result<(), VersionError> {
153 *self = self.previous()?;
154 Ok(())
155 }
156
157 pub fn new_congested_with_suggested_gas_price(
163 suggested_gas_price: u64,
164 ) -> Result<Self, VersionError> {
165 let (version, overflows) = Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK
166 .0
167 .overflowing_add(suggested_gas_price);
168 if overflows {
169 return Err(VersionError::Overflow);
170 }
171
172 Ok(Self(version))
173 }
174
175 pub fn is_congested(&self) -> bool {
178 *self == Self::CONGESTED_PRIOR_TO_GAS_PRICE_FEEDBACK
179 || *self >= Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK
180 }
181
182 pub fn get_congested_version_suggested_gas_price(&self) -> Result<u64, VersionError> {
187 if *self < Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK {
188 return Err(VersionError::InvalidCongestedVersion);
189 }
190
191 Ok(self.0 - Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK.0)
192 }
193
194 pub fn lamport_increment(inputs: impl IntoIterator<Item = Self>) -> Result<Self, VersionError> {
197 let max_input = inputs.into_iter().max().unwrap_or_default();
198 max_input.next()
199 }
200
201 pub fn is_canceled(&self) -> bool {
204 *self == Self::CANCELED_READ || *self == Self::RANDOMNESS_UNAVAILABLE || self.is_congested()
205 }
206
207 pub fn is_valid(&self) -> bool {
210 *self < Self::MAX_VALID_EXCL
211 }
212}
213
214impl Add<u64> for Version {
215 type Output = Self;
216
217 fn add(self, rhs: u64) -> Self::Output {
218 Self(self.0 + rhs)
219 }
220}
221
222impl AddAssign<u64> for Version {
223 fn add_assign(&mut self, rhs: u64) {
224 self.0 += rhs;
225 }
226}
227
228impl Sub<u64> for Version {
229 type Output = Self;
230
231 fn sub(self, rhs: u64) -> Self::Output {
232 Self(self.0 - rhs)
233 }
234}
235
236impl SubAssign<u64> for Version {
237 fn sub_assign(&mut self, rhs: u64) {
238 self.0 -= rhs;
239 }
240}
241
242impl TryFrom<i64> for Version {
243 type Error = <u64 as TryFrom<i64>>::Error;
244
245 fn try_from(value: i64) -> Result<Self, Self::Error> {
246 value.try_into().map(Self)
247 }
248}
249
250impl PartialEq<u64> for Version {
251 fn eq(&self, other: &u64) -> bool {
252 self.0.eq(other)
253 }
254}
255
256impl PartialEq<Version> for u64 {
257 fn eq(&self, other: &Version) -> bool {
258 self.eq(&other.0)
259 }
260}
261
262impl PartialOrd<u64> for Version {
263 fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
264 self.0.partial_cmp(other)
265 }
266}
267
268impl PartialOrd<Version> for u64 {
269 fn partial_cmp(&self, other: &Version) -> Option<std::cmp::Ordering> {
270 self.partial_cmp(&other.0)
271 }
272}