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