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 crypto::{
42 AuthorityPublicKeyBytes, DefaultHash, IotaPublicKey, IotaSignature, PublicKey,
43 SignatureScheme,
44 },
45 dynamic_field::{DynamicFieldInfo, DynamicFieldType},
46 effects::{TransactionEffects, TransactionEffectsAPI},
47 epoch_data::EpochData,
48 error::{ExecutionError, ExecutionErrorKind, IotaError, IotaResult},
49 gas_coin::{GAS, GasCoin},
50 governance::{STAKED_IOTA_STRUCT_NAME, STAKING_POOL_MODULE_NAME, StakedIota},
51 id::RESOLVED_IOTA_ID,
52 iota_serde::{HexAccountAddress, Readable, to_iota_struct_tag_string},
53 messages_checkpoint::CheckpointTimestamp,
54 multisig::MultiSigPublicKey,
55 object::{Object, Owner},
56 parse_iota_struct_tag,
57 signature::GenericSignature,
58 stardust::output::{AliasOutput, BasicOutput, Nft, NftOutput},
59 timelock::{
60 timelock::{self, TimeLock},
61 timelocked_staked_iota::TimelockedStakedIota,
62 },
63 transaction::{Transaction, VerifiedTransaction},
64 zk_login_authenticator::ZkLoginAuthenticator,
65};
66pub use crate::{
67 committee::EpochId,
68 digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest},
69};
70
71#[cfg(test)]
72#[path = "unit_tests/base_types_tests.rs"]
73mod base_types_tests;
74
75#[derive(
76 Eq,
77 PartialEq,
78 Ord,
79 PartialOrd,
80 Copy,
81 Clone,
82 Hash,
83 Default,
84 Debug,
85 Serialize,
86 Deserialize,
87 JsonSchema,
88)]
89#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
90pub struct SequenceNumber(u64);
91
92impl SequenceNumber {
93 pub fn one_before(&self) -> Option<SequenceNumber> {
94 if self.0 == 0 {
95 None
96 } else {
97 Some(SequenceNumber(self.0 - 1))
98 }
99 }
100
101 pub fn next(&self) -> SequenceNumber {
102 SequenceNumber(self.0 + 1)
103 }
104}
105
106pub type TxSequenceNumber = u64;
107
108impl fmt::Display for SequenceNumber {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 write!(f, "{:#x}", self.0)
111 }
112}
113
114pub type VersionNumber = SequenceNumber;
115
116pub type CommitRound = u64;
118
119#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Default, Debug, Serialize, Deserialize)]
120pub struct UserData(pub Option<[u8; 32]>);
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
152pub fn update_object_ref_for_testing(object_ref: ObjectRef) -> ObjectRef {
153 (
154 object_ref.0,
155 object_ref.1.next(),
156 ObjectDigest::new([0; 32]),
157 )
158}
159
160#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
166pub struct MoveObjectType(MoveObjectType_);
167
168#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
171pub enum MoveObjectType_ {
172 Other(StructTag),
174 GasCoin,
176 StakedIota,
178 Coin(TypeTag),
181 }
185
186impl MoveObjectType {
187 pub fn gas_coin() -> Self {
188 Self(MoveObjectType_::GasCoin)
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_treasury_cap(&self) -> bool {
321 match &self.0 {
322 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
323 false
324 }
325 MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
326 }
327 }
328
329 pub fn is_upgrade_cap(&self) -> bool {
330 self.address() == IOTA_FRAMEWORK_ADDRESS
331 && self.module().as_str() == "package"
332 && self.name().as_str() == "UpgradeCap"
333 }
334
335 pub fn is_regulated_coin_metadata(&self) -> bool {
336 self.address() == IOTA_FRAMEWORK_ADDRESS
337 && self.module().as_str() == "coin"
338 && self.name().as_str() == "RegulatedCoinMetadata"
339 }
340
341 pub fn is_coin_deny_cap_v1(&self) -> bool {
342 self.address() == IOTA_FRAMEWORK_ADDRESS
343 && self.module().as_str() == "coin"
344 && self.name().as_str() == "DenyCapV1"
345 }
346
347 pub fn is_dynamic_field(&self) -> bool {
348 match &self.0 {
349 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
350 false
351 }
352 MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
353 }
354 }
355
356 pub fn is_timelock(&self) -> bool {
357 match &self.0 {
358 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
359 false
360 }
361 MoveObjectType_::Other(s) => timelock::is_timelock(s),
362 }
363 }
364
365 pub fn is_timelocked_balance(&self) -> bool {
366 match &self.0 {
367 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
368 false
369 }
370 MoveObjectType_::Other(s) => timelock::is_timelocked_balance(s),
371 }
372 }
373
374 pub fn is_timelocked_staked_iota(&self) -> bool {
375 match &self.0 {
376 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
377 false
378 }
379 MoveObjectType_::Other(s) => TimelockedStakedIota::is_timelocked_staked_iota(s),
380 }
381 }
382
383 pub fn is_alias_output(&self) -> bool {
384 match &self.0 {
385 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
386 false
387 }
388 MoveObjectType_::Other(s) => AliasOutput::is_alias_output(s),
389 }
390 }
391
392 pub fn is_basic_output(&self) -> bool {
393 match &self.0 {
394 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
395 false
396 }
397 MoveObjectType_::Other(s) => BasicOutput::is_basic_output(s),
398 }
399 }
400
401 pub fn is_nft_output(&self) -> bool {
402 match &self.0 {
403 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
404 false
405 }
406 MoveObjectType_::Other(s) => NftOutput::is_nft_output(s),
407 }
408 }
409
410 pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> IotaResult<TypeTag> {
411 match &self.0 {
412 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
413 Err(IotaError::ObjectDeserialization {
414 error: "Error extracting dynamic object name from Coin object".to_string(),
415 })
416 }
417 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
418 }
419 }
420
421 pub fn try_extract_field_value(&self) -> IotaResult<TypeTag> {
422 match &self.0 {
423 MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => {
424 Err(IotaError::ObjectDeserialization {
425 error: "Error extracting dynamic object value from Coin object".to_string(),
426 })
427 }
428 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
429 }
430 }
431
432 pub fn is(&self, s: &StructTag) -> bool {
433 match &self.0 {
434 MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
435 MoveObjectType_::StakedIota => StakedIota::is_staked_iota(s),
436 MoveObjectType_::Coin(inner) => {
437 Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
438 }
439 MoveObjectType_::Other(o) => s == o,
440 }
441 }
442
443 pub fn other(&self) -> Option<&StructTag> {
444 if let MoveObjectType_::Other(s) = &self.0 {
445 Some(s)
446 } else {
447 None
448 }
449 }
450
451 pub fn to_canonical_string(&self, with_prefix: bool) -> String {
454 StructTag::from(self.clone()).to_canonical_string(with_prefix)
455 }
456}
457
458impl From<StructTag> for MoveObjectType {
459 fn from(mut s: StructTag) -> Self {
460 Self(if GasCoin::is_gas_coin(&s) {
461 MoveObjectType_::GasCoin
462 } else if Coin::is_coin(&s) {
463 MoveObjectType_::Coin(s.type_params.pop().unwrap())
465 } else if StakedIota::is_staked_iota(&s) {
466 MoveObjectType_::StakedIota
467 } else {
468 MoveObjectType_::Other(s)
469 })
470 }
471}
472
473impl From<MoveObjectType> for StructTag {
474 fn from(t: MoveObjectType) -> Self {
475 match t.0 {
476 MoveObjectType_::GasCoin => GasCoin::type_(),
477 MoveObjectType_::StakedIota => StakedIota::type_(),
478 MoveObjectType_::Coin(inner) => Coin::type_(inner),
479 MoveObjectType_::Other(s) => s,
480 }
481 }
482}
483
484impl From<MoveObjectType> for TypeTag {
485 fn from(o: MoveObjectType) -> TypeTag {
486 let s: StructTag = o.into();
487 TypeTag::Struct(Box::new(s))
488 }
489}
490
491pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
493 use TypeTag as T;
494
495 match t {
496 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
497 T::Vector(inner) => is_primitive_type_tag(inner),
498 T::Struct(st) => {
499 let StructTag {
500 address,
501 module,
502 name,
503 type_params: type_args,
504 } = &**st;
505 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
506 if resolved_struct == RESOLVED_IOTA_ID {
508 return true;
509 }
510 if resolved_struct == RESOLVED_UTF8_STR {
512 return true;
513 }
514 if resolved_struct == RESOLVED_ASCII_STR {
516 return true;
517 }
518 resolved_struct == RESOLVED_STD_OPTION
520 && type_args.len() == 1
521 && is_primitive_type_tag(&type_args[0])
522 }
523 T::Signer => false,
524 }
525}
526
527#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
529pub enum ObjectType {
530 Package,
532 Struct(MoveObjectType),
534}
535
536impl From<&Object> for ObjectType {
537 fn from(o: &Object) -> Self {
538 o.data
539 .type_()
540 .map(|t| ObjectType::Struct(t.clone()))
541 .unwrap_or(ObjectType::Package)
542 }
543}
544
545impl TryFrom<ObjectType> for StructTag {
546 type Error = anyhow::Error;
547
548 fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
549 match o {
550 ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
551 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
552 }
553 }
554}
555
556impl FromStr for ObjectType {
557 type Err = anyhow::Error;
558
559 fn from_str(s: &str) -> Result<Self, Self::Err> {
560 if s.to_lowercase() == PACKAGE {
561 Ok(ObjectType::Package)
562 } else {
563 let tag = parse_iota_struct_tag(s)?;
564 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
565 }
566 }
567}
568
569#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
570pub struct ObjectInfo {
571 pub object_id: ObjectID,
572 pub version: SequenceNumber,
573 pub digest: ObjectDigest,
574 pub type_: ObjectType,
575 pub owner: Owner,
576 pub previous_transaction: TransactionDigest,
577}
578
579impl ObjectInfo {
580 pub fn new(oref: &ObjectRef, o: &Object) -> Self {
581 let (object_id, version, digest) = *oref;
582 Self {
583 object_id,
584 version,
585 digest,
586 type_: o.into(),
587 owner: o.owner,
588 previous_transaction: o.previous_transaction,
589 }
590 }
591
592 pub fn from_object(object: &Object) -> Self {
593 Self {
594 object_id: object.id(),
595 version: object.version(),
596 digest: object.digest(),
597 type_: object.into(),
598 owner: object.owner,
599 previous_transaction: object.previous_transaction,
600 }
601 }
602}
603const PACKAGE: &str = "package";
604impl ObjectType {
605 pub fn is_gas_coin(&self) -> bool {
606 matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
607 }
608
609 pub fn is_coin(&self) -> bool {
610 matches!(self, ObjectType::Struct(s) if s.is_coin())
611 }
612
613 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
615 matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
616 }
617
618 pub fn is_package(&self) -> bool {
619 matches!(self, ObjectType::Package)
620 }
621}
622
623impl From<ObjectInfo> for ObjectRef {
624 fn from(info: ObjectInfo) -> Self {
625 (info.object_id, info.version, info.digest)
626 }
627}
628
629impl From<&ObjectInfo> for ObjectRef {
630 fn from(info: &ObjectInfo) -> Self {
631 (info.object_id, info.version, info.digest)
632 }
633}
634
635pub const IOTA_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
636
637#[serde_as]
638#[derive(
639 Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
640)]
641#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
642pub struct IotaAddress(
643 #[schemars(with = "Hex")]
644 #[serde_as(as = "Readable<Hex, _>")]
645 [u8; IOTA_ADDRESS_LENGTH],
646);
647
648impl IotaAddress {
649 pub const ZERO: Self = Self([0u8; IOTA_ADDRESS_LENGTH]);
650
651 pub fn new(bytes: [u8; IOTA_ADDRESS_LENGTH]) -> Self {
652 Self(bytes)
653 }
654
655 pub fn to_vec(&self) -> Vec<u8> {
657 self.0.to_vec()
658 }
659
660 pub fn random_for_testing_only() -> Self {
662 AccountAddress::random().into()
663 }
664
665 pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
666 let buf: [u8; IOTA_ADDRESS_LENGTH] = rng.gen();
667 Self(buf)
668 }
669
670 pub fn optional_address_as_hex<S>(
672 key: &Option<IotaAddress>,
673 serializer: S,
674 ) -> Result<S::Ok, S::Error>
675 where
676 S: serde::ser::Serializer,
677 {
678 serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
679 }
680
681 pub fn optional_address_from_hex<'de, D>(
683 deserializer: D,
684 ) -> Result<Option<IotaAddress>, D::Error>
685 where
686 D: serde::de::Deserializer<'de>,
687 {
688 let s = String::deserialize(deserializer)?;
689 let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
690 Ok(Some(value))
691 }
692
693 pub fn to_inner(self) -> [u8; IOTA_ADDRESS_LENGTH] {
695 self.0
696 }
697
698 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, IotaError> {
700 <[u8; IOTA_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
701 .map_err(|_| IotaError::InvalidAddress)
702 .map(IotaAddress)
703 }
704
705 pub fn try_from_padded(inputs: &ZkLoginInputs) -> IotaResult<Self> {
710 Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
711 }
712
713 pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> IotaResult<Self> {
715 let mut hasher = DefaultHash::default();
716 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
717 let iss_bytes = inputs.get_iss().as_bytes();
718 hasher.update([iss_bytes.len() as u8]);
719 hasher.update(iss_bytes);
720 hasher.update(inputs.get_address_seed().unpadded());
721 Ok(IotaAddress(hasher.finalize().digest))
722 }
723}
724
725impl From<ObjectID> for IotaAddress {
726 fn from(object_id: ObjectID) -> IotaAddress {
727 Self(object_id.into_bytes())
728 }
729}
730
731impl From<AccountAddress> for IotaAddress {
732 fn from(address: AccountAddress) -> IotaAddress {
733 Self(address.into_bytes())
734 }
735}
736
737impl TryFrom<&[u8]> for IotaAddress {
738 type Error = IotaError;
739
740 fn try_from(bytes: &[u8]) -> Result<Self, IotaError> {
742 Self::from_bytes(bytes)
743 }
744}
745
746impl TryFrom<Vec<u8>> for IotaAddress {
747 type Error = IotaError;
748
749 fn try_from(bytes: Vec<u8>) -> Result<Self, IotaError> {
751 Self::from_bytes(bytes)
752 }
753}
754
755impl AsRef<[u8]> for IotaAddress {
756 fn as_ref(&self) -> &[u8] {
757 &self.0[..]
758 }
759}
760
761impl FromStr for IotaAddress {
762 type Err = anyhow::Error;
763 fn from_str(s: &str) -> Result<Self, Self::Err> {
764 decode_bytes_hex(s).map_err(|e| anyhow!(e))
765 }
766}
767
768impl<T: IotaPublicKey> From<&T> for IotaAddress {
769 fn from(pk: &T) -> Self {
770 let mut hasher = DefaultHash::default();
771 T::SIGNATURE_SCHEME.update_hasher_with_flag(&mut hasher);
772 hasher.update(pk);
773 let g_arr = hasher.finalize();
774 IotaAddress(g_arr.digest)
775 }
776}
777
778impl From<&PublicKey> for IotaAddress {
779 fn from(pk: &PublicKey) -> Self {
780 let mut hasher = DefaultHash::default();
781 pk.scheme().update_hasher_with_flag(&mut hasher);
782 hasher.update(pk);
783 let g_arr = hasher.finalize();
784 IotaAddress(g_arr.digest)
785 }
786}
787
788impl From<&MultiSigPublicKey> for IotaAddress {
789 fn from(multisig_pk: &MultiSigPublicKey) -> Self {
798 let mut hasher = DefaultHash::default();
799 hasher.update([SignatureScheme::MultiSig.flag()]);
800 hasher.update(multisig_pk.threshold().to_le_bytes());
801 multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
802 pk.scheme().update_hasher_with_flag(&mut hasher);
803 hasher.update(pk.as_ref());
804 hasher.update(w.to_le_bytes());
805 });
806 IotaAddress(hasher.finalize().digest)
807 }
808}
809
810impl TryFrom<&ZkLoginAuthenticator> for IotaAddress {
814 type Error = IotaError;
815 fn try_from(authenticator: &ZkLoginAuthenticator) -> IotaResult<Self> {
816 IotaAddress::try_from_unpadded(&authenticator.inputs)
817 }
818}
819
820impl TryFrom<&GenericSignature> for IotaAddress {
821 type Error = IotaError;
822 fn try_from(sig: &GenericSignature) -> IotaResult<Self> {
825 match sig {
826 GenericSignature::Signature(sig) => {
827 let scheme = sig.scheme();
828 let pub_key_bytes = sig.public_key_bytes();
829 let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
830 IotaError::InvalidSignature {
831 error: "Cannot parse pubkey".to_string(),
832 }
833 })?;
834 Ok(IotaAddress::from(&pub_key))
835 }
836 GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
837 GenericSignature::ZkLoginAuthenticator(zklogin) => {
838 IotaAddress::try_from_unpadded(&zklogin.inputs)
839 }
840 GenericSignature::PasskeyAuthenticator(s) => Ok(IotaAddress::from(&s.get_pk()?)),
841 }
842 }
843}
844
845impl fmt::Display for IotaAddress {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 write!(f, "0x{}", Hex::encode(self.0))
848 }
849}
850
851impl fmt::Debug for IotaAddress {
852 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
853 write!(f, "0x{}", Hex::encode(self.0))
854 }
855}
856
857pub fn dbg_addr(name: u8) -> IotaAddress {
859 let addr = [name; IOTA_ADDRESS_LENGTH];
860 IotaAddress(addr)
861}
862
863#[derive(
864 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
865)]
866pub struct ExecutionDigests {
867 pub transaction: TransactionDigest,
868 pub effects: TransactionEffectsDigest,
869}
870
871impl ExecutionDigests {
872 pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
873 Self {
874 transaction,
875 effects,
876 }
877 }
878
879 pub fn random() -> Self {
880 Self {
881 transaction: TransactionDigest::random(),
882 effects: TransactionEffectsDigest::random(),
883 }
884 }
885}
886
887#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
888pub struct ExecutionData {
889 pub transaction: Transaction,
890 pub effects: TransactionEffects,
891}
892
893impl ExecutionData {
894 pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
895 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
896 Self {
897 transaction,
898 effects,
899 }
900 }
901
902 pub fn digests(&self) -> ExecutionDigests {
903 self.effects.execution_digests()
904 }
905}
906
907#[derive(Clone, Eq, PartialEq, Debug)]
908pub struct VerifiedExecutionData {
909 pub transaction: VerifiedTransaction,
910 pub effects: TransactionEffects,
911}
912
913impl VerifiedExecutionData {
914 pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
915 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
916 Self {
917 transaction,
918 effects,
919 }
920 }
921
922 pub fn new_unchecked(data: ExecutionData) -> Self {
923 Self {
924 transaction: VerifiedTransaction::new_unchecked(data.transaction),
925 effects: data.effects,
926 }
927 }
928
929 pub fn into_inner(self) -> ExecutionData {
930 ExecutionData {
931 transaction: self.transaction.into_inner(),
932 effects: self.effects,
933 }
934 }
935
936 pub fn digests(&self) -> ExecutionDigests {
937 self.effects.execution_digests()
938 }
939}
940
941pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
942pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
943pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
944 &MOVE_STDLIB_ADDRESS,
945 STD_OPTION_MODULE_NAME,
946 STD_OPTION_STRUCT_NAME,
947);
948
949pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
950pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
951pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
952 &MOVE_STDLIB_ADDRESS,
953 STD_ASCII_MODULE_NAME,
954 STD_ASCII_STRUCT_NAME,
955);
956
957pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
958pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
959pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
960 &MOVE_STDLIB_ADDRESS,
961 STD_UTF8_MODULE_NAME,
962 STD_UTF8_STRUCT_NAME,
963);
964
965pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
966pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
967
968pub fn move_ascii_str_layout() -> A::MoveStructLayout {
969 A::MoveStructLayout {
970 type_: StructTag {
971 address: MOVE_STDLIB_ADDRESS,
972 module: STD_ASCII_MODULE_NAME.to_owned(),
973 name: STD_ASCII_STRUCT_NAME.to_owned(),
974 type_params: vec![],
975 },
976 fields: vec![A::MoveFieldLayout::new(
977 ident_str!("bytes").into(),
978 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
979 )],
980 }
981}
982
983pub fn move_utf8_str_layout() -> A::MoveStructLayout {
984 A::MoveStructLayout {
985 type_: StructTag {
986 address: MOVE_STDLIB_ADDRESS,
987 module: STD_UTF8_MODULE_NAME.to_owned(),
988 name: STD_UTF8_STRUCT_NAME.to_owned(),
989 type_params: vec![],
990 },
991 fields: vec![A::MoveFieldLayout::new(
992 ident_str!("bytes").into(),
993 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
994 )],
995 }
996}
997
998#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
999pub struct TxContext {
1000 sender: AccountAddress,
1002 digest: Vec<u8>,
1004 epoch: EpochId,
1006 epoch_timestamp_ms: CheckpointTimestamp,
1008 ids_created: u64,
1011}
1012
1013#[derive(PartialEq, Eq, Clone, Copy)]
1014pub enum TxContextKind {
1015 None,
1017 Mutable,
1019 Immutable,
1021}
1022
1023impl TxContext {
1024 pub fn new(sender: &IotaAddress, digest: &TransactionDigest, epoch_data: &EpochData) -> Self {
1025 Self::new_from_components(
1026 sender,
1027 digest,
1028 &epoch_data.epoch_id(),
1029 epoch_data.epoch_start_timestamp(),
1030 )
1031 }
1032
1033 pub fn new_from_components(
1034 sender: &IotaAddress,
1035 digest: &TransactionDigest,
1036 epoch_id: &EpochId,
1037 epoch_timestamp_ms: u64,
1038 ) -> Self {
1039 Self {
1040 sender: AccountAddress::new(sender.0),
1041 digest: digest.into_inner().to_vec(),
1042 epoch: *epoch_id,
1043 epoch_timestamp_ms,
1044 ids_created: 0,
1045 }
1046 }
1047
1048 pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1051 use SignatureToken as S;
1052 let (kind, s) = match s {
1053 S::MutableReference(s) => (TxContextKind::Mutable, s),
1054 S::Reference(s) => (TxContextKind::Immutable, s),
1055 _ => return TxContextKind::None,
1056 };
1057
1058 let S::Datatype(idx) = &**s else {
1059 return TxContextKind::None;
1060 };
1061
1062 let (module_addr, module_name, struct_name) = resolve_struct(view, *idx);
1063 let is_tx_context_type = module_name == TX_CONTEXT_MODULE_NAME
1064 && module_addr == &IOTA_FRAMEWORK_ADDRESS
1065 && struct_name == TX_CONTEXT_STRUCT_NAME;
1066
1067 if is_tx_context_type {
1068 kind
1069 } else {
1070 TxContextKind::None
1071 }
1072 }
1073
1074 pub fn epoch(&self) -> EpochId {
1075 self.epoch
1076 }
1077
1078 pub fn fresh_id(&mut self) -> ObjectID {
1081 let id = ObjectID::derive_id(self.digest(), self.ids_created);
1082
1083 self.ids_created += 1;
1084 id
1085 }
1086
1087 pub fn digest(&self) -> TransactionDigest {
1089 TransactionDigest::new(self.digest.clone().try_into().unwrap())
1090 }
1091
1092 pub fn sender(&self) -> IotaAddress {
1093 IotaAddress::from(ObjectID(self.sender))
1094 }
1095
1096 pub fn to_vec(&self) -> Vec<u8> {
1097 bcs::to_bytes(&self).unwrap()
1098 }
1099
1100 pub fn update_state(&mut self, other: TxContext) -> Result<(), ExecutionError> {
1105 if self.sender != other.sender
1106 || self.digest != other.digest
1107 || other.ids_created < self.ids_created
1108 {
1109 return Err(ExecutionError::new_with_source(
1110 ExecutionErrorKind::InvariantViolation,
1111 "Immutable fields for TxContext changed",
1112 ));
1113 }
1114 self.ids_created = other.ids_created;
1115 Ok(())
1116 }
1117
1118 pub fn random_for_testing_only() -> Self {
1120 Self::new(
1121 &IotaAddress::random_for_testing_only(),
1122 &TransactionDigest::random(),
1123 &EpochData::new_test(),
1124 )
1125 }
1126
1127 pub fn with_sender_for_testing_only(sender: &IotaAddress) -> Self {
1129 Self::new(sender, &TransactionDigest::random(), &EpochData::new_test())
1130 }
1131}
1132
1133impl SequenceNumber {
1135 pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1136 pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1137 pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1138 pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1139 pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1140 SequenceNumber(SequenceNumber::MAX.value() + 3);
1141
1142 pub const fn new() -> Self {
1143 SequenceNumber(0)
1144 }
1145
1146 pub const fn value(&self) -> u64 {
1147 self.0
1148 }
1149
1150 pub const fn from_u64(u: u64) -> Self {
1151 SequenceNumber(u)
1152 }
1153
1154 pub fn increment(&mut self) {
1155 assert_ne!(self.0, u64::MAX);
1156 self.0 += 1;
1157 }
1158
1159 pub fn increment_to(&mut self, next: SequenceNumber) {
1160 debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1161 *self = next;
1162 }
1163
1164 pub fn decrement(&mut self) {
1165 assert_ne!(self.0, 0);
1166 self.0 -= 1;
1167 }
1168
1169 pub fn decrement_to(&mut self, prev: SequenceNumber) {
1170 debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1171 *self = prev;
1172 }
1173
1174 #[must_use]
1177 pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1178 let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1179
1180 assert_ne!(max_input.0, u64::MAX);
1185
1186 SequenceNumber(max_input.0 + 1)
1187 }
1188
1189 pub fn is_cancelled(&self) -> bool {
1190 self == &SequenceNumber::CANCELLED_READ
1191 || self == &SequenceNumber::CONGESTED
1192 || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1193 }
1194
1195 pub fn is_valid(&self) -> bool {
1196 self < &SequenceNumber::MAX
1197 }
1198}
1199
1200impl From<SequenceNumber> for u64 {
1201 fn from(val: SequenceNumber) -> Self {
1202 val.0
1203 }
1204}
1205
1206impl From<u64> for SequenceNumber {
1207 fn from(value: u64) -> Self {
1208 SequenceNumber(value)
1209 }
1210}
1211
1212impl From<SequenceNumber> for usize {
1213 fn from(value: SequenceNumber) -> Self {
1214 value.0 as usize
1215 }
1216}
1217
1218impl ObjectID {
1219 pub const LENGTH: usize = AccountAddress::LENGTH;
1221 pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1223 pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1224 pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1226 Self(AccountAddress::new(obj_id))
1227 }
1228
1229 pub const fn from_address(addr: AccountAddress) -> Self {
1231 Self(addr)
1232 }
1233
1234 pub fn random() -> Self {
1236 Self::from(AccountAddress::random())
1237 }
1238
1239 pub fn random_from_rng<R>(rng: &mut R) -> Self
1241 where
1242 R: AllowedRng,
1243 {
1244 let buf: [u8; Self::LENGTH] = rng.gen();
1245 ObjectID::new(buf)
1246 }
1247
1248 pub fn to_vec(&self) -> Vec<u8> {
1250 self.0.to_vec()
1251 }
1252
1253 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1255 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1256 .map_err(|_| ObjectIDParseError::TryFromSlice)
1257 .map(ObjectID::new)
1258 }
1259
1260 pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1262 self.0.into_bytes()
1263 }
1264
1265 pub const fn from_single_byte(byte: u8) -> ObjectID {
1267 let mut bytes = [0u8; Self::LENGTH];
1268 bytes[Self::LENGTH - 1] = byte;
1269 ObjectID::new(bytes)
1270 }
1271
1272 pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1275 if !literal.starts_with("0x") {
1276 return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1277 }
1278
1279 let hex_len = literal.len() - 2;
1280
1281 if hex_len < Self::LENGTH * 2 {
1283 let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1284 for _ in 0..Self::LENGTH * 2 - hex_len {
1285 hex_str.push('0');
1286 }
1287 hex_str.push_str(&literal[2..]);
1288 Self::from_str(&hex_str)
1289 } else {
1290 Self::from_str(&literal[2..])
1291 }
1292 }
1293
1294 pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1297 let mut hasher = DefaultHash::default();
1298 hasher.update([HashingIntentScope::RegularObjectId as u8]);
1299 hasher.update(digest);
1300 hasher.update(creation_num.to_le_bytes());
1301 let hash = hasher.finalize();
1302
1303 ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1307 }
1308
1309 pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1312 let mut curr_vec = self.to_vec();
1313 let mut step_copy = step;
1314
1315 let mut carry = 0;
1316 for idx in (0..Self::LENGTH).rev() {
1317 if step_copy == 0 {
1318 break;
1320 }
1321 let g = (step_copy % 0x100) as u16;
1323 step_copy >>= 8;
1325 let mut val = curr_vec[idx] as u16;
1326 (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1327 curr_vec[idx] = val as u8;
1328 }
1329
1330 if carry > 0 {
1331 return Err(anyhow!("Increment will cause overflow"));
1332 }
1333 ObjectID::try_from(curr_vec).map_err(|w| w.into())
1334 }
1335
1336 pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1339 let mut prev_val = self.to_vec();
1340 let mx = [0xFF; Self::LENGTH];
1341
1342 if prev_val == mx {
1343 return Err(anyhow!("Increment will cause overflow"));
1344 }
1345
1346 for idx in (0..Self::LENGTH).rev() {
1348 if prev_val[idx] == 0xFF {
1349 prev_val[idx] = 0;
1350 } else {
1351 prev_val[idx] += 1;
1352 break;
1353 };
1354 }
1355 ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1356 }
1357
1358 pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1360 let mut ret = Vec::new();
1361 let mut prev = offset;
1362 for o in 0..count {
1363 if o != 0 {
1364 prev = prev.next_increment()?;
1365 }
1366 ret.push(prev);
1367 }
1368 Ok(ret)
1369 }
1370
1371 pub fn to_hex_uncompressed(&self) -> String {
1375 format!("{self}")
1376 }
1377
1378 pub fn is_clock(&self) -> bool {
1379 *self == IOTA_CLOCK_OBJECT_ID
1380 }
1381}
1382
1383impl From<IotaAddress> for ObjectID {
1384 fn from(address: IotaAddress) -> ObjectID {
1385 let tmp: AccountAddress = address.into();
1386 tmp.into()
1387 }
1388}
1389
1390impl From<AccountAddress> for ObjectID {
1391 fn from(address: AccountAddress) -> Self {
1392 Self(address)
1393 }
1394}
1395
1396impl fmt::Display for ObjectID {
1397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1398 write!(f, "0x{}", Hex::encode(self.0))
1399 }
1400}
1401
1402impl fmt::Debug for ObjectID {
1403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1404 write!(f, "0x{}", Hex::encode(self.0))
1405 }
1406}
1407
1408impl AsRef<[u8]> for ObjectID {
1409 fn as_ref(&self) -> &[u8] {
1410 self.0.as_slice()
1411 }
1412}
1413
1414impl TryFrom<&[u8]> for ObjectID {
1415 type Error = ObjectIDParseError;
1416
1417 fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1419 Self::from_bytes(bytes)
1420 }
1421}
1422
1423impl TryFrom<Vec<u8>> for ObjectID {
1424 type Error = ObjectIDParseError;
1425
1426 fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1428 Self::from_bytes(bytes)
1429 }
1430}
1431
1432impl FromStr for ObjectID {
1433 type Err = ObjectIDParseError;
1434
1435 fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1438 decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1439 }
1440}
1441
1442impl std::ops::Deref for ObjectID {
1443 type Target = AccountAddress;
1444
1445 fn deref(&self) -> &Self::Target {
1446 &self.0
1447 }
1448}
1449
1450pub fn dbg_object_id(name: u8) -> ObjectID {
1452 ObjectID::new([name; ObjectID::LENGTH])
1453}
1454
1455#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1456pub enum ObjectIDParseError {
1457 #[error("ObjectID hex literal must start with 0x")]
1458 HexLiteralPrefixMissing,
1459
1460 #[error("Could not convert from bytes slice")]
1461 TryFromSlice,
1462}
1463
1464impl From<ObjectID> for AccountAddress {
1465 fn from(obj_id: ObjectID) -> Self {
1466 obj_id.0
1467 }
1468}
1469
1470impl From<IotaAddress> for AccountAddress {
1471 fn from(address: IotaAddress) -> Self {
1472 Self::new(address.0)
1473 }
1474}
1475
1476impl fmt::Display for MoveObjectType {
1477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1478 let s: StructTag = self.clone().into();
1479 write!(
1480 f,
1481 "{}",
1482 to_iota_struct_tag_string(&s).map_err(fmt::Error::custom)?
1483 )
1484 }
1485}
1486
1487impl fmt::Display for ObjectType {
1488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1489 match self {
1490 ObjectType::Package => write!(f, "{}", PACKAGE),
1491 ObjectType::Struct(t) => write!(f, "{}", t),
1492 }
1493 }
1494}
1495
1496#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1502#[serde(try_from = "Vec<T>")]
1503pub struct SizeOneVec<T> {
1504 e: T,
1505}
1506
1507impl<T> SizeOneVec<T> {
1508 pub fn new(e: T) -> Self {
1509 Self { e }
1510 }
1511
1512 pub fn element(&self) -> &T {
1513 &self.e
1514 }
1515
1516 pub fn element_mut(&mut self) -> &mut T {
1517 &mut self.e
1518 }
1519
1520 pub fn into_inner(self) -> T {
1521 self.e
1522 }
1523
1524 pub fn iter(&self) -> std::iter::Once<&T> {
1525 std::iter::once(&self.e)
1526 }
1527}
1528
1529impl<T> Serialize for SizeOneVec<T>
1530where
1531 T: Serialize,
1532{
1533 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1534 where
1535 S: Serializer,
1536 {
1537 let mut seq = serializer.serialize_seq(Some(1))?;
1538 seq.serialize_element(&self.e)?;
1539 seq.end()
1540 }
1541}
1542
1543impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1544 type Error = anyhow::Error;
1545
1546 fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1547 if v.len() != 1 {
1548 Err(anyhow!("Expected a vec of size 1"))
1549 } else {
1550 Ok(SizeOneVec {
1551 e: v.pop().unwrap(),
1552 })
1553 }
1554 }
1555}
1556
1557#[test]
1558fn test_size_one_vec_is_transparent() {
1559 let regular = vec![42u8];
1560 let size_one = SizeOneVec::new(42u8);
1561
1562 let regular_ser = bcs::to_bytes(®ular).unwrap();
1564 let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(®ular_ser).unwrap();
1565 assert_eq!(size_one, size_one_deser);
1566
1567 let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1569 let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1570 assert_eq!(regular_deser, vec![43u8]);
1571
1572 let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1574 bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1575
1576 let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1577 bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1578}