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