sui_types/
base_types.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::MOVE_STDLIB_ADDRESS;
6use crate::MoveTypeTagTrait;
7use crate::MoveTypeTagTraitGeneric;
8use crate::SUI_CLOCK_OBJECT_ID;
9use crate::SUI_FRAMEWORK_ADDRESS;
10use crate::SUI_SYSTEM_ADDRESS;
11use crate::accumulator_root::extract_balance_type_from_field;
12use crate::accumulator_root::is_balance_accumulator_field;
13use crate::balance::Balance;
14use crate::coin::COIN_MODULE_NAME;
15use crate::coin::COIN_STRUCT_NAME;
16use crate::coin::Coin;
17use crate::coin::CoinMetadata;
18use crate::coin::TreasuryCap;
19use crate::coin_registry::Currency;
20pub use crate::committee::EpochId;
21use crate::crypto::{
22    AuthorityPublicKeyBytes, DefaultHash, PublicKey, SignatureScheme, SuiPublicKey, SuiSignature,
23};
24pub use crate::digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest};
25use crate::dynamic_field::DynamicFieldInfo;
26use crate::dynamic_field::DynamicFieldType;
27use crate::dynamic_field::{DYNAMIC_FIELD_FIELD_STRUCT_NAME, DYNAMIC_FIELD_MODULE_NAME};
28use crate::effects::TransactionEffects;
29use crate::effects::TransactionEffectsAPI;
30use crate::epoch_data::EpochData;
31use crate::error::ExecutionErrorKind;
32use crate::error::SuiError;
33use crate::error::SuiErrorKind;
34use crate::error::{ExecutionError, SuiResult};
35use crate::gas_coin::GAS;
36use crate::gas_coin::GasCoin;
37use crate::governance::STAKED_SUI_STRUCT_NAME;
38use crate::governance::STAKING_POOL_MODULE_NAME;
39use crate::governance::StakedSui;
40use crate::id::RESOLVED_SUI_ID;
41use crate::messages_checkpoint::CheckpointTimestamp;
42use crate::multisig::MultiSigPublicKey;
43use crate::object::{Object, Owner};
44use crate::parse_sui_struct_tag;
45use crate::signature::GenericSignature;
46use crate::sui_serde::Readable;
47use crate::sui_serde::to_custom_deser_error;
48use crate::sui_serde::to_sui_struct_tag_string;
49use crate::transaction::Transaction;
50use crate::transaction::VerifiedTransaction;
51use crate::zk_login_authenticator::ZkLoginAuthenticator;
52use anyhow::anyhow;
53use fastcrypto::encoding::decode_bytes_hex;
54use fastcrypto::encoding::{Encoding, Hex};
55use fastcrypto::hash::HashFunction;
56use fastcrypto::traits::AllowedRng;
57use fastcrypto_zkp::bn254::zk_login::ZkLoginInputs;
58use move_binary_format::CompiledModule;
59use move_binary_format::file_format::SignatureToken;
60use move_bytecode_utils::resolve_struct;
61use move_core_types::account_address::AccountAddress;
62use move_core_types::annotated_value as A;
63use move_core_types::ident_str;
64use move_core_types::identifier::IdentStr;
65use move_core_types::language_storage::ModuleId;
66use move_core_types::language_storage::StructTag;
67use move_core_types::language_storage::TypeTag;
68use rand::Rng;
69use schemars::JsonSchema;
70use serde::Deserializer;
71use serde::Serializer;
72use serde::ser::Error;
73use serde::ser::SerializeSeq;
74use serde::{Deserialize, Serialize};
75use serde_with::DeserializeAs;
76use serde_with::SerializeAs;
77use serde_with::serde_as;
78use shared_crypto::intent::HashingIntentScope;
79use std::borrow::Cow;
80use std::cmp::max;
81use std::convert::{TryFrom, TryInto};
82use std::fmt;
83use std::str::FromStr;
84use sui_protocol_config::ProtocolConfig;
85
86#[cfg(test)]
87#[path = "unit_tests/base_types_tests.rs"]
88mod base_types_tests;
89
90#[cfg(test)]
91#[path = "unit_tests/accumulator_types_tests.rs"]
92mod accumulator_types_tests;
93
94#[derive(
95    Eq,
96    PartialEq,
97    Ord,
98    PartialOrd,
99    Copy,
100    Clone,
101    Hash,
102    Default,
103    Debug,
104    Serialize,
105    Deserialize,
106    JsonSchema,
107)]
108#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
109pub struct SequenceNumber(u64);
110
111impl SequenceNumber {
112    pub fn one_before(&self) -> Option<SequenceNumber> {
113        if self.0 == 0 {
114            None
115        } else {
116            Some(SequenceNumber(self.0 - 1))
117        }
118    }
119
120    pub fn next(&self) -> SequenceNumber {
121        SequenceNumber(self.0 + 1)
122    }
123}
124
125pub type TxSequenceNumber = u64;
126
127impl fmt::Display for SequenceNumber {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(f, "{:#x}", self.0)
130    }
131}
132
133pub type VersionNumber = SequenceNumber;
134
135#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Default, Debug, Serialize, Deserialize)]
136pub struct UserData(pub Option<[u8; 32]>);
137
138pub type AuthorityName = AuthorityPublicKeyBytes;
139
140pub trait ConciseableName<'a> {
141    type ConciseTypeRef: std::fmt::Debug;
142    type ConciseType: std::fmt::Debug;
143
144    fn concise(&'a self) -> Self::ConciseTypeRef;
145    fn concise_owned(&self) -> Self::ConciseType;
146}
147
148#[serde_as]
149#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
150pub struct ObjectID(
151    #[schemars(with = "Hex")]
152    #[serde_as(as = "Readable<HexAccountAddress, _>")]
153    AccountAddress,
154);
155
156#[serde_as]
157#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
158pub enum FullObjectID {
159    Fastpath(ObjectID),
160    Consensus(ConsensusObjectSequenceKey),
161}
162
163impl FullObjectID {
164    pub fn new(object_id: ObjectID, start_version: Option<SequenceNumber>) -> Self {
165        if let Some(start_version) = start_version {
166            Self::Consensus((object_id, start_version))
167        } else {
168            Self::Fastpath(object_id)
169        }
170    }
171
172    pub fn id(&self) -> ObjectID {
173        match &self {
174            FullObjectID::Fastpath(object_id) => *object_id,
175            FullObjectID::Consensus(consensus_object_sequence_key) => {
176                consensus_object_sequence_key.0
177            }
178        }
179    }
180}
181
182pub type VersionDigest = (SequenceNumber, ObjectDigest);
183
184pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest);
185
186pub fn random_object_ref() -> ObjectRef {
187    (
188        ObjectID::random(),
189        SequenceNumber::new(),
190        ObjectDigest::new([0; 32]),
191    )
192}
193
194pub fn update_object_ref_for_testing(object_ref: ObjectRef) -> ObjectRef {
195    (
196        object_ref.0,
197        object_ref.1.next(),
198        ObjectDigest::new([0; 32]),
199    )
200}
201
202#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
203pub struct FullObjectRef(pub FullObjectID, pub SequenceNumber, pub ObjectDigest);
204
205impl FullObjectRef {
206    pub fn from_fastpath_ref(object_ref: ObjectRef) -> Self {
207        Self(
208            FullObjectID::Fastpath(object_ref.0),
209            object_ref.1,
210            object_ref.2,
211        )
212    }
213
214    pub fn from_object_ref_and_owner(object_ref: ObjectRef, owner: &Owner) -> Self {
215        let full_id = if let Some(start_version) = owner.start_version() {
216            FullObjectID::Consensus((object_ref.0, start_version))
217        } else {
218            FullObjectID::Fastpath(object_ref.0)
219        };
220        Self(full_id, object_ref.1, object_ref.2)
221    }
222
223    pub fn as_object_ref(&self) -> ObjectRef {
224        (self.0.id(), self.1, self.2)
225    }
226}
227/// Represents an distinct stream of object versions for a consensus object,
228/// based on the object ID and start version.
229pub type ConsensusObjectSequenceKey = (ObjectID, SequenceNumber);
230
231/// Wrapper around StructTag with a space-efficient representation for common types like coins
232/// The StructTag for a gas coin is 84 bytes, so using 1 byte instead is a win.
233/// The inner representation is private to prevent incorrectly constructing an `Other` instead of
234/// one of the specialized variants, e.g. `Other(GasCoin::type_())` instead of `GasCoin`
235#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
236pub struct MoveObjectType(MoveObjectType_);
237
238/// Even though it is declared public, it is the "private", internal representation for
239/// `MoveObjectType`
240#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
241pub enum MoveObjectType_ {
242    /// A type that is not `0x2::coin::Coin<T>`
243    Other(StructTag),
244    /// A SUI coin (i.e., `0x2::coin::Coin<0x2::sui::SUI>`)
245    GasCoin,
246    /// A record of a staked SUI coin (i.e., `0x3::staking_pool::StakedSui`)
247    StakedSui,
248    /// A non-SUI coin type (i.e., `0x2::coin::Coin<T> where T != 0x2::sui::SUI`)
249    Coin(TypeTag),
250    /// A SUI balance accumulator field
251    /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<0x2::sui::SUI>>, 0x2::accumulator::U128>`)
252    SuiBalanceAccumulatorField,
253    /// A non-SUI balance accumulator field
254    /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<T>>, 0x2::accumulator::U128>`
255    /// where T != 0x2::sui::SUI)
256    BalanceAccumulatorField(TypeTag),
257    // NOTE: if adding a new type here, and there are existing on-chain objects of that
258    // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
259    // to make sure the new type and Other(_) are interpreted consistently.
260}
261
262impl MoveObjectType {
263    pub fn gas_coin() -> Self {
264        Self(MoveObjectType_::GasCoin)
265    }
266
267    pub fn is_efficient_representation(&self) -> bool {
268        !matches!(self.0, MoveObjectType_::Other(_))
269    }
270
271    pub fn coin(coin_type: TypeTag) -> Self {
272        Self(if GAS::is_gas_type(&coin_type) {
273            MoveObjectType_::GasCoin
274        } else {
275            MoveObjectType_::Coin(coin_type)
276        })
277    }
278
279    pub fn staked_sui() -> Self {
280        Self(MoveObjectType_::StakedSui)
281    }
282
283    pub fn address(&self) -> AccountAddress {
284        match &self.0 {
285            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => SUI_FRAMEWORK_ADDRESS,
286            MoveObjectType_::StakedSui => SUI_SYSTEM_ADDRESS,
287            MoveObjectType_::SuiBalanceAccumulatorField
288            | MoveObjectType_::BalanceAccumulatorField(_) => SUI_FRAMEWORK_ADDRESS,
289            MoveObjectType_::Other(s) => s.address,
290        }
291    }
292
293    pub fn module(&self) -> &IdentStr {
294        match &self.0 {
295            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME,
296            MoveObjectType_::StakedSui => STAKING_POOL_MODULE_NAME,
297            MoveObjectType_::SuiBalanceAccumulatorField
298            | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_MODULE_NAME,
299            MoveObjectType_::Other(s) => &s.module,
300        }
301    }
302
303    pub fn name(&self) -> &IdentStr {
304        match &self.0 {
305            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME,
306            MoveObjectType_::StakedSui => STAKED_SUI_STRUCT_NAME,
307            MoveObjectType_::SuiBalanceAccumulatorField
308            | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_FIELD_STRUCT_NAME,
309            MoveObjectType_::Other(s) => &s.name,
310        }
311    }
312
313    pub fn type_params(&self) -> Vec<Cow<'_, TypeTag>> {
314        match &self.0 {
315            MoveObjectType_::GasCoin => vec![Cow::Owned(GAS::type_tag())],
316            MoveObjectType_::StakedSui => vec![],
317            MoveObjectType_::Coin(inner) => vec![Cow::Borrowed(inner)],
318            MoveObjectType_::SuiBalanceAccumulatorField => {
319                Self::balance_accumulator_field_type_params(GAS::type_tag())
320                    .into_iter()
321                    .map(Cow::Owned)
322                    .collect()
323            }
324            MoveObjectType_::BalanceAccumulatorField(inner) => {
325                Self::balance_accumulator_field_type_params(inner.clone())
326                    .into_iter()
327                    .map(Cow::Owned)
328                    .collect()
329            }
330            MoveObjectType_::Other(s) => s.type_params.iter().map(Cow::Borrowed).collect(),
331        }
332    }
333
334    pub fn into_type_params(self) -> Vec<TypeTag> {
335        match self.0 {
336            MoveObjectType_::GasCoin => vec![GAS::type_tag()],
337            MoveObjectType_::StakedSui => vec![],
338            MoveObjectType_::Coin(inner) => vec![inner],
339            MoveObjectType_::SuiBalanceAccumulatorField => {
340                Self::balance_accumulator_field_type_params(GAS::type_tag())
341            }
342            MoveObjectType_::BalanceAccumulatorField(inner) => {
343                Self::balance_accumulator_field_type_params(inner)
344            }
345            MoveObjectType_::Other(s) => s.type_params,
346        }
347    }
348
349    pub fn coin_type_maybe(&self) -> Option<TypeTag> {
350        match &self.0 {
351            MoveObjectType_::GasCoin => Some(GAS::type_tag()),
352            MoveObjectType_::Coin(inner) => Some(inner.clone()),
353            MoveObjectType_::StakedSui => None,
354            MoveObjectType_::SuiBalanceAccumulatorField => None,
355            MoveObjectType_::BalanceAccumulatorField(_) => None,
356            MoveObjectType_::Other(_) => None,
357        }
358    }
359
360    pub fn balance_accumulator_field_type_maybe(&self) -> Option<TypeTag> {
361        match &self.0 {
362            MoveObjectType_::SuiBalanceAccumulatorField => Some(GAS::type_tag()),
363            MoveObjectType_::BalanceAccumulatorField(inner) => Some(inner.clone()),
364            _ => None,
365        }
366    }
367
368    pub fn is_balance_accumulator_field(&self) -> bool {
369        matches!(
370            self.0,
371            MoveObjectType_::SuiBalanceAccumulatorField
372                | MoveObjectType_::BalanceAccumulatorField(_)
373        )
374    }
375
376    pub fn is_sui_balance_accumulator_field(&self) -> bool {
377        matches!(self.0, MoveObjectType_::SuiBalanceAccumulatorField)
378    }
379
380    pub fn module_id(&self) -> ModuleId {
381        ModuleId::new(self.address(), self.module().to_owned())
382    }
383
384    pub fn size_for_gas_metering(&self) -> usize {
385        // unwraps safe because a `StructTag` cannot fail to serialize
386        match &self.0 {
387            MoveObjectType_::GasCoin => 1,
388            MoveObjectType_::StakedSui => 1,
389            MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
390            MoveObjectType_::SuiBalanceAccumulatorField => 1,
391            MoveObjectType_::BalanceAccumulatorField(inner) => {
392                bcs::serialized_size(inner).unwrap() + 1
393            }
394            MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
395        }
396    }
397
398    /// Return true if `self` is `0x2::coin::Coin<T>` for some T (note: T can be SUI)
399    pub fn is_coin(&self) -> bool {
400        match &self.0 {
401            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
402            MoveObjectType_::StakedSui
403            | MoveObjectType_::SuiBalanceAccumulatorField
404            | MoveObjectType_::BalanceAccumulatorField(_)
405            | MoveObjectType_::Other(_) => false,
406        }
407    }
408
409    /// Return true if `self` is 0x2::coin::Coin<0x2::sui::SUI>
410    pub fn is_gas_coin(&self) -> bool {
411        match &self.0 {
412            MoveObjectType_::GasCoin => true,
413            MoveObjectType_::StakedSui
414            | MoveObjectType_::Coin(_)
415            | MoveObjectType_::SuiBalanceAccumulatorField
416            | MoveObjectType_::BalanceAccumulatorField(_)
417            | MoveObjectType_::Other(_) => false,
418        }
419    }
420
421    /// Return true if `self` is `0x2::coin::Coin<t>`
422    pub fn is_coin_t(&self, t: &TypeTag) -> bool {
423        match &self.0 {
424            MoveObjectType_::GasCoin => GAS::is_gas_type(t),
425            MoveObjectType_::Coin(c) => t == c,
426            MoveObjectType_::StakedSui
427            | MoveObjectType_::SuiBalanceAccumulatorField
428            | MoveObjectType_::BalanceAccumulatorField(_)
429            | MoveObjectType_::Other(_) => false,
430        }
431    }
432
433    pub fn is_staked_sui(&self) -> bool {
434        match &self.0 {
435            MoveObjectType_::StakedSui => true,
436            MoveObjectType_::GasCoin
437            | MoveObjectType_::Coin(_)
438            | MoveObjectType_::SuiBalanceAccumulatorField
439            | MoveObjectType_::BalanceAccumulatorField(_)
440            | MoveObjectType_::Other(_) => false,
441        }
442    }
443
444    pub fn is_coin_metadata(&self) -> bool {
445        match &self.0 {
446            MoveObjectType_::GasCoin
447            | MoveObjectType_::StakedSui
448            | MoveObjectType_::Coin(_)
449            | MoveObjectType_::SuiBalanceAccumulatorField
450            | MoveObjectType_::BalanceAccumulatorField(_) => false,
451            MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s),
452        }
453    }
454
455    pub fn is_currency(&self) -> bool {
456        match &self.0 {
457            MoveObjectType_::GasCoin
458            | MoveObjectType_::StakedSui
459            | MoveObjectType_::Coin(_)
460            | MoveObjectType_::SuiBalanceAccumulatorField
461            | MoveObjectType_::BalanceAccumulatorField(_) => false,
462            MoveObjectType_::Other(s) => Currency::is_currency(s),
463        }
464    }
465
466    pub fn is_treasury_cap(&self) -> bool {
467        match &self.0 {
468            MoveObjectType_::GasCoin
469            | MoveObjectType_::StakedSui
470            | MoveObjectType_::Coin(_)
471            | MoveObjectType_::SuiBalanceAccumulatorField
472            | MoveObjectType_::BalanceAccumulatorField(_) => false,
473            MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
474        }
475    }
476
477    pub fn is_upgrade_cap(&self) -> bool {
478        self.address() == SUI_FRAMEWORK_ADDRESS
479            && self.module().as_str() == "package"
480            && self.name().as_str() == "UpgradeCap"
481    }
482
483    pub fn is_regulated_coin_metadata(&self) -> bool {
484        self.address() == SUI_FRAMEWORK_ADDRESS
485            && self.module().as_str() == "coin"
486            && self.name().as_str() == "RegulatedCoinMetadata"
487    }
488
489    pub fn is_coin_deny_cap(&self) -> bool {
490        self.address() == SUI_FRAMEWORK_ADDRESS
491            && self.module().as_str() == "coin"
492            && self.name().as_str() == "DenyCap"
493    }
494
495    pub fn is_coin_deny_cap_v2(&self) -> bool {
496        self.address() == SUI_FRAMEWORK_ADDRESS
497            && self.module().as_str() == "coin"
498            && self.name().as_str() == "DenyCapV2"
499    }
500
501    pub fn is_dynamic_field(&self) -> bool {
502        match &self.0 {
503            MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
504                false
505            }
506            MoveObjectType_::SuiBalanceAccumulatorField
507            | MoveObjectType_::BalanceAccumulatorField(_) => {
508                true // These are dynamic fields
509            }
510            MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
511        }
512    }
513
514    pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> SuiResult<TypeTag> {
515        match &self.0 {
516            MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
517                Err(SuiErrorKind::ObjectDeserializationError {
518                    error: "Error extracting dynamic object name from specialized object type"
519                        .to_string(),
520                }
521                .into())
522            }
523            MoveObjectType_::SuiBalanceAccumulatorField
524            | MoveObjectType_::BalanceAccumulatorField(_) => {
525                let struct_tag: StructTag = self.clone().into();
526                DynamicFieldInfo::try_extract_field_name(&struct_tag, type_)
527            }
528            MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
529        }
530    }
531
532    pub fn try_extract_field_value(&self) -> SuiResult<TypeTag> {
533        match &self.0 {
534            MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
535                Err(SuiErrorKind::ObjectDeserializationError {
536                    error: "Error extracting dynamic object value from specialized object type"
537                        .to_string(),
538                }
539                .into())
540            }
541            MoveObjectType_::SuiBalanceAccumulatorField
542            | MoveObjectType_::BalanceAccumulatorField(_) => {
543                let struct_tag: StructTag = self.clone().into();
544                DynamicFieldInfo::try_extract_field_value(&struct_tag)
545            }
546            MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
547        }
548    }
549
550    pub fn is(&self, s: &StructTag) -> bool {
551        match &self.0 {
552            MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
553            MoveObjectType_::StakedSui => StakedSui::is_staked_sui(s),
554            MoveObjectType_::Coin(inner) => {
555                Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
556            }
557            MoveObjectType_::SuiBalanceAccumulatorField => {
558                is_balance_accumulator_field(s)
559                    && extract_balance_type_from_field(s)
560                        .map(|t| GAS::is_gas_type(&t))
561                        .unwrap_or(false)
562            }
563            MoveObjectType_::BalanceAccumulatorField(inner) => {
564                is_balance_accumulator_field(s)
565                    && extract_balance_type_from_field(s)
566                        .map(|t| &t == inner)
567                        .unwrap_or(false)
568            }
569            MoveObjectType_::Other(o) => s == o,
570        }
571    }
572
573    pub fn other(&self) -> Option<&StructTag> {
574        if let MoveObjectType_::Other(s) = &self.0 {
575            Some(s)
576        } else {
577            None
578        }
579    }
580
581    /// Returns the string representation of this object's type using the canonical display.
582    pub fn to_canonical_string(&self, with_prefix: bool) -> String {
583        StructTag::from(self.clone()).to_canonical_string(with_prefix)
584    }
585
586    /// Helper function to construct type parameters for balance accumulator fields
587    /// Field<Key<Balance<T>>, U128> has two type params
588    fn balance_accumulator_field_type_params(inner_type: TypeTag) -> Vec<TypeTag> {
589        use crate::accumulator_root::{AccumulatorKey, U128};
590        let balance_type = Balance::type_tag(inner_type);
591        let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
592        let u128_type = U128::get_type_tag();
593        vec![key_type, u128_type]
594    }
595
596    /// Map from T to Field<AccumulatorKey<Balance<T>>, U128>
597    fn balance_accumulator_field_struct_tag(inner_type: TypeTag) -> StructTag {
598        use crate::accumulator_root::{AccumulatorKey, U128};
599        let balance_type = Balance::type_tag(inner_type);
600        let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
601        let u128_type = U128::get_type_tag();
602        DynamicFieldInfo::dynamic_field_type(key_type, u128_type)
603    }
604}
605
606impl From<StructTag> for MoveObjectType {
607    fn from(mut s: StructTag) -> Self {
608        Self(if GasCoin::is_gas_coin(&s) {
609            MoveObjectType_::GasCoin
610        } else if Coin::is_coin(&s) {
611            // unwrap safe because a coin has exactly one type parameter
612            MoveObjectType_::Coin(s.type_params.pop().unwrap())
613        } else if StakedSui::is_staked_sui(&s) {
614            MoveObjectType_::StakedSui
615        } else if is_balance_accumulator_field(&s) {
616            if let Some(balance_type) = extract_balance_type_from_field(&s) {
617                if GAS::is_gas_type(&balance_type) {
618                    MoveObjectType_::SuiBalanceAccumulatorField
619                } else {
620                    MoveObjectType_::BalanceAccumulatorField(balance_type)
621                }
622            } else {
623                MoveObjectType_::Other(s)
624            }
625        } else {
626            MoveObjectType_::Other(s)
627        })
628    }
629}
630
631impl From<MoveObjectType> for StructTag {
632    fn from(t: MoveObjectType) -> Self {
633        match t.0 {
634            MoveObjectType_::GasCoin => GasCoin::type_(),
635            MoveObjectType_::StakedSui => StakedSui::type_(),
636            MoveObjectType_::Coin(inner) => Coin::type_(inner),
637            MoveObjectType_::SuiBalanceAccumulatorField => {
638                MoveObjectType::balance_accumulator_field_struct_tag(GAS::type_tag())
639            }
640            MoveObjectType_::BalanceAccumulatorField(inner) => {
641                MoveObjectType::balance_accumulator_field_struct_tag(inner)
642            }
643            MoveObjectType_::Other(s) => s,
644        }
645    }
646}
647
648impl From<MoveObjectType> for TypeTag {
649    fn from(o: MoveObjectType) -> TypeTag {
650        let s: StructTag = o.into();
651        TypeTag::Struct(Box::new(s))
652    }
653}
654
655/// Whether this type is valid as a primitive (pure) transaction input.
656pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
657    use TypeTag as T;
658
659    match t {
660        T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
661        T::Vector(inner) => is_primitive_type_tag(inner),
662        T::Struct(st) => {
663            let StructTag {
664                address,
665                module,
666                name,
667                type_params: type_args,
668            } = &**st;
669            let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
670            // is id or..
671            if resolved_struct == RESOLVED_SUI_ID {
672                return true;
673            }
674            // is utf8 string
675            if resolved_struct == RESOLVED_UTF8_STR {
676                return true;
677            }
678            // is ascii string
679            if resolved_struct == RESOLVED_ASCII_STR {
680                return true;
681            }
682            // is option of a primitive
683            resolved_struct == RESOLVED_STD_OPTION
684                && type_args.len() == 1
685                && is_primitive_type_tag(&type_args[0])
686        }
687        T::Signer => false,
688    }
689}
690
691/// Type of a Sui object
692#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
693pub enum ObjectType {
694    /// Move package containing one or more bytecode modules
695    Package,
696    /// A Move struct of the given type
697    Struct(MoveObjectType),
698}
699
700impl From<&Object> for ObjectType {
701    fn from(o: &Object) -> Self {
702        o.data
703            .type_()
704            .map(|t| ObjectType::Struct(t.clone()))
705            .unwrap_or(ObjectType::Package)
706    }
707}
708
709impl TryFrom<ObjectType> for StructTag {
710    type Error = anyhow::Error;
711
712    fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
713        match o {
714            ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
715            ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
716        }
717    }
718}
719
720impl FromStr for ObjectType {
721    type Err = anyhow::Error;
722
723    fn from_str(s: &str) -> Result<Self, Self::Err> {
724        if s.to_lowercase() == PACKAGE {
725            Ok(ObjectType::Package)
726        } else {
727            let tag = parse_sui_struct_tag(s)?;
728            Ok(ObjectType::Struct(MoveObjectType::from(tag)))
729        }
730    }
731}
732
733#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
734pub struct ObjectInfo {
735    pub object_id: ObjectID,
736    pub version: SequenceNumber,
737    pub digest: ObjectDigest,
738    pub type_: ObjectType,
739    pub owner: Owner,
740    pub previous_transaction: TransactionDigest,
741}
742
743impl ObjectInfo {
744    pub fn new(oref: &ObjectRef, o: &Object) -> Self {
745        let (object_id, version, digest) = *oref;
746        Self {
747            object_id,
748            version,
749            digest,
750            type_: o.into(),
751            owner: o.owner.clone(),
752            previous_transaction: o.previous_transaction,
753        }
754    }
755
756    pub fn from_object(object: &Object) -> Self {
757        Self {
758            object_id: object.id(),
759            version: object.version(),
760            digest: object.digest(),
761            type_: object.into(),
762            owner: object.owner.clone(),
763            previous_transaction: object.previous_transaction,
764        }
765    }
766}
767const PACKAGE: &str = "package";
768impl ObjectType {
769    pub fn is_gas_coin(&self) -> bool {
770        matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
771    }
772
773    pub fn is_coin(&self) -> bool {
774        matches!(self, ObjectType::Struct(s) if s.is_coin())
775    }
776
777    /// Return true if `self` is `0x2::coin::Coin<t>`
778    pub fn is_coin_t(&self, t: &TypeTag) -> bool {
779        matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
780    }
781
782    pub fn is_package(&self) -> bool {
783        matches!(self, ObjectType::Package)
784    }
785}
786
787impl From<ObjectInfo> for ObjectRef {
788    fn from(info: ObjectInfo) -> Self {
789        (info.object_id, info.version, info.digest)
790    }
791}
792
793impl From<&ObjectInfo> for ObjectRef {
794    fn from(info: &ObjectInfo) -> Self {
795        (info.object_id, info.version, info.digest)
796    }
797}
798
799pub const SUI_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
800
801#[serde_as]
802#[derive(
803    Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
804)]
805#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
806pub struct SuiAddress(
807    #[schemars(with = "Hex")]
808    #[serde_as(as = "Readable<Hex, _>")]
809    [u8; SUI_ADDRESS_LENGTH],
810);
811
812impl SuiAddress {
813    pub const ZERO: Self = Self([0u8; SUI_ADDRESS_LENGTH]);
814
815    /// Convert the address to a byte buffer.
816    pub fn to_vec(&self) -> Vec<u8> {
817        self.0.to_vec()
818    }
819
820    /// Return a random SuiAddress.
821    pub fn random_for_testing_only() -> Self {
822        AccountAddress::random().into()
823    }
824
825    pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
826        let buf: [u8; SUI_ADDRESS_LENGTH] = rng.r#gen();
827        Self(buf)
828    }
829
830    /// Serialize an `Option<SuiAddress>` in Hex.
831    pub fn optional_address_as_hex<S>(
832        key: &Option<SuiAddress>,
833        serializer: S,
834    ) -> Result<S::Ok, S::Error>
835    where
836        S: serde::ser::Serializer,
837    {
838        serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
839    }
840
841    /// Deserialize into an `Option<SuiAddress>`.
842    pub fn optional_address_from_hex<'de, D>(
843        deserializer: D,
844    ) -> Result<Option<SuiAddress>, D::Error>
845    where
846        D: serde::de::Deserializer<'de>,
847    {
848        let s = String::deserialize(deserializer)?;
849        let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
850        Ok(Some(value))
851    }
852
853    /// Return the underlying byte array of a SuiAddress.
854    pub fn to_inner(self) -> [u8; SUI_ADDRESS_LENGTH] {
855        self.0
856    }
857
858    /// Parse a SuiAddress from a byte array or buffer.
859    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, SuiError> {
860        <[u8; SUI_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
861            .map_err(|_| SuiErrorKind::InvalidAddress.into())
862            .map(SuiAddress)
863    }
864
865    /// This derives a zkLogin address by parsing the iss and address_seed from [struct ZkLoginAuthenticator].
866    /// Define as iss_bytes_len || iss_bytes || padded_32_byte_address_seed. This is to be differentiated with
867    /// try_from_unpadded defined below.
868    pub fn try_from_padded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
869        Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
870    }
871
872    /// Define as iss_bytes_len || iss_bytes || unpadded_32_byte_address_seed.
873    pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
874        let mut hasher = DefaultHash::default();
875        hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
876        let iss_bytes = inputs.get_iss().as_bytes();
877        hasher.update([iss_bytes.len() as u8]);
878        hasher.update(iss_bytes);
879        hasher.update(inputs.get_address_seed().unpadded());
880        Ok(SuiAddress(hasher.finalize().digest))
881    }
882}
883
884impl From<ObjectID> for SuiAddress {
885    fn from(object_id: ObjectID) -> SuiAddress {
886        Self(object_id.into_bytes())
887    }
888}
889
890impl From<AccountAddress> for SuiAddress {
891    fn from(address: AccountAddress) -> SuiAddress {
892        Self(address.into_bytes())
893    }
894}
895
896impl TryFrom<&[u8]> for SuiAddress {
897    type Error = SuiError;
898
899    /// Tries to convert the provided byte array into a SuiAddress.
900    fn try_from(bytes: &[u8]) -> Result<Self, SuiError> {
901        Self::from_bytes(bytes)
902    }
903}
904
905impl TryFrom<Vec<u8>> for SuiAddress {
906    type Error = SuiError;
907
908    /// Tries to convert the provided byte buffer into a SuiAddress.
909    fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
910        Self::from_bytes(bytes)
911    }
912}
913
914impl AsRef<[u8]> for SuiAddress {
915    fn as_ref(&self) -> &[u8] {
916        &self.0[..]
917    }
918}
919
920impl FromStr for SuiAddress {
921    type Err = anyhow::Error;
922    fn from_str(s: &str) -> Result<Self, Self::Err> {
923        decode_bytes_hex(s).map_err(|e| anyhow!(e))
924    }
925}
926
927impl<T: SuiPublicKey> From<&T> for SuiAddress {
928    fn from(pk: &T) -> Self {
929        let mut hasher = DefaultHash::default();
930        hasher.update([T::SIGNATURE_SCHEME.flag()]);
931        hasher.update(pk);
932        let g_arr = hasher.finalize();
933        SuiAddress(g_arr.digest)
934    }
935}
936
937impl From<&PublicKey> for SuiAddress {
938    fn from(pk: &PublicKey) -> Self {
939        let mut hasher = DefaultHash::default();
940        hasher.update([pk.flag()]);
941        hasher.update(pk);
942        let g_arr = hasher.finalize();
943        SuiAddress(g_arr.digest)
944    }
945}
946
947impl From<&MultiSigPublicKey> for SuiAddress {
948    /// Derive a SuiAddress from [struct MultiSigPublicKey]. A MultiSig address
949    /// is defined as the 32-byte Blake2b hash of serializing the flag, the
950    /// threshold, concatenation of all n flag, public keys and
951    /// its weight. `flag_MultiSig || threshold || flag_1 || pk_1 || weight_1
952    /// || ... || flag_n || pk_n || weight_n`.
953    ///
954    /// When flag_i is ZkLogin, pk_i refers to [struct ZkLoginPublicIdentifier]
955    /// derived from padded address seed in bytes and iss.
956    fn from(multisig_pk: &MultiSigPublicKey) -> Self {
957        let mut hasher = DefaultHash::default();
958        hasher.update([SignatureScheme::MultiSig.flag()]);
959        hasher.update(multisig_pk.threshold().to_le_bytes());
960        multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
961            hasher.update([pk.flag()]);
962            hasher.update(pk.as_ref());
963            hasher.update(w.to_le_bytes());
964        });
965        SuiAddress(hasher.finalize().digest)
966    }
967}
968
969/// Sui address for [struct ZkLoginAuthenticator] is defined as the black2b hash of
970/// [zklogin_flag || iss_bytes_length || iss_bytes || unpadded_address_seed_in_bytes].
971impl TryFrom<&ZkLoginAuthenticator> for SuiAddress {
972    type Error = SuiError;
973    fn try_from(authenticator: &ZkLoginAuthenticator) -> SuiResult<Self> {
974        SuiAddress::try_from_unpadded(&authenticator.inputs)
975    }
976}
977
978impl TryFrom<&GenericSignature> for SuiAddress {
979    type Error = SuiError;
980    /// Derive a SuiAddress from a serialized signature in Sui [GenericSignature].
981    fn try_from(sig: &GenericSignature) -> SuiResult<Self> {
982        match sig {
983            GenericSignature::Signature(sig) => {
984                let scheme = sig.scheme();
985                let pub_key_bytes = sig.public_key_bytes();
986                let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
987                    SuiErrorKind::InvalidSignature {
988                        error: "Cannot parse pubkey".to_string(),
989                    }
990                })?;
991                Ok(SuiAddress::from(&pub_key))
992            }
993            GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
994            GenericSignature::MultiSigLegacy(ms) => {
995                Ok(crate::multisig::MultiSig::try_from(ms.clone())
996                    .map_err(|_| SuiErrorKind::InvalidSignature {
997                        error: "Invalid legacy multisig".to_string(),
998                    })?
999                    .get_pk()
1000                    .into())
1001            }
1002            GenericSignature::ZkLoginAuthenticator(zklogin) => {
1003                SuiAddress::try_from_unpadded(&zklogin.inputs)
1004            }
1005            GenericSignature::PasskeyAuthenticator(s) => Ok(SuiAddress::from(&s.get_pk()?)),
1006        }
1007    }
1008}
1009
1010impl fmt::Display for SuiAddress {
1011    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012        write!(f, "0x{}", Hex::encode(self.0))
1013    }
1014}
1015
1016impl fmt::Debug for SuiAddress {
1017    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1018        write!(f, "0x{}", Hex::encode(self.0))
1019    }
1020}
1021
1022/// Generate a fake SuiAddress with repeated one byte.
1023pub fn dbg_addr(name: u8) -> SuiAddress {
1024    let addr = [name; SUI_ADDRESS_LENGTH];
1025    SuiAddress(addr)
1026}
1027
1028#[derive(
1029    Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
1030)]
1031pub struct ExecutionDigests {
1032    pub transaction: TransactionDigest,
1033    pub effects: TransactionEffectsDigest,
1034}
1035
1036impl ExecutionDigests {
1037    pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
1038        Self {
1039            transaction,
1040            effects,
1041        }
1042    }
1043
1044    pub fn random() -> Self {
1045        Self {
1046            transaction: TransactionDigest::random(),
1047            effects: TransactionEffectsDigest::random(),
1048        }
1049    }
1050}
1051
1052#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
1053pub struct ExecutionData {
1054    pub transaction: Transaction,
1055    pub effects: TransactionEffects,
1056}
1057
1058impl ExecutionData {
1059    pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
1060        debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1061        Self {
1062            transaction,
1063            effects,
1064        }
1065    }
1066
1067    pub fn digests(&self) -> ExecutionDigests {
1068        self.effects.execution_digests()
1069    }
1070}
1071
1072#[derive(Clone, Eq, PartialEq, Debug)]
1073pub struct VerifiedExecutionData {
1074    pub transaction: VerifiedTransaction,
1075    pub effects: TransactionEffects,
1076}
1077
1078impl VerifiedExecutionData {
1079    pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
1080        debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1081        Self {
1082            transaction,
1083            effects,
1084        }
1085    }
1086
1087    pub fn new_unchecked(data: ExecutionData) -> Self {
1088        Self {
1089            transaction: VerifiedTransaction::new_unchecked(data.transaction),
1090            effects: data.effects,
1091        }
1092    }
1093
1094    pub fn into_inner(self) -> ExecutionData {
1095        ExecutionData {
1096            transaction: self.transaction.into_inner(),
1097            effects: self.effects,
1098        }
1099    }
1100
1101    pub fn digests(&self) -> ExecutionDigests {
1102        self.effects.execution_digests()
1103    }
1104}
1105
1106pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
1107pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
1108pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
1109    &MOVE_STDLIB_ADDRESS,
1110    STD_OPTION_MODULE_NAME,
1111    STD_OPTION_STRUCT_NAME,
1112);
1113
1114pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
1115pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
1116pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1117    &MOVE_STDLIB_ADDRESS,
1118    STD_ASCII_MODULE_NAME,
1119    STD_ASCII_STRUCT_NAME,
1120);
1121
1122pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
1123pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
1124pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1125    &MOVE_STDLIB_ADDRESS,
1126    STD_UTF8_MODULE_NAME,
1127    STD_UTF8_STRUCT_NAME,
1128);
1129
1130pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
1131pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
1132pub const RESOLVED_TX_CONTEXT: (&AccountAddress, &IdentStr, &IdentStr) = (
1133    &SUI_FRAMEWORK_ADDRESS,
1134    TX_CONTEXT_MODULE_NAME,
1135    TX_CONTEXT_STRUCT_NAME,
1136);
1137
1138pub const URL_MODULE_NAME: &IdentStr = ident_str!("url");
1139pub const URL_STRUCT_NAME: &IdentStr = ident_str!("Url");
1140
1141pub const VEC_MAP_MODULE_NAME: &IdentStr = ident_str!("vec_map");
1142pub const VEC_MAP_STRUCT_NAME: &IdentStr = ident_str!("VecMap");
1143pub const VEC_MAP_ENTRY_STRUCT_NAME: &IdentStr = ident_str!("Entry");
1144
1145pub fn move_ascii_str_layout() -> A::MoveStructLayout {
1146    A::MoveStructLayout {
1147        type_: StructTag {
1148            address: MOVE_STDLIB_ADDRESS,
1149            module: STD_ASCII_MODULE_NAME.to_owned(),
1150            name: STD_ASCII_STRUCT_NAME.to_owned(),
1151            type_params: vec![],
1152        },
1153        fields: vec![A::MoveFieldLayout::new(
1154            ident_str!("bytes").into(),
1155            A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1156        )],
1157    }
1158}
1159
1160pub fn move_utf8_str_layout() -> A::MoveStructLayout {
1161    A::MoveStructLayout {
1162        type_: StructTag {
1163            address: MOVE_STDLIB_ADDRESS,
1164            module: STD_UTF8_MODULE_NAME.to_owned(),
1165            name: STD_UTF8_STRUCT_NAME.to_owned(),
1166            type_params: vec![],
1167        },
1168        fields: vec![A::MoveFieldLayout::new(
1169            ident_str!("bytes").into(),
1170            A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1171        )],
1172    }
1173}
1174
1175pub fn url_layout() -> A::MoveStructLayout {
1176    A::MoveStructLayout {
1177        type_: StructTag {
1178            address: SUI_FRAMEWORK_ADDRESS,
1179            module: URL_MODULE_NAME.to_owned(),
1180            name: URL_STRUCT_NAME.to_owned(),
1181            type_params: vec![],
1182        },
1183        fields: vec![A::MoveFieldLayout::new(
1184            ident_str!("url").to_owned(),
1185            A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1186        )],
1187    }
1188}
1189
1190// The Rust representation of the Move `TxContext`.
1191// This struct must be kept in sync with the Move `TxContext` definition.
1192// Moving forward we are going to zero all fields of the Move `TxContext`
1193// and use native functions to retrieve info about the transaction.
1194// However we cannot remove the Move type and so this struct is going to
1195// be the Rust equivalent to the Move `TxContext` for legacy usages.
1196//
1197// `TxContext` in Rust (see below) is going to be purely used in Rust and can
1198// evolve as needed without worrying any compatibility with Move.
1199#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1200pub struct MoveLegacyTxContext {
1201    // Signer/sender of the transaction
1202    sender: AccountAddress,
1203    // Digest of the current transaction
1204    digest: Vec<u8>,
1205    // The current epoch number
1206    epoch: EpochId,
1207    // Timestamp that the epoch started at
1208    epoch_timestamp_ms: CheckpointTimestamp,
1209    // Number of `ObjectID`'s generated during execution of the current transaction
1210    ids_created: u64,
1211}
1212
1213impl From<&TxContext> for MoveLegacyTxContext {
1214    fn from(tx_context: &TxContext) -> Self {
1215        Self {
1216            sender: tx_context.sender,
1217            digest: tx_context.digest.clone(),
1218            epoch: tx_context.epoch,
1219            epoch_timestamp_ms: tx_context.epoch_timestamp_ms,
1220            ids_created: tx_context.ids_created,
1221        }
1222    }
1223}
1224
1225// Information about the transaction context.
1226// This struct is not related to Move and can evolve as needed/required.
1227#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1228pub struct TxContext {
1229    /// Signer/sender of the transaction
1230    sender: AccountAddress,
1231    /// Digest of the current transaction
1232    digest: Vec<u8>,
1233    /// The current epoch number
1234    epoch: EpochId,
1235    /// Timestamp that the epoch started at
1236    epoch_timestamp_ms: CheckpointTimestamp,
1237    /// Number of `ObjectID`'s generated during execution of the current transaction
1238    ids_created: u64,
1239    // Reference gas price
1240    rgp: u64,
1241    // gas price passed to transaction as input
1242    gas_price: u64,
1243    // gas budget passed to transaction as input
1244    gas_budget: u64,
1245    // address of the sponsor if any
1246    sponsor: Option<AccountAddress>,
1247    // whether the `TxContext` is native or not
1248    // (TODO: once we version execution we could drop this field)
1249    is_native: bool,
1250}
1251
1252#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1253pub enum TxContextKind {
1254    // No TxContext
1255    None,
1256    // &mut TxContext
1257    Mutable,
1258    // &TxContext
1259    Immutable,
1260}
1261
1262impl TxContext {
1263    pub fn new(
1264        sender: &SuiAddress,
1265        digest: &TransactionDigest,
1266        epoch_data: &EpochData,
1267        rgp: u64,
1268        gas_price: u64,
1269        gas_budget: u64,
1270        sponsor: Option<SuiAddress>,
1271        protocol_config: &ProtocolConfig,
1272    ) -> Self {
1273        Self::new_from_components(
1274            sender,
1275            digest,
1276            &epoch_data.epoch_id(),
1277            epoch_data.epoch_start_timestamp(),
1278            rgp,
1279            gas_price,
1280            gas_budget,
1281            sponsor,
1282            protocol_config,
1283        )
1284    }
1285
1286    pub fn new_from_components(
1287        sender: &SuiAddress,
1288        digest: &TransactionDigest,
1289        epoch_id: &EpochId,
1290        epoch_timestamp_ms: u64,
1291        rgp: u64,
1292        gas_price: u64,
1293        gas_budget: u64,
1294        sponsor: Option<SuiAddress>,
1295        protocol_config: &ProtocolConfig,
1296    ) -> Self {
1297        Self {
1298            sender: AccountAddress::new(sender.0),
1299            digest: digest.into_inner().to_vec(),
1300            epoch: *epoch_id,
1301            epoch_timestamp_ms,
1302            ids_created: 0,
1303            rgp,
1304            gas_price,
1305            gas_budget,
1306            sponsor: sponsor.map(|s| s.into()),
1307            is_native: protocol_config.move_native_context(),
1308        }
1309    }
1310
1311    /// Returns whether the type signature is &mut TxContext, &TxContext, or none of the above.
1312    pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1313        use SignatureToken as S;
1314        let (kind, s) = match s {
1315            S::MutableReference(s) => (TxContextKind::Mutable, s),
1316            S::Reference(s) => (TxContextKind::Immutable, s),
1317            _ => return TxContextKind::None,
1318        };
1319
1320        let S::Datatype(idx) = &**s else {
1321            return TxContextKind::None;
1322        };
1323
1324        if resolve_struct(view, *idx) == RESOLVED_TX_CONTEXT {
1325            kind
1326        } else {
1327            TxContextKind::None
1328        }
1329    }
1330
1331    pub fn type_() -> StructTag {
1332        StructTag {
1333            address: SUI_FRAMEWORK_ADDRESS,
1334            module: TX_CONTEXT_MODULE_NAME.to_owned(),
1335            name: TX_CONTEXT_STRUCT_NAME.to_owned(),
1336            type_params: vec![],
1337        }
1338    }
1339
1340    pub fn epoch(&self) -> EpochId {
1341        self.epoch
1342    }
1343
1344    pub fn sender(&self) -> SuiAddress {
1345        self.sender.into()
1346    }
1347
1348    pub fn epoch_timestamp_ms(&self) -> u64 {
1349        self.epoch_timestamp_ms
1350    }
1351
1352    /// Return the transaction digest, to include in new objects
1353    pub fn digest(&self) -> TransactionDigest {
1354        TransactionDigest::new(self.digest.clone().try_into().unwrap())
1355    }
1356
1357    pub fn sponsor(&self) -> Option<SuiAddress> {
1358        self.sponsor.map(SuiAddress::from)
1359    }
1360
1361    pub fn rgp(&self) -> u64 {
1362        self.rgp
1363    }
1364
1365    pub fn gas_price(&self) -> u64 {
1366        self.gas_price
1367    }
1368
1369    pub fn gas_budget(&self) -> u64 {
1370        self.gas_budget
1371    }
1372
1373    pub fn ids_created(&self) -> u64 {
1374        self.ids_created
1375    }
1376
1377    /// Derive a globally unique object ID by hashing self.digest | self.ids_created
1378    pub fn fresh_id(&mut self) -> ObjectID {
1379        let id = ObjectID::derive_id(self.digest(), self.ids_created);
1380
1381        self.ids_created += 1;
1382        id
1383    }
1384
1385    pub fn to_bcs_legacy_context(&self) -> Vec<u8> {
1386        let move_context: MoveLegacyTxContext = if self.is_native {
1387            let tx_context = &TxContext {
1388                sender: AccountAddress::ZERO,
1389                digest: self.digest.clone(),
1390                epoch: 0,
1391                epoch_timestamp_ms: 0,
1392                ids_created: 0,
1393                rgp: 0,
1394                gas_price: 0,
1395                gas_budget: 0,
1396                sponsor: None,
1397                is_native: true,
1398            };
1399            tx_context.into()
1400        } else {
1401            self.into()
1402        };
1403        bcs::to_bytes(&move_context).unwrap()
1404    }
1405
1406    pub fn to_vec(&self) -> Vec<u8> {
1407        bcs::to_bytes(&self).unwrap()
1408    }
1409
1410    /// Updates state of the context instance. It's intended to use
1411    /// when mutable context is passed over some boundary via
1412    /// serialize/deserialize and this is the reason why this method
1413    /// consumes the other context..
1414    pub fn update_state(&mut self, other: MoveLegacyTxContext) -> Result<(), ExecutionError> {
1415        if !self.is_native {
1416            if self.sender != other.sender
1417                || self.digest != other.digest
1418                || other.ids_created < self.ids_created
1419            {
1420                return Err(ExecutionError::new_with_source(
1421                    ExecutionErrorKind::InvariantViolation,
1422                    "Immutable fields for TxContext changed",
1423                ));
1424            }
1425            self.ids_created = other.ids_created;
1426        }
1427        Ok(())
1428    }
1429
1430    //
1431    // Move test only API
1432    //
1433    pub fn replace(
1434        &mut self,
1435        sender: AccountAddress,
1436        tx_hash: Vec<u8>,
1437        epoch: u64,
1438        epoch_timestamp_ms: u64,
1439        ids_created: u64,
1440        rgp: u64,
1441        gas_price: u64,
1442        gas_budget: u64,
1443        sponsor: Option<AccountAddress>,
1444    ) {
1445        self.sender = sender;
1446        self.digest = tx_hash;
1447        self.epoch = epoch;
1448        self.epoch_timestamp_ms = epoch_timestamp_ms;
1449        self.ids_created = ids_created;
1450        self.rgp = rgp;
1451        self.gas_price = gas_price;
1452        self.gas_budget = gas_budget;
1453        self.sponsor = sponsor;
1454    }
1455}
1456
1457// TODO: rename to version
1458impl SequenceNumber {
1459    pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1460    pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1461    pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1462    pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1463    pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1464        SequenceNumber(SequenceNumber::MAX.value() + 3);
1465    // Used to represent a sequence number whose value is unknown.
1466    // For internal use only. This should never appear on chain.
1467    pub const UNKNOWN: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 4);
1468
1469    pub const fn new() -> Self {
1470        SequenceNumber(0)
1471    }
1472
1473    pub const fn value(&self) -> u64 {
1474        self.0
1475    }
1476
1477    pub const fn from_u64(u: u64) -> Self {
1478        SequenceNumber(u)
1479    }
1480
1481    pub fn increment(&mut self) {
1482        assert_ne!(self.0, u64::MAX);
1483        self.0 += 1;
1484    }
1485
1486    pub fn increment_to(&mut self, next: SequenceNumber) {
1487        debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1488        *self = next;
1489    }
1490
1491    pub fn decrement(&mut self) {
1492        assert_ne!(self.0, 0);
1493        self.0 -= 1;
1494    }
1495
1496    pub fn decrement_to(&mut self, prev: SequenceNumber) {
1497        debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1498        *self = prev;
1499    }
1500
1501    /// Returns a new sequence number that is greater than all `SequenceNumber`s in `inputs`,
1502    /// assuming this operation will not overflow.
1503    #[must_use]
1504    pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1505        let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1506
1507        // TODO: Ensure this never overflows.
1508        // Option 1: Freeze the object when sequence number reaches MAX.
1509        // Option 2: Reject tx with MAX sequence number.
1510        // Issue #182.
1511        assert_ne!(max_input.0, u64::MAX);
1512
1513        SequenceNumber(max_input.0 + 1)
1514    }
1515
1516    pub fn is_cancelled(&self) -> bool {
1517        self == &SequenceNumber::CANCELLED_READ
1518            || self == &SequenceNumber::CONGESTED
1519            || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1520    }
1521
1522    pub fn is_valid(&self) -> bool {
1523        self < &SequenceNumber::MAX
1524    }
1525}
1526
1527impl From<SequenceNumber> for u64 {
1528    fn from(val: SequenceNumber) -> Self {
1529        val.0
1530    }
1531}
1532
1533impl From<u64> for SequenceNumber {
1534    fn from(value: u64) -> Self {
1535        SequenceNumber(value)
1536    }
1537}
1538
1539impl From<SequenceNumber> for usize {
1540    fn from(value: SequenceNumber) -> Self {
1541        value.0 as usize
1542    }
1543}
1544
1545impl ObjectID {
1546    /// The number of bytes in an address.
1547    pub const LENGTH: usize = AccountAddress::LENGTH;
1548    /// Hex address: 0x0
1549    pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1550    pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1551    /// Create a new ObjectID
1552    pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1553        Self(AccountAddress::new(obj_id))
1554    }
1555
1556    /// Const fn variant of `<ObjectID as From<AccountAddress>>::from`
1557    pub const fn from_address(addr: AccountAddress) -> Self {
1558        Self(addr)
1559    }
1560
1561    /// Return a random ObjectID.
1562    pub fn random() -> Self {
1563        Self::from(AccountAddress::random())
1564    }
1565
1566    /// Return a random ObjectID from a given RNG.
1567    pub fn random_from_rng<R>(rng: &mut R) -> Self
1568    where
1569        R: AllowedRng,
1570    {
1571        let buf: [u8; Self::LENGTH] = rng.r#gen();
1572        ObjectID::new(buf)
1573    }
1574
1575    /// Return the underlying bytes buffer of the ObjectID.
1576    pub fn to_vec(&self) -> Vec<u8> {
1577        self.0.to_vec()
1578    }
1579
1580    /// Parse the ObjectID from byte array or buffer.
1581    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1582        <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1583            .map_err(|_| ObjectIDParseError::TryFromSliceError)
1584            .map(ObjectID::new)
1585    }
1586
1587    /// Return the underlying bytes array of the ObjectID.
1588    pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1589        self.0.into_bytes()
1590    }
1591
1592    /// Make an ObjectID with padding 0s before the single byte.
1593    pub const fn from_single_byte(byte: u8) -> ObjectID {
1594        let mut bytes = [0u8; Self::LENGTH];
1595        bytes[Self::LENGTH - 1] = byte;
1596        ObjectID::new(bytes)
1597    }
1598
1599    /// Convert from hex string to ObjectID where the string is prefixed with 0x
1600    /// Padding 0s if the string is too short.
1601    pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1602        if !literal.starts_with("0x") {
1603            return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1604        }
1605
1606        let hex_len = literal.len() - 2;
1607
1608        // If the string is too short, pad it
1609        if hex_len < Self::LENGTH * 2 {
1610            let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1611            for _ in 0..Self::LENGTH * 2 - hex_len {
1612                hex_str.push('0');
1613            }
1614            hex_str.push_str(&literal[2..]);
1615            Self::from_str(&hex_str)
1616        } else {
1617            Self::from_str(&literal[2..])
1618        }
1619    }
1620
1621    /// Create an ObjectID from `TransactionDigest` and `creation_num`.
1622    /// Caller is responsible for ensuring that `creation_num` is fresh
1623    pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1624        let mut hasher = DefaultHash::default();
1625        hasher.update([HashingIntentScope::RegularObjectId as u8]);
1626        hasher.update(digest);
1627        hasher.update(creation_num.to_le_bytes());
1628        let hash = hasher.finalize();
1629
1630        // truncate into an ObjectID.
1631        // OK to access slice because digest should never be shorter than ObjectID::LENGTH.
1632        ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1633    }
1634
1635    /// Incremenent the ObjectID by usize IDs, assuming the ObjectID hex is a number represented as an array of bytes
1636    pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1637        let mut curr_vec = self.to_vec();
1638        let mut step_copy = step;
1639
1640        let mut carry = 0;
1641        for idx in (0..Self::LENGTH).rev() {
1642            if step_copy == 0 {
1643                // Nothing else to do
1644                break;
1645            }
1646            // Extract the relevant part
1647            let g = (step_copy % 0x100) as u16;
1648            // Shift to next group
1649            step_copy >>= 8;
1650            let mut val = curr_vec[idx] as u16;
1651            (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1652            curr_vec[idx] = val as u8;
1653        }
1654
1655        if carry > 0 {
1656            return Err(anyhow!("Increment will cause overflow"));
1657        }
1658        ObjectID::try_from(curr_vec).map_err(|w| w.into())
1659    }
1660
1661    /// Increment the ObjectID by one, assuming the ObjectID hex is a number represented as an array of bytes
1662    pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1663        let mut prev_val = self.to_vec();
1664        let mx = [0xFF; Self::LENGTH];
1665
1666        if prev_val == mx {
1667            return Err(anyhow!("Increment will cause overflow"));
1668        }
1669
1670        // This logic increments the integer representation of an ObjectID u8 array
1671        for idx in (0..Self::LENGTH).rev() {
1672            if prev_val[idx] == 0xFF {
1673                prev_val[idx] = 0;
1674            } else {
1675                prev_val[idx] += 1;
1676                break;
1677            };
1678        }
1679        ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1680    }
1681
1682    /// Create `count` object IDs starting with one at `offset`
1683    pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1684        let mut ret = Vec::new();
1685        let mut prev = offset;
1686        for o in 0..count {
1687            if o != 0 {
1688                prev = prev.next_increment()?;
1689            }
1690            ret.push(prev);
1691        }
1692        Ok(ret)
1693    }
1694
1695    /// Return the full hex string with 0x prefix without removing trailing 0s. Prefer this
1696    /// over [fn to_hex_literal] if the string needs to be fully preserved.
1697    pub fn to_hex_uncompressed(&self) -> String {
1698        format!("{self}")
1699    }
1700
1701    pub fn is_clock(&self) -> bool {
1702        *self == SUI_CLOCK_OBJECT_ID
1703    }
1704}
1705
1706impl From<SuiAddress> for ObjectID {
1707    fn from(address: SuiAddress) -> ObjectID {
1708        let tmp: AccountAddress = address.into();
1709        tmp.into()
1710    }
1711}
1712
1713impl From<AccountAddress> for ObjectID {
1714    fn from(address: AccountAddress) -> Self {
1715        Self(address)
1716    }
1717}
1718
1719impl fmt::Display for ObjectID {
1720    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1721        write!(f, "0x{}", Hex::encode(self.0))
1722    }
1723}
1724
1725impl fmt::Debug for ObjectID {
1726    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1727        write!(f, "0x{}", Hex::encode(self.0))
1728    }
1729}
1730
1731impl AsRef<[u8]> for ObjectID {
1732    fn as_ref(&self) -> &[u8] {
1733        self.0.as_slice()
1734    }
1735}
1736
1737impl TryFrom<&[u8]> for ObjectID {
1738    type Error = ObjectIDParseError;
1739
1740    /// Tries to convert the provided byte array into ObjectID.
1741    fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1742        Self::from_bytes(bytes)
1743    }
1744}
1745
1746impl TryFrom<Vec<u8>> for ObjectID {
1747    type Error = ObjectIDParseError;
1748
1749    /// Tries to convert the provided byte buffer into ObjectID.
1750    fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1751        Self::from_bytes(bytes)
1752    }
1753}
1754
1755impl FromStr for ObjectID {
1756    type Err = ObjectIDParseError;
1757
1758    /// Parse ObjectID from hex string with or without 0x prefix, pad with 0s if needed.
1759    fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1760        decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1761    }
1762}
1763
1764impl std::ops::Deref for ObjectID {
1765    type Target = AccountAddress;
1766
1767    fn deref(&self) -> &Self::Target {
1768        &self.0
1769    }
1770}
1771
1772/// Generate a fake ObjectID with repeated one byte.
1773pub fn dbg_object_id(name: u8) -> ObjectID {
1774    ObjectID::new([name; ObjectID::LENGTH])
1775}
1776
1777#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1778pub enum ObjectIDParseError {
1779    #[error("ObjectID hex literal must start with 0x")]
1780    HexLiteralPrefixMissing,
1781
1782    #[error("Could not convert from bytes slice")]
1783    TryFromSliceError,
1784}
1785
1786impl From<ObjectID> for AccountAddress {
1787    fn from(obj_id: ObjectID) -> Self {
1788        obj_id.0
1789    }
1790}
1791
1792impl From<SuiAddress> for AccountAddress {
1793    fn from(address: SuiAddress) -> Self {
1794        Self::new(address.0)
1795    }
1796}
1797
1798/// Hex serde for AccountAddress
1799struct HexAccountAddress;
1800
1801impl SerializeAs<AccountAddress> for HexAccountAddress {
1802    fn serialize_as<S>(value: &AccountAddress, serializer: S) -> Result<S::Ok, S::Error>
1803    where
1804        S: Serializer,
1805    {
1806        Hex::serialize_as(value, serializer)
1807    }
1808}
1809
1810impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress {
1811    fn deserialize_as<D>(deserializer: D) -> Result<AccountAddress, D::Error>
1812    where
1813        D: Deserializer<'de>,
1814    {
1815        let s = String::deserialize(deserializer)?;
1816        if s.starts_with("0x") {
1817            AccountAddress::from_hex_literal(&s)
1818        } else {
1819            AccountAddress::from_hex(&s)
1820        }
1821        .map_err(to_custom_deser_error::<'de, D, _>)
1822    }
1823}
1824
1825impl fmt::Display for MoveObjectType {
1826    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1827        let s: StructTag = self.clone().into();
1828        write!(
1829            f,
1830            "{}",
1831            to_sui_struct_tag_string(&s).map_err(fmt::Error::custom)?
1832        )
1833    }
1834}
1835
1836impl fmt::Display for ObjectType {
1837    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1838        match self {
1839            ObjectType::Package => write!(f, "{}", PACKAGE),
1840            ObjectType::Struct(t) => write!(f, "{}", t),
1841        }
1842    }
1843}
1844
1845// SizeOneVec is a wrapper around Vec<T> that enforces the size of the vec to be 1.
1846// This seems pointless, but it allows us to have fields in protocol messages that are
1847// current enforced to be of size 1, but might later allow other sizes, and to have
1848// that constraint enforced in the serialization/deserialization layer, instead of
1849// requiring manual input validation.
1850#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1851#[serde(try_from = "Vec<T>")]
1852pub struct SizeOneVec<T> {
1853    e: T,
1854}
1855
1856impl<T> SizeOneVec<T> {
1857    pub fn new(e: T) -> Self {
1858        Self { e }
1859    }
1860
1861    pub fn element(&self) -> &T {
1862        &self.e
1863    }
1864
1865    pub fn element_mut(&mut self) -> &mut T {
1866        &mut self.e
1867    }
1868
1869    pub fn into_inner(self) -> T {
1870        self.e
1871    }
1872
1873    pub fn iter(&self) -> std::iter::Once<&T> {
1874        std::iter::once(&self.e)
1875    }
1876}
1877
1878impl<T> Serialize for SizeOneVec<T>
1879where
1880    T: Serialize,
1881{
1882    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1883    where
1884        S: Serializer,
1885    {
1886        let mut seq = serializer.serialize_seq(Some(1))?;
1887        seq.serialize_element(&self.e)?;
1888        seq.end()
1889    }
1890}
1891
1892impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1893    type Error = anyhow::Error;
1894
1895    fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1896        if v.len() != 1 {
1897            Err(anyhow!("Expected a vec of size 1"))
1898        } else {
1899            Ok(SizeOneVec {
1900                e: v.pop().unwrap(),
1901            })
1902        }
1903    }
1904}
1905
1906#[test]
1907fn test_size_one_vec_is_transparent() {
1908    let regular = vec![42u8];
1909    let size_one = SizeOneVec::new(42u8);
1910
1911    // Vec -> SizeOneVec serialization is transparent
1912    let regular_ser = bcs::to_bytes(&regular).unwrap();
1913    let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(&regular_ser).unwrap();
1914    assert_eq!(size_one, size_one_deser);
1915
1916    // other direction works too
1917    let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1918    let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1919    assert_eq!(regular_deser, vec![43u8]);
1920
1921    // we get a deserialize error when deserializing a vec with size != 1
1922    let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1923    bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1924
1925    let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1926    bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1927}