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