1use std::{
7 cmp::max,
8 convert::{TryFrom, TryInto},
9 fmt,
10 str::FromStr,
11};
12
13use anyhow::anyhow;
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 staked_iota() -> Self {
193 Self(MoveObjectType_::StakedIota)
194 }
195
196 pub fn timelocked_iota_balance() -> Self {
197 Self(MoveObjectType_::Other(TimeLock::<Balance>::type_(
198 Balance::type_(GAS::type_().into()).into(),
199 )))
200 }
201
202 pub fn timelocked_staked_iota() -> Self {
203 Self(MoveObjectType_::Other(TimelockedStakedIota::type_()))
204 }
205
206 pub fn stardust_nft() -> Self {
207 Self(MoveObjectType_::Other(Nft::tag()))
208 }
209
210 pub fn address(&self) -> AccountAddress {
211 match &self.0 {
212 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => IOTA_FRAMEWORK_ADDRESS,
213 MoveObjectType_::StakedIota => IOTA_SYSTEM_ADDRESS,
214 MoveObjectType_::Other(s) => s.address,
215 }
216 }
217
218 pub fn module(&self) -> &IdentStr {
219 match &self.0 {
220 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME,
221 MoveObjectType_::StakedIota => STAKING_POOL_MODULE_NAME,
222 MoveObjectType_::Other(s) => &s.module,
223 }
224 }
225
226 pub fn name(&self) -> &IdentStr {
227 match &self.0 {
228 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME,
229 MoveObjectType_::StakedIota => STAKED_IOTA_STRUCT_NAME,
230 MoveObjectType_::Other(s) => &s.name,
231 }
232 }
233
234 pub fn type_params(&self) -> Vec<TypeTag> {
235 match &self.0 {
236 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
237 MoveObjectType_::StakedIota => vec![],
238 MoveObjectType_::Coin(inner) => vec![inner.clone()],
239 MoveObjectType_::Other(s) => s.type_params.clone(),
240 }
241 }
242
243 pub fn into_type_params(self) -> Vec<TypeTag> {
244 match self.0 {
245 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
246 MoveObjectType_::StakedIota => vec![],
247 MoveObjectType_::Coin(inner) => vec![inner],
248 MoveObjectType_::Other(s) => s.type_params,
249 }
250 }
251
252 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
253 match &self.0 {
254 MoveObjectType_::GasCoin => Some(GAS::type_tag()),
255 MoveObjectType_::Coin(inner) => Some(inner.clone()),
256 MoveObjectType_::StakedIota => None,
257 MoveObjectType_::Other(_) => None,
258 }
259 }
260
261 pub fn module_id(&self) -> ModuleId {
262 ModuleId::new(self.address(), self.module().to_owned())
263 }
264
265 pub fn size_for_gas_metering(&self) -> usize {
266 match &self.0 {
268 MoveObjectType_::GasCoin => 1,
269 MoveObjectType_::StakedIota => 1,
270 MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
271 MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
272 }
273 }
274
275 pub fn is_coin(&self) -> bool {
278 match &self.0 {
279 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
280 MoveObjectType_::StakedIota | MoveObjectType_::Other(_) => false,
281 }
282 }
283
284 pub fn is_gas_coin(&self) -> bool {
286 match &self.0 {
287 MoveObjectType_::GasCoin => true,
288 MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => {
289 false
290 }
291 }
292 }
293
294 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
296 match &self.0 {
297 MoveObjectType_::GasCoin => GAS::is_gas_type(t),
298 MoveObjectType_::Coin(c) => t == c,
299 MoveObjectType_::StakedIota | MoveObjectType_::Other(_) => false,
300 }
301 }
302
303 pub fn is_staked_iota(&self) -> bool {
304 match &self.0 {
305 MoveObjectType_::StakedIota => true,
306 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => {
307 false
308 }
309 }
310 }
311
312 pub fn is_coin_metadata(&self) -> bool {
313 match &self.0 {
314 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
315 false
316 }
317 MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s),
318 }
319 }
320
321 pub fn is_coin_manager(&self) -> bool {
322 matches!(&self.0, MoveObjectType_::Other(struct_tag) if CoinManager::is_coin_manager(struct_tag))
323 }
324
325 pub fn is_treasury_cap(&self) -> bool {
326 match &self.0 {
327 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
328 false
329 }
330 MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
331 }
332 }
333
334 pub fn is_upgrade_cap(&self) -> bool {
335 self.address() == IOTA_FRAMEWORK_ADDRESS
336 && self.module().as_str() == "package"
337 && self.name().as_str() == "UpgradeCap"
338 }
339
340 pub fn is_regulated_coin_metadata(&self) -> bool {
341 self.address() == IOTA_FRAMEWORK_ADDRESS
342 && self.module().as_str() == "coin"
343 && self.name().as_str() == "RegulatedCoinMetadata"
344 }
345
346 pub fn is_coin_deny_cap_v1(&self) -> bool {
347 self.address() == IOTA_FRAMEWORK_ADDRESS
348 && self.module().as_str() == "coin"
349 && self.name().as_str() == "DenyCapV1"
350 }
351
352 pub fn is_dynamic_field(&self) -> bool {
353 match &self.0 {
354 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
355 false
356 }
357 MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
358 }
359 }
360
361 pub fn is_timelock(&self) -> bool {
362 match &self.0 {
363 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
364 false
365 }
366 MoveObjectType_::Other(s) => timelock::is_timelock(s),
367 }
368 }
369
370 pub fn is_timelocked_balance(&self) -> bool {
371 match &self.0 {
372 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
373 false
374 }
375 MoveObjectType_::Other(s) => timelock::is_timelocked_balance(s),
376 }
377 }
378
379 pub fn is_timelocked_staked_iota(&self) -> bool {
380 match &self.0 {
381 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
382 false
383 }
384 MoveObjectType_::Other(s) => TimelockedStakedIota::is_timelocked_staked_iota(s),
385 }
386 }
387
388 pub fn is_alias_output(&self) -> bool {
389 match &self.0 {
390 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
391 false
392 }
393 MoveObjectType_::Other(s) => AliasOutput::is_alias_output(s),
394 }
395 }
396
397 pub fn is_basic_output(&self) -> bool {
398 match &self.0 {
399 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
400 false
401 }
402 MoveObjectType_::Other(s) => BasicOutput::is_basic_output(s),
403 }
404 }
405
406 pub fn is_nft_output(&self) -> bool {
407 match &self.0 {
408 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
409 false
410 }
411 MoveObjectType_::Other(s) => NftOutput::is_nft_output(s),
412 }
413 }
414
415 pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> IotaResult<TypeTag> {
416 match &self.0 {
417 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
418 Err(IotaError::ObjectDeserialization {
419 error: "Error extracting dynamic object name from Coin object".to_string(),
420 })
421 }
422 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
423 }
424 }
425
426 pub fn try_extract_field_value(&self) -> IotaResult<TypeTag> {
427 match &self.0 {
428 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
429 Err(IotaError::ObjectDeserialization {
430 error: "Error extracting dynamic object value from Coin object".to_string(),
431 })
432 }
433 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
434 }
435 }
436
437 pub fn is(&self, s: &StructTag) -> bool {
438 match &self.0 {
439 MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
440 MoveObjectType_::StakedIota => StakedIota::is_staked_iota(s),
441 MoveObjectType_::Coin(inner) => {
442 Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
443 }
444 MoveObjectType_::Other(o) => s == o,
445 }
446 }
447
448 pub fn other(&self) -> Option<&StructTag> {
449 if let MoveObjectType_::Other(s) = &self.0 {
450 Some(s)
451 } else {
452 None
453 }
454 }
455
456 pub fn to_canonical_string(&self, with_prefix: bool) -> String {
459 StructTag::from(self.clone()).to_canonical_string(with_prefix)
460 }
461}
462
463impl From<StructTag> for MoveObjectType {
464 fn from(mut s: StructTag) -> Self {
465 Self(if GasCoin::is_gas_coin(&s) {
466 MoveObjectType_::GasCoin
467 } else if Coin::is_coin(&s) {
468 MoveObjectType_::Coin(s.type_params.pop().unwrap())
470 } else if StakedIota::is_staked_iota(&s) {
471 MoveObjectType_::StakedIota
472 } else {
473 MoveObjectType_::Other(s)
474 })
475 }
476}
477
478impl From<MoveObjectType> for StructTag {
479 fn from(t: MoveObjectType) -> Self {
480 match t.0 {
481 MoveObjectType_::GasCoin => GasCoin::type_(),
482 MoveObjectType_::StakedIota => StakedIota::type_(),
483 MoveObjectType_::Coin(inner) => Coin::type_(inner),
484 MoveObjectType_::Other(s) => s,
485 }
486 }
487}
488
489impl From<MoveObjectType> for TypeTag {
490 fn from(o: MoveObjectType) -> TypeTag {
491 let s: StructTag = o.into();
492 TypeTag::Struct(Box::new(s))
493 }
494}
495
496pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
498 use TypeTag as T;
499
500 match t {
501 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
502 T::Vector(inner) => is_primitive_type_tag(inner),
503 T::Struct(st) => {
504 let StructTag {
505 address,
506 module,
507 name,
508 type_params: type_args,
509 } = &**st;
510 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
511 if resolved_struct == RESOLVED_IOTA_ID {
513 return true;
514 }
515 if resolved_struct == RESOLVED_UTF8_STR {
517 return true;
518 }
519 if resolved_struct == RESOLVED_ASCII_STR {
521 return true;
522 }
523 resolved_struct == RESOLVED_STD_OPTION
525 && type_args.len() == 1
526 && is_primitive_type_tag(&type_args[0])
527 }
528 T::Signer => false,
529 }
530}
531
532#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
534pub enum ObjectType {
535 Package,
537 Struct(MoveObjectType),
539}
540
541impl From<&Object> for ObjectType {
542 fn from(o: &Object) -> Self {
543 o.data
544 .type_()
545 .map(|t| ObjectType::Struct(t.clone()))
546 .unwrap_or(ObjectType::Package)
547 }
548}
549
550impl TryFrom<ObjectType> for StructTag {
551 type Error = anyhow::Error;
552
553 fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
554 match o {
555 ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
556 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
557 }
558 }
559}
560
561impl FromStr for ObjectType {
562 type Err = anyhow::Error;
563
564 fn from_str(s: &str) -> Result<Self, Self::Err> {
565 if s.to_lowercase() == PACKAGE {
566 Ok(ObjectType::Package)
567 } else {
568 let tag = parse_iota_struct_tag(s)?;
569 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
570 }
571 }
572}
573
574#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
575pub struct ObjectInfo {
576 pub object_id: ObjectID,
577 pub version: SequenceNumber,
578 pub digest: ObjectDigest,
579 pub type_: ObjectType,
580 pub owner: Owner,
581 pub previous_transaction: TransactionDigest,
582}
583
584impl ObjectInfo {
585 pub fn new(oref: &ObjectRef, o: &Object) -> Self {
586 let (object_id, version, digest) = *oref;
587 Self {
588 object_id,
589 version,
590 digest,
591 type_: o.into(),
592 owner: o.owner,
593 previous_transaction: o.previous_transaction,
594 }
595 }
596
597 pub fn from_object(object: &Object) -> Self {
598 Self {
599 object_id: object.id(),
600 version: object.version(),
601 digest: object.digest(),
602 type_: object.into(),
603 owner: object.owner,
604 previous_transaction: object.previous_transaction,
605 }
606 }
607}
608const PACKAGE: &str = "package";
609impl ObjectType {
610 pub fn is_gas_coin(&self) -> bool {
611 matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
612 }
613
614 pub fn is_coin(&self) -> bool {
615 matches!(self, ObjectType::Struct(s) if s.is_coin())
616 }
617
618 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
620 matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
621 }
622
623 pub fn is_package(&self) -> bool {
624 matches!(self, ObjectType::Package)
625 }
626}
627
628impl From<ObjectInfo> for ObjectRef {
629 fn from(info: ObjectInfo) -> Self {
630 (info.object_id, info.version, info.digest)
631 }
632}
633
634impl From<&ObjectInfo> for ObjectRef {
635 fn from(info: &ObjectInfo) -> Self {
636 (info.object_id, info.version, info.digest)
637 }
638}
639
640pub const IOTA_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
641
642#[serde_as]
643#[derive(
644 Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
645)]
646#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
647pub struct IotaAddress(
648 #[schemars(with = "Hex")]
649 #[serde_as(as = "Readable<Hex, _>")]
650 [u8; IOTA_ADDRESS_LENGTH],
651);
652
653impl IotaAddress {
654 pub const ZERO: Self = Self([0u8; IOTA_ADDRESS_LENGTH]);
655
656 pub fn new(bytes: [u8; IOTA_ADDRESS_LENGTH]) -> Self {
657 Self(bytes)
658 }
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 optional_address_as_hex<S>(
677 key: &Option<IotaAddress>,
678 serializer: S,
679 ) -> Result<S::Ok, S::Error>
680 where
681 S: serde::ser::Serializer,
682 {
683 serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
684 }
685
686 pub fn optional_address_from_hex<'de, D>(
688 deserializer: D,
689 ) -> Result<Option<IotaAddress>, D::Error>
690 where
691 D: serde::de::Deserializer<'de>,
692 {
693 let s = String::deserialize(deserializer)?;
694 let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
695 Ok(Some(value))
696 }
697
698 pub fn to_inner(self) -> [u8; IOTA_ADDRESS_LENGTH] {
700 self.0
701 }
702
703 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, IotaError> {
705 <[u8; IOTA_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
706 .map_err(|_| IotaError::InvalidAddress)
707 .map(IotaAddress)
708 }
709
710 pub fn try_from_padded(inputs: &ZkLoginInputs) -> IotaResult<Self> {
715 Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
716 }
717
718 pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> IotaResult<Self> {
720 let mut hasher = DefaultHash::default();
721 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
722 let iss_bytes = inputs.get_iss().as_bytes();
723 hasher.update([iss_bytes.len() as u8]);
724 hasher.update(iss_bytes);
725 hasher.update(inputs.get_address_seed().unpadded());
726 Ok(IotaAddress(hasher.finalize().digest))
727 }
728}
729
730impl From<ObjectID> for IotaAddress {
731 fn from(object_id: ObjectID) -> IotaAddress {
732 Self(object_id.into_bytes())
733 }
734}
735
736impl From<AccountAddress> for IotaAddress {
737 fn from(address: AccountAddress) -> IotaAddress {
738 Self(address.into_bytes())
739 }
740}
741
742impl TryFrom<&[u8]> for IotaAddress {
743 type Error = IotaError;
744
745 fn try_from(bytes: &[u8]) -> Result<Self, IotaError> {
747 Self::from_bytes(bytes)
748 }
749}
750
751impl TryFrom<Vec<u8>> for IotaAddress {
752 type Error = IotaError;
753
754 fn try_from(bytes: Vec<u8>) -> Result<Self, IotaError> {
756 Self::from_bytes(bytes)
757 }
758}
759
760impl AsRef<[u8]> for IotaAddress {
761 fn as_ref(&self) -> &[u8] {
762 &self.0[..]
763 }
764}
765
766impl FromStr for IotaAddress {
767 type Err = anyhow::Error;
768 fn from_str(s: &str) -> Result<Self, Self::Err> {
769 decode_bytes_hex(s).map_err(|e| anyhow!(e))
770 }
771}
772
773impl<T: IotaPublicKey> From<&T> for IotaAddress {
774 fn from(pk: &T) -> Self {
775 let mut hasher = DefaultHash::default();
776 T::SIGNATURE_SCHEME.update_hasher_with_flag(&mut hasher);
777 hasher.update(pk);
778 let g_arr = hasher.finalize();
779 IotaAddress(g_arr.digest)
780 }
781}
782
783impl From<&PublicKey> for IotaAddress {
784 fn from(pk: &PublicKey) -> Self {
785 let mut hasher = DefaultHash::default();
786 pk.scheme().update_hasher_with_flag(&mut hasher);
787 hasher.update(pk);
788 let g_arr = hasher.finalize();
789 IotaAddress(g_arr.digest)
790 }
791}
792
793impl From<&MultiSigPublicKey> for IotaAddress {
794 fn from(multisig_pk: &MultiSigPublicKey) -> Self {
803 let mut hasher = DefaultHash::default();
804 hasher.update([SignatureScheme::MultiSig.flag()]);
805 hasher.update(multisig_pk.threshold().to_le_bytes());
806 multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
807 pk.scheme().update_hasher_with_flag(&mut hasher);
808 hasher.update(pk.as_ref());
809 hasher.update(w.to_le_bytes());
810 });
811 IotaAddress(hasher.finalize().digest)
812 }
813}
814
815impl TryFrom<&ZkLoginAuthenticator> for IotaAddress {
819 type Error = IotaError;
820 fn try_from(authenticator: &ZkLoginAuthenticator) -> IotaResult<Self> {
821 IotaAddress::try_from_unpadded(&authenticator.inputs)
822 }
823}
824
825impl TryFrom<&GenericSignature> for IotaAddress {
826 type Error = IotaError;
827 fn try_from(sig: &GenericSignature) -> IotaResult<Self> {
830 match sig {
831 GenericSignature::Signature(sig) => {
832 let scheme = sig.scheme();
833 let pub_key_bytes = sig.public_key_bytes();
834 let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
835 IotaError::InvalidSignature {
836 error: "Cannot parse pubkey".to_string(),
837 }
838 })?;
839 Ok(IotaAddress::from(&pub_key))
840 }
841 GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
842 GenericSignature::ZkLoginAuthenticator(zklogin) => {
843 IotaAddress::try_from_unpadded(&zklogin.inputs)
844 }
845 GenericSignature::PasskeyAuthenticator(s) => Ok(IotaAddress::from(&s.get_pk()?)),
846 }
847 }
848}
849
850impl fmt::Display for IotaAddress {
851 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852 write!(f, "0x{}", Hex::encode(self.0))
853 }
854}
855
856impl fmt::Debug for IotaAddress {
857 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
858 write!(f, "0x{}", Hex::encode(self.0))
859 }
860}
861
862pub fn dbg_addr(name: u8) -> IotaAddress {
864 let addr = [name; IOTA_ADDRESS_LENGTH];
865 IotaAddress(addr)
866}
867
868#[derive(
869 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
870)]
871pub struct ExecutionDigests {
872 pub transaction: TransactionDigest,
873 pub effects: TransactionEffectsDigest,
874}
875
876impl ExecutionDigests {
877 pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
878 Self {
879 transaction,
880 effects,
881 }
882 }
883
884 pub fn random() -> Self {
885 Self {
886 transaction: TransactionDigest::random(),
887 effects: TransactionEffectsDigest::random(),
888 }
889 }
890}
891
892#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
893pub struct ExecutionData {
894 pub transaction: Transaction,
895 pub effects: TransactionEffects,
896}
897
898impl ExecutionData {
899 pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
900 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
901 Self {
902 transaction,
903 effects,
904 }
905 }
906
907 pub fn digests(&self) -> ExecutionDigests {
908 self.effects.execution_digests()
909 }
910}
911
912#[derive(Clone, Eq, PartialEq, Debug)]
913pub struct VerifiedExecutionData {
914 pub transaction: VerifiedTransaction,
915 pub effects: TransactionEffects,
916}
917
918impl VerifiedExecutionData {
919 pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
920 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
921 Self {
922 transaction,
923 effects,
924 }
925 }
926
927 pub fn new_unchecked(data: ExecutionData) -> Self {
928 Self {
929 transaction: VerifiedTransaction::new_unchecked(data.transaction),
930 effects: data.effects,
931 }
932 }
933
934 pub fn into_inner(self) -> ExecutionData {
935 ExecutionData {
936 transaction: self.transaction.into_inner(),
937 effects: self.effects,
938 }
939 }
940
941 pub fn digests(&self) -> ExecutionDigests {
942 self.effects.execution_digests()
943 }
944}
945
946pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
947pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
948pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
949 &MOVE_STDLIB_ADDRESS,
950 STD_OPTION_MODULE_NAME,
951 STD_OPTION_STRUCT_NAME,
952);
953
954pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
955pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
956pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
957 &MOVE_STDLIB_ADDRESS,
958 STD_ASCII_MODULE_NAME,
959 STD_ASCII_STRUCT_NAME,
960);
961
962pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
963pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
964pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
965 &MOVE_STDLIB_ADDRESS,
966 STD_UTF8_MODULE_NAME,
967 STD_UTF8_STRUCT_NAME,
968);
969
970pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
971pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
972
973pub fn move_ascii_str_layout() -> A::MoveStructLayout {
974 A::MoveStructLayout {
975 type_: StructTag {
976 address: MOVE_STDLIB_ADDRESS,
977 module: STD_ASCII_MODULE_NAME.to_owned(),
978 name: STD_ASCII_STRUCT_NAME.to_owned(),
979 type_params: vec![],
980 },
981 fields: vec![A::MoveFieldLayout::new(
982 ident_str!("bytes").into(),
983 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
984 )],
985 }
986}
987
988pub fn move_utf8_str_layout() -> A::MoveStructLayout {
989 A::MoveStructLayout {
990 type_: StructTag {
991 address: MOVE_STDLIB_ADDRESS,
992 module: STD_UTF8_MODULE_NAME.to_owned(),
993 name: STD_UTF8_STRUCT_NAME.to_owned(),
994 type_params: vec![],
995 },
996 fields: vec![A::MoveFieldLayout::new(
997 ident_str!("bytes").into(),
998 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
999 )],
1000 }
1001}
1002
1003#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1004pub struct TxContext {
1005 sender: AccountAddress,
1007 digest: Vec<u8>,
1009 epoch: EpochId,
1011 epoch_timestamp_ms: CheckpointTimestamp,
1013 ids_created: u64,
1016}
1017
1018#[derive(PartialEq, Eq, Clone, Copy)]
1019pub enum TxContextKind {
1020 None,
1022 Mutable,
1024 Immutable,
1026}
1027
1028impl TxContext {
1029 pub fn new(sender: &IotaAddress, digest: &TransactionDigest, epoch_data: &EpochData) -> Self {
1030 Self::new_from_components(
1031 sender,
1032 digest,
1033 &epoch_data.epoch_id(),
1034 epoch_data.epoch_start_timestamp(),
1035 )
1036 }
1037
1038 pub fn new_from_components(
1039 sender: &IotaAddress,
1040 digest: &TransactionDigest,
1041 epoch_id: &EpochId,
1042 epoch_timestamp_ms: u64,
1043 ) -> Self {
1044 Self {
1045 sender: AccountAddress::new(sender.0),
1046 digest: digest.into_inner().to_vec(),
1047 epoch: *epoch_id,
1048 epoch_timestamp_ms,
1049 ids_created: 0,
1050 }
1051 }
1052
1053 pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1056 use SignatureToken as S;
1057 let (kind, s) = match s {
1058 S::MutableReference(s) => (TxContextKind::Mutable, s),
1059 S::Reference(s) => (TxContextKind::Immutable, s),
1060 _ => return TxContextKind::None,
1061 };
1062
1063 let S::Datatype(idx) = &**s else {
1064 return TxContextKind::None;
1065 };
1066
1067 let (module_addr, module_name, struct_name) = resolve_struct(view, *idx);
1068 let is_tx_context_type = module_name == TX_CONTEXT_MODULE_NAME
1069 && module_addr == &IOTA_FRAMEWORK_ADDRESS
1070 && struct_name == TX_CONTEXT_STRUCT_NAME;
1071
1072 if is_tx_context_type {
1073 kind
1074 } else {
1075 TxContextKind::None
1076 }
1077 }
1078
1079 pub fn epoch(&self) -> EpochId {
1080 self.epoch
1081 }
1082
1083 pub fn fresh_id(&mut self) -> ObjectID {
1086 let id = ObjectID::derive_id(self.digest(), self.ids_created);
1087
1088 self.ids_created += 1;
1089 id
1090 }
1091
1092 pub fn digest(&self) -> TransactionDigest {
1094 TransactionDigest::new(self.digest.clone().try_into().unwrap())
1095 }
1096
1097 pub fn sender(&self) -> IotaAddress {
1098 IotaAddress::from(ObjectID(self.sender))
1099 }
1100
1101 pub fn to_vec(&self) -> Vec<u8> {
1102 bcs::to_bytes(&self).unwrap()
1103 }
1104
1105 pub fn update_state(&mut self, other: TxContext) -> Result<(), ExecutionError> {
1110 if self.sender != other.sender
1111 || self.digest != other.digest
1112 || other.ids_created < self.ids_created
1113 {
1114 return Err(ExecutionError::new_with_source(
1115 ExecutionErrorKind::InvariantViolation,
1116 "Immutable fields for TxContext changed",
1117 ));
1118 }
1119 self.ids_created = other.ids_created;
1120 Ok(())
1121 }
1122
1123 pub fn random_for_testing_only() -> Self {
1125 Self::new(
1126 &IotaAddress::random_for_testing_only(),
1127 &TransactionDigest::random(),
1128 &EpochData::new_test(),
1129 )
1130 }
1131
1132 pub fn with_sender_for_testing_only(sender: &IotaAddress) -> Self {
1134 Self::new(sender, &TransactionDigest::random(), &EpochData::new_test())
1135 }
1136}
1137
1138impl SequenceNumber {
1140 pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1141 pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1142 pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1143 pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1144 pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1145 SequenceNumber(SequenceNumber::MAX.value() + 3);
1146
1147 pub const fn new() -> Self {
1148 SequenceNumber(0)
1149 }
1150
1151 pub const fn value(&self) -> u64 {
1152 self.0
1153 }
1154
1155 pub const fn from_u64(u: u64) -> Self {
1156 SequenceNumber(u)
1157 }
1158
1159 pub fn increment(&mut self) {
1160 assert_ne!(self.0, u64::MAX);
1161 self.0 += 1;
1162 }
1163
1164 pub fn increment_to(&mut self, next: SequenceNumber) {
1165 debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1166 *self = next;
1167 }
1168
1169 pub fn decrement(&mut self) {
1170 assert_ne!(self.0, 0);
1171 self.0 -= 1;
1172 }
1173
1174 pub fn decrement_to(&mut self, prev: SequenceNumber) {
1175 debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1176 *self = prev;
1177 }
1178
1179 #[must_use]
1182 pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1183 let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1184
1185 assert_ne!(max_input.0, u64::MAX);
1190
1191 SequenceNumber(max_input.0 + 1)
1192 }
1193
1194 pub fn is_cancelled(&self) -> bool {
1195 self == &SequenceNumber::CANCELLED_READ
1196 || self == &SequenceNumber::CONGESTED
1197 || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1198 }
1199
1200 pub fn is_valid(&self) -> bool {
1201 self < &SequenceNumber::MAX
1202 }
1203}
1204
1205impl From<SequenceNumber> for u64 {
1206 fn from(val: SequenceNumber) -> Self {
1207 val.0
1208 }
1209}
1210
1211impl From<u64> for SequenceNumber {
1212 fn from(value: u64) -> Self {
1213 SequenceNumber(value)
1214 }
1215}
1216
1217impl From<SequenceNumber> for usize {
1218 fn from(value: SequenceNumber) -> Self {
1219 value.0 as usize
1220 }
1221}
1222
1223impl ObjectID {
1224 pub const LENGTH: usize = AccountAddress::LENGTH;
1226 pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1228 pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1229 pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1231 Self(AccountAddress::new(obj_id))
1232 }
1233
1234 pub const fn from_address(addr: AccountAddress) -> Self {
1236 Self(addr)
1237 }
1238
1239 pub fn random() -> Self {
1241 Self::from(AccountAddress::random())
1242 }
1243
1244 pub fn random_from_rng<R>(rng: &mut R) -> Self
1246 where
1247 R: AllowedRng,
1248 {
1249 let buf: [u8; Self::LENGTH] = rng.gen();
1250 ObjectID::new(buf)
1251 }
1252
1253 pub fn to_vec(&self) -> Vec<u8> {
1255 self.0.to_vec()
1256 }
1257
1258 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1260 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1261 .map_err(|_| ObjectIDParseError::TryFromSlice)
1262 .map(ObjectID::new)
1263 }
1264
1265 pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1267 self.0.into_bytes()
1268 }
1269
1270 pub const fn from_single_byte(byte: u8) -> ObjectID {
1272 let mut bytes = [0u8; Self::LENGTH];
1273 bytes[Self::LENGTH - 1] = byte;
1274 ObjectID::new(bytes)
1275 }
1276
1277 pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1280 if !literal.starts_with("0x") {
1281 return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1282 }
1283
1284 let hex_len = literal.len() - 2;
1285
1286 if hex_len < Self::LENGTH * 2 {
1288 let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1289 for _ in 0..Self::LENGTH * 2 - hex_len {
1290 hex_str.push('0');
1291 }
1292 hex_str.push_str(&literal[2..]);
1293 Self::from_str(&hex_str)
1294 } else {
1295 Self::from_str(&literal[2..])
1296 }
1297 }
1298
1299 pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1302 let mut hasher = DefaultHash::default();
1303 hasher.update([HashingIntentScope::RegularObjectId as u8]);
1304 hasher.update(digest);
1305 hasher.update(creation_num.to_le_bytes());
1306 let hash = hasher.finalize();
1307
1308 ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1312 }
1313
1314 pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1317 let mut curr_vec = self.to_vec();
1318 let mut step_copy = step;
1319
1320 let mut carry = 0;
1321 for idx in (0..Self::LENGTH).rev() {
1322 if step_copy == 0 {
1323 break;
1325 }
1326 let g = (step_copy % 0x100) as u16;
1328 step_copy >>= 8;
1330 let mut val = curr_vec[idx] as u16;
1331 (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1332 curr_vec[idx] = val as u8;
1333 }
1334
1335 if carry > 0 {
1336 return Err(anyhow!("Increment will cause overflow"));
1337 }
1338 ObjectID::try_from(curr_vec).map_err(|w| w.into())
1339 }
1340
1341 pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1344 let mut prev_val = self.to_vec();
1345 let mx = [0xFF; Self::LENGTH];
1346
1347 if prev_val == mx {
1348 return Err(anyhow!("Increment will cause overflow"));
1349 }
1350
1351 for idx in (0..Self::LENGTH).rev() {
1353 if prev_val[idx] == 0xFF {
1354 prev_val[idx] = 0;
1355 } else {
1356 prev_val[idx] += 1;
1357 break;
1358 };
1359 }
1360 ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1361 }
1362
1363 pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1365 let mut ret = Vec::new();
1366 let mut prev = offset;
1367 for o in 0..count {
1368 if o != 0 {
1369 prev = prev.next_increment()?;
1370 }
1371 ret.push(prev);
1372 }
1373 Ok(ret)
1374 }
1375
1376 pub fn to_hex_uncompressed(&self) -> String {
1380 format!("{self}")
1381 }
1382
1383 pub fn is_clock(&self) -> bool {
1384 *self == IOTA_CLOCK_OBJECT_ID
1385 }
1386}
1387
1388impl From<IotaAddress> for ObjectID {
1389 fn from(address: IotaAddress) -> ObjectID {
1390 let tmp: AccountAddress = address.into();
1391 tmp.into()
1392 }
1393}
1394
1395impl From<AccountAddress> for ObjectID {
1396 fn from(address: AccountAddress) -> Self {
1397 Self(address)
1398 }
1399}
1400
1401impl fmt::Display for ObjectID {
1402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1403 write!(f, "0x{}", Hex::encode(self.0))
1404 }
1405}
1406
1407impl fmt::Debug for ObjectID {
1408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1409 write!(f, "0x{}", Hex::encode(self.0))
1410 }
1411}
1412
1413impl AsRef<[u8]> for ObjectID {
1414 fn as_ref(&self) -> &[u8] {
1415 self.0.as_slice()
1416 }
1417}
1418
1419impl TryFrom<&[u8]> for ObjectID {
1420 type Error = ObjectIDParseError;
1421
1422 fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1424 Self::from_bytes(bytes)
1425 }
1426}
1427
1428impl TryFrom<Vec<u8>> for ObjectID {
1429 type Error = ObjectIDParseError;
1430
1431 fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1433 Self::from_bytes(bytes)
1434 }
1435}
1436
1437impl FromStr for ObjectID {
1438 type Err = ObjectIDParseError;
1439
1440 fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1443 decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1444 }
1445}
1446
1447impl std::ops::Deref for ObjectID {
1448 type Target = AccountAddress;
1449
1450 fn deref(&self) -> &Self::Target {
1451 &self.0
1452 }
1453}
1454
1455pub fn dbg_object_id(name: u8) -> ObjectID {
1457 ObjectID::new([name; ObjectID::LENGTH])
1458}
1459
1460#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1461pub enum ObjectIDParseError {
1462 #[error("ObjectID hex literal must start with 0x")]
1463 HexLiteralPrefixMissing,
1464
1465 #[error("Could not convert from bytes slice")]
1466 TryFromSlice,
1467}
1468
1469impl From<ObjectID> for AccountAddress {
1470 fn from(obj_id: ObjectID) -> Self {
1471 obj_id.0
1472 }
1473}
1474
1475impl From<IotaAddress> for AccountAddress {
1476 fn from(address: IotaAddress) -> Self {
1477 Self::new(address.0)
1478 }
1479}
1480
1481impl fmt::Display for MoveObjectType {
1482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1483 let s: StructTag = self.clone().into();
1484 write!(
1485 f,
1486 "{}",
1487 to_iota_struct_tag_string(&s).map_err(fmt::Error::custom)?
1488 )
1489 }
1490}
1491
1492impl fmt::Display for ObjectType {
1493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1494 match self {
1495 ObjectType::Package => write!(f, "{}", PACKAGE),
1496 ObjectType::Struct(t) => write!(f, "{}", t),
1497 }
1498 }
1499}
1500
1501#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1507#[serde(try_from = "Vec<T>")]
1508pub struct SizeOneVec<T> {
1509 e: T,
1510}
1511
1512impl<T> SizeOneVec<T> {
1513 pub fn new(e: T) -> Self {
1514 Self { e }
1515 }
1516
1517 pub fn element(&self) -> &T {
1518 &self.e
1519 }
1520
1521 pub fn element_mut(&mut self) -> &mut T {
1522 &mut self.e
1523 }
1524
1525 pub fn into_inner(self) -> T {
1526 self.e
1527 }
1528
1529 pub fn iter(&self) -> std::iter::Once<&T> {
1530 std::iter::once(&self.e)
1531 }
1532}
1533
1534impl<T> Serialize for SizeOneVec<T>
1535where
1536 T: Serialize,
1537{
1538 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1539 where
1540 S: Serializer,
1541 {
1542 let mut seq = serializer.serialize_seq(Some(1))?;
1543 seq.serialize_element(&self.e)?;
1544 seq.end()
1545 }
1546}
1547
1548impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1549 type Error = anyhow::Error;
1550
1551 fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1552 if v.len() != 1 {
1553 Err(anyhow!("Expected a vec of size 1"))
1554 } else {
1555 Ok(SizeOneVec {
1556 e: v.pop().unwrap(),
1557 })
1558 }
1559 }
1560}
1561
1562#[test]
1563fn test_size_one_vec_is_transparent() {
1564 let regular = vec![42u8];
1565 let size_one = SizeOneVec::new(42u8);
1566
1567 let regular_ser = bcs::to_bytes(®ular).unwrap();
1569 let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(®ular_ser).unwrap();
1570 assert_eq!(size_one, size_one_deser);
1571
1572 let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1574 let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1575 assert_eq!(regular_deser, vec![43u8]);
1576
1577 let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1579 bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1580
1581 let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1582 bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1583}