1use std::{
7 cmp::max,
8 convert::{TryFrom, TryInto},
9 fmt,
10 str::FromStr,
11};
12
13use anyhow::{anyhow, bail};
14use fastcrypto::{
15 encoding::{Encoding, Hex, decode_bytes_hex},
16 hash::HashFunction,
17 traits::AllowedRng,
18};
19use fastcrypto_zkp::bn254::zk_login::ZkLoginInputs;
20use iota_sdk_types::crypto::HashingIntentScope;
21use move_binary_format::{CompiledModule, file_format::SignatureToken};
22use move_bytecode_utils::resolve_struct;
23use move_core_types::{
24 account_address::AccountAddress,
25 annotated_value as A, ident_str,
26 identifier::IdentStr,
27 language_storage::{ModuleId, StructTag, TypeTag},
28};
29use rand::Rng;
30use schemars::JsonSchema;
31use serde::{
32 Deserialize, Serialize, Serializer,
33 ser::{Error, SerializeSeq},
34};
35use serde_with::serde_as;
36
37use crate::{
38 IOTA_CLOCK_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, MOVE_STDLIB_ADDRESS,
39 account_abstraction::authenticator_function::AuthenticatorFunctionRefV1,
40 balance::Balance,
41 coin::{COIN_MODULE_NAME, COIN_STRUCT_NAME, Coin, CoinMetadata, TreasuryCap},
42 coin_manager::CoinManager,
43 crypto::{
44 AuthorityPublicKeyBytes, DefaultHash, IotaPublicKey, IotaSignature, PublicKey,
45 SignatureScheme,
46 },
47 dynamic_field::{DynamicFieldInfo, DynamicFieldType},
48 effects::{TransactionEffects, TransactionEffectsAPI},
49 epoch_data::EpochData,
50 error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
51 gas_coin::{GAS, GasCoin},
52 governance::{STAKED_IOTA_STRUCT_NAME, STAKING_POOL_MODULE_NAME, StakedIota},
53 id::RESOLVED_IOTA_ID,
54 iota_serde::{HexAccountAddress, Readable, to_iota_struct_tag_string},
55 messages_checkpoint::CheckpointTimestamp,
56 multisig::MultiSigPublicKey,
57 object::{Object, Owner},
58 parse_iota_struct_tag,
59 signature::GenericSignature,
60 stardust::output::{AliasOutput, BasicOutput, Nft, NftOutput},
61 timelock::{
62 timelock::{self, TimeLock},
63 timelocked_staked_iota::TimelockedStakedIota,
64 },
65 transaction::{Transaction, VerifiedTransaction},
66 zk_login_authenticator::ZkLoginAuthenticator,
67};
68pub use crate::{
69 committee::EpochId,
70 digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest},
71};
72
73#[cfg(test)]
74#[path = "unit_tests/base_types_tests.rs"]
75mod base_types_tests;
76
77#[derive(
78 Eq,
79 PartialEq,
80 Ord,
81 PartialOrd,
82 Copy,
83 Clone,
84 Hash,
85 Default,
86 Debug,
87 Serialize,
88 Deserialize,
89 JsonSchema,
90)]
91#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
92pub struct SequenceNumber(u64);
93
94impl SequenceNumber {
95 pub fn one_before(&self) -> Option<SequenceNumber> {
96 if self.0 == 0 {
97 None
98 } else {
99 Some(SequenceNumber(self.0 - 1))
100 }
101 }
102
103 pub fn next(&self) -> SequenceNumber {
104 SequenceNumber(self.0 + 1)
105 }
106}
107
108pub type TxSequenceNumber = u64;
109
110impl fmt::Display for SequenceNumber {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(f, "{:#x}", self.0)
113 }
114}
115
116pub type VersionNumber = SequenceNumber;
117
118pub type CommitRound = u64;
120
121pub type AuthorityName = AuthorityPublicKeyBytes;
122
123pub trait ConciseableName<'a> {
124 type ConciseTypeRef: std::fmt::Debug;
125 type ConciseType: std::fmt::Debug;
126
127 fn concise(&'a self) -> Self::ConciseTypeRef;
128 fn concise_owned(&self) -> Self::ConciseType;
129}
130
131#[serde_as]
132#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
133pub struct ObjectID(
134 #[schemars(with = "Hex")]
135 #[serde_as(as = "Readable<HexAccountAddress, _>")]
136 AccountAddress,
137);
138
139pub type VersionDigest = (SequenceNumber, ObjectDigest);
140
141pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest);
142
143pub fn random_object_ref() -> ObjectRef {
144 (
145 ObjectID::random(),
146 SequenceNumber::new(),
147 ObjectDigest::new([0; 32]),
148 )
149}
150
151#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
157pub struct MoveObjectType(MoveObjectType_);
158
159#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
162pub enum MoveObjectType_ {
163 Other(StructTag),
165 GasCoin,
167 StakedIota,
169 Coin(TypeTag),
172 }
176
177impl MoveObjectType {
178 pub fn gas_coin() -> Self {
179 Self(MoveObjectType_::GasCoin)
180 }
181
182 pub fn coin(coin_type: TypeTag) -> Self {
183 Self(if GAS::is_gas_type(&coin_type) {
184 MoveObjectType_::GasCoin
185 } else {
186 MoveObjectType_::Coin(coin_type)
187 })
188 }
189
190 pub fn staked_iota() -> Self {
191 Self(MoveObjectType_::StakedIota)
192 }
193
194 pub fn timelocked_iota_balance() -> Self {
195 Self(MoveObjectType_::Other(TimeLock::<Balance>::type_(
196 Balance::type_(GAS::type_().into()).into(),
197 )))
198 }
199
200 pub fn timelocked_staked_iota() -> Self {
201 Self(MoveObjectType_::Other(TimelockedStakedIota::type_()))
202 }
203
204 pub fn stardust_nft() -> Self {
205 Self(MoveObjectType_::Other(Nft::tag()))
206 }
207
208 pub fn address(&self) -> AccountAddress {
209 match &self.0 {
210 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => IOTA_FRAMEWORK_ADDRESS,
211 MoveObjectType_::StakedIota => IOTA_SYSTEM_ADDRESS,
212 MoveObjectType_::Other(s) => s.address,
213 }
214 }
215
216 pub fn module(&self) -> &IdentStr {
217 match &self.0 {
218 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME,
219 MoveObjectType_::StakedIota => STAKING_POOL_MODULE_NAME,
220 MoveObjectType_::Other(s) => &s.module,
221 }
222 }
223
224 pub fn name(&self) -> &IdentStr {
225 match &self.0 {
226 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME,
227 MoveObjectType_::StakedIota => STAKED_IOTA_STRUCT_NAME,
228 MoveObjectType_::Other(s) => &s.name,
229 }
230 }
231
232 pub fn type_params(&self) -> Vec<TypeTag> {
233 match &self.0 {
234 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
235 MoveObjectType_::StakedIota => vec![],
236 MoveObjectType_::Coin(inner) => vec![inner.clone()],
237 MoveObjectType_::Other(s) => s.type_params.clone(),
238 }
239 }
240
241 pub fn into_type_params(self) -> Vec<TypeTag> {
242 match self.0 {
243 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
244 MoveObjectType_::StakedIota => vec![],
245 MoveObjectType_::Coin(inner) => vec![inner],
246 MoveObjectType_::Other(s) => s.type_params,
247 }
248 }
249
250 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
251 match &self.0 {
252 MoveObjectType_::GasCoin => Some(GAS::type_tag()),
253 MoveObjectType_::Coin(inner) => Some(inner.clone()),
254 MoveObjectType_::StakedIota => None,
255 MoveObjectType_::Other(_) => None,
256 }
257 }
258
259 pub fn module_id(&self) -> ModuleId {
260 ModuleId::new(self.address(), self.module().to_owned())
261 }
262
263 pub fn size_for_gas_metering(&self) -> usize {
264 match &self.0 {
266 MoveObjectType_::GasCoin => 1,
267 MoveObjectType_::StakedIota => 1,
268 MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
269 MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
270 }
271 }
272
273 pub fn is_coin(&self) -> bool {
276 match &self.0 {
277 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
278 MoveObjectType_::StakedIota | MoveObjectType_::Other(_) => false,
279 }
280 }
281
282 pub fn is_gas_coin(&self) -> bool {
284 match &self.0 {
285 MoveObjectType_::GasCoin => true,
286 MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => {
287 false
288 }
289 }
290 }
291
292 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
294 match &self.0 {
295 MoveObjectType_::GasCoin => GAS::is_gas_type(t),
296 MoveObjectType_::Coin(c) => t == c,
297 MoveObjectType_::StakedIota | MoveObjectType_::Other(_) => false,
298 }
299 }
300
301 pub fn is_staked_iota(&self) -> bool {
302 match &self.0 {
303 MoveObjectType_::StakedIota => true,
304 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => {
305 false
306 }
307 }
308 }
309
310 pub fn is_coin_metadata(&self) -> bool {
311 match &self.0 {
312 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
313 false
314 }
315 MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s),
316 }
317 }
318
319 pub fn is_coin_manager(&self) -> bool {
320 matches!(&self.0, MoveObjectType_::Other(struct_tag) if CoinManager::is_coin_manager(struct_tag))
321 }
322
323 pub fn is_treasury_cap(&self) -> bool {
324 match &self.0 {
325 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
326 false
327 }
328 MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
329 }
330 }
331
332 pub fn is_regulated_coin_metadata(&self) -> bool {
333 self.address() == IOTA_FRAMEWORK_ADDRESS
334 && self.module().as_str() == "coin"
335 && self.name().as_str() == "RegulatedCoinMetadata"
336 }
337
338 pub fn is_coin_deny_cap_v1(&self) -> bool {
339 self.address() == IOTA_FRAMEWORK_ADDRESS
340 && self.module().as_str() == "coin"
341 && self.name().as_str() == "DenyCapV1"
342 }
343
344 pub fn is_dynamic_field(&self) -> bool {
345 match &self.0 {
346 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
347 false
348 }
349 MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
350 }
351 }
352
353 pub fn is_timelock(&self) -> bool {
354 match &self.0 {
355 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
356 false
357 }
358 MoveObjectType_::Other(s) => timelock::is_timelock(s),
359 }
360 }
361
362 pub fn is_timelocked_balance(&self) -> bool {
363 match &self.0 {
364 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
365 false
366 }
367 MoveObjectType_::Other(s) => timelock::is_timelocked_balance(s),
368 }
369 }
370
371 pub fn is_timelocked_staked_iota(&self) -> bool {
372 match &self.0 {
373 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
374 false
375 }
376 MoveObjectType_::Other(s) => TimelockedStakedIota::is_timelocked_staked_iota(s),
377 }
378 }
379
380 pub fn is_alias_output(&self) -> bool {
381 match &self.0 {
382 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
383 false
384 }
385 MoveObjectType_::Other(s) => AliasOutput::is_alias_output(s),
386 }
387 }
388
389 pub fn is_basic_output(&self) -> bool {
390 match &self.0 {
391 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
392 false
393 }
394 MoveObjectType_::Other(s) => BasicOutput::is_basic_output(s),
395 }
396 }
397
398 pub fn is_nft_output(&self) -> bool {
399 match &self.0 {
400 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
401 false
402 }
403 MoveObjectType_::Other(s) => NftOutput::is_nft_output(s),
404 }
405 }
406
407 pub fn is_authenticator_function_ref_v1(&self) -> bool {
408 match &self.0 {
409 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
410 false
411 }
412 MoveObjectType_::Other(s) => {
413 AuthenticatorFunctionRefV1::is_authenticator_function_ref_v1(s)
414 }
415 }
416 }
417
418 pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> IotaResult<TypeTag> {
419 match &self.0 {
420 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
421 Err(IotaError::ObjectDeserialization {
422 error: "Error extracting dynamic object name from Coin object".to_string(),
423 })
424 }
425 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
426 }
427 }
428
429 pub fn try_extract_field_value(&self) -> IotaResult<TypeTag> {
430 match &self.0 {
431 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
432 Err(IotaError::ObjectDeserialization {
433 error: "Error extracting dynamic object value from Coin object".to_string(),
434 })
435 }
436 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
437 }
438 }
439
440 pub fn is(&self, s: &StructTag) -> bool {
441 match &self.0 {
442 MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
443 MoveObjectType_::StakedIota => StakedIota::is_staked_iota(s),
444 MoveObjectType_::Coin(inner) => {
445 Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
446 }
447 MoveObjectType_::Other(o) => s == o,
448 }
449 }
450
451 pub fn other(&self) -> Option<&StructTag> {
452 if let MoveObjectType_::Other(s) = &self.0 {
453 Some(s)
454 } else {
455 None
456 }
457 }
458
459 pub fn to_canonical_string(&self, with_prefix: bool) -> String {
462 StructTag::from(self.clone()).to_canonical_string(with_prefix)
463 }
464}
465
466impl From<StructTag> for MoveObjectType {
467 fn from(mut s: StructTag) -> Self {
468 Self(if GasCoin::is_gas_coin(&s) {
469 MoveObjectType_::GasCoin
470 } else if Coin::is_coin(&s) {
471 MoveObjectType_::Coin(s.type_params.pop().unwrap())
473 } else if StakedIota::is_staked_iota(&s) {
474 MoveObjectType_::StakedIota
475 } else {
476 MoveObjectType_::Other(s)
477 })
478 }
479}
480
481impl From<MoveObjectType> for StructTag {
482 fn from(t: MoveObjectType) -> Self {
483 match t.0 {
484 MoveObjectType_::GasCoin => GasCoin::type_(),
485 MoveObjectType_::StakedIota => StakedIota::type_(),
486 MoveObjectType_::Coin(inner) => Coin::type_(inner),
487 MoveObjectType_::Other(s) => s,
488 }
489 }
490}
491
492impl From<MoveObjectType> for TypeTag {
493 fn from(o: MoveObjectType) -> TypeTag {
494 let s: StructTag = o.into();
495 TypeTag::Struct(Box::new(s))
496 }
497}
498
499pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
501 use TypeTag as T;
502
503 match t {
504 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
505 T::Vector(inner) => is_primitive_type_tag(inner),
506 T::Struct(st) => {
507 let StructTag {
508 address,
509 module,
510 name,
511 type_params: type_args,
512 } = &**st;
513 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
514 if resolved_struct == RESOLVED_IOTA_ID {
516 return true;
517 }
518 if resolved_struct == RESOLVED_UTF8_STR {
520 return true;
521 }
522 if resolved_struct == RESOLVED_ASCII_STR {
524 return true;
525 }
526 resolved_struct == RESOLVED_STD_OPTION
528 && type_args.len() == 1
529 && is_primitive_type_tag(&type_args[0])
530 }
531 T::Signer => false,
532 }
533}
534
535#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
537pub enum ObjectType {
538 Package,
540 Struct(MoveObjectType),
542}
543
544impl From<&Object> for ObjectType {
545 fn from(o: &Object) -> Self {
546 o.data
547 .type_()
548 .map(|t| ObjectType::Struct(t.clone()))
549 .unwrap_or(ObjectType::Package)
550 }
551}
552
553impl TryFrom<ObjectType> for StructTag {
554 type Error = anyhow::Error;
555
556 fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
557 match o {
558 ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
559 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
560 }
561 }
562}
563
564impl FromStr for ObjectType {
565 type Err = anyhow::Error;
566
567 fn from_str(s: &str) -> Result<Self, Self::Err> {
568 if s.to_lowercase() == PACKAGE {
569 Ok(ObjectType::Package)
570 } else {
571 let tag = parse_iota_struct_tag(s)?;
572 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
573 }
574 }
575}
576
577#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
578pub struct ObjectInfo {
579 pub object_id: ObjectID,
580 pub version: SequenceNumber,
581 pub digest: ObjectDigest,
582 pub type_: ObjectType,
583 pub owner: Owner,
584 pub previous_transaction: TransactionDigest,
585}
586
587impl ObjectInfo {
588 pub fn new(oref: &ObjectRef, o: &Object) -> Self {
589 let (object_id, version, digest) = *oref;
590 Self {
591 object_id,
592 version,
593 digest,
594 type_: o.into(),
595 owner: o.owner,
596 previous_transaction: o.previous_transaction,
597 }
598 }
599
600 pub fn from_object(object: &Object) -> Self {
601 Self {
602 object_id: object.id(),
603 version: object.version(),
604 digest: object.digest(),
605 type_: object.into(),
606 owner: object.owner,
607 previous_transaction: object.previous_transaction,
608 }
609 }
610}
611const PACKAGE: &str = "package";
612impl ObjectType {
613 pub fn is_gas_coin(&self) -> bool {
614 matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
615 }
616
617 pub fn is_coin(&self) -> bool {
618 matches!(self, ObjectType::Struct(s) if s.is_coin())
619 }
620
621 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
623 matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
624 }
625
626 pub fn is_package(&self) -> bool {
627 matches!(self, ObjectType::Package)
628 }
629}
630
631impl From<ObjectInfo> for ObjectRef {
632 fn from(info: ObjectInfo) -> Self {
633 (info.object_id, info.version, info.digest)
634 }
635}
636
637impl From<&ObjectInfo> for ObjectRef {
638 fn from(info: &ObjectInfo) -> Self {
639 (info.object_id, info.version, info.digest)
640 }
641}
642
643pub const IOTA_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
644
645#[serde_as]
646#[derive(
647 Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
648)]
649#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
650pub struct IotaAddress(
651 #[schemars(with = "Hex")]
652 #[serde_as(as = "Readable<Hex, _>")]
653 [u8; IOTA_ADDRESS_LENGTH],
654);
655
656impl IotaAddress {
657 pub const ZERO: Self = Self([0u8; IOTA_ADDRESS_LENGTH]);
658
659 pub fn to_vec(&self) -> Vec<u8> {
661 self.0.to_vec()
662 }
663
664 pub fn random_for_testing_only() -> Self {
666 AccountAddress::random().into()
667 }
668
669 pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
670 let buf: [u8; IOTA_ADDRESS_LENGTH] = rng.gen();
671 Self(buf)
672 }
673
674 pub fn to_inner(self) -> [u8; IOTA_ADDRESS_LENGTH] {
676 self.0
677 }
678
679 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, IotaError> {
681 <[u8; IOTA_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
682 .map_err(|_| IotaError::InvalidAddress)
683 .map(IotaAddress)
684 }
685
686 pub fn try_from_padded(inputs: &ZkLoginInputs) -> IotaResult<Self> {
691 Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
692 }
693
694 pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> IotaResult<Self> {
696 let mut hasher = DefaultHash::default();
697 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
698 let iss_bytes = inputs.get_iss().as_bytes();
699 hasher.update([iss_bytes.len() as u8]);
700 hasher.update(iss_bytes);
701 hasher.update(inputs.get_address_seed().unpadded());
702 Ok(IotaAddress(hasher.finalize().digest))
703 }
704}
705
706impl From<ObjectID> for IotaAddress {
707 fn from(object_id: ObjectID) -> IotaAddress {
708 Self(object_id.into_bytes())
709 }
710}
711
712impl From<AccountAddress> for IotaAddress {
713 fn from(address: AccountAddress) -> IotaAddress {
714 Self(address.into_bytes())
715 }
716}
717
718impl TryFrom<&[u8]> for IotaAddress {
719 type Error = IotaError;
720
721 fn try_from(bytes: &[u8]) -> Result<Self, IotaError> {
723 Self::from_bytes(bytes)
724 }
725}
726
727impl TryFrom<Vec<u8>> for IotaAddress {
728 type Error = IotaError;
729
730 fn try_from(bytes: Vec<u8>) -> Result<Self, IotaError> {
732 Self::from_bytes(bytes)
733 }
734}
735
736impl AsRef<[u8]> for IotaAddress {
737 fn as_ref(&self) -> &[u8] {
738 &self.0[..]
739 }
740}
741
742impl FromStr for IotaAddress {
743 type Err = anyhow::Error;
744 fn from_str(s: &str) -> Result<Self, Self::Err> {
745 decode_bytes_hex(s).map_err(|e| anyhow!(e))
746 }
747}
748
749impl<T: IotaPublicKey> From<&T> for IotaAddress {
750 fn from(pk: &T) -> Self {
751 let mut hasher = DefaultHash::default();
752 T::SIGNATURE_SCHEME.update_hasher_with_flag(&mut hasher);
753 hasher.update(pk);
754 let g_arr = hasher.finalize();
755 IotaAddress(g_arr.digest)
756 }
757}
758
759impl From<&PublicKey> for IotaAddress {
760 fn from(pk: &PublicKey) -> Self {
761 let mut hasher = DefaultHash::default();
762 pk.scheme().update_hasher_with_flag(&mut hasher);
763 hasher.update(pk);
764 let g_arr = hasher.finalize();
765 IotaAddress(g_arr.digest)
766 }
767}
768
769impl From<&MultiSigPublicKey> for IotaAddress {
770 fn from(multisig_pk: &MultiSigPublicKey) -> Self {
779 let mut hasher = DefaultHash::default();
780 hasher.update([SignatureScheme::MultiSig.flag()]);
781 hasher.update(multisig_pk.threshold().to_le_bytes());
782 multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
783 pk.scheme().update_hasher_with_flag(&mut hasher);
784 hasher.update(pk.as_ref());
785 hasher.update(w.to_le_bytes());
786 });
787 IotaAddress(hasher.finalize().digest)
788 }
789}
790
791impl TryFrom<&ZkLoginAuthenticator> for IotaAddress {
795 type Error = IotaError;
796 fn try_from(authenticator: &ZkLoginAuthenticator) -> IotaResult<Self> {
797 IotaAddress::try_from_unpadded(&authenticator.inputs)
798 }
799}
800
801impl TryFrom<&GenericSignature> for IotaAddress {
802 type Error = IotaError;
803 fn try_from(sig: &GenericSignature) -> IotaResult<Self> {
806 match sig {
807 GenericSignature::Signature(sig) => {
808 let scheme = sig.scheme();
809 let pub_key_bytes = sig.public_key_bytes();
810 let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
811 IotaError::InvalidSignature {
812 error: "Cannot parse pubkey".to_string(),
813 }
814 })?;
815 Ok(IotaAddress::from(&pub_key))
816 }
817 GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
818 GenericSignature::ZkLoginAuthenticator(zklogin) => {
819 IotaAddress::try_from_unpadded(&zklogin.inputs)
820 }
821 GenericSignature::PasskeyAuthenticator(s) => Ok(IotaAddress::from(&s.get_pk()?)),
822 GenericSignature::MoveAuthenticator(move_authenticator) => move_authenticator.address(),
823 }
824 }
825}
826
827impl fmt::Display for IotaAddress {
828 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829 write!(f, "0x{}", Hex::encode(self.0))
830 }
831}
832
833impl fmt::Debug for IotaAddress {
834 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
835 write!(f, "0x{}", Hex::encode(self.0))
836 }
837}
838
839pub fn dbg_addr(name: u8) -> IotaAddress {
841 let addr = [name; IOTA_ADDRESS_LENGTH];
842 IotaAddress(addr)
843}
844
845#[derive(
846 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
847)]
848pub struct ExecutionDigests {
849 pub transaction: TransactionDigest,
850 pub effects: TransactionEffectsDigest,
851}
852
853impl ExecutionDigests {
854 pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
855 Self {
856 transaction,
857 effects,
858 }
859 }
860
861 pub fn random() -> Self {
862 Self {
863 transaction: TransactionDigest::random(),
864 effects: TransactionEffectsDigest::random(),
865 }
866 }
867}
868
869#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
870pub struct ExecutionData {
871 pub transaction: Transaction,
872 pub effects: TransactionEffects,
873}
874
875impl ExecutionData {
876 pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
877 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
878 Self {
879 transaction,
880 effects,
881 }
882 }
883
884 pub fn digests(&self) -> ExecutionDigests {
885 self.effects.execution_digests()
886 }
887}
888
889#[derive(Clone, Eq, PartialEq, Debug)]
890pub struct VerifiedExecutionData {
891 pub transaction: VerifiedTransaction,
892 pub effects: TransactionEffects,
893}
894
895impl VerifiedExecutionData {
896 pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
897 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
898 Self {
899 transaction,
900 effects,
901 }
902 }
903
904 pub fn new_unchecked(data: ExecutionData) -> Self {
905 Self {
906 transaction: VerifiedTransaction::new_unchecked(data.transaction),
907 effects: data.effects,
908 }
909 }
910
911 pub fn into_inner(self) -> ExecutionData {
912 ExecutionData {
913 transaction: self.transaction.into_inner(),
914 effects: self.effects,
915 }
916 }
917
918 pub fn digests(&self) -> ExecutionDigests {
919 self.effects.execution_digests()
920 }
921}
922
923pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
924pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
925pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
926 &MOVE_STDLIB_ADDRESS,
927 STD_OPTION_MODULE_NAME,
928 STD_OPTION_STRUCT_NAME,
929);
930
931pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
932pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
933pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
934 &MOVE_STDLIB_ADDRESS,
935 STD_ASCII_MODULE_NAME,
936 STD_ASCII_STRUCT_NAME,
937);
938
939pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
940pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
941pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
942 &MOVE_STDLIB_ADDRESS,
943 STD_UTF8_MODULE_NAME,
944 STD_UTF8_STRUCT_NAME,
945);
946
947pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
948pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
949
950pub const URL_MODULE_NAME: &IdentStr = ident_str!("url");
951pub const URL_STRUCT_NAME: &IdentStr = ident_str!("Url");
952
953pub fn move_ascii_str_layout() -> A::MoveStructLayout {
954 A::MoveStructLayout {
955 type_: StructTag {
956 address: MOVE_STDLIB_ADDRESS,
957 module: STD_ASCII_MODULE_NAME.to_owned(),
958 name: STD_ASCII_STRUCT_NAME.to_owned(),
959 type_params: vec![],
960 },
961 fields: vec![A::MoveFieldLayout::new(
962 ident_str!("bytes").into(),
963 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
964 )],
965 }
966}
967
968pub fn move_utf8_str_layout() -> A::MoveStructLayout {
969 A::MoveStructLayout {
970 type_: StructTag {
971 address: MOVE_STDLIB_ADDRESS,
972 module: STD_UTF8_MODULE_NAME.to_owned(),
973 name: STD_UTF8_STRUCT_NAME.to_owned(),
974 type_params: vec![],
975 },
976 fields: vec![A::MoveFieldLayout::new(
977 ident_str!("bytes").into(),
978 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
979 )],
980 }
981}
982
983pub fn url_layout() -> A::MoveStructLayout {
984 A::MoveStructLayout {
985 type_: StructTag {
986 address: IOTA_FRAMEWORK_ADDRESS,
987 module: URL_MODULE_NAME.to_owned(),
988 name: URL_STRUCT_NAME.to_owned(),
989 type_params: vec![],
990 },
991 fields: vec![A::MoveFieldLayout::new(
992 ident_str!("url").to_owned(),
993 A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
994 )],
995 }
996}
997
998#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
999pub struct TxContext {
1000 sender: AccountAddress,
1002 digest: Vec<u8>,
1004 epoch: EpochId,
1006 epoch_timestamp_ms: CheckpointTimestamp,
1008 ids_created: u64,
1011}
1012
1013#[derive(PartialEq, Eq, Clone, Copy)]
1014pub enum TxContextKind {
1015 None,
1017 Mutable,
1019 Immutable,
1021}
1022
1023impl TxContext {
1024 pub fn new(sender: &IotaAddress, digest: &TransactionDigest, epoch_data: &EpochData) -> Self {
1025 Self::new_from_components(
1026 sender,
1027 digest,
1028 &epoch_data.epoch_id(),
1029 epoch_data.epoch_start_timestamp(),
1030 )
1031 }
1032
1033 pub fn new_from_components(
1034 sender: &IotaAddress,
1035 digest: &TransactionDigest,
1036 epoch_id: &EpochId,
1037 epoch_timestamp_ms: u64,
1038 ) -> Self {
1039 Self {
1040 sender: AccountAddress::new(sender.0),
1041 digest: digest.into_inner().to_vec(),
1042 epoch: *epoch_id,
1043 epoch_timestamp_ms,
1044 ids_created: 0,
1045 }
1046 }
1047
1048 pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1051 use SignatureToken as S;
1052 let (kind, s) = match s {
1053 S::MutableReference(s) => (TxContextKind::Mutable, s),
1054 S::Reference(s) => (TxContextKind::Immutable, s),
1055 _ => return TxContextKind::None,
1056 };
1057
1058 let S::Datatype(idx) = &**s else {
1059 return TxContextKind::None;
1060 };
1061
1062 let (module_addr, module_name, struct_name) = resolve_struct(view, *idx);
1063 let is_tx_context_type = module_name == TX_CONTEXT_MODULE_NAME
1064 && module_addr == &IOTA_FRAMEWORK_ADDRESS
1065 && struct_name == TX_CONTEXT_STRUCT_NAME;
1066
1067 if is_tx_context_type {
1068 kind
1069 } else {
1070 TxContextKind::None
1071 }
1072 }
1073
1074 pub fn epoch(&self) -> EpochId {
1075 self.epoch
1076 }
1077
1078 pub fn fresh_id(&mut self) -> ObjectID {
1081 let id = ObjectID::derive_id(self.digest(), self.ids_created);
1082
1083 self.ids_created += 1;
1084 id
1085 }
1086
1087 pub fn digest(&self) -> TransactionDigest {
1089 TransactionDigest::new(self.digest.clone().try_into().unwrap())
1090 }
1091
1092 pub fn sender(&self) -> IotaAddress {
1093 IotaAddress::from(ObjectID(self.sender))
1094 }
1095
1096 pub fn to_vec(&self) -> Vec<u8> {
1097 bcs::to_bytes(&self).unwrap()
1098 }
1099
1100 pub fn update_state(&mut self, other: TxContext) -> Result<(), ExecutionError> {
1105 if self.sender != other.sender
1106 || self.digest != other.digest
1107 || other.ids_created < self.ids_created
1108 {
1109 return Err(ExecutionError::new_with_source(
1110 ExecutionErrorKind::InvariantViolation,
1111 "Immutable fields for TxContext changed",
1112 ));
1113 }
1114 self.ids_created = other.ids_created;
1115 Ok(())
1116 }
1117
1118 pub fn random_for_testing_only() -> Self {
1120 Self::new(
1121 &IotaAddress::random_for_testing_only(),
1122 &TransactionDigest::random(),
1123 &EpochData::new_test(),
1124 )
1125 }
1126
1127 pub fn with_sender_for_testing_only(sender: &IotaAddress) -> Self {
1129 Self::new(sender, &TransactionDigest::random(), &EpochData::new_test())
1130 }
1131}
1132
1133impl SequenceNumber {
1135 pub const MIN_VALID_INCL: SequenceNumber = SequenceNumber(u64::MIN);
1140
1141 pub const MAX_VALID_EXCL: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1149
1150 pub const CANCELLED_READ: SequenceNumber =
1153 SequenceNumber(SequenceNumber::MAX_VALID_EXCL.value() + 1);
1154
1155 pub const CONGESTED_PRIOR_TO_GAS_PRICE_FEEDBACK: SequenceNumber =
1160 SequenceNumber(SequenceNumber::MAX_VALID_EXCL.value() + 2);
1161
1162 pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1165 SequenceNumber(SequenceNumber::MAX_VALID_EXCL.value() + 3);
1166
1167 const CONGESTED_BASE_OFFSET_FOR_GAS_PRICE_FEEDBACK: u64 = 1_000;
1189
1190 const MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK: SequenceNumber = SequenceNumber(
1194 SequenceNumber::MAX_VALID_EXCL.value() + Self::CONGESTED_BASE_OFFSET_FOR_GAS_PRICE_FEEDBACK,
1195 );
1196
1197 pub const fn new() -> Self {
1198 SequenceNumber(0)
1199 }
1200
1201 pub const fn value(&self) -> u64 {
1202 self.0
1203 }
1204
1205 pub const fn from_u64(u: u64) -> Self {
1206 SequenceNumber(u)
1207 }
1208
1209 pub fn new_congested_with_suggested_gas_price(suggested_gas_price: u64) -> Self {
1215 let (version, overflows) = Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK
1216 .value()
1217 .overflowing_add(suggested_gas_price);
1218 debug_assert!(
1219 !overflows,
1220 "the calculated version for a congested shared objects overflows"
1221 );
1222
1223 Self(version)
1224 }
1225
1226 pub fn is_congested(&self) -> bool {
1229 *self == Self::CONGESTED_PRIOR_TO_GAS_PRICE_FEEDBACK
1230 || self >= &Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK
1231 }
1232
1233 pub fn get_congested_version_suggested_gas_price(&self) -> u64 {
1238 assert!(
1239 *self >= Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK,
1240 "this is not a version used for congested shared objects in the gas price feedback \
1241 mechanism"
1242 );
1243
1244 self.value() - Self::MIN_CONGESTED_FOR_GAS_PRICE_FEEDBACK.value()
1245 }
1246
1247 pub fn increment(&mut self) {
1248 assert!(
1249 self.is_valid(),
1250 "cannot increment a sequence number: \
1251 maximum valid sequence number has already been reached"
1252 );
1253 self.0 += 1;
1254 }
1255
1256 pub fn increment_to(&mut self, next: SequenceNumber) {
1257 debug_assert!(*self < next, "Not an increment: {self} to {next}");
1258 *self = next;
1259 }
1260
1261 pub fn decrement(&mut self) {
1262 assert_ne!(
1263 *self,
1264 Self::MIN_VALID_INCL,
1265 "cannot decrement a sequence number: \
1266 minimum valid sequence number has already been reached"
1267 );
1268 self.0 -= 1;
1269 }
1270
1271 pub fn decrement_to(&mut self, prev: SequenceNumber) {
1272 debug_assert!(prev < *self, "Not a decrement: {self} to {prev}");
1273 *self = prev;
1274 }
1275
1276 #[must_use]
1279 pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1280 let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1281
1282 assert!(
1287 max_input.is_valid(),
1288 "cannot increment a sequence number: \
1289 maximum valid sequence number has already been reached"
1290 );
1291
1292 SequenceNumber(max_input.0 + 1)
1293 }
1294
1295 pub fn is_cancelled(&self) -> bool {
1298 self == &SequenceNumber::CANCELLED_READ
1299 || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1300 || self.is_congested()
1301 }
1302
1303 pub fn is_valid(&self) -> bool {
1306 self < &SequenceNumber::MAX_VALID_EXCL
1307 }
1308}
1309
1310impl From<SequenceNumber> for u64 {
1311 fn from(val: SequenceNumber) -> Self {
1312 val.0
1313 }
1314}
1315
1316impl From<u64> for SequenceNumber {
1317 fn from(value: u64) -> Self {
1318 SequenceNumber(value)
1319 }
1320}
1321
1322impl From<SequenceNumber> for usize {
1323 fn from(value: SequenceNumber) -> Self {
1324 value.0 as usize
1325 }
1326}
1327
1328impl ObjectID {
1329 pub const LENGTH: usize = AccountAddress::LENGTH;
1331 pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1333 pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1334 pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1336 Self(AccountAddress::new(obj_id))
1337 }
1338
1339 pub const fn from_address(addr: AccountAddress) -> Self {
1341 Self(addr)
1342 }
1343
1344 pub fn random() -> Self {
1346 Self::from(AccountAddress::random())
1347 }
1348
1349 pub fn random_from_rng<R>(rng: &mut R) -> Self
1351 where
1352 R: AllowedRng,
1353 {
1354 let buf: [u8; Self::LENGTH] = rng.gen();
1355 ObjectID::new(buf)
1356 }
1357
1358 pub fn to_vec(&self) -> Vec<u8> {
1360 self.0.to_vec()
1361 }
1362
1363 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1365 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1366 .map_err(|_| ObjectIDParseError::TryFromSlice)
1367 .map(ObjectID::new)
1368 }
1369
1370 pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1372 self.0.into_bytes()
1373 }
1374
1375 pub const fn from_single_byte(byte: u8) -> ObjectID {
1377 let mut bytes = [0u8; Self::LENGTH];
1378 bytes[Self::LENGTH - 1] = byte;
1379 ObjectID::new(bytes)
1380 }
1381
1382 pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1385 if !literal.starts_with("0x") {
1386 return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1387 }
1388
1389 let hex_len = literal.len() - 2;
1390
1391 if hex_len < Self::LENGTH * 2 {
1393 let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1394 for _ in 0..Self::LENGTH * 2 - hex_len {
1395 hex_str.push('0');
1396 }
1397 hex_str.push_str(&literal[2..]);
1398 Self::from_str(&hex_str)
1399 } else {
1400 Self::from_str(&literal[2..])
1401 }
1402 }
1403
1404 pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1407 let mut hasher = DefaultHash::default();
1408 hasher.update([HashingIntentScope::RegularObjectId as u8]);
1409 hasher.update(digest);
1410 hasher.update(creation_num.to_le_bytes());
1411 let hash = hasher.finalize();
1412
1413 ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1417 }
1418
1419 pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1422 let mut prev_val = self.to_vec();
1423 let mx = [0xFF; Self::LENGTH];
1424
1425 if prev_val == mx {
1426 bail!("Increment will cause overflow");
1427 }
1428
1429 for idx in (0..Self::LENGTH).rev() {
1431 if prev_val[idx] == 0xFF {
1432 prev_val[idx] = 0;
1433 } else {
1434 prev_val[idx] += 1;
1435 break;
1436 };
1437 }
1438 ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1439 }
1440
1441 pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1443 let mut ret = Vec::new();
1444 let mut prev = offset;
1445 for o in 0..count {
1446 if o != 0 {
1447 prev = prev.next_increment()?;
1448 }
1449 ret.push(prev);
1450 }
1451 Ok(ret)
1452 }
1453
1454 pub fn to_hex_uncompressed(&self) -> String {
1458 format!("{self}")
1459 }
1460
1461 pub fn is_clock(&self) -> bool {
1462 *self == IOTA_CLOCK_OBJECT_ID
1463 }
1464}
1465
1466impl From<IotaAddress> for ObjectID {
1467 fn from(address: IotaAddress) -> ObjectID {
1468 let tmp: AccountAddress = address.into();
1469 tmp.into()
1470 }
1471}
1472
1473impl From<AccountAddress> for ObjectID {
1474 fn from(address: AccountAddress) -> Self {
1475 Self(address)
1476 }
1477}
1478
1479impl fmt::Display for ObjectID {
1480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1481 write!(f, "0x{}", Hex::encode(self.0))
1482 }
1483}
1484
1485impl fmt::Debug for ObjectID {
1486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1487 write!(f, "0x{}", Hex::encode(self.0))
1488 }
1489}
1490
1491impl AsRef<[u8]> for ObjectID {
1492 fn as_ref(&self) -> &[u8] {
1493 self.0.as_slice()
1494 }
1495}
1496
1497impl TryFrom<&[u8]> for ObjectID {
1498 type Error = ObjectIDParseError;
1499
1500 fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1502 Self::from_bytes(bytes)
1503 }
1504}
1505
1506impl TryFrom<Vec<u8>> for ObjectID {
1507 type Error = ObjectIDParseError;
1508
1509 fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1511 Self::from_bytes(bytes)
1512 }
1513}
1514
1515impl FromStr for ObjectID {
1516 type Err = ObjectIDParseError;
1517
1518 fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1521 decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1522 }
1523}
1524
1525impl std::ops::Deref for ObjectID {
1526 type Target = AccountAddress;
1527
1528 fn deref(&self) -> &Self::Target {
1529 &self.0
1530 }
1531}
1532
1533pub fn dbg_object_id(name: u8) -> ObjectID {
1535 ObjectID::new([name; ObjectID::LENGTH])
1536}
1537
1538#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1539pub enum ObjectIDParseError {
1540 #[error("ObjectID hex literal must start with 0x")]
1541 HexLiteralPrefixMissing,
1542
1543 #[error("Could not convert from bytes slice")]
1544 TryFromSlice,
1545}
1546
1547impl From<ObjectID> for AccountAddress {
1548 fn from(obj_id: ObjectID) -> Self {
1549 obj_id.0
1550 }
1551}
1552
1553impl From<IotaAddress> for AccountAddress {
1554 fn from(address: IotaAddress) -> Self {
1555 Self::new(address.0)
1556 }
1557}
1558
1559impl fmt::Display for MoveObjectType {
1560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1561 let s: StructTag = self.clone().into();
1562 write!(
1563 f,
1564 "{}",
1565 to_iota_struct_tag_string(&s).map_err(fmt::Error::custom)?
1566 )
1567 }
1568}
1569
1570impl fmt::Display for ObjectType {
1571 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1572 match self {
1573 ObjectType::Package => write!(f, "{PACKAGE}"),
1574 ObjectType::Struct(t) => write!(f, "{t}"),
1575 }
1576 }
1577}
1578
1579#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1585#[serde(try_from = "Vec<T>")]
1586pub struct SizeOneVec<T> {
1587 e: T,
1588}
1589
1590impl<T> SizeOneVec<T> {
1591 pub fn new(e: T) -> Self {
1592 Self { e }
1593 }
1594
1595 pub fn element(&self) -> &T {
1596 &self.e
1597 }
1598
1599 pub fn element_mut(&mut self) -> &mut T {
1600 &mut self.e
1601 }
1602
1603 pub fn into_inner(self) -> T {
1604 self.e
1605 }
1606
1607 pub fn iter(&self) -> std::iter::Once<&T> {
1608 std::iter::once(&self.e)
1609 }
1610}
1611
1612impl<T> Serialize for SizeOneVec<T>
1613where
1614 T: Serialize,
1615{
1616 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1617 where
1618 S: Serializer,
1619 {
1620 let mut seq = serializer.serialize_seq(Some(1))?;
1621 seq.serialize_element(&self.e)?;
1622 seq.end()
1623 }
1624}
1625
1626impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1627 type Error = anyhow::Error;
1628
1629 fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1630 if v.len() != 1 {
1631 Err(anyhow!("Expected a vec of size 1"))
1632 } else {
1633 Ok(SizeOneVec {
1634 e: v.pop().unwrap(),
1635 })
1636 }
1637 }
1638}
1639
1640#[test]
1641fn test_size_one_vec_is_transparent() {
1642 let regular = vec![42u8];
1643 let size_one = SizeOneVec::new(42u8);
1644
1645 let regular_ser = bcs::to_bytes(®ular).unwrap();
1647 let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(®ular_ser).unwrap();
1648 assert_eq!(size_one, size_one_deser);
1649
1650 let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1652 let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1653 assert_eq!(regular_deser, vec![43u8]);
1654
1655 let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1657 bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1658
1659 let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1660 bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1661}