1use 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::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}
226pub type ConsensusObjectSequenceKey = (ObjectID, SequenceNumber);
229
230#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
235pub struct MoveObjectType(MoveObjectType_);
236
237#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
240pub enum MoveObjectType_ {
241 Other(StructTag),
243 GasCoin,
245 StakedSui,
247 Coin(TypeTag),
249 SuiBalanceAccumulatorField,
252 BalanceAccumulatorField(TypeTag),
256 }
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<TypeTag> {
313 match &self.0 {
314 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
315 MoveObjectType_::StakedSui => vec![],
316 MoveObjectType_::Coin(inner) => vec![inner.clone()],
317 MoveObjectType_::SuiBalanceAccumulatorField => {
318 Self::balance_accumulator_field_type_params(GAS::type_tag())
319 }
320 MoveObjectType_::BalanceAccumulatorField(inner) => {
321 Self::balance_accumulator_field_type_params(inner.clone())
322 }
323 MoveObjectType_::Other(s) => s.type_params.clone(),
324 }
325 }
326
327 pub fn into_type_params(self) -> Vec<TypeTag> {
328 match self.0 {
329 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
330 MoveObjectType_::StakedSui => vec![],
331 MoveObjectType_::Coin(inner) => vec![inner],
332 MoveObjectType_::SuiBalanceAccumulatorField => {
333 Self::balance_accumulator_field_type_params(GAS::type_tag())
334 }
335 MoveObjectType_::BalanceAccumulatorField(inner) => {
336 Self::balance_accumulator_field_type_params(inner)
337 }
338 MoveObjectType_::Other(s) => s.type_params,
339 }
340 }
341
342 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
343 match &self.0 {
344 MoveObjectType_::GasCoin => Some(GAS::type_tag()),
345 MoveObjectType_::Coin(inner) => Some(inner.clone()),
346 MoveObjectType_::StakedSui => None,
347 MoveObjectType_::SuiBalanceAccumulatorField => None,
348 MoveObjectType_::BalanceAccumulatorField(_) => None,
349 MoveObjectType_::Other(_) => None,
350 }
351 }
352
353 pub fn balance_accumulator_field_type_maybe(&self) -> Option<TypeTag> {
354 match &self.0 {
355 MoveObjectType_::SuiBalanceAccumulatorField => Some(GAS::type_tag()),
356 MoveObjectType_::BalanceAccumulatorField(inner) => Some(inner.clone()),
357 _ => None,
358 }
359 }
360
361 pub fn is_balance_accumulator_field(&self) -> bool {
362 matches!(
363 self.0,
364 MoveObjectType_::SuiBalanceAccumulatorField
365 | MoveObjectType_::BalanceAccumulatorField(_)
366 )
367 }
368
369 pub fn is_sui_balance_accumulator_field(&self) -> bool {
370 matches!(self.0, MoveObjectType_::SuiBalanceAccumulatorField)
371 }
372
373 pub fn module_id(&self) -> ModuleId {
374 ModuleId::new(self.address(), self.module().to_owned())
375 }
376
377 pub fn size_for_gas_metering(&self) -> usize {
378 match &self.0 {
380 MoveObjectType_::GasCoin => 1,
381 MoveObjectType_::StakedSui => 1,
382 MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
383 MoveObjectType_::SuiBalanceAccumulatorField => 1,
384 MoveObjectType_::BalanceAccumulatorField(inner) => {
385 bcs::serialized_size(inner).unwrap() + 1
386 }
387 MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
388 }
389 }
390
391 pub fn is_coin(&self) -> bool {
393 match &self.0 {
394 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
395 MoveObjectType_::StakedSui
396 | MoveObjectType_::SuiBalanceAccumulatorField
397 | MoveObjectType_::BalanceAccumulatorField(_)
398 | MoveObjectType_::Other(_) => false,
399 }
400 }
401
402 pub fn is_gas_coin(&self) -> bool {
404 match &self.0 {
405 MoveObjectType_::GasCoin => true,
406 MoveObjectType_::StakedSui
407 | MoveObjectType_::Coin(_)
408 | MoveObjectType_::SuiBalanceAccumulatorField
409 | MoveObjectType_::BalanceAccumulatorField(_)
410 | MoveObjectType_::Other(_) => false,
411 }
412 }
413
414 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
416 match &self.0 {
417 MoveObjectType_::GasCoin => GAS::is_gas_type(t),
418 MoveObjectType_::Coin(c) => t == c,
419 MoveObjectType_::StakedSui
420 | MoveObjectType_::SuiBalanceAccumulatorField
421 | MoveObjectType_::BalanceAccumulatorField(_)
422 | MoveObjectType_::Other(_) => false,
423 }
424 }
425
426 pub fn is_staked_sui(&self) -> bool {
427 match &self.0 {
428 MoveObjectType_::StakedSui => true,
429 MoveObjectType_::GasCoin
430 | MoveObjectType_::Coin(_)
431 | MoveObjectType_::SuiBalanceAccumulatorField
432 | MoveObjectType_::BalanceAccumulatorField(_)
433 | MoveObjectType_::Other(_) => false,
434 }
435 }
436
437 pub fn is_coin_metadata(&self) -> bool {
438 match &self.0 {
439 MoveObjectType_::GasCoin
440 | MoveObjectType_::StakedSui
441 | MoveObjectType_::Coin(_)
442 | MoveObjectType_::SuiBalanceAccumulatorField
443 | MoveObjectType_::BalanceAccumulatorField(_) => false,
444 MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s),
445 }
446 }
447
448 pub fn is_currency(&self) -> bool {
449 match &self.0 {
450 MoveObjectType_::GasCoin
451 | MoveObjectType_::StakedSui
452 | MoveObjectType_::Coin(_)
453 | MoveObjectType_::SuiBalanceAccumulatorField
454 | MoveObjectType_::BalanceAccumulatorField(_) => false,
455 MoveObjectType_::Other(s) => Currency::is_currency(s),
456 }
457 }
458
459 pub fn is_treasury_cap(&self) -> bool {
460 match &self.0 {
461 MoveObjectType_::GasCoin
462 | MoveObjectType_::StakedSui
463 | MoveObjectType_::Coin(_)
464 | MoveObjectType_::SuiBalanceAccumulatorField
465 | MoveObjectType_::BalanceAccumulatorField(_) => false,
466 MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
467 }
468 }
469
470 pub fn is_upgrade_cap(&self) -> bool {
471 self.address() == SUI_FRAMEWORK_ADDRESS
472 && self.module().as_str() == "package"
473 && self.name().as_str() == "UpgradeCap"
474 }
475
476 pub fn is_regulated_coin_metadata(&self) -> bool {
477 self.address() == SUI_FRAMEWORK_ADDRESS
478 && self.module().as_str() == "coin"
479 && self.name().as_str() == "RegulatedCoinMetadata"
480 }
481
482 pub fn is_coin_deny_cap(&self) -> bool {
483 self.address() == SUI_FRAMEWORK_ADDRESS
484 && self.module().as_str() == "coin"
485 && self.name().as_str() == "DenyCap"
486 }
487
488 pub fn is_coin_deny_cap_v2(&self) -> bool {
489 self.address() == SUI_FRAMEWORK_ADDRESS
490 && self.module().as_str() == "coin"
491 && self.name().as_str() == "DenyCapV2"
492 }
493
494 pub fn is_dynamic_field(&self) -> bool {
495 match &self.0 {
496 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
497 false
498 }
499 MoveObjectType_::SuiBalanceAccumulatorField
500 | MoveObjectType_::BalanceAccumulatorField(_) => {
501 true }
503 MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
504 }
505 }
506
507 pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> SuiResult<TypeTag> {
508 match &self.0 {
509 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
510 Err(SuiErrorKind::ObjectDeserializationError {
511 error: "Error extracting dynamic object name from specialized object type"
512 .to_string(),
513 }
514 .into())
515 }
516 MoveObjectType_::SuiBalanceAccumulatorField
517 | MoveObjectType_::BalanceAccumulatorField(_) => {
518 let struct_tag: StructTag = self.clone().into();
519 DynamicFieldInfo::try_extract_field_name(&struct_tag, type_)
520 }
521 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
522 }
523 }
524
525 pub fn try_extract_field_value(&self) -> SuiResult<TypeTag> {
526 match &self.0 {
527 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
528 Err(SuiErrorKind::ObjectDeserializationError {
529 error: "Error extracting dynamic object value from specialized object type"
530 .to_string(),
531 }
532 .into())
533 }
534 MoveObjectType_::SuiBalanceAccumulatorField
535 | MoveObjectType_::BalanceAccumulatorField(_) => {
536 let struct_tag: StructTag = self.clone().into();
537 DynamicFieldInfo::try_extract_field_value(&struct_tag)
538 }
539 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
540 }
541 }
542
543 pub fn is(&self, s: &StructTag) -> bool {
544 match &self.0 {
545 MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
546 MoveObjectType_::StakedSui => StakedSui::is_staked_sui(s),
547 MoveObjectType_::Coin(inner) => {
548 Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
549 }
550 MoveObjectType_::SuiBalanceAccumulatorField => {
551 is_balance_accumulator_field(s)
552 && extract_balance_type_from_field(s)
553 .map(|t| GAS::is_gas_type(&t))
554 .unwrap_or(false)
555 }
556 MoveObjectType_::BalanceAccumulatorField(inner) => {
557 is_balance_accumulator_field(s)
558 && extract_balance_type_from_field(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 pub fn to_canonical_string(&self, with_prefix: bool) -> String {
576 StructTag::from(self.clone()).to_canonical_string(with_prefix)
577 }
578
579 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 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 MoveObjectType_::Coin(s.type_params.pop().unwrap())
606 } else if StakedSui::is_staked_sui(&s) {
607 MoveObjectType_::StakedSui
608 } else if is_balance_accumulator_field(&s) {
609 if let Some(balance_type) = extract_balance_type_from_field(&s) {
610 if GAS::is_gas_type(&balance_type) {
611 MoveObjectType_::SuiBalanceAccumulatorField
612 } else {
613 MoveObjectType_::BalanceAccumulatorField(balance_type)
614 }
615 } else {
616 MoveObjectType_::Other(s)
617 }
618 } else {
619 MoveObjectType_::Other(s)
620 })
621 }
622}
623
624impl From<MoveObjectType> for StructTag {
625 fn from(t: MoveObjectType) -> Self {
626 match t.0 {
627 MoveObjectType_::GasCoin => GasCoin::type_(),
628 MoveObjectType_::StakedSui => StakedSui::type_(),
629 MoveObjectType_::Coin(inner) => Coin::type_(inner),
630 MoveObjectType_::SuiBalanceAccumulatorField => {
631 MoveObjectType::balance_accumulator_field_struct_tag(GAS::type_tag())
632 }
633 MoveObjectType_::BalanceAccumulatorField(inner) => {
634 MoveObjectType::balance_accumulator_field_struct_tag(inner)
635 }
636 MoveObjectType_::Other(s) => s,
637 }
638 }
639}
640
641impl From<MoveObjectType> for TypeTag {
642 fn from(o: MoveObjectType) -> TypeTag {
643 let s: StructTag = o.into();
644 TypeTag::Struct(Box::new(s))
645 }
646}
647
648pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
650 use TypeTag as T;
651
652 match t {
653 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
654 T::Vector(inner) => is_primitive_type_tag(inner),
655 T::Struct(st) => {
656 let StructTag {
657 address,
658 module,
659 name,
660 type_params: type_args,
661 } = &**st;
662 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
663 if resolved_struct == RESOLVED_SUI_ID {
665 return true;
666 }
667 if resolved_struct == RESOLVED_UTF8_STR {
669 return true;
670 }
671 if resolved_struct == RESOLVED_ASCII_STR {
673 return true;
674 }
675 resolved_struct == RESOLVED_STD_OPTION
677 && type_args.len() == 1
678 && is_primitive_type_tag(&type_args[0])
679 }
680 T::Signer => false,
681 }
682}
683
684#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
686pub enum ObjectType {
687 Package,
689 Struct(MoveObjectType),
691}
692
693impl From<&Object> for ObjectType {
694 fn from(o: &Object) -> Self {
695 o.data
696 .type_()
697 .map(|t| ObjectType::Struct(t.clone()))
698 .unwrap_or(ObjectType::Package)
699 }
700}
701
702impl TryFrom<ObjectType> for StructTag {
703 type Error = anyhow::Error;
704
705 fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
706 match o {
707 ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
708 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
709 }
710 }
711}
712
713impl FromStr for ObjectType {
714 type Err = anyhow::Error;
715
716 fn from_str(s: &str) -> Result<Self, Self::Err> {
717 if s.to_lowercase() == PACKAGE {
718 Ok(ObjectType::Package)
719 } else {
720 let tag = parse_sui_struct_tag(s)?;
721 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
722 }
723 }
724}
725
726#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
727pub struct ObjectInfo {
728 pub object_id: ObjectID,
729 pub version: SequenceNumber,
730 pub digest: ObjectDigest,
731 pub type_: ObjectType,
732 pub owner: Owner,
733 pub previous_transaction: TransactionDigest,
734}
735
736impl ObjectInfo {
737 pub fn new(oref: &ObjectRef, o: &Object) -> Self {
738 let (object_id, version, digest) = *oref;
739 Self {
740 object_id,
741 version,
742 digest,
743 type_: o.into(),
744 owner: o.owner.clone(),
745 previous_transaction: o.previous_transaction,
746 }
747 }
748
749 pub fn from_object(object: &Object) -> Self {
750 Self {
751 object_id: object.id(),
752 version: object.version(),
753 digest: object.digest(),
754 type_: object.into(),
755 owner: object.owner.clone(),
756 previous_transaction: object.previous_transaction,
757 }
758 }
759}
760const PACKAGE: &str = "package";
761impl ObjectType {
762 pub fn is_gas_coin(&self) -> bool {
763 matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
764 }
765
766 pub fn is_coin(&self) -> bool {
767 matches!(self, ObjectType::Struct(s) if s.is_coin())
768 }
769
770 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
772 matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
773 }
774
775 pub fn is_package(&self) -> bool {
776 matches!(self, ObjectType::Package)
777 }
778}
779
780impl From<ObjectInfo> for ObjectRef {
781 fn from(info: ObjectInfo) -> Self {
782 (info.object_id, info.version, info.digest)
783 }
784}
785
786impl From<&ObjectInfo> for ObjectRef {
787 fn from(info: &ObjectInfo) -> Self {
788 (info.object_id, info.version, info.digest)
789 }
790}
791
792pub const SUI_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
793
794#[serde_as]
795#[derive(
796 Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
797)]
798#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
799pub struct SuiAddress(
800 #[schemars(with = "Hex")]
801 #[serde_as(as = "Readable<Hex, _>")]
802 [u8; SUI_ADDRESS_LENGTH],
803);
804
805impl SuiAddress {
806 pub const ZERO: Self = Self([0u8; SUI_ADDRESS_LENGTH]);
807
808 pub fn to_vec(&self) -> Vec<u8> {
810 self.0.to_vec()
811 }
812
813 pub fn random_for_testing_only() -> Self {
815 AccountAddress::random().into()
816 }
817
818 pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
819 let buf: [u8; SUI_ADDRESS_LENGTH] = rng.r#gen();
820 Self(buf)
821 }
822
823 pub fn optional_address_as_hex<S>(
825 key: &Option<SuiAddress>,
826 serializer: S,
827 ) -> Result<S::Ok, S::Error>
828 where
829 S: serde::ser::Serializer,
830 {
831 serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
832 }
833
834 pub fn optional_address_from_hex<'de, D>(
836 deserializer: D,
837 ) -> Result<Option<SuiAddress>, D::Error>
838 where
839 D: serde::de::Deserializer<'de>,
840 {
841 let s = String::deserialize(deserializer)?;
842 let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
843 Ok(Some(value))
844 }
845
846 pub fn to_inner(self) -> [u8; SUI_ADDRESS_LENGTH] {
848 self.0
849 }
850
851 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, SuiError> {
853 <[u8; SUI_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
854 .map_err(|_| SuiErrorKind::InvalidAddress.into())
855 .map(SuiAddress)
856 }
857
858 pub fn try_from_padded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
862 Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
863 }
864
865 pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
867 let mut hasher = DefaultHash::default();
868 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
869 let iss_bytes = inputs.get_iss().as_bytes();
870 hasher.update([iss_bytes.len() as u8]);
871 hasher.update(iss_bytes);
872 hasher.update(inputs.get_address_seed().unpadded());
873 Ok(SuiAddress(hasher.finalize().digest))
874 }
875}
876
877impl From<ObjectID> for SuiAddress {
878 fn from(object_id: ObjectID) -> SuiAddress {
879 Self(object_id.into_bytes())
880 }
881}
882
883impl From<AccountAddress> for SuiAddress {
884 fn from(address: AccountAddress) -> SuiAddress {
885 Self(address.into_bytes())
886 }
887}
888
889impl TryFrom<&[u8]> for SuiAddress {
890 type Error = SuiError;
891
892 fn try_from(bytes: &[u8]) -> Result<Self, SuiError> {
894 Self::from_bytes(bytes)
895 }
896}
897
898impl TryFrom<Vec<u8>> for SuiAddress {
899 type Error = SuiError;
900
901 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
903 Self::from_bytes(bytes)
904 }
905}
906
907impl AsRef<[u8]> for SuiAddress {
908 fn as_ref(&self) -> &[u8] {
909 &self.0[..]
910 }
911}
912
913impl FromStr for SuiAddress {
914 type Err = anyhow::Error;
915 fn from_str(s: &str) -> Result<Self, Self::Err> {
916 decode_bytes_hex(s).map_err(|e| anyhow!(e))
917 }
918}
919
920impl<T: SuiPublicKey> From<&T> for SuiAddress {
921 fn from(pk: &T) -> Self {
922 let mut hasher = DefaultHash::default();
923 hasher.update([T::SIGNATURE_SCHEME.flag()]);
924 hasher.update(pk);
925 let g_arr = hasher.finalize();
926 SuiAddress(g_arr.digest)
927 }
928}
929
930impl From<&PublicKey> for SuiAddress {
931 fn from(pk: &PublicKey) -> Self {
932 let mut hasher = DefaultHash::default();
933 hasher.update([pk.flag()]);
934 hasher.update(pk);
935 let g_arr = hasher.finalize();
936 SuiAddress(g_arr.digest)
937 }
938}
939
940impl From<&MultiSigPublicKey> for SuiAddress {
941 fn from(multisig_pk: &MultiSigPublicKey) -> Self {
950 let mut hasher = DefaultHash::default();
951 hasher.update([SignatureScheme::MultiSig.flag()]);
952 hasher.update(multisig_pk.threshold().to_le_bytes());
953 multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
954 hasher.update([pk.flag()]);
955 hasher.update(pk.as_ref());
956 hasher.update(w.to_le_bytes());
957 });
958 SuiAddress(hasher.finalize().digest)
959 }
960}
961
962impl TryFrom<&ZkLoginAuthenticator> for SuiAddress {
965 type Error = SuiError;
966 fn try_from(authenticator: &ZkLoginAuthenticator) -> SuiResult<Self> {
967 SuiAddress::try_from_unpadded(&authenticator.inputs)
968 }
969}
970
971impl TryFrom<&GenericSignature> for SuiAddress {
972 type Error = SuiError;
973 fn try_from(sig: &GenericSignature) -> SuiResult<Self> {
975 match sig {
976 GenericSignature::Signature(sig) => {
977 let scheme = sig.scheme();
978 let pub_key_bytes = sig.public_key_bytes();
979 let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
980 SuiErrorKind::InvalidSignature {
981 error: "Cannot parse pubkey".to_string(),
982 }
983 })?;
984 Ok(SuiAddress::from(&pub_key))
985 }
986 GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
987 GenericSignature::MultiSigLegacy(ms) => {
988 Ok(crate::multisig::MultiSig::try_from(ms.clone())
989 .map_err(|_| SuiErrorKind::InvalidSignature {
990 error: "Invalid legacy multisig".to_string(),
991 })?
992 .get_pk()
993 .into())
994 }
995 GenericSignature::ZkLoginAuthenticator(zklogin) => {
996 SuiAddress::try_from_unpadded(&zklogin.inputs)
997 }
998 GenericSignature::PasskeyAuthenticator(s) => Ok(SuiAddress::from(&s.get_pk()?)),
999 }
1000 }
1001}
1002
1003impl fmt::Display for SuiAddress {
1004 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1005 write!(f, "0x{}", Hex::encode(self.0))
1006 }
1007}
1008
1009impl fmt::Debug for SuiAddress {
1010 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1011 write!(f, "0x{}", Hex::encode(self.0))
1012 }
1013}
1014
1015pub fn dbg_addr(name: u8) -> SuiAddress {
1017 let addr = [name; SUI_ADDRESS_LENGTH];
1018 SuiAddress(addr)
1019}
1020
1021#[derive(
1022 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
1023)]
1024pub struct ExecutionDigests {
1025 pub transaction: TransactionDigest,
1026 pub effects: TransactionEffectsDigest,
1027}
1028
1029impl ExecutionDigests {
1030 pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
1031 Self {
1032 transaction,
1033 effects,
1034 }
1035 }
1036
1037 pub fn random() -> Self {
1038 Self {
1039 transaction: TransactionDigest::random(),
1040 effects: TransactionEffectsDigest::random(),
1041 }
1042 }
1043}
1044
1045#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
1046pub struct ExecutionData {
1047 pub transaction: Transaction,
1048 pub effects: TransactionEffects,
1049}
1050
1051impl ExecutionData {
1052 pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
1053 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1054 Self {
1055 transaction,
1056 effects,
1057 }
1058 }
1059
1060 pub fn digests(&self) -> ExecutionDigests {
1061 self.effects.execution_digests()
1062 }
1063}
1064
1065#[derive(Clone, Eq, PartialEq, Debug)]
1066pub struct VerifiedExecutionData {
1067 pub transaction: VerifiedTransaction,
1068 pub effects: TransactionEffects,
1069}
1070
1071impl VerifiedExecutionData {
1072 pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
1073 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1074 Self {
1075 transaction,
1076 effects,
1077 }
1078 }
1079
1080 pub fn new_unchecked(data: ExecutionData) -> Self {
1081 Self {
1082 transaction: VerifiedTransaction::new_unchecked(data.transaction),
1083 effects: data.effects,
1084 }
1085 }
1086
1087 pub fn into_inner(self) -> ExecutionData {
1088 ExecutionData {
1089 transaction: self.transaction.into_inner(),
1090 effects: self.effects,
1091 }
1092 }
1093
1094 pub fn digests(&self) -> ExecutionDigests {
1095 self.effects.execution_digests()
1096 }
1097}
1098
1099pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
1100pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
1101pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
1102 &MOVE_STDLIB_ADDRESS,
1103 STD_OPTION_MODULE_NAME,
1104 STD_OPTION_STRUCT_NAME,
1105);
1106
1107pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
1108pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
1109pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1110 &MOVE_STDLIB_ADDRESS,
1111 STD_ASCII_MODULE_NAME,
1112 STD_ASCII_STRUCT_NAME,
1113);
1114
1115pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
1116pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
1117pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1118 &MOVE_STDLIB_ADDRESS,
1119 STD_UTF8_MODULE_NAME,
1120 STD_UTF8_STRUCT_NAME,
1121);
1122
1123pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
1124pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
1125pub const RESOLVED_TX_CONTEXT: (&AccountAddress, &IdentStr, &IdentStr) = (
1126 &SUI_FRAMEWORK_ADDRESS,
1127 TX_CONTEXT_MODULE_NAME,
1128 TX_CONTEXT_STRUCT_NAME,
1129);
1130
1131pub const URL_MODULE_NAME: &IdentStr = ident_str!("url");
1132pub const URL_STRUCT_NAME: &IdentStr = ident_str!("Url");
1133
1134pub fn move_ascii_str_layout() -> A::MoveStructLayout {
1135 A::MoveStructLayout {
1136 type_: StructTag {
1137 address: MOVE_STDLIB_ADDRESS,
1138 module: STD_ASCII_MODULE_NAME.to_owned(),
1139 name: STD_ASCII_STRUCT_NAME.to_owned(),
1140 type_params: vec![],
1141 },
1142 fields: vec![A::MoveFieldLayout::new(
1143 ident_str!("bytes").into(),
1144 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1145 )],
1146 }
1147}
1148
1149pub fn move_utf8_str_layout() -> A::MoveStructLayout {
1150 A::MoveStructLayout {
1151 type_: StructTag {
1152 address: MOVE_STDLIB_ADDRESS,
1153 module: STD_UTF8_MODULE_NAME.to_owned(),
1154 name: STD_UTF8_STRUCT_NAME.to_owned(),
1155 type_params: vec![],
1156 },
1157 fields: vec![A::MoveFieldLayout::new(
1158 ident_str!("bytes").into(),
1159 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1160 )],
1161 }
1162}
1163
1164pub fn url_layout() -> A::MoveStructLayout {
1165 A::MoveStructLayout {
1166 type_: StructTag {
1167 address: SUI_FRAMEWORK_ADDRESS,
1168 module: URL_MODULE_NAME.to_owned(),
1169 name: URL_STRUCT_NAME.to_owned(),
1170 type_params: vec![],
1171 },
1172 fields: vec![A::MoveFieldLayout::new(
1173 ident_str!("url").to_owned(),
1174 A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1175 )],
1176 }
1177}
1178
1179#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1189pub struct MoveLegacyTxContext {
1190 sender: AccountAddress,
1192 digest: Vec<u8>,
1194 epoch: EpochId,
1196 epoch_timestamp_ms: CheckpointTimestamp,
1198 ids_created: u64,
1200}
1201
1202impl From<&TxContext> for MoveLegacyTxContext {
1203 fn from(tx_context: &TxContext) -> Self {
1204 Self {
1205 sender: tx_context.sender,
1206 digest: tx_context.digest.clone(),
1207 epoch: tx_context.epoch,
1208 epoch_timestamp_ms: tx_context.epoch_timestamp_ms,
1209 ids_created: tx_context.ids_created,
1210 }
1211 }
1212}
1213
1214#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1217pub struct TxContext {
1218 sender: AccountAddress,
1220 digest: Vec<u8>,
1222 epoch: EpochId,
1224 epoch_timestamp_ms: CheckpointTimestamp,
1226 ids_created: u64,
1228 rgp: u64,
1230 gas_price: u64,
1232 gas_budget: u64,
1234 sponsor: Option<AccountAddress>,
1236 is_native: bool,
1239}
1240
1241#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1242pub enum TxContextKind {
1243 None,
1245 Mutable,
1247 Immutable,
1249}
1250
1251impl TxContext {
1252 pub fn new(
1253 sender: &SuiAddress,
1254 digest: &TransactionDigest,
1255 epoch_data: &EpochData,
1256 rgp: u64,
1257 gas_price: u64,
1258 gas_budget: u64,
1259 sponsor: Option<SuiAddress>,
1260 protocol_config: &ProtocolConfig,
1261 ) -> Self {
1262 Self::new_from_components(
1263 sender,
1264 digest,
1265 &epoch_data.epoch_id(),
1266 epoch_data.epoch_start_timestamp(),
1267 rgp,
1268 gas_price,
1269 gas_budget,
1270 sponsor,
1271 protocol_config,
1272 )
1273 }
1274
1275 pub fn new_from_components(
1276 sender: &SuiAddress,
1277 digest: &TransactionDigest,
1278 epoch_id: &EpochId,
1279 epoch_timestamp_ms: u64,
1280 rgp: u64,
1281 gas_price: u64,
1282 gas_budget: u64,
1283 sponsor: Option<SuiAddress>,
1284 protocol_config: &ProtocolConfig,
1285 ) -> Self {
1286 Self {
1287 sender: AccountAddress::new(sender.0),
1288 digest: digest.into_inner().to_vec(),
1289 epoch: *epoch_id,
1290 epoch_timestamp_ms,
1291 ids_created: 0,
1292 rgp,
1293 gas_price,
1294 gas_budget,
1295 sponsor: sponsor.map(|s| s.into()),
1296 is_native: protocol_config.move_native_context(),
1297 }
1298 }
1299
1300 pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1302 use SignatureToken as S;
1303 let (kind, s) = match s {
1304 S::MutableReference(s) => (TxContextKind::Mutable, s),
1305 S::Reference(s) => (TxContextKind::Immutable, s),
1306 _ => return TxContextKind::None,
1307 };
1308
1309 let S::Datatype(idx) = &**s else {
1310 return TxContextKind::None;
1311 };
1312
1313 if resolve_struct(view, *idx) == RESOLVED_TX_CONTEXT {
1314 kind
1315 } else {
1316 TxContextKind::None
1317 }
1318 }
1319
1320 pub fn type_() -> StructTag {
1321 StructTag {
1322 address: SUI_FRAMEWORK_ADDRESS,
1323 module: TX_CONTEXT_MODULE_NAME.to_owned(),
1324 name: TX_CONTEXT_STRUCT_NAME.to_owned(),
1325 type_params: vec![],
1326 }
1327 }
1328
1329 pub fn epoch(&self) -> EpochId {
1330 self.epoch
1331 }
1332
1333 pub fn sender(&self) -> SuiAddress {
1334 self.sender.into()
1335 }
1336
1337 pub fn epoch_timestamp_ms(&self) -> u64 {
1338 self.epoch_timestamp_ms
1339 }
1340
1341 pub fn digest(&self) -> TransactionDigest {
1343 TransactionDigest::new(self.digest.clone().try_into().unwrap())
1344 }
1345
1346 pub fn sponsor(&self) -> Option<SuiAddress> {
1347 self.sponsor.map(SuiAddress::from)
1348 }
1349
1350 pub fn rgp(&self) -> u64 {
1351 self.rgp
1352 }
1353
1354 pub fn gas_price(&self) -> u64 {
1355 self.gas_price
1356 }
1357
1358 pub fn gas_budget(&self) -> u64 {
1359 self.gas_budget
1360 }
1361
1362 pub fn ids_created(&self) -> u64 {
1363 self.ids_created
1364 }
1365
1366 pub fn fresh_id(&mut self) -> ObjectID {
1368 let id = ObjectID::derive_id(self.digest(), self.ids_created);
1369
1370 self.ids_created += 1;
1371 id
1372 }
1373
1374 pub fn to_bcs_legacy_context(&self) -> Vec<u8> {
1375 let move_context: MoveLegacyTxContext = if self.is_native {
1376 let tx_context = &TxContext {
1377 sender: AccountAddress::ZERO,
1378 digest: self.digest.clone(),
1379 epoch: 0,
1380 epoch_timestamp_ms: 0,
1381 ids_created: 0,
1382 rgp: 0,
1383 gas_price: 0,
1384 gas_budget: 0,
1385 sponsor: None,
1386 is_native: true,
1387 };
1388 tx_context.into()
1389 } else {
1390 self.into()
1391 };
1392 bcs::to_bytes(&move_context).unwrap()
1393 }
1394
1395 pub fn to_vec(&self) -> Vec<u8> {
1396 bcs::to_bytes(&self).unwrap()
1397 }
1398
1399 pub fn update_state(&mut self, other: MoveLegacyTxContext) -> Result<(), ExecutionError> {
1404 if !self.is_native {
1405 if self.sender != other.sender
1406 || self.digest != other.digest
1407 || other.ids_created < self.ids_created
1408 {
1409 return Err(ExecutionError::new_with_source(
1410 ExecutionErrorKind::InvariantViolation,
1411 "Immutable fields for TxContext changed",
1412 ));
1413 }
1414 self.ids_created = other.ids_created;
1415 }
1416 Ok(())
1417 }
1418
1419 pub fn replace(
1423 &mut self,
1424 sender: AccountAddress,
1425 tx_hash: Vec<u8>,
1426 epoch: u64,
1427 epoch_timestamp_ms: u64,
1428 ids_created: u64,
1429 rgp: u64,
1430 gas_price: u64,
1431 gas_budget: u64,
1432 sponsor: Option<AccountAddress>,
1433 ) {
1434 self.sender = sender;
1435 self.digest = tx_hash;
1436 self.epoch = epoch;
1437 self.epoch_timestamp_ms = epoch_timestamp_ms;
1438 self.ids_created = ids_created;
1439 self.rgp = rgp;
1440 self.gas_price = gas_price;
1441 self.gas_budget = gas_budget;
1442 self.sponsor = sponsor;
1443 }
1444}
1445
1446impl SequenceNumber {
1448 pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1449 pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1450 pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1451 pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1452 pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1453 SequenceNumber(SequenceNumber::MAX.value() + 3);
1454 pub const UNKNOWN: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 4);
1457
1458 pub const fn new() -> Self {
1459 SequenceNumber(0)
1460 }
1461
1462 pub const fn value(&self) -> u64 {
1463 self.0
1464 }
1465
1466 pub const fn from_u64(u: u64) -> Self {
1467 SequenceNumber(u)
1468 }
1469
1470 pub fn increment(&mut self) {
1471 assert_ne!(self.0, u64::MAX);
1472 self.0 += 1;
1473 }
1474
1475 pub fn increment_to(&mut self, next: SequenceNumber) {
1476 debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1477 *self = next;
1478 }
1479
1480 pub fn decrement(&mut self) {
1481 assert_ne!(self.0, 0);
1482 self.0 -= 1;
1483 }
1484
1485 pub fn decrement_to(&mut self, prev: SequenceNumber) {
1486 debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1487 *self = prev;
1488 }
1489
1490 #[must_use]
1493 pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1494 let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1495
1496 assert_ne!(max_input.0, u64::MAX);
1501
1502 SequenceNumber(max_input.0 + 1)
1503 }
1504
1505 pub fn is_cancelled(&self) -> bool {
1506 self == &SequenceNumber::CANCELLED_READ
1507 || self == &SequenceNumber::CONGESTED
1508 || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1509 }
1510
1511 pub fn is_valid(&self) -> bool {
1512 self < &SequenceNumber::MAX
1513 }
1514}
1515
1516impl From<SequenceNumber> for u64 {
1517 fn from(val: SequenceNumber) -> Self {
1518 val.0
1519 }
1520}
1521
1522impl From<u64> for SequenceNumber {
1523 fn from(value: u64) -> Self {
1524 SequenceNumber(value)
1525 }
1526}
1527
1528impl From<SequenceNumber> for usize {
1529 fn from(value: SequenceNumber) -> Self {
1530 value.0 as usize
1531 }
1532}
1533
1534impl ObjectID {
1535 pub const LENGTH: usize = AccountAddress::LENGTH;
1537 pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1539 pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1540 pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1542 Self(AccountAddress::new(obj_id))
1543 }
1544
1545 pub const fn from_address(addr: AccountAddress) -> Self {
1547 Self(addr)
1548 }
1549
1550 pub fn random() -> Self {
1552 Self::from(AccountAddress::random())
1553 }
1554
1555 pub fn random_from_rng<R>(rng: &mut R) -> Self
1557 where
1558 R: AllowedRng,
1559 {
1560 let buf: [u8; Self::LENGTH] = rng.r#gen();
1561 ObjectID::new(buf)
1562 }
1563
1564 pub fn to_vec(&self) -> Vec<u8> {
1566 self.0.to_vec()
1567 }
1568
1569 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1571 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1572 .map_err(|_| ObjectIDParseError::TryFromSliceError)
1573 .map(ObjectID::new)
1574 }
1575
1576 pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1578 self.0.into_bytes()
1579 }
1580
1581 pub const fn from_single_byte(byte: u8) -> ObjectID {
1583 let mut bytes = [0u8; Self::LENGTH];
1584 bytes[Self::LENGTH - 1] = byte;
1585 ObjectID::new(bytes)
1586 }
1587
1588 pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1591 if !literal.starts_with("0x") {
1592 return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1593 }
1594
1595 let hex_len = literal.len() - 2;
1596
1597 if hex_len < Self::LENGTH * 2 {
1599 let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1600 for _ in 0..Self::LENGTH * 2 - hex_len {
1601 hex_str.push('0');
1602 }
1603 hex_str.push_str(&literal[2..]);
1604 Self::from_str(&hex_str)
1605 } else {
1606 Self::from_str(&literal[2..])
1607 }
1608 }
1609
1610 pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1613 let mut hasher = DefaultHash::default();
1614 hasher.update([HashingIntentScope::RegularObjectId as u8]);
1615 hasher.update(digest);
1616 hasher.update(creation_num.to_le_bytes());
1617 let hash = hasher.finalize();
1618
1619 ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1622 }
1623
1624 pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1626 let mut curr_vec = self.to_vec();
1627 let mut step_copy = step;
1628
1629 let mut carry = 0;
1630 for idx in (0..Self::LENGTH).rev() {
1631 if step_copy == 0 {
1632 break;
1634 }
1635 let g = (step_copy % 0x100) as u16;
1637 step_copy >>= 8;
1639 let mut val = curr_vec[idx] as u16;
1640 (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1641 curr_vec[idx] = val as u8;
1642 }
1643
1644 if carry > 0 {
1645 return Err(anyhow!("Increment will cause overflow"));
1646 }
1647 ObjectID::try_from(curr_vec).map_err(|w| w.into())
1648 }
1649
1650 pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1652 let mut prev_val = self.to_vec();
1653 let mx = [0xFF; Self::LENGTH];
1654
1655 if prev_val == mx {
1656 return Err(anyhow!("Increment will cause overflow"));
1657 }
1658
1659 for idx in (0..Self::LENGTH).rev() {
1661 if prev_val[idx] == 0xFF {
1662 prev_val[idx] = 0;
1663 } else {
1664 prev_val[idx] += 1;
1665 break;
1666 };
1667 }
1668 ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1669 }
1670
1671 pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1673 let mut ret = Vec::new();
1674 let mut prev = offset;
1675 for o in 0..count {
1676 if o != 0 {
1677 prev = prev.next_increment()?;
1678 }
1679 ret.push(prev);
1680 }
1681 Ok(ret)
1682 }
1683
1684 pub fn to_hex_uncompressed(&self) -> String {
1687 format!("{self}")
1688 }
1689
1690 pub fn is_clock(&self) -> bool {
1691 *self == SUI_CLOCK_OBJECT_ID
1692 }
1693}
1694
1695impl From<SuiAddress> for ObjectID {
1696 fn from(address: SuiAddress) -> ObjectID {
1697 let tmp: AccountAddress = address.into();
1698 tmp.into()
1699 }
1700}
1701
1702impl From<AccountAddress> for ObjectID {
1703 fn from(address: AccountAddress) -> Self {
1704 Self(address)
1705 }
1706}
1707
1708impl fmt::Display for ObjectID {
1709 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1710 write!(f, "0x{}", Hex::encode(self.0))
1711 }
1712}
1713
1714impl fmt::Debug for ObjectID {
1715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1716 write!(f, "0x{}", Hex::encode(self.0))
1717 }
1718}
1719
1720impl AsRef<[u8]> for ObjectID {
1721 fn as_ref(&self) -> &[u8] {
1722 self.0.as_slice()
1723 }
1724}
1725
1726impl TryFrom<&[u8]> for ObjectID {
1727 type Error = ObjectIDParseError;
1728
1729 fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1731 Self::from_bytes(bytes)
1732 }
1733}
1734
1735impl TryFrom<Vec<u8>> for ObjectID {
1736 type Error = ObjectIDParseError;
1737
1738 fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1740 Self::from_bytes(bytes)
1741 }
1742}
1743
1744impl FromStr for ObjectID {
1745 type Err = ObjectIDParseError;
1746
1747 fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1749 decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1750 }
1751}
1752
1753impl std::ops::Deref for ObjectID {
1754 type Target = AccountAddress;
1755
1756 fn deref(&self) -> &Self::Target {
1757 &self.0
1758 }
1759}
1760
1761pub fn dbg_object_id(name: u8) -> ObjectID {
1763 ObjectID::new([name; ObjectID::LENGTH])
1764}
1765
1766#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1767pub enum ObjectIDParseError {
1768 #[error("ObjectID hex literal must start with 0x")]
1769 HexLiteralPrefixMissing,
1770
1771 #[error("Could not convert from bytes slice")]
1772 TryFromSliceError,
1773}
1774
1775impl From<ObjectID> for AccountAddress {
1776 fn from(obj_id: ObjectID) -> Self {
1777 obj_id.0
1778 }
1779}
1780
1781impl From<SuiAddress> for AccountAddress {
1782 fn from(address: SuiAddress) -> Self {
1783 Self::new(address.0)
1784 }
1785}
1786
1787struct HexAccountAddress;
1789
1790impl SerializeAs<AccountAddress> for HexAccountAddress {
1791 fn serialize_as<S>(value: &AccountAddress, serializer: S) -> Result<S::Ok, S::Error>
1792 where
1793 S: Serializer,
1794 {
1795 Hex::serialize_as(value, serializer)
1796 }
1797}
1798
1799impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress {
1800 fn deserialize_as<D>(deserializer: D) -> Result<AccountAddress, D::Error>
1801 where
1802 D: Deserializer<'de>,
1803 {
1804 let s = String::deserialize(deserializer)?;
1805 if s.starts_with("0x") {
1806 AccountAddress::from_hex_literal(&s)
1807 } else {
1808 AccountAddress::from_hex(&s)
1809 }
1810 .map_err(to_custom_deser_error::<'de, D, _>)
1811 }
1812}
1813
1814impl fmt::Display for MoveObjectType {
1815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1816 let s: StructTag = self.clone().into();
1817 write!(
1818 f,
1819 "{}",
1820 to_sui_struct_tag_string(&s).map_err(fmt::Error::custom)?
1821 )
1822 }
1823}
1824
1825impl fmt::Display for ObjectType {
1826 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1827 match self {
1828 ObjectType::Package => write!(f, "{}", PACKAGE),
1829 ObjectType::Struct(t) => write!(f, "{}", t),
1830 }
1831 }
1832}
1833
1834#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1840#[serde(try_from = "Vec<T>")]
1841pub struct SizeOneVec<T> {
1842 e: T,
1843}
1844
1845impl<T> SizeOneVec<T> {
1846 pub fn new(e: T) -> Self {
1847 Self { e }
1848 }
1849
1850 pub fn element(&self) -> &T {
1851 &self.e
1852 }
1853
1854 pub fn element_mut(&mut self) -> &mut T {
1855 &mut self.e
1856 }
1857
1858 pub fn into_inner(self) -> T {
1859 self.e
1860 }
1861
1862 pub fn iter(&self) -> std::iter::Once<&T> {
1863 std::iter::once(&self.e)
1864 }
1865}
1866
1867impl<T> Serialize for SizeOneVec<T>
1868where
1869 T: Serialize,
1870{
1871 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1872 where
1873 S: Serializer,
1874 {
1875 let mut seq = serializer.serialize_seq(Some(1))?;
1876 seq.serialize_element(&self.e)?;
1877 seq.end()
1878 }
1879}
1880
1881impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1882 type Error = anyhow::Error;
1883
1884 fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1885 if v.len() != 1 {
1886 Err(anyhow!("Expected a vec of size 1"))
1887 } else {
1888 Ok(SizeOneVec {
1889 e: v.pop().unwrap(),
1890 })
1891 }
1892 }
1893}
1894
1895#[test]
1896fn test_size_one_vec_is_transparent() {
1897 let regular = vec![42u8];
1898 let size_one = SizeOneVec::new(42u8);
1899
1900 let regular_ser = bcs::to_bytes(®ular).unwrap();
1902 let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(®ular_ser).unwrap();
1903 assert_eq!(size_one, size_one_deser);
1904
1905 let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1907 let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1908 assert_eq!(regular_deser, vec![43u8]);
1909
1910 let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1912 bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1913
1914 let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1915 bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1916}