1use std::{
7 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
8 fmt::{Debug, Display, Formatter, Write},
9 hash::Hash,
10 iter::{self, once},
11 sync::Arc,
12};
13
14use anyhow::bail;
15use enum_dispatch::enum_dispatch;
16use fastcrypto::{encoding::Base64, hash::HashFunction};
17use iota_protocol_config::ProtocolConfig;
18use iota_sdk_types::crypto::{Intent, IntentMessage, IntentScope};
19use itertools::Either;
20use move_core_types::{
21 ident_str,
22 identifier::{self, Identifier},
23 language_storage::TypeTag,
24};
25use nonempty::{NonEmpty, nonempty};
26use schemars::JsonSchema;
27use serde::{Deserialize, Serialize};
28use strum::IntoStaticStr;
29use tap::Pipe;
30use tracing::{instrument, trace};
31
32use super::{base_types::*, error::*};
33use crate::{
34 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION,
35 IOTA_FRAMEWORK_PACKAGE_ID, IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID,
36 IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
37 authenticator_state::ActiveJwk,
38 committee::{Committee, EpochId, ProtocolVersion},
39 crypto::{
40 AuthoritySignInfo, AuthoritySignInfoTrait, AuthoritySignature,
41 AuthorityStrongQuorumSignInfo, DefaultHash, Ed25519IotaSignature, EmptySignInfo,
42 IotaSignatureInner, RandomnessRound, Signature, Signer, ToFromBytes, default_hash,
43 },
44 digests::{
45 CertificateDigest, ConsensusCommitDigest, SenderSignedDataDigest, ZKLoginInputsDigest,
46 },
47 event::Event,
48 execution::SharedInput,
49 message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
50 messages_checkpoint::CheckpointTimestamp,
51 messages_consensus::{ConsensusCommitPrologueV1, ConsensusDeterminedVersionAssignments},
52 move_authenticator::MoveAuthenticator,
53 object::{MoveObject, Object, Owner},
54 programmable_transaction_builder::ProgrammableTransactionBuilder,
55 signature::{GenericSignature, VerifyParams},
56 signature_verification::{VerifiedDigestCache, verify_sender_signed_data_message_signatures},
57 type_input::TypeInput,
58};
59
60pub const TEST_ONLY_GAS_UNIT_FOR_TRANSFER: u64 = 10_000;
61pub const TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS: u64 = 50_000;
62pub const TEST_ONLY_GAS_UNIT_FOR_PUBLISH: u64 = 50_000;
63pub const TEST_ONLY_GAS_UNIT_FOR_STAKING: u64 = 50_000;
64pub const TEST_ONLY_GAS_UNIT_FOR_GENERIC: u64 = 50_000;
65pub const TEST_ONLY_GAS_UNIT_FOR_SPLIT_COIN: u64 = 10_000;
66pub const TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE: u64 = 5_000_000;
71
72pub const GAS_PRICE_FOR_SYSTEM_TX: u64 = 1;
73
74pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = 1000;
75
76const BLOCKED_MOVE_FUNCTIONS: [(ObjectID, &str, &str); 0] = [];
77
78#[cfg(test)]
79#[path = "unit_tests/messages_tests.rs"]
80mod messages_tests;
81
82#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
83pub enum CallArg {
84 Pure(Vec<u8>),
86 Object(ObjectArg),
88}
89
90impl CallArg {
91 pub const IOTA_SYSTEM_MUT: Self = Self::Object(ObjectArg::IOTA_SYSTEM_MUT);
92 pub const CLOCK_IMM: Self = Self::Object(ObjectArg::SharedObject {
93 id: IOTA_CLOCK_OBJECT_ID,
94 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
95 mutable: false,
96 });
97 pub const CLOCK_MUT: Self = Self::Object(ObjectArg::SharedObject {
98 id: IOTA_CLOCK_OBJECT_ID,
99 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
100 mutable: true,
101 });
102}
103
104#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, JsonSchema)]
105pub enum ObjectArg {
106 ImmOrOwnedObject(ObjectRef),
108 SharedObject {
112 id: ObjectID,
113 initial_shared_version: SequenceNumber,
114 mutable: bool,
115 },
116 Receiving(ObjectRef),
118}
119
120pub fn type_input_validity_check(
121 tag: &TypeInput,
122 config: &ProtocolConfig,
123 starting_count: &mut usize,
124) -> UserInputResult<()> {
125 let mut stack = vec![(tag, 1)];
126 while let Some((tag, depth)) = stack.pop() {
127 *starting_count += 1;
128 fp_ensure!(
129 *starting_count < config.max_type_arguments() as usize,
130 UserInputError::SizeLimitExceeded {
131 limit: "maximum type arguments in a call transaction".to_string(),
132 value: config.max_type_arguments().to_string()
133 }
134 );
135 fp_ensure!(
136 depth < config.max_type_argument_depth(),
137 UserInputError::SizeLimitExceeded {
138 limit: "maximum type argument depth in a call transaction".to_string(),
139 value: config.max_type_argument_depth().to_string()
140 }
141 );
142 match tag {
143 TypeInput::Bool
144 | TypeInput::U8
145 | TypeInput::U64
146 | TypeInput::U128
147 | TypeInput::Address
148 | TypeInput::Signer
149 | TypeInput::U16
150 | TypeInput::U32
151 | TypeInput::U256 => (),
152 TypeInput::Vector(t) => {
153 stack.push((t, depth + 1));
154 }
155 TypeInput::Struct(s) => {
156 let next_depth = depth + 1;
157 if config.validate_identifier_inputs() {
158 fp_ensure!(
159 identifier::is_valid(&s.module),
160 UserInputError::InvalidIdentifier {
161 error: s.module.clone()
162 }
163 );
164 fp_ensure!(
165 identifier::is_valid(&s.name),
166 UserInputError::InvalidIdentifier {
167 error: s.name.clone()
168 }
169 );
170 }
171 stack.extend(s.type_params.iter().map(|t| (t, next_depth)));
172 }
173 }
174 }
175 Ok(())
176}
177
178#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
180pub struct ChangeEpoch {
181 pub epoch: EpochId,
183 pub protocol_version: ProtocolVersion,
185 pub storage_charge: u64,
187 pub computation_charge: u64,
189 pub storage_rebate: u64,
191 pub non_refundable_storage_fee: u64,
193 pub epoch_start_timestamp_ms: u64,
195 pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
202}
203
204#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
208pub struct ChangeEpochV2 {
209 pub epoch: EpochId,
211 pub protocol_version: ProtocolVersion,
213 pub storage_charge: u64,
215 pub computation_charge: u64,
217 pub computation_charge_burned: u64,
219 pub storage_rebate: u64,
221 pub non_refundable_storage_fee: u64,
223 pub epoch_start_timestamp_ms: u64,
225 pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
232}
233
234#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
238pub struct ChangeEpochV3 {
239 pub epoch: EpochId,
241 pub protocol_version: ProtocolVersion,
243 pub storage_charge: u64,
245 pub computation_charge: u64,
247 pub computation_charge_burned: u64,
249 pub storage_rebate: u64,
251 pub non_refundable_storage_fee: u64,
255 pub epoch_start_timestamp_ms: u64,
257 pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
263 pub eligible_active_validators: Vec<u64>,
266}
267#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
271pub struct ChangeEpochV4 {
272 pub epoch: EpochId,
274 pub protocol_version: ProtocolVersion,
276 pub storage_charge: u64,
278 pub computation_charge: u64,
280 pub computation_charge_burned: u64,
282 pub storage_rebate: u64,
284 pub non_refundable_storage_fee: u64,
286 pub epoch_start_timestamp_ms: u64,
288 pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
295 pub eligible_active_validators: Vec<u64>,
298 pub scores: Vec<u64>,
301 pub adjust_rewards_by_score: bool,
303}
304
305#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
306pub struct GenesisTransaction {
307 pub objects: Vec<GenesisObject>,
308 pub events: Vec<Event>,
309}
310
311#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
312pub enum GenesisObject {
313 RawObject {
314 data: crate::object::Data,
315 owner: crate::object::Owner,
316 },
317}
318
319impl GenesisObject {
320 pub fn id(&self) -> ObjectID {
321 match self {
322 GenesisObject::RawObject { data, .. } => data.id(),
323 }
324 }
325}
326
327#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
328pub struct AuthenticatorStateExpire {
329 pub min_epoch: u64,
331 pub authenticator_obj_initial_shared_version: SequenceNumber,
333}
334
335impl AuthenticatorStateExpire {
336 pub fn authenticator_obj_initial_shared_version(&self) -> SequenceNumber {
337 self.authenticator_obj_initial_shared_version
338 }
339}
340
341#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
342pub struct AuthenticatorStateUpdateV1 {
343 pub epoch: u64,
345 pub round: u64,
347 pub new_active_jwks: Vec<ActiveJwk>,
349 pub authenticator_obj_initial_shared_version: SequenceNumber,
351 }
354
355impl AuthenticatorStateUpdateV1 {
356 pub fn authenticator_obj_initial_shared_version(&self) -> SequenceNumber {
357 self.authenticator_obj_initial_shared_version
358 }
359}
360
361#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
362pub struct RandomnessStateUpdate {
363 pub epoch: u64,
365 pub randomness_round: RandomnessRound,
367 pub random_bytes: Vec<u8>,
369 pub randomness_obj_initial_shared_version: SequenceNumber,
371 }
374
375impl RandomnessStateUpdate {
376 pub fn randomness_obj_initial_shared_version(&self) -> SequenceNumber {
377 self.randomness_obj_initial_shared_version
378 }
379}
380
381#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)]
382pub enum TransactionKind {
383 ProgrammableTransaction(ProgrammableTransaction),
386 Genesis(GenesisTransaction),
387 ConsensusCommitPrologueV1(ConsensusCommitPrologueV1),
388 AuthenticatorStateUpdateV1(AuthenticatorStateUpdateV1),
389
390 EndOfEpochTransaction(Vec<EndOfEpochTransactionKind>),
393
394 RandomnessStateUpdate(RandomnessStateUpdate),
395 }
401
402#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)]
404pub enum EndOfEpochTransactionKind {
405 ChangeEpoch(ChangeEpoch),
406 ChangeEpochV2(ChangeEpochV2),
407 ChangeEpochV3(ChangeEpochV3),
408 ChangeEpochV4(ChangeEpochV4),
409 AuthenticatorStateCreate,
414 AuthenticatorStateExpire(AuthenticatorStateExpire),
415}
416
417impl EndOfEpochTransactionKind {
418 pub fn new_change_epoch(
419 next_epoch: EpochId,
420 protocol_version: ProtocolVersion,
421 storage_charge: u64,
422 computation_charge: u64,
423 storage_rebate: u64,
424 non_refundable_storage_fee: u64,
425 epoch_start_timestamp_ms: u64,
426 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
427 ) -> Self {
428 Self::ChangeEpoch(ChangeEpoch {
429 epoch: next_epoch,
430 protocol_version,
431 storage_charge,
432 computation_charge,
433 storage_rebate,
434 non_refundable_storage_fee,
435 epoch_start_timestamp_ms,
436 system_packages,
437 })
438 }
439
440 pub fn new_change_epoch_v2(
441 next_epoch: EpochId,
442 protocol_version: ProtocolVersion,
443 storage_charge: u64,
444 computation_charge: u64,
445 computation_charge_burned: u64,
446 storage_rebate: u64,
447 non_refundable_storage_fee: u64,
448 epoch_start_timestamp_ms: u64,
449 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
450 ) -> Self {
451 Self::ChangeEpochV2(ChangeEpochV2 {
452 epoch: next_epoch,
453 protocol_version,
454 storage_charge,
455 computation_charge,
456 computation_charge_burned,
457 storage_rebate,
458 non_refundable_storage_fee,
459 epoch_start_timestamp_ms,
460 system_packages,
461 })
462 }
463
464 pub fn new_change_epoch_v3(
465 next_epoch: EpochId,
466 protocol_version: ProtocolVersion,
467 storage_charge: u64,
468 computation_charge: u64,
469 computation_charge_burned: u64,
470 storage_rebate: u64,
471 non_refundable_storage_fee: u64,
472 epoch_start_timestamp_ms: u64,
473 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
474 eligible_active_validators: Vec<u64>,
475 ) -> Self {
476 Self::ChangeEpochV3(ChangeEpochV3 {
477 epoch: next_epoch,
478 protocol_version,
479 storage_charge,
480 computation_charge,
481 computation_charge_burned,
482 storage_rebate,
483 non_refundable_storage_fee,
484 epoch_start_timestamp_ms,
485 system_packages,
486 eligible_active_validators,
487 })
488 }
489
490 pub fn new_change_epoch_v4(
491 next_epoch: EpochId,
492 protocol_version: ProtocolVersion,
493 storage_charge: u64,
494 computation_charge: u64,
495 computation_charge_burned: u64,
496 storage_rebate: u64,
497 non_refundable_storage_fee: u64,
498 epoch_start_timestamp_ms: u64,
499 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
500 eligible_active_validators: Vec<u64>,
501 scores: Vec<u64>,
502 adjust_rewards_by_score: bool,
503 ) -> Self {
504 Self::ChangeEpochV4(ChangeEpochV4 {
505 epoch: next_epoch,
506 protocol_version,
507 storage_charge,
508 computation_charge,
509 computation_charge_burned,
510 storage_rebate,
511 non_refundable_storage_fee,
512 epoch_start_timestamp_ms,
513 system_packages,
514 eligible_active_validators,
515 scores,
516 adjust_rewards_by_score,
517 })
518 }
519
520 pub fn new_authenticator_state_expire(
521 min_epoch: u64,
522 authenticator_obj_initial_shared_version: SequenceNumber,
523 ) -> Self {
524 Self::AuthenticatorStateExpire(AuthenticatorStateExpire {
525 min_epoch,
526 authenticator_obj_initial_shared_version,
527 })
528 }
529
530 pub fn new_authenticator_state_create() -> Self {
531 Self::AuthenticatorStateCreate
532 }
533
534 fn input_objects(&self) -> Vec<InputObjectKind> {
535 match self {
536 Self::ChangeEpoch(_) => {
537 vec![InputObjectKind::SharedMoveObject {
538 id: IOTA_SYSTEM_STATE_OBJECT_ID,
539 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
540 mutable: true,
541 }]
542 }
543 Self::ChangeEpochV2(_) => {
544 vec![InputObjectKind::SharedMoveObject {
545 id: IOTA_SYSTEM_STATE_OBJECT_ID,
546 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
547 mutable: true,
548 }]
549 }
550 Self::ChangeEpochV3(_) => {
551 vec![InputObjectKind::SharedMoveObject {
552 id: IOTA_SYSTEM_STATE_OBJECT_ID,
553 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
554 mutable: true,
555 }]
556 }
557 Self::ChangeEpochV4(_) => {
558 vec![InputObjectKind::SharedMoveObject {
559 id: IOTA_SYSTEM_STATE_OBJECT_ID,
560 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
561 mutable: true,
562 }]
563 }
564 Self::AuthenticatorStateCreate => vec![],
565 Self::AuthenticatorStateExpire(expire) => {
566 vec![InputObjectKind::SharedMoveObject {
567 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
568 initial_shared_version: expire.authenticator_obj_initial_shared_version(),
569 mutable: true,
570 }]
571 }
572 }
573 }
574
575 fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
576 match self {
577 Self::ChangeEpoch(_) => {
578 Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
579 }
580 Self::ChangeEpochV2(_) => {
581 Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
582 }
583 Self::ChangeEpochV3(_) => {
584 Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
585 }
586 Self::ChangeEpochV4(_) => {
587 Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
588 }
589 Self::AuthenticatorStateExpire(expire) => Either::Left(
590 vec![SharedInputObject {
591 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
592 initial_shared_version: expire.authenticator_obj_initial_shared_version(),
593 mutable: true,
594 }]
595 .into_iter(),
596 ),
597 Self::AuthenticatorStateCreate => Either::Right(iter::empty()),
598 }
599 }
600
601 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
602 match self {
603 Self::ChangeEpoch(_) => {
604 if config.protocol_defined_base_fee() {
605 return Err(UserInputError::Unsupported(
606 "protocol defined base fee not supported".to_string(),
607 ));
608 }
609 if config.select_committee_from_eligible_validators() {
610 return Err(UserInputError::Unsupported(
611 "selecting committee only among validators supporting the protocol version not supported".to_string(),
612 ));
613 }
614 if config.pass_validator_scores_to_advance_epoch() {
615 return Err(UserInputError::Unsupported(
616 "passing of validator scores not supported".to_string(),
617 ));
618 }
619 if config.adjust_rewards_by_score() {
620 return Err(UserInputError::Unsupported(
621 "adjusting rewards by score not supported".to_string(),
622 ));
623 }
624 }
625 Self::ChangeEpochV2(_) => {
626 if !config.protocol_defined_base_fee() {
627 return Err(UserInputError::Unsupported(
628 "protocol defined base fee required".to_string(),
629 ));
630 }
631 if config.select_committee_from_eligible_validators() {
632 return Err(UserInputError::Unsupported(
633 "selecting committee only among validators supporting the protocol version not supported".to_string(),
634 ));
635 }
636 if config.pass_validator_scores_to_advance_epoch() {
637 return Err(UserInputError::Unsupported(
638 "passing of validator scores not supported".to_string(),
639 ));
640 }
641 if config.adjust_rewards_by_score() {
642 return Err(UserInputError::Unsupported(
643 "adjusting rewards by score not supported".to_string(),
644 ));
645 }
646 }
647 Self::ChangeEpochV3(_) => {
648 if !config.protocol_defined_base_fee() {
649 return Err(UserInputError::Unsupported(
650 "protocol defined base fee required".to_string(),
651 ));
652 }
653 if !config.select_committee_from_eligible_validators() {
654 return Err(UserInputError::Unsupported(
655 "selecting committee only among validators supporting the protocol version required".to_string(),
656 ));
657 }
658 if config.pass_validator_scores_to_advance_epoch() {
659 return Err(UserInputError::Unsupported(
660 "passing of validator scores not supported".to_string(),
661 ));
662 }
663 if config.adjust_rewards_by_score() {
664 return Err(UserInputError::Unsupported(
665 "adjusting rewards by score not supported".to_string(),
666 ));
667 }
668 }
669 Self::ChangeEpochV4(_) => {
670 if !config.protocol_defined_base_fee() {
671 return Err(UserInputError::Unsupported(
672 "protocol defined base fee required".to_string(),
673 ));
674 }
675 if !config.select_committee_from_eligible_validators() {
676 return Err(UserInputError::Unsupported(
677 "selecting committee only among validators supporting the protocol version required".to_string(),
678 ));
679 }
680 if !config.pass_validator_scores_to_advance_epoch() {
681 return Err(UserInputError::Unsupported(
682 "passing of validator scores required".to_string(),
683 ));
684 }
685 }
686 Self::AuthenticatorStateCreate | Self::AuthenticatorStateExpire(_) => {
687 if !config.enable_jwk_consensus_updates() {
688 return Err(UserInputError::Unsupported(
689 "authenticator state updates not enabled".to_string(),
690 ));
691 }
692 }
693 }
694 Ok(())
695 }
696}
697
698impl CallArg {
699 pub fn input_objects(&self) -> Vec<InputObjectKind> {
700 match self {
701 CallArg::Pure(_) => vec![],
702 CallArg::Object(ObjectArg::ImmOrOwnedObject(object_ref)) => {
703 vec![InputObjectKind::ImmOrOwnedMoveObject(*object_ref)]
704 }
705 CallArg::Object(ObjectArg::SharedObject {
706 id,
707 initial_shared_version,
708 mutable,
709 }) => {
710 let id = *id;
711 let initial_shared_version = *initial_shared_version;
712 let mutable = *mutable;
713 vec![InputObjectKind::SharedMoveObject {
714 id,
715 initial_shared_version,
716 mutable,
717 }]
718 }
719 CallArg::Object(ObjectArg::Receiving(_)) => vec![],
721 }
722 }
723
724 pub fn receiving_objects(&self) -> Vec<ObjectRef> {
725 match self {
726 CallArg::Pure(_) => vec![],
727 CallArg::Object(o) => match o {
728 ObjectArg::ImmOrOwnedObject(_) => vec![],
729 ObjectArg::SharedObject { .. } => vec![],
730 ObjectArg::Receiving(obj_ref) => vec![*obj_ref],
731 },
732 }
733 }
734
735 pub fn shared_objects(&self) -> Vec<SharedInputObject> {
736 match self {
737 CallArg::Pure(_) => vec![],
738 CallArg::Object(o) => match o {
739 ObjectArg::ImmOrOwnedObject(_) | ObjectArg::Receiving(_) => vec![],
740 ObjectArg::SharedObject {
741 id,
742 initial_shared_version,
743 mutable,
744 } => vec![SharedInputObject {
745 id: *id,
746 initial_shared_version: *initial_shared_version,
747 mutable: *mutable,
748 }],
749 },
750 }
751 }
752
753 pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
754 match self {
755 CallArg::Pure(p) => {
756 fp_ensure!(
757 p.len() < config.max_pure_argument_size() as usize,
758 UserInputError::SizeLimitExceeded {
759 limit: "maximum pure argument size".to_string(),
760 value: config.max_pure_argument_size().to_string()
761 }
762 );
763 }
764 CallArg::Object(o) => match o {
765 ObjectArg::Receiving(_)
766 | ObjectArg::ImmOrOwnedObject(_)
767 | ObjectArg::SharedObject { .. } => (),
768 },
769 }
770 Ok(())
771 }
772}
773
774impl From<bool> for CallArg {
775 fn from(b: bool) -> Self {
776 CallArg::Pure(bcs::to_bytes(&b).unwrap())
778 }
779}
780
781impl From<u8> for CallArg {
782 fn from(n: u8) -> Self {
783 CallArg::Pure(bcs::to_bytes(&n).unwrap())
785 }
786}
787
788impl From<u16> for CallArg {
789 fn from(n: u16) -> Self {
790 CallArg::Pure(bcs::to_bytes(&n).unwrap())
792 }
793}
794
795impl From<u32> for CallArg {
796 fn from(n: u32) -> Self {
797 CallArg::Pure(bcs::to_bytes(&n).unwrap())
799 }
800}
801
802impl From<u64> for CallArg {
803 fn from(n: u64) -> Self {
804 CallArg::Pure(bcs::to_bytes(&n).unwrap())
806 }
807}
808
809impl From<u128> for CallArg {
810 fn from(n: u128) -> Self {
811 CallArg::Pure(bcs::to_bytes(&n).unwrap())
813 }
814}
815
816impl From<&Vec<u8>> for CallArg {
817 fn from(v: &Vec<u8>) -> Self {
818 CallArg::Pure(bcs::to_bytes(v).unwrap())
820 }
821}
822
823impl From<ObjectRef> for CallArg {
824 fn from(obj: ObjectRef) -> Self {
825 CallArg::Object(ObjectArg::ImmOrOwnedObject(obj))
826 }
827}
828
829impl ObjectArg {
830 pub const IOTA_SYSTEM_MUT: Self = Self::SharedObject {
831 id: IOTA_SYSTEM_STATE_OBJECT_ID,
832 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
833 mutable: true,
834 };
835
836 pub fn id(&self) -> ObjectID {
837 match self {
838 ObjectArg::Receiving((id, _, _))
839 | ObjectArg::ImmOrOwnedObject((id, _, _))
840 | ObjectArg::SharedObject { id, .. } => *id,
841 }
842 }
843}
844
845fn add_type_input_packages(packages: &mut BTreeSet<ObjectID>, type_argument: &TypeInput) {
847 let mut stack = vec![type_argument];
848 while let Some(cur) = stack.pop() {
849 match cur {
850 TypeInput::Bool
851 | TypeInput::U8
852 | TypeInput::U64
853 | TypeInput::U128
854 | TypeInput::Address
855 | TypeInput::Signer
856 | TypeInput::U16
857 | TypeInput::U32
858 | TypeInput::U256 => (),
859 TypeInput::Vector(inner) => stack.push(inner),
860 TypeInput::Struct(struct_tag) => {
861 packages.insert(struct_tag.address.into());
862 stack.extend(struct_tag.type_params.iter())
863 }
864 }
865 }
866}
867
868#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
871pub struct ProgrammableTransaction {
872 pub inputs: Vec<CallArg>,
874 pub commands: Vec<Command>,
877}
878
879#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
881pub enum Command {
882 MoveCall(Box<ProgrammableMoveCall>),
884 TransferObjects(Vec<Argument>, Argument),
889 SplitCoins(Argument, Vec<Argument>),
892 MergeCoins(Argument, Vec<Argument>),
895 Publish(Vec<Vec<u8>>, Vec<ObjectID>),
898 MakeMoveVec(Option<TypeInput>, Vec<Argument>),
902 Upgrade(Vec<Vec<u8>>, Vec<ObjectID>, ObjectID, Argument),
911}
912
913#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
915pub enum Argument {
916 GasCoin,
919 Input(u16),
922 Result(u16),
924 NestedResult(u16, u16),
928}
929
930#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
933pub struct ProgrammableMoveCall {
934 pub package: ObjectID,
936 pub module: String,
938 pub function: String,
940 pub type_arguments: Vec<TypeInput>,
942 pub arguments: Vec<Argument>,
944}
945
946impl ProgrammableMoveCall {
947 fn input_objects(&self) -> Vec<InputObjectKind> {
948 let ProgrammableMoveCall {
949 package,
950 type_arguments,
951 ..
952 } = self;
953 let mut packages = BTreeSet::from([*package]);
954 for type_argument in type_arguments {
955 add_type_input_packages(&mut packages, type_argument)
956 }
957 packages
958 .into_iter()
959 .map(InputObjectKind::MovePackage)
960 .collect()
961 }
962
963 pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
964 let is_blocked = BLOCKED_MOVE_FUNCTIONS.contains(&(
965 self.package,
966 self.module.as_str(),
967 self.function.as_str(),
968 ));
969 fp_ensure!(!is_blocked, UserInputError::BlockedMoveFunction);
970 let mut type_arguments_count = 0;
971 for tag in &self.type_arguments {
972 type_input_validity_check(tag, config, &mut type_arguments_count)?;
973 }
974 fp_ensure!(
975 self.arguments.len() < config.max_arguments() as usize,
976 UserInputError::SizeLimitExceeded {
977 limit: "maximum arguments in a move call".to_string(),
978 value: config.max_arguments().to_string()
979 }
980 );
981 if config.validate_identifier_inputs() {
982 fp_ensure!(
983 identifier::is_valid(&self.module),
984 UserInputError::InvalidIdentifier {
985 error: self.module.clone()
986 }
987 );
988 fp_ensure!(
989 identifier::is_valid(&self.function),
990 UserInputError::InvalidIdentifier {
991 error: self.module.clone()
992 }
993 );
994 }
995 Ok(())
996 }
997
998 fn is_input_arg_used(&self, arg: u16) -> bool {
999 self.arguments
1000 .iter()
1001 .any(|a| matches!(a, Argument::Input(inp) if *inp == arg))
1002 }
1003}
1004
1005impl Command {
1006 pub fn move_call(
1007 package: ObjectID,
1008 module: Identifier,
1009 function: Identifier,
1010 type_arguments: Vec<TypeTag>,
1011 arguments: Vec<Argument>,
1012 ) -> Self {
1013 let module = module.to_string();
1014 let function = function.to_string();
1015 let type_arguments = type_arguments.into_iter().map(TypeInput::from).collect();
1016 Command::MoveCall(Box::new(ProgrammableMoveCall {
1017 package,
1018 module,
1019 function,
1020 type_arguments,
1021 arguments,
1022 }))
1023 }
1024
1025 pub fn make_move_vec(ty: Option<TypeTag>, args: Vec<Argument>) -> Self {
1026 Command::MakeMoveVec(ty.map(TypeInput::from), args)
1027 }
1028
1029 fn input_objects(&self) -> Vec<InputObjectKind> {
1030 match self {
1031 Command::Upgrade(_, deps, package_id, _) => deps
1032 .iter()
1033 .map(|id| InputObjectKind::MovePackage(*id))
1034 .chain(Some(InputObjectKind::MovePackage(*package_id)))
1035 .collect(),
1036 Command::Publish(_, deps) => deps
1037 .iter()
1038 .map(|id| InputObjectKind::MovePackage(*id))
1039 .collect(),
1040 Command::MoveCall(c) => c.input_objects(),
1041 Command::MakeMoveVec(Some(t), _) => {
1042 let mut packages = BTreeSet::new();
1043 add_type_input_packages(&mut packages, t);
1044 packages
1045 .into_iter()
1046 .map(InputObjectKind::MovePackage)
1047 .collect()
1048 }
1049 Command::MakeMoveVec(None, _)
1050 | Command::TransferObjects(_, _)
1051 | Command::SplitCoins(_, _)
1052 | Command::MergeCoins(_, _) => vec![],
1053 }
1054 }
1055
1056 fn non_system_packages_to_be_published(&self) -> Option<&Vec<Vec<u8>>> {
1057 match self {
1058 Command::Upgrade(v, _, _, _) => Some(v),
1059 Command::Publish(v, _) => Some(v),
1060 Command::MoveCall(_)
1061 | Command::TransferObjects(_, _)
1062 | Command::SplitCoins(_, _)
1063 | Command::MergeCoins(_, _)
1064 | Command::MakeMoveVec(_, _) => None,
1065 }
1066 }
1067
1068 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1069 match self {
1070 Command::MoveCall(call) => call.validity_check(config)?,
1071 Command::TransferObjects(args, _)
1072 | Command::MergeCoins(_, args)
1073 | Command::SplitCoins(_, args) => {
1074 fp_ensure!(!args.is_empty(), UserInputError::EmptyCommandInput);
1075 fp_ensure!(
1076 args.len() < config.max_arguments() as usize,
1077 UserInputError::SizeLimitExceeded {
1078 limit: "maximum arguments in a programmable transaction command"
1079 .to_string(),
1080 value: config.max_arguments().to_string()
1081 }
1082 );
1083 }
1084 Command::MakeMoveVec(ty_opt, args) => {
1085 fp_ensure!(
1087 ty_opt.is_some() || !args.is_empty(),
1088 UserInputError::EmptyCommandInput
1089 );
1090 if let Some(ty) = ty_opt {
1091 let mut type_arguments_count = 0;
1092 type_input_validity_check(ty, config, &mut type_arguments_count)?;
1093 }
1094 fp_ensure!(
1095 args.len() < config.max_arguments() as usize,
1096 UserInputError::SizeLimitExceeded {
1097 limit: "maximum arguments in a programmable transaction command"
1098 .to_string(),
1099 value: config.max_arguments().to_string()
1100 }
1101 );
1102 }
1103 Command::Publish(modules, deps) | Command::Upgrade(modules, deps, _, _) => {
1104 fp_ensure!(!modules.is_empty(), UserInputError::EmptyCommandInput);
1105 fp_ensure!(
1106 modules.len() < config.max_modules_in_publish() as usize,
1107 UserInputError::SizeLimitExceeded {
1108 limit: "maximum modules in a programmable transaction upgrade command"
1109 .to_string(),
1110 value: config.max_modules_in_publish().to_string()
1111 }
1112 );
1113 if let Some(max_package_dependencies) = config.max_package_dependencies_as_option()
1114 {
1115 fp_ensure!(
1116 deps.len() < max_package_dependencies as usize,
1117 UserInputError::SizeLimitExceeded {
1118 limit: "maximum package dependencies".to_string(),
1119 value: max_package_dependencies.to_string()
1120 }
1121 );
1122 };
1123 }
1124 };
1125 Ok(())
1126 }
1127
1128 fn is_input_arg_used(&self, input_arg: u16) -> bool {
1129 match self {
1130 Command::MoveCall(c) => c.is_input_arg_used(input_arg),
1131 Command::TransferObjects(args, arg)
1132 | Command::MergeCoins(arg, args)
1133 | Command::SplitCoins(arg, args) => args
1134 .iter()
1135 .chain(once(arg))
1136 .any(|a| matches!(a, Argument::Input(inp) if *inp == input_arg)),
1137 Command::MakeMoveVec(_, args) => args
1138 .iter()
1139 .any(|a| matches!(a, Argument::Input(inp) if *inp == input_arg)),
1140 Command::Upgrade(_, _, _, arg) => {
1141 matches!(arg, Argument::Input(inp) if *inp == input_arg)
1142 }
1143 Command::Publish(_, _) => false,
1144 }
1145 }
1146}
1147
1148pub fn write_sep<T: Display>(
1149 f: &mut Formatter<'_>,
1150 items: impl IntoIterator<Item = T>,
1151 sep: &str,
1152) -> std::fmt::Result {
1153 let mut xs = items.into_iter();
1154 let Some(x) = xs.next() else {
1155 return Ok(());
1156 };
1157 write!(f, "{x}")?;
1158 for x in xs {
1159 write!(f, "{sep}{x}")?;
1160 }
1161 Ok(())
1162}
1163
1164impl ProgrammableTransaction {
1165 pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1166 let ProgrammableTransaction { inputs, commands } = self;
1167 let input_arg_objects = inputs
1168 .iter()
1169 .flat_map(|arg| arg.input_objects())
1170 .collect::<Vec<_>>();
1171 let mut used = HashSet::new();
1173 if !input_arg_objects.iter().all(|o| used.insert(o.object_id())) {
1174 return Err(UserInputError::DuplicateObjectRefInput);
1175 }
1176 let command_input_objects: BTreeSet<InputObjectKind> = commands
1178 .iter()
1179 .flat_map(|command| command.input_objects())
1180 .collect();
1181 Ok(input_arg_objects
1182 .into_iter()
1183 .chain(command_input_objects)
1184 .collect())
1185 }
1186
1187 fn receiving_objects(&self) -> Vec<ObjectRef> {
1188 let ProgrammableTransaction { inputs, .. } = self;
1189 inputs
1190 .iter()
1191 .flat_map(|arg| arg.receiving_objects())
1192 .collect()
1193 }
1194
1195 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1196 let ProgrammableTransaction { inputs, commands } = self;
1197 fp_ensure!(
1198 commands.len() < config.max_programmable_tx_commands() as usize,
1199 UserInputError::SizeLimitExceeded {
1200 limit: "maximum commands in a programmable transaction".to_string(),
1201 value: config.max_programmable_tx_commands().to_string()
1202 }
1203 );
1204 let total_inputs = self.input_objects()?.len() + self.receiving_objects().len();
1205 fp_ensure!(
1206 total_inputs <= config.max_input_objects() as usize,
1207 UserInputError::SizeLimitExceeded {
1208 limit: "maximum input + receiving objects in a transaction".to_string(),
1209 value: config.max_input_objects().to_string()
1210 }
1211 );
1212 for input in inputs {
1213 input.validity_check(config)?
1214 }
1215 if let Some(max_publish_commands) = config.max_publish_or_upgrade_per_ptb_as_option() {
1216 let publish_count = commands
1217 .iter()
1218 .filter(|c| matches!(c, Command::Publish(_, _) | Command::Upgrade(_, _, _, _)))
1219 .count() as u64;
1220 fp_ensure!(
1221 publish_count <= max_publish_commands,
1222 UserInputError::MaxPublishCountExceeded {
1223 max_publish_commands,
1224 publish_count,
1225 }
1226 );
1227 }
1228 for command in commands {
1229 command.validity_check(config)?;
1230 }
1231
1232 if let Some(random_index) = inputs.iter().position(|obj| {
1236 matches!(obj, CallArg::Object(ObjectArg::SharedObject { id, .. }) if *id == IOTA_RANDOMNESS_STATE_OBJECT_ID)
1237 }) {
1238 let mut used_random_object = false;
1239 let random_index = random_index.try_into().unwrap();
1240 for command in commands {
1241 if !used_random_object {
1242 used_random_object = command.is_input_arg_used(random_index);
1243 } else {
1244 fp_ensure!(
1245 matches!(
1246 command,
1247 Command::TransferObjects(_, _) | Command::MergeCoins(_, _)
1248 ),
1249 UserInputError::PostRandomCommandRestrictions
1250 );
1251 }
1252 }
1253 }
1254
1255 Ok(())
1256 }
1257
1258 fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
1259 self.inputs
1260 .iter()
1261 .filter_map(|arg| match arg {
1262 CallArg::Pure(_)
1263 | CallArg::Object(ObjectArg::Receiving(_))
1264 | CallArg::Object(ObjectArg::ImmOrOwnedObject(_)) => None,
1265 CallArg::Object(ObjectArg::SharedObject {
1266 id,
1267 initial_shared_version,
1268 mutable,
1269 }) => Some(vec![SharedInputObject {
1270 id: *id,
1271 initial_shared_version: *initial_shared_version,
1272 mutable: *mutable,
1273 }]),
1274 })
1275 .flatten()
1276 }
1277
1278 fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)> {
1279 self.commands
1280 .iter()
1281 .filter_map(|command| match command {
1282 Command::MoveCall(m) => Some((&m.package, m.module.as_str(), m.function.as_str())),
1283 _ => None,
1284 })
1285 .collect()
1286 }
1287
1288 pub fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>> + '_ {
1289 self.commands
1290 .iter()
1291 .filter_map(|q| q.non_system_packages_to_be_published())
1292 }
1293}
1294
1295impl Display for Argument {
1296 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1297 match self {
1298 Argument::GasCoin => write!(f, "GasCoin"),
1299 Argument::Input(i) => write!(f, "Input({i})"),
1300 Argument::Result(i) => write!(f, "Result({i})"),
1301 Argument::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
1302 }
1303 }
1304}
1305
1306impl Display for ProgrammableMoveCall {
1307 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1308 let ProgrammableMoveCall {
1309 package,
1310 module,
1311 function,
1312 type_arguments,
1313 arguments,
1314 } = self;
1315 write!(f, "{package}::{module}::{function}")?;
1316 if !type_arguments.is_empty() {
1317 write!(f, "<")?;
1318 write_sep(f, type_arguments, ",")?;
1319 write!(f, ">")?;
1320 }
1321 write!(f, "(")?;
1322 write_sep(f, arguments, ",")?;
1323 write!(f, ")")
1324 }
1325}
1326
1327impl Display for Command {
1328 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1329 match self {
1330 Command::MoveCall(p) => {
1331 write!(f, "MoveCall({p})")
1332 }
1333 Command::MakeMoveVec(ty_opt, elems) => {
1334 write!(f, "MakeMoveVec(")?;
1335 if let Some(ty) = ty_opt {
1336 write!(f, "Some{ty}")?;
1337 } else {
1338 write!(f, "None")?;
1339 }
1340 write!(f, ",[")?;
1341 write_sep(f, elems, ",")?;
1342 write!(f, "])")
1343 }
1344 Command::TransferObjects(objs, addr) => {
1345 write!(f, "TransferObjects([")?;
1346 write_sep(f, objs, ",")?;
1347 write!(f, "],{addr})")
1348 }
1349 Command::SplitCoins(coin, amounts) => {
1350 write!(f, "SplitCoins({coin}")?;
1351 write_sep(f, amounts, ",")?;
1352 write!(f, ")")
1353 }
1354 Command::MergeCoins(target, coins) => {
1355 write!(f, "MergeCoins({target},")?;
1356 write_sep(f, coins, ",")?;
1357 write!(f, ")")
1358 }
1359 Command::Publish(_bytes, deps) => {
1360 write!(f, "Publish(_,")?;
1361 write_sep(f, deps, ",")?;
1362 write!(f, ")")
1363 }
1364 Command::Upgrade(_bytes, deps, current_package_id, ticket) => {
1365 write!(f, "Upgrade(_,")?;
1366 write_sep(f, deps, ",")?;
1367 write!(f, ", {current_package_id}")?;
1368 write!(f, ", {ticket}")?;
1369 write!(f, ")")
1370 }
1371 }
1372 }
1373}
1374
1375impl Display for ProgrammableTransaction {
1376 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1377 let ProgrammableTransaction { inputs, commands } = self;
1378 writeln!(f, "Inputs: {inputs:?}")?;
1379 writeln!(f, "Commands: [")?;
1380 for c in commands {
1381 writeln!(f, " {c},")?;
1382 }
1383 writeln!(f, "]")
1384 }
1385}
1386
1387#[derive(Clone, Hash, Debug, PartialEq, Eq)]
1388pub struct SharedInputObject {
1389 pub id: ObjectID,
1390 pub initial_shared_version: SequenceNumber,
1391 pub mutable: bool,
1392}
1393
1394impl SharedInputObject {
1395 pub const IOTA_SYSTEM_OBJ: Self = Self {
1396 id: IOTA_SYSTEM_STATE_OBJECT_ID,
1397 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
1398 mutable: true,
1399 };
1400
1401 pub fn id(&self) -> ObjectID {
1402 self.id
1403 }
1404
1405 pub fn into_id_and_version(self) -> (ObjectID, SequenceNumber) {
1406 (self.id, self.initial_shared_version)
1407 }
1408
1409 pub fn left_union(&mut self, other: &SharedInputObject) -> UserInputResult<()> {
1413 fp_ensure!(self.id == other.id, UserInputError::SharedObjectIdMismatch);
1414 fp_ensure!(
1415 self.initial_shared_version == other.initial_shared_version,
1416 UserInputError::SharedObjectStartingVersionMismatch
1417 );
1418
1419 if !self.mutable && other.mutable {
1420 self.mutable = other.mutable;
1421 }
1422
1423 Ok(())
1424 }
1425}
1426
1427impl TransactionKind {
1428 pub fn programmable(pt: ProgrammableTransaction) -> Self {
1431 TransactionKind::ProgrammableTransaction(pt)
1432 }
1433
1434 pub fn is_system_tx(&self) -> bool {
1435 match self {
1437 TransactionKind::Genesis(_)
1438 | TransactionKind::ConsensusCommitPrologueV1(_)
1439 | TransactionKind::AuthenticatorStateUpdateV1(_)
1440 | TransactionKind::RandomnessStateUpdate(_)
1441 | TransactionKind::EndOfEpochTransaction(_) => true,
1442 TransactionKind::ProgrammableTransaction(_) => false,
1443 }
1444 }
1445
1446 pub fn is_end_of_epoch_tx(&self) -> bool {
1447 matches!(self, TransactionKind::EndOfEpochTransaction(_))
1448 }
1449
1450 pub fn is_programmable_transaction(&self) -> bool {
1451 matches!(self, TransactionKind::ProgrammableTransaction(_))
1452 }
1453
1454 pub fn get_advance_epoch_tx_gas_summary(&self) -> Option<(u64, u64)> {
1458 match self {
1459 Self::EndOfEpochTransaction(txns) => {
1460 match txns.last().expect("at least one end-of-epoch txn required") {
1461 EndOfEpochTransactionKind::ChangeEpoch(e) => {
1462 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1463 }
1464 EndOfEpochTransactionKind::ChangeEpochV2(e) => {
1465 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1466 }
1467 EndOfEpochTransactionKind::ChangeEpochV3(e) => {
1468 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1469 }
1470 EndOfEpochTransactionKind::ChangeEpochV4(e) => {
1471 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1472 }
1473 _ => panic!("final end-of-epoch txn must be ChangeEpoch"),
1474 }
1475 }
1476 _ => None,
1477 }
1478 }
1479
1480 pub fn contains_shared_object(&self) -> bool {
1481 self.shared_input_objects().next().is_some()
1482 }
1483
1484 pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
1487 match &self {
1488 Self::ConsensusCommitPrologueV1(_) => {
1489 Either::Left(Either::Left(iter::once(SharedInputObject {
1490 id: IOTA_CLOCK_OBJECT_ID,
1491 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
1492 mutable: true,
1493 })))
1494 }
1495 Self::AuthenticatorStateUpdateV1(update) => {
1496 Either::Left(Either::Left(iter::once(SharedInputObject {
1497 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1498 initial_shared_version: update.authenticator_obj_initial_shared_version,
1499 mutable: true,
1500 })))
1501 }
1502 Self::RandomnessStateUpdate(update) => {
1503 Either::Left(Either::Left(iter::once(SharedInputObject {
1504 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1505 initial_shared_version: update.randomness_obj_initial_shared_version,
1506 mutable: true,
1507 })))
1508 }
1509 Self::EndOfEpochTransaction(txns) => Either::Left(Either::Right(
1510 txns.iter().flat_map(|txn| txn.shared_input_objects()),
1511 )),
1512 Self::ProgrammableTransaction(pt) => {
1513 Either::Right(Either::Left(pt.shared_input_objects()))
1514 }
1515 _ => Either::Right(Either::Right(iter::empty())),
1516 }
1517 }
1518
1519 fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)> {
1520 match &self {
1521 Self::ProgrammableTransaction(pt) => pt.move_calls(),
1522 _ => vec![],
1523 }
1524 }
1525
1526 pub fn receiving_objects(&self) -> Vec<ObjectRef> {
1527 match &self {
1528 TransactionKind::Genesis(_)
1529 | TransactionKind::ConsensusCommitPrologueV1(_)
1530 | TransactionKind::AuthenticatorStateUpdateV1(_)
1531 | TransactionKind::RandomnessStateUpdate(_)
1532 | TransactionKind::EndOfEpochTransaction(_) => vec![],
1533 TransactionKind::ProgrammableTransaction(pt) => pt.receiving_objects(),
1534 }
1535 }
1536
1537 pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1543 let input_objects = match &self {
1544 Self::Genesis(_) => {
1545 vec![]
1546 }
1547 Self::ConsensusCommitPrologueV1(_) => {
1548 vec![InputObjectKind::SharedMoveObject {
1549 id: IOTA_CLOCK_OBJECT_ID,
1550 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
1551 mutable: true,
1552 }]
1553 }
1554 Self::AuthenticatorStateUpdateV1(update) => {
1555 vec![InputObjectKind::SharedMoveObject {
1556 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1557 initial_shared_version: update.authenticator_obj_initial_shared_version(),
1558 mutable: true,
1559 }]
1560 }
1561 Self::RandomnessStateUpdate(update) => {
1562 vec![InputObjectKind::SharedMoveObject {
1563 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1564 initial_shared_version: update.randomness_obj_initial_shared_version(),
1565 mutable: true,
1566 }]
1567 }
1568 Self::EndOfEpochTransaction(txns) => {
1569 let before_dedup: Vec<_> =
1572 txns.iter().flat_map(|txn| txn.input_objects()).collect();
1573 let mut has_seen = HashSet::new();
1574 let mut after_dedup = vec![];
1575 for obj in before_dedup {
1576 if has_seen.insert(obj) {
1577 after_dedup.push(obj);
1578 }
1579 }
1580 after_dedup
1581 }
1582 Self::ProgrammableTransaction(p) => return p.input_objects(),
1583 };
1584 let mut used = HashSet::new();
1592 if !input_objects.iter().all(|o| used.insert(o.object_id())) {
1593 return Err(UserInputError::DuplicateObjectRefInput);
1594 }
1595 Ok(input_objects)
1596 }
1597
1598 pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1599 match self {
1600 TransactionKind::ProgrammableTransaction(p) => p.validity_check(config)?,
1601 TransactionKind::Genesis(_) | TransactionKind::ConsensusCommitPrologueV1(_) => (),
1604 TransactionKind::EndOfEpochTransaction(txns) => {
1605 for tx in txns {
1606 tx.validity_check(config)?;
1607 }
1608 }
1609
1610 TransactionKind::AuthenticatorStateUpdateV1(_) => {
1611 if !config.enable_jwk_consensus_updates() {
1612 return Err(UserInputError::Unsupported(
1613 "authenticator state updates not enabled".to_string(),
1614 ));
1615 }
1616 }
1617 TransactionKind::RandomnessStateUpdate(_) => (),
1618 };
1619 Ok(())
1620 }
1621
1622 pub fn num_commands(&self) -> usize {
1624 match self {
1625 TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1626 _ => 0,
1627 }
1628 }
1629
1630 pub fn iter_commands(&self) -> impl Iterator<Item = &Command> {
1631 match self {
1632 TransactionKind::ProgrammableTransaction(pt) => pt.commands.iter(),
1633 _ => [].iter(),
1634 }
1635 }
1636
1637 pub fn tx_count(&self) -> usize {
1639 match self {
1640 TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1641 _ => 1,
1642 }
1643 }
1644
1645 pub fn name(&self) -> &'static str {
1646 match self {
1647 Self::Genesis(_) => "Genesis",
1648 Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
1649 Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
1650 Self::AuthenticatorStateUpdateV1(_) => "AuthenticatorStateUpdateV1",
1651 Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
1652 Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
1653 }
1654 }
1655}
1656
1657impl Display for TransactionKind {
1658 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1659 let mut writer = String::new();
1660 match &self {
1661 Self::Genesis(_) => {
1662 writeln!(writer, "Transaction Kind : Genesis")?;
1663 }
1664 Self::ConsensusCommitPrologueV1(p) => {
1665 writeln!(writer, "Transaction Kind : Consensus Commit Prologue V1")?;
1666 writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1667 writeln!(writer, "Consensus Digest: {}", p.consensus_commit_digest)?;
1668 writeln!(
1669 writer,
1670 "Consensus determined version assignment: {:?}",
1671 p.consensus_determined_version_assignments
1672 )?;
1673 }
1674 Self::ProgrammableTransaction(p) => {
1675 writeln!(writer, "Transaction Kind : Programmable")?;
1676 write!(writer, "{p}")?;
1677 }
1678 Self::AuthenticatorStateUpdateV1(_) => {
1679 writeln!(writer, "Transaction Kind : Authenticator State Update")?;
1680 }
1681 Self::RandomnessStateUpdate(_) => {
1682 writeln!(writer, "Transaction Kind : Randomness State Update")?;
1683 }
1684 Self::EndOfEpochTransaction(_) => {
1685 writeln!(writer, "Transaction Kind : End of Epoch Transaction")?;
1686 }
1687 }
1688 write!(f, "{writer}")
1689 }
1690}
1691
1692#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1693pub struct GasData {
1694 pub payment: Vec<ObjectRef>,
1695 pub owner: IotaAddress,
1696 pub price: u64,
1697 pub budget: u64,
1698}
1699
1700#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
1701pub enum TransactionExpiration {
1702 None,
1704 Epoch(EpochId),
1707}
1708
1709#[enum_dispatch(TransactionDataAPI)]
1710#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1711pub enum TransactionData {
1712 V1(TransactionDataV1),
1713 }
1716
1717#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1718pub struct TransactionDataV1 {
1719 pub kind: TransactionKind,
1720 pub sender: IotaAddress,
1721 pub gas_data: GasData,
1722 pub expiration: TransactionExpiration,
1723}
1724
1725impl TransactionData {
1726 fn new_system_transaction(kind: TransactionKind) -> Self {
1727 assert!(kind.is_system_tx());
1729 let sender = IotaAddress::default();
1730 TransactionData::V1(TransactionDataV1 {
1731 kind,
1732 sender,
1733 gas_data: GasData {
1734 price: GAS_PRICE_FOR_SYSTEM_TX,
1735 owner: sender,
1736 payment: vec![(ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN)],
1737 budget: 0,
1738 },
1739 expiration: TransactionExpiration::None,
1740 })
1741 }
1742
1743 pub fn new(
1744 kind: TransactionKind,
1745 sender: IotaAddress,
1746 gas_payment: ObjectRef,
1747 gas_budget: u64,
1748 gas_price: u64,
1749 ) -> Self {
1750 TransactionData::V1(TransactionDataV1 {
1751 kind,
1752 sender,
1753 gas_data: GasData {
1754 price: gas_price,
1755 owner: sender,
1756 payment: vec![gas_payment],
1757 budget: gas_budget,
1758 },
1759 expiration: TransactionExpiration::None,
1760 })
1761 }
1762
1763 pub fn new_with_gas_coins(
1764 kind: TransactionKind,
1765 sender: IotaAddress,
1766 gas_payment: Vec<ObjectRef>,
1767 gas_budget: u64,
1768 gas_price: u64,
1769 ) -> Self {
1770 Self::new_with_gas_coins_allow_sponsor(
1771 kind,
1772 sender,
1773 gas_payment,
1774 gas_budget,
1775 gas_price,
1776 sender,
1777 )
1778 }
1779
1780 pub fn new_with_gas_coins_allow_sponsor(
1781 kind: TransactionKind,
1782 sender: IotaAddress,
1783 gas_payment: Vec<ObjectRef>,
1784 gas_budget: u64,
1785 gas_price: u64,
1786 gas_sponsor: IotaAddress,
1787 ) -> Self {
1788 TransactionData::V1(TransactionDataV1 {
1789 kind,
1790 sender,
1791 gas_data: GasData {
1792 price: gas_price,
1793 owner: gas_sponsor,
1794 payment: gas_payment,
1795 budget: gas_budget,
1796 },
1797 expiration: TransactionExpiration::None,
1798 })
1799 }
1800
1801 pub fn new_with_gas_data(
1802 kind: TransactionKind,
1803 sender: IotaAddress,
1804 gas_data: GasData,
1805 ) -> Self {
1806 TransactionData::V1(TransactionDataV1 {
1807 kind,
1808 sender,
1809 gas_data,
1810 expiration: TransactionExpiration::None,
1811 })
1812 }
1813
1814 pub fn new_move_call(
1815 sender: IotaAddress,
1816 package: ObjectID,
1817 module: Identifier,
1818 function: Identifier,
1819 type_arguments: Vec<TypeTag>,
1820 gas_payment: ObjectRef,
1821 arguments: Vec<CallArg>,
1822 gas_budget: u64,
1823 gas_price: u64,
1824 ) -> anyhow::Result<Self> {
1825 Self::new_move_call_with_gas_coins(
1826 sender,
1827 package,
1828 module,
1829 function,
1830 type_arguments,
1831 vec![gas_payment],
1832 arguments,
1833 gas_budget,
1834 gas_price,
1835 )
1836 }
1837
1838 pub fn new_move_call_with_gas_coins(
1839 sender: IotaAddress,
1840 package: ObjectID,
1841 module: Identifier,
1842 function: Identifier,
1843 type_arguments: Vec<TypeTag>,
1844 gas_payment: Vec<ObjectRef>,
1845 arguments: Vec<CallArg>,
1846 gas_budget: u64,
1847 gas_price: u64,
1848 ) -> anyhow::Result<Self> {
1849 let pt = {
1850 let mut builder = ProgrammableTransactionBuilder::new();
1851 builder.move_call(package, module, function, type_arguments, arguments)?;
1852 builder.finish()
1853 };
1854 Ok(Self::new_programmable(
1855 sender,
1856 gas_payment,
1857 pt,
1858 gas_budget,
1859 gas_price,
1860 ))
1861 }
1862
1863 pub fn new_transfer(
1864 recipient: IotaAddress,
1865 object_ref: ObjectRef,
1866 sender: IotaAddress,
1867 gas_payment: ObjectRef,
1868 gas_budget: u64,
1869 gas_price: u64,
1870 ) -> Self {
1871 let pt = {
1872 let mut builder = ProgrammableTransactionBuilder::new();
1873 builder.transfer_object(recipient, object_ref).unwrap();
1874 builder.finish()
1875 };
1876 Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1877 }
1878
1879 pub fn new_transfer_iota(
1880 recipient: IotaAddress,
1881 sender: IotaAddress,
1882 amount: Option<u64>,
1883 gas_payment: ObjectRef,
1884 gas_budget: u64,
1885 gas_price: u64,
1886 ) -> Self {
1887 Self::new_transfer_iota_allow_sponsor(
1888 recipient,
1889 sender,
1890 amount,
1891 gas_payment,
1892 gas_budget,
1893 gas_price,
1894 sender,
1895 )
1896 }
1897
1898 pub fn new_transfer_iota_allow_sponsor(
1899 recipient: IotaAddress,
1900 sender: IotaAddress,
1901 amount: Option<u64>,
1902 gas_payment: ObjectRef,
1903 gas_budget: u64,
1904 gas_price: u64,
1905 gas_sponsor: IotaAddress,
1906 ) -> Self {
1907 let pt = {
1908 let mut builder = ProgrammableTransactionBuilder::new();
1909 builder.transfer_iota(recipient, amount);
1910 builder.finish()
1911 };
1912 Self::new_programmable_allow_sponsor(
1913 sender,
1914 vec![gas_payment],
1915 pt,
1916 gas_budget,
1917 gas_price,
1918 gas_sponsor,
1919 )
1920 }
1921
1922 pub fn new_pay(
1923 sender: IotaAddress,
1924 coins: Vec<ObjectRef>,
1925 recipients: Vec<IotaAddress>,
1926 amounts: Vec<u64>,
1927 gas_payment: ObjectRef,
1928 gas_budget: u64,
1929 gas_price: u64,
1930 ) -> anyhow::Result<Self> {
1931 let pt = {
1932 let mut builder = ProgrammableTransactionBuilder::new();
1933 builder.pay(coins, recipients, amounts)?;
1934 builder.finish()
1935 };
1936 Ok(Self::new_programmable(
1937 sender,
1938 vec![gas_payment],
1939 pt,
1940 gas_budget,
1941 gas_price,
1942 ))
1943 }
1944
1945 pub fn new_pay_iota(
1946 sender: IotaAddress,
1947 mut coins: Vec<ObjectRef>,
1948 recipients: Vec<IotaAddress>,
1949 amounts: Vec<u64>,
1950 gas_payment: ObjectRef,
1951 gas_budget: u64,
1952 gas_price: u64,
1953 ) -> anyhow::Result<Self> {
1954 coins.insert(0, gas_payment);
1955 let pt = {
1956 let mut builder = ProgrammableTransactionBuilder::new();
1957 builder.pay_iota(recipients, amounts)?;
1958 builder.finish()
1959 };
1960 Ok(Self::new_programmable(
1961 sender, coins, pt, gas_budget, gas_price,
1962 ))
1963 }
1964
1965 pub fn new_pay_all_iota(
1966 sender: IotaAddress,
1967 mut coins: Vec<ObjectRef>,
1968 recipient: IotaAddress,
1969 gas_payment: ObjectRef,
1970 gas_budget: u64,
1971 gas_price: u64,
1972 ) -> Self {
1973 coins.insert(0, gas_payment);
1974 let pt = {
1975 let mut builder = ProgrammableTransactionBuilder::new();
1976 builder.pay_all_iota(recipient);
1977 builder.finish()
1978 };
1979 Self::new_programmable(sender, coins, pt, gas_budget, gas_price)
1980 }
1981
1982 pub fn new_split_coin(
1983 sender: IotaAddress,
1984 coin: ObjectRef,
1985 amounts: Vec<u64>,
1986 gas_payment: ObjectRef,
1987 gas_budget: u64,
1988 gas_price: u64,
1989 ) -> Self {
1990 let pt = {
1991 let mut builder = ProgrammableTransactionBuilder::new();
1992 builder.split_coin(sender, coin, amounts);
1993 builder.finish()
1994 };
1995 Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1996 }
1997
1998 pub fn new_module(
1999 sender: IotaAddress,
2000 gas_payment: ObjectRef,
2001 modules: Vec<Vec<u8>>,
2002 dep_ids: Vec<ObjectID>,
2003 gas_budget: u64,
2004 gas_price: u64,
2005 ) -> Self {
2006 let pt = {
2007 let mut builder = ProgrammableTransactionBuilder::new();
2008 let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
2009 builder.transfer_arg(sender, upgrade_cap);
2010 builder.finish()
2011 };
2012 Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
2013 }
2014
2015 pub fn new_upgrade(
2016 sender: IotaAddress,
2017 gas_payment: ObjectRef,
2018 package_id: ObjectID,
2019 modules: Vec<Vec<u8>>,
2020 dep_ids: Vec<ObjectID>,
2021 (upgrade_capability, capability_owner): (ObjectRef, Owner),
2022 upgrade_policy: u8,
2023 digest: Vec<u8>,
2024 gas_budget: u64,
2025 gas_price: u64,
2026 ) -> anyhow::Result<Self> {
2027 let pt = {
2028 let mut builder = ProgrammableTransactionBuilder::new();
2029 let capability_arg = match capability_owner {
2030 Owner::AddressOwner(_) => ObjectArg::ImmOrOwnedObject(upgrade_capability),
2031 Owner::Shared {
2032 initial_shared_version,
2033 } => ObjectArg::SharedObject {
2034 id: upgrade_capability.0,
2035 initial_shared_version,
2036 mutable: true,
2037 },
2038 Owner::Immutable => {
2039 bail!("Upgrade capability is stored immutably and cannot be used for upgrades");
2040 }
2041 Owner::ObjectOwner(_) => {
2044 bail!("Upgrade capability controlled by object");
2045 }
2046 };
2047 builder.obj(capability_arg).unwrap();
2048 let upgrade_arg = builder.pure(upgrade_policy).unwrap();
2049 let digest_arg = builder.pure(digest).unwrap();
2050 let upgrade_ticket = builder.programmable_move_call(
2051 IOTA_FRAMEWORK_PACKAGE_ID,
2052 ident_str!("package").to_owned(),
2053 ident_str!("authorize_upgrade").to_owned(),
2054 vec![],
2055 vec![Argument::Input(0), upgrade_arg, digest_arg],
2056 );
2057 let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
2058
2059 builder.programmable_move_call(
2060 IOTA_FRAMEWORK_PACKAGE_ID,
2061 ident_str!("package").to_owned(),
2062 ident_str!("commit_upgrade").to_owned(),
2063 vec![],
2064 vec![Argument::Input(0), upgrade_receipt],
2065 );
2066
2067 builder.finish()
2068 };
2069 Ok(Self::new_programmable(
2070 sender,
2071 vec![gas_payment],
2072 pt,
2073 gas_budget,
2074 gas_price,
2075 ))
2076 }
2077
2078 pub fn new_programmable(
2079 sender: IotaAddress,
2080 gas_payment: Vec<ObjectRef>,
2081 pt: ProgrammableTransaction,
2082 gas_budget: u64,
2083 gas_price: u64,
2084 ) -> Self {
2085 Self::new_programmable_allow_sponsor(sender, gas_payment, pt, gas_budget, gas_price, sender)
2086 }
2087
2088 pub fn new_programmable_allow_sponsor(
2089 sender: IotaAddress,
2090 gas_payment: Vec<ObjectRef>,
2091 pt: ProgrammableTransaction,
2092 gas_budget: u64,
2093 gas_price: u64,
2094 sponsor: IotaAddress,
2095 ) -> Self {
2096 let kind = TransactionKind::ProgrammableTransaction(pt);
2097 Self::new_with_gas_coins_allow_sponsor(
2098 kind,
2099 sender,
2100 gas_payment,
2101 gas_budget,
2102 gas_price,
2103 sponsor,
2104 )
2105 }
2106
2107 pub fn message_version(&self) -> u64 {
2108 match self {
2109 TransactionData::V1(_) => 1,
2110 }
2111 }
2112
2113 pub fn execution_parts(&self) -> (TransactionKind, IotaAddress, GasData) {
2114 (self.kind().clone(), self.sender(), self.gas_data().clone())
2115 }
2116
2117 pub fn uses_randomness(&self) -> bool {
2124 self.shared_input_objects()
2125 .iter()
2126 .any(|obj| obj.id() == IOTA_RANDOMNESS_STATE_OBJECT_ID)
2127 }
2128
2129 pub fn digest(&self) -> TransactionDigest {
2130 TransactionDigest::new(default_hash(self))
2131 }
2132}
2133
2134#[enum_dispatch]
2135pub trait TransactionDataAPI {
2136 fn sender(&self) -> IotaAddress;
2137
2138 fn kind(&self) -> &TransactionKind;
2142
2143 fn kind_mut(&mut self) -> &mut TransactionKind;
2145
2146 fn into_kind(self) -> TransactionKind;
2148
2149 fn signers(&self) -> NonEmpty<IotaAddress>;
2151
2152 fn gas_data(&self) -> &GasData;
2153
2154 fn gas_owner(&self) -> IotaAddress;
2155
2156 fn gas(&self) -> &[ObjectRef];
2157
2158 fn gas_price(&self) -> u64;
2159
2160 fn gas_budget(&self) -> u64;
2161
2162 fn expiration(&self) -> &TransactionExpiration;
2163
2164 fn contains_shared_object(&self) -> bool;
2170
2171 fn shared_input_objects(&self) -> Vec<SharedInputObject>;
2177
2178 fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)>;
2179
2180 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
2181
2182 fn receiving_objects(&self) -> Vec<ObjectRef>;
2183
2184 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
2185
2186 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult;
2187
2188 fn check_sponsorship(&self) -> UserInputResult;
2190
2191 fn is_system_tx(&self) -> bool;
2192 fn is_genesis_tx(&self) -> bool;
2193
2194 fn is_end_of_epoch_tx(&self) -> bool;
2197
2198 fn is_sponsored_tx(&self) -> bool;
2200
2201 fn sender_mut_for_testing(&mut self) -> &mut IotaAddress;
2202
2203 fn gas_data_mut(&mut self) -> &mut GasData;
2204
2205 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration;
2207}
2208
2209impl TransactionDataAPI for TransactionDataV1 {
2210 fn sender(&self) -> IotaAddress {
2211 self.sender
2212 }
2213
2214 fn kind(&self) -> &TransactionKind {
2215 &self.kind
2216 }
2217
2218 fn kind_mut(&mut self) -> &mut TransactionKind {
2219 &mut self.kind
2220 }
2221
2222 fn into_kind(self) -> TransactionKind {
2223 self.kind
2224 }
2225
2226 fn signers(&self) -> NonEmpty<IotaAddress> {
2228 let mut signers = nonempty![self.sender];
2229 if self.gas_owner() != self.sender {
2230 signers.push(self.gas_owner());
2231 }
2232 signers
2233 }
2234
2235 fn gas_data(&self) -> &GasData {
2236 &self.gas_data
2237 }
2238
2239 fn gas_owner(&self) -> IotaAddress {
2240 self.gas_data.owner
2241 }
2242
2243 fn gas(&self) -> &[ObjectRef] {
2244 &self.gas_data.payment
2245 }
2246
2247 fn gas_price(&self) -> u64 {
2248 self.gas_data.price
2249 }
2250
2251 fn gas_budget(&self) -> u64 {
2252 self.gas_data.budget
2253 }
2254
2255 fn expiration(&self) -> &TransactionExpiration {
2256 &self.expiration
2257 }
2258
2259 fn contains_shared_object(&self) -> bool {
2260 self.kind.shared_input_objects().next().is_some()
2261 }
2262
2263 fn shared_input_objects(&self) -> Vec<SharedInputObject> {
2264 self.kind.shared_input_objects().collect()
2265 }
2266
2267 fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)> {
2268 self.kind.move_calls()
2269 }
2270
2271 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
2272 let mut inputs = self.kind.input_objects()?;
2273
2274 if !self.kind.is_system_tx() {
2275 inputs.extend(
2276 self.gas()
2277 .iter()
2278 .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
2279 );
2280 }
2281 Ok(inputs)
2282 }
2283
2284 fn receiving_objects(&self) -> Vec<ObjectRef> {
2285 self.kind.receiving_objects()
2286 }
2287
2288 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
2289 fp_ensure!(!self.gas().is_empty(), UserInputError::MissingGasPayment);
2290 fp_ensure!(
2291 self.gas().len() < config.max_gas_payment_objects() as usize,
2292 UserInputError::SizeLimitExceeded {
2293 limit: "maximum number of gas payment objects".to_string(),
2294 value: config.max_gas_payment_objects().to_string()
2295 }
2296 );
2297 self.validity_check_no_gas_check(config)
2298 }
2299
2300 #[instrument(level = "trace", skip_all)]
2303 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
2304 self.kind().validity_check(config)?;
2305 self.check_sponsorship()
2306 }
2307
2308 fn is_sponsored_tx(&self) -> bool {
2310 self.gas_owner() != self.sender
2311 }
2312
2313 fn check_sponsorship(&self) -> UserInputResult {
2315 if self.gas_owner() == self.sender() {
2317 return Ok(());
2318 }
2319 if matches!(&self.kind, TransactionKind::ProgrammableTransaction(_)) {
2320 return Ok(());
2321 }
2322 Err(UserInputError::UnsupportedSponsoredTransactionKind)
2323 }
2324
2325 fn is_end_of_epoch_tx(&self) -> bool {
2326 matches!(self.kind, TransactionKind::EndOfEpochTransaction(_))
2327 }
2328
2329 fn is_system_tx(&self) -> bool {
2330 self.kind.is_system_tx()
2331 }
2332
2333 fn is_genesis_tx(&self) -> bool {
2334 matches!(self.kind, TransactionKind::Genesis(_))
2335 }
2336
2337 fn sender_mut_for_testing(&mut self) -> &mut IotaAddress {
2338 &mut self.sender
2339 }
2340
2341 fn gas_data_mut(&mut self) -> &mut GasData {
2342 &mut self.gas_data
2343 }
2344
2345 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
2346 &mut self.expiration
2347 }
2348}
2349
2350impl TransactionDataV1 {}
2351
2352#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
2353pub struct SenderSignedData(SizeOneVec<SenderSignedTransaction>);
2354
2355#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2356pub struct SenderSignedTransaction {
2357 pub intent_message: IntentMessage<TransactionData>,
2358 pub tx_signatures: Vec<GenericSignature>,
2362}
2363
2364impl Serialize for SenderSignedTransaction {
2365 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2366 where
2367 S: serde::Serializer,
2368 {
2369 #[derive(Serialize)]
2370 #[serde(rename = "SenderSignedTransaction")]
2371 struct SignedTxn<'a> {
2372 intent_message: &'a IntentMessage<TransactionData>,
2373 tx_signatures: &'a Vec<GenericSignature>,
2374 }
2375
2376 if self.intent_message().intent != Intent::iota_transaction() {
2377 return Err(serde::ser::Error::custom("invalid Intent for Transaction"));
2378 }
2379
2380 let txn = SignedTxn {
2381 intent_message: self.intent_message(),
2382 tx_signatures: &self.tx_signatures,
2383 };
2384 txn.serialize(serializer)
2385 }
2386}
2387
2388impl<'de> Deserialize<'de> for SenderSignedTransaction {
2389 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2390 where
2391 D: serde::Deserializer<'de>,
2392 {
2393 #[derive(Deserialize)]
2394 #[serde(rename = "SenderSignedTransaction")]
2395 struct SignedTxn {
2396 intent_message: IntentMessage<TransactionData>,
2397 tx_signatures: Vec<GenericSignature>,
2398 }
2399
2400 let SignedTxn {
2401 intent_message,
2402 tx_signatures,
2403 } = Deserialize::deserialize(deserializer)?;
2404
2405 if intent_message.intent != Intent::iota_transaction() {
2406 return Err(serde::de::Error::custom("invalid Intent for Transaction"));
2407 }
2408
2409 Ok(Self {
2410 intent_message,
2411 tx_signatures,
2412 })
2413 }
2414}
2415
2416impl SenderSignedTransaction {
2417 pub(crate) fn get_signer_sig_mapping(
2418 &self,
2419 ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2420 let mut mapping = BTreeMap::new();
2421 for sig in &self.tx_signatures {
2422 let address = sig.try_into()?;
2423 mapping.insert(address, sig);
2424 }
2425 Ok(mapping)
2426 }
2427
2428 pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2429 &self.intent_message
2430 }
2431}
2432
2433impl SenderSignedData {
2434 pub fn new(tx_data: TransactionData, tx_signatures: Vec<GenericSignature>) -> Self {
2435 Self(SizeOneVec::new(SenderSignedTransaction {
2436 intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2437 tx_signatures,
2438 }))
2439 }
2440
2441 pub fn new_from_sender_signature(tx_data: TransactionData, tx_signature: Signature) -> Self {
2442 Self(SizeOneVec::new(SenderSignedTransaction {
2443 intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2444 tx_signatures: vec![tx_signature.into()],
2445 }))
2446 }
2447
2448 pub fn inner(&self) -> &SenderSignedTransaction {
2449 self.0.element()
2450 }
2451
2452 pub fn into_inner(self) -> SenderSignedTransaction {
2453 self.0.into_inner()
2454 }
2455
2456 pub fn inner_mut(&mut self) -> &mut SenderSignedTransaction {
2457 self.0.element_mut()
2458 }
2459
2460 pub fn add_signature(&mut self, new_signature: Signature) {
2463 self.inner_mut().tx_signatures.push(new_signature.into());
2464 }
2465
2466 pub(crate) fn get_signer_sig_mapping(
2467 &self,
2468 ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2469 self.inner().get_signer_sig_mapping()
2470 }
2471
2472 pub fn transaction_data(&self) -> &TransactionData {
2473 &self.intent_message().value
2474 }
2475
2476 pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2477 self.inner().intent_message()
2478 }
2479
2480 pub fn tx_signatures(&self) -> &[GenericSignature] {
2481 &self.inner().tx_signatures
2482 }
2483
2484 pub fn has_zklogin_sig(&self) -> bool {
2485 self.tx_signatures().iter().any(|sig| sig.is_zklogin())
2486 }
2487
2488 pub fn has_upgraded_multisig(&self) -> bool {
2489 self.tx_signatures()
2490 .iter()
2491 .any(|sig| sig.is_upgraded_multisig())
2492 }
2493
2494 #[cfg(test)]
2495 pub fn intent_message_mut_for_testing(&mut self) -> &mut IntentMessage<TransactionData> {
2496 &mut self.inner_mut().intent_message
2497 }
2498
2499 pub fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<GenericSignature> {
2501 &mut self.inner_mut().tx_signatures
2502 }
2503
2504 pub fn full_message_digest(&self) -> SenderSignedDataDigest {
2505 let mut digest = DefaultHash::default();
2506 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2507 let hash = digest.finalize();
2508 SenderSignedDataDigest::new(hash.into())
2509 }
2510
2511 pub fn serialized_size(&self) -> IotaResult<usize> {
2512 bcs::serialized_size(self).map_err(|e| IotaError::TransactionSerialization {
2513 error: e.to_string(),
2514 })
2515 }
2516
2517 fn check_user_signature_protocol_compatibility(&self, config: &ProtocolConfig) -> IotaResult {
2518 for sig in &self.inner().tx_signatures {
2519 match sig {
2520 GenericSignature::ZkLoginAuthenticator(_) => {
2521 if !config.zklogin_auth() {
2522 return Err(IotaError::UserInput {
2523 error: UserInputError::Unsupported(
2524 "zklogin is not enabled on this network".to_string(),
2525 ),
2526 });
2527 }
2528 }
2529 GenericSignature::PasskeyAuthenticator(_) => {
2530 if !config.passkey_auth() {
2531 return Err(IotaError::UserInput {
2532 error: UserInputError::Unsupported(
2533 "passkey is not enabled on this network".to_string(),
2534 ),
2535 });
2536 }
2537 }
2538 GenericSignature::MoveAuthenticator(_) => {
2539 if !config.enable_move_authentication() {
2540 return Err(IotaError::UserInput {
2541 error: UserInputError::Unsupported(
2542 "`Move authentication` is not enabled on this network".to_string(),
2543 ),
2544 });
2545 }
2546 }
2547 GenericSignature::Signature(_) | GenericSignature::MultiSig(_) => (),
2548 }
2549 }
2550
2551 Ok(())
2552 }
2553
2554 pub fn validity_check(
2558 &self,
2559 config: &ProtocolConfig,
2560 epoch: EpochId,
2561 ) -> Result<usize, IotaError> {
2562 self.check_user_signature_protocol_compatibility(config)?;
2565
2566 let tx_data = self.transaction_data();
2569 fp_ensure!(
2570 !tx_data.is_system_tx(),
2571 IotaError::UserInput {
2572 error: UserInputError::Unsupported(
2573 "SenderSignedData must not contain system transaction".to_string()
2574 )
2575 }
2576 );
2577
2578 if match &tx_data.expiration() {
2580 TransactionExpiration::None => false,
2581 TransactionExpiration::Epoch(exp_poch) => *exp_poch < epoch,
2582 } {
2583 return Err(IotaError::TransactionExpired);
2584 }
2585
2586 let tx_size = self.serialized_size()?;
2588 let max_tx_size_bytes = config.max_tx_size_bytes();
2589 fp_ensure!(
2590 tx_size as u64 <= max_tx_size_bytes,
2591 IotaError::UserInput {
2592 error: UserInputError::SizeLimitExceeded {
2593 limit: format!(
2594 "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
2595 ),
2596 value: tx_size.to_string(),
2597 }
2598 }
2599 );
2600
2601 tx_data
2602 .validity_check(config)
2603 .map_err(Into::<IotaError>::into)?;
2604
2605 self.move_authenticators_validity_check(config)?;
2606
2607 Ok(tx_size)
2608 }
2609
2610 pub fn move_authenticators(&self) -> Vec<&MoveAuthenticator> {
2611 self.tx_signatures()
2612 .iter()
2613 .filter_map(|sig| {
2614 if let GenericSignature::MoveAuthenticator(move_authenticator) = sig {
2615 Some(move_authenticator)
2616 } else {
2617 None
2618 }
2619 })
2620 .collect()
2621 }
2622
2623 pub fn sender_move_authenticator(&self) -> Option<&MoveAuthenticator> {
2624 let sender = self.intent_message().value.sender();
2625
2626 self.move_authenticators()
2627 .into_iter()
2628 .find(|a| match a.address() {
2629 Ok(addr) => addr == sender,
2630 Err(_) => false,
2631 })
2632 }
2633
2634 pub fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>> {
2642 let mut input_objects_set = self
2643 .transaction_data()
2644 .input_objects()?
2645 .into_iter()
2646 .collect::<HashSet<_>>();
2647
2648 if let Some(move_authenticator) = self.sender_move_authenticator() {
2649 input_objects_set.extend(move_authenticator.input_objects());
2650 }
2651
2652 Ok(input_objects_set.into_iter().collect::<Vec<_>>())
2653 }
2654
2655 pub fn split_input_objects_into_groups_for_reading(
2662 &self,
2663 input_objects: InputObjects,
2664 ) -> IotaResult<(InputObjects, Option<InputObjects>, Option<ObjectReadResult>)> {
2665 let input_objects_map = input_objects
2666 .iter()
2667 .map(|o| (&o.input_object_kind, o))
2668 .collect::<HashMap<_, _>>();
2669
2670 let tx_input_objects = self
2671 .transaction_data()
2672 .input_objects()?
2673 .iter()
2674 .map(|k| {
2675 input_objects_map
2676 .get(k)
2677 .map(|&r| r.clone())
2678 .expect("All transaction input objects are expected to be present")
2679 })
2680 .collect::<Vec<_>>()
2681 .into();
2682
2683 let (auth_input_objects, account_object) =
2684 if let Some(move_authenticator) = self.sender_move_authenticator() {
2685 let auth_input_objects = move_authenticator
2686 .input_objects()
2687 .iter()
2688 .map(|k| {
2689 input_objects_map
2690 .get(k)
2691 .map(|&r| r.clone())
2692 .expect("All authenticator input objects are expected to be present")
2693 })
2694 .collect::<Vec<_>>()
2695 .into();
2696
2697 let account_objects = move_authenticator
2698 .object_to_authenticate()
2699 .input_objects()
2700 .iter()
2701 .map(|k| {
2702 input_objects_map
2703 .get(k)
2704 .map(|&r| r.clone())
2705 .expect("Account object is expected to be present")
2706 })
2707 .collect::<Vec<_>>();
2708
2709 debug_assert!(
2710 account_objects.len() == 1,
2711 "Only one account object must be loaded"
2712 );
2713
2714 (Some(auth_input_objects), account_objects.into_iter().next())
2715 } else {
2716 (None, None)
2717 };
2718
2719 Ok((tx_input_objects, auth_input_objects, account_object))
2720 }
2721
2722 pub fn contains_shared_object(&self) -> bool {
2725 !self.shared_input_objects().is_empty()
2726 }
2727
2728 pub fn shared_input_objects(&self) -> Vec<SharedInputObject> {
2738 let mut input_objects = self.transaction_data().shared_input_objects();
2740
2741 if let Some(move_authenticator) = self.sender_move_authenticator() {
2743 for auth_shared_object in move_authenticator.shared_objects() {
2744 let entry = input_objects
2745 .iter_mut()
2746 .find(|o| o.id == auth_shared_object.id);
2747
2748 match entry {
2749 None => input_objects.push(auth_shared_object),
2750 Some(existing) => existing
2751 .left_union(&auth_shared_object)
2752 .expect("union of shared objects should not fail"),
2753 }
2754 }
2755 }
2756
2757 input_objects
2758 }
2759
2760 pub fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>> {
2772 let mut input_objects = self.transaction_data().input_objects()?;
2775
2776 if let Some(move_authenticator) = self.sender_move_authenticator() {
2778 for auth_object in move_authenticator.input_objects() {
2779 let entry = input_objects
2780 .iter_mut()
2781 .find(|o| o.object_id() == auth_object.object_id());
2782
2783 match entry {
2784 None => input_objects.push(auth_object),
2785 Some(existing) => existing.left_union_with_checks(&auth_object)?,
2786 }
2787 }
2788 }
2789
2790 Ok(input_objects)
2791 }
2792
2793 pub fn uses_randomness(&self) -> bool {
2797 self.shared_input_objects()
2798 .iter()
2799 .any(|obj| obj.id() == IOTA_RANDOMNESS_STATE_OBJECT_ID)
2800 }
2801
2802 fn move_authenticators_validity_check(&self, config: &ProtocolConfig) -> IotaResult {
2803 let authenticators = self.move_authenticators();
2804
2805 authenticators
2807 .iter()
2808 .try_for_each(|authenticator| authenticator.validity_check(config))?;
2809
2810 let authenticators_num = authenticators.len();
2812 if authenticators_num > 0 {
2813 let tx_data = self.transaction_data();
2814
2815 fp_ensure!(
2816 tx_data.kind().is_programmable_transaction(),
2817 UserInputError::Unsupported(
2818 "SenderSignedData with MoveAuthenticator must be a programmable transaction"
2819 .to_string(),
2820 )
2821 .into()
2822 );
2823
2824 fp_ensure!(
2829 authenticators_num == 1,
2830 UserInputError::Unsupported(
2831 "SenderSignedData with more than one MoveAuthenticator is not supported"
2832 .to_string(),
2833 )
2834 .into()
2835 );
2836
2837 fp_ensure!(
2838 self.sender_move_authenticator().is_some(),
2839 UserInputError::Unsupported(
2840 "SenderSignedData can have MoveAuthenticator only for the sender".to_string(),
2841 )
2842 .into()
2843 );
2844
2845 Self::check_move_authenticators_input_consistency(tx_data, &authenticators)?;
2846 }
2847
2848 Ok(())
2849 }
2850
2851 fn check_move_authenticators_input_consistency(
2852 tx_data: &TransactionData,
2853 authenticators: &[&MoveAuthenticator],
2854 ) -> IotaResult {
2855 let mut checked_inputs = tx_data
2857 .kind()
2858 .input_objects()?
2859 .into_iter()
2860 .map(|o| (o.object_id(), o))
2861 .collect::<HashMap<_, _>>();
2862
2863 authenticators.iter().try_for_each(|authenticator| {
2864 authenticator
2865 .input_objects()
2866 .iter()
2867 .try_for_each(|auth_input_object| {
2868 match checked_inputs.get(&auth_input_object.object_id()) {
2869 Some(existing) => {
2870 auth_input_object.check_consistency_for_authentication(existing)?
2871 }
2872 None => {
2873 checked_inputs
2874 .insert(auth_input_object.object_id(), *auth_input_object);
2875 }
2876 };
2877
2878 Ok(())
2879 })
2880 })
2881 }
2882}
2883
2884impl Message for SenderSignedData {
2885 type DigestType = TransactionDigest;
2886 const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2887
2888 fn digest(&self) -> Self::DigestType {
2891 self.intent_message().value.digest()
2892 }
2893}
2894
2895impl<S> Envelope<SenderSignedData, S> {
2896 pub fn sender_address(&self) -> IotaAddress {
2897 self.data().intent_message().value.sender()
2898 }
2899
2900 pub fn gas(&self) -> &[ObjectRef] {
2901 self.data().intent_message().value.gas()
2902 }
2903
2904 pub fn key(&self) -> TransactionKey {
2906 match &self.data().intent_message().value.kind() {
2907 TransactionKind::RandomnessStateUpdate(rsu) => {
2908 TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2909 }
2910 _ => TransactionKey::Digest(*self.digest()),
2911 }
2912 }
2913
2914 pub fn non_digest_key(&self) -> Option<TransactionKey> {
2919 match &self.data().intent_message().value.kind() {
2920 TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2921 rsu.epoch,
2922 rsu.randomness_round,
2923 )),
2924 _ => None,
2925 }
2926 }
2927
2928 pub fn is_system_tx(&self) -> bool {
2929 self.data().intent_message().value.is_system_tx()
2930 }
2931
2932 pub fn is_sponsored_tx(&self) -> bool {
2933 self.data().intent_message().value.is_sponsored_tx()
2934 }
2935}
2936
2937impl Transaction {
2938 pub fn from_data_and_signer(
2939 data: TransactionData,
2940 signers: Vec<&dyn Signer<Signature>>,
2941 ) -> Self {
2942 let signatures = {
2943 let intent_msg = IntentMessage::new(Intent::iota_transaction(), &data);
2944 signers
2945 .into_iter()
2946 .map(|s| Signature::new_secure(&intent_msg, s))
2947 .collect()
2948 };
2949 Self::from_data(data, signatures)
2950 }
2951
2952 pub fn from_data(data: TransactionData, signatures: Vec<Signature>) -> Self {
2954 Self::from_generic_sig_data(data, signatures.into_iter().map(|s| s.into()).collect())
2955 }
2956
2957 pub fn signature_from_signer(
2958 data: TransactionData,
2959 intent: Intent,
2960 signer: &dyn Signer<Signature>,
2961 ) -> Signature {
2962 let intent_msg = IntentMessage::new(intent, data);
2963 Signature::new_secure(&intent_msg, signer)
2964 }
2965
2966 pub fn from_generic_sig_data(data: TransactionData, signatures: Vec<GenericSignature>) -> Self {
2967 Self::new(SenderSignedData::new(data, signatures))
2968 }
2969
2970 pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2973 (
2974 Base64::from_bytes(&bcs::to_bytes(&self.data().intent_message().value).unwrap()),
2975 self.data()
2976 .inner()
2977 .tx_signatures
2978 .iter()
2979 .map(|s| Base64::from_bytes(s.as_ref()))
2980 .collect(),
2981 )
2982 }
2983}
2984
2985impl VerifiedTransaction {
2986 pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2987 GenesisTransaction { objects, events }
2988 .pipe(TransactionKind::Genesis)
2989 .pipe(Self::new_system_transaction)
2990 }
2991
2992 pub fn new_consensus_commit_prologue_v1(
2993 epoch: u64,
2994 round: u64,
2995 commit_timestamp_ms: CheckpointTimestamp,
2996 consensus_commit_digest: ConsensusCommitDigest,
2997 cancelled_txn_version_assignment: Vec<(TransactionDigest, Vec<(ObjectID, SequenceNumber)>)>,
2998 ) -> Self {
2999 ConsensusCommitPrologueV1 {
3000 epoch,
3001 round,
3002 sub_dag_index: None,
3004 commit_timestamp_ms,
3005 consensus_commit_digest,
3006 consensus_determined_version_assignments:
3007 ConsensusDeterminedVersionAssignments::CancelledTransactions(
3008 cancelled_txn_version_assignment,
3009 ),
3010 }
3011 .pipe(TransactionKind::ConsensusCommitPrologueV1)
3012 .pipe(Self::new_system_transaction)
3013 }
3014
3015 pub fn new_authenticator_state_update(
3016 epoch: u64,
3017 round: u64,
3018 new_active_jwks: Vec<ActiveJwk>,
3019 authenticator_obj_initial_shared_version: SequenceNumber,
3020 ) -> Self {
3021 AuthenticatorStateUpdateV1 {
3022 epoch,
3023 round,
3024 new_active_jwks,
3025 authenticator_obj_initial_shared_version,
3026 }
3027 .pipe(TransactionKind::AuthenticatorStateUpdateV1)
3028 .pipe(Self::new_system_transaction)
3029 }
3030
3031 pub fn new_randomness_state_update(
3032 epoch: u64,
3033 randomness_round: RandomnessRound,
3034 random_bytes: Vec<u8>,
3035 randomness_obj_initial_shared_version: SequenceNumber,
3036 ) -> Self {
3037 RandomnessStateUpdate {
3038 epoch,
3039 randomness_round,
3040 random_bytes,
3041 randomness_obj_initial_shared_version,
3042 }
3043 .pipe(TransactionKind::RandomnessStateUpdate)
3044 .pipe(Self::new_system_transaction)
3045 }
3046
3047 pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
3048 TransactionKind::EndOfEpochTransaction(txns).pipe(Self::new_system_transaction)
3049 }
3050
3051 fn new_system_transaction(system_transaction: TransactionKind) -> Self {
3052 system_transaction
3053 .pipe(TransactionData::new_system_transaction)
3054 .pipe(|data| {
3055 SenderSignedData::new_from_sender_signature(
3056 data,
3057 Ed25519IotaSignature::from_bytes(&[0; Ed25519IotaSignature::LENGTH])
3058 .unwrap()
3059 .into(),
3060 )
3061 })
3062 .pipe(Transaction::new)
3063 .pipe(Self::new_from_verified)
3064 }
3065}
3066
3067impl VerifiedSignedTransaction {
3068 #[instrument(level = "trace", skip_all)]
3070 pub fn new(
3071 epoch: EpochId,
3072 transaction: VerifiedTransaction,
3073 authority: AuthorityName,
3074 secret: &dyn Signer<AuthoritySignature>,
3075 ) -> Self {
3076 Self::new_from_verified(SignedTransaction::new(
3077 epoch,
3078 transaction.into_inner().into_data(),
3079 secret,
3080 authority,
3081 ))
3082 }
3083}
3084
3085pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
3087pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
3088pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
3089
3090pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
3092pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
3093
3094impl Transaction {
3095 pub fn verify_signature_for_testing(
3096 &self,
3097 current_epoch: EpochId,
3098 verify_params: &VerifyParams,
3099 ) -> IotaResult {
3100 verify_sender_signed_data_message_signatures(
3101 self.data(),
3102 current_epoch,
3103 verify_params,
3104 Arc::new(VerifiedDigestCache::new_empty()),
3105 )
3106 }
3107
3108 pub fn try_into_verified_for_testing(
3109 self,
3110 current_epoch: EpochId,
3111 verify_params: &VerifyParams,
3112 ) -> IotaResult<VerifiedTransaction> {
3113 self.verify_signature_for_testing(current_epoch, verify_params)?;
3114 Ok(VerifiedTransaction::new_from_verified(self))
3115 }
3116}
3117
3118impl SignedTransaction {
3119 pub fn verify_signatures_authenticated_for_testing(
3120 &self,
3121 committee: &Committee,
3122 verify_params: &VerifyParams,
3123 ) -> IotaResult {
3124 verify_sender_signed_data_message_signatures(
3125 self.data(),
3126 committee.epoch(),
3127 verify_params,
3128 Arc::new(VerifiedDigestCache::new_empty()),
3129 )?;
3130
3131 self.auth_sig().verify_secure(
3132 self.data(),
3133 Intent::iota_app(IntentScope::SenderSignedTransaction),
3134 committee,
3135 )
3136 }
3137
3138 pub fn try_into_verified_for_testing(
3139 self,
3140 committee: &Committee,
3141 verify_params: &VerifyParams,
3142 ) -> IotaResult<VerifiedSignedTransaction> {
3143 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
3144 Ok(VerifiedSignedTransaction::new_from_verified(self))
3145 }
3146}
3147
3148pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3149
3150impl CertifiedTransaction {
3151 pub fn certificate_digest(&self) -> CertificateDigest {
3152 let mut digest = DefaultHash::default();
3153 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
3154 let hash = digest.finalize();
3155 CertificateDigest::new(hash.into())
3156 }
3157
3158 pub fn gas_price(&self) -> u64 {
3159 self.data().transaction_data().gas_price()
3160 }
3161
3162 #[instrument(level = "trace", skip_all)]
3165 pub fn verify_signatures_authenticated(
3166 &self,
3167 committee: &Committee,
3168 verify_params: &VerifyParams,
3169 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
3170 ) -> IotaResult {
3171 verify_sender_signed_data_message_signatures(
3172 self.data(),
3173 committee.epoch(),
3174 verify_params,
3175 zklogin_inputs_cache,
3176 )?;
3177 self.auth_sig().verify_secure(
3178 self.data(),
3179 Intent::iota_app(IntentScope::SenderSignedTransaction),
3180 committee,
3181 )
3182 }
3183
3184 pub fn try_into_verified_for_testing(
3185 self,
3186 committee: &Committee,
3187 verify_params: &VerifyParams,
3188 ) -> IotaResult<VerifiedCertificate> {
3189 self.verify_signatures_authenticated(
3190 committee,
3191 verify_params,
3192 Arc::new(VerifiedDigestCache::new_empty()),
3193 )?;
3194 Ok(VerifiedCertificate::new_from_verified(self))
3195 }
3196
3197 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
3198 self.auth_sig().verify_secure(
3199 self.data(),
3200 Intent::iota_app(IntentScope::SenderSignedTransaction),
3201 committee,
3202 )
3203 }
3204}
3205
3206pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3207pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3208
3209#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
3210pub enum InputObjectKind {
3211 MovePackage(ObjectID),
3213 ImmOrOwnedMoveObject(ObjectRef),
3215 SharedMoveObject {
3217 id: ObjectID,
3218 initial_shared_version: SequenceNumber,
3219 mutable: bool,
3220 },
3221}
3222
3223impl InputObjectKind {
3224 pub fn object_id(&self) -> ObjectID {
3225 match self {
3226 Self::MovePackage(id) => *id,
3227 Self::ImmOrOwnedMoveObject((id, _, _)) => *id,
3228 Self::SharedMoveObject { id, .. } => *id,
3229 }
3230 }
3231
3232 pub fn version(&self) -> Option<SequenceNumber> {
3233 match self {
3234 Self::MovePackage(..) => None,
3235 Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version),
3236 Self::SharedMoveObject { .. } => None,
3237 }
3238 }
3239
3240 pub fn object_not_found_error(&self) -> UserInputError {
3241 match *self {
3242 Self::MovePackage(package_id) => {
3243 UserInputError::DependentPackageNotFound { package_id }
3244 }
3245 Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound {
3246 object_id,
3247 version: Some(version),
3248 },
3249 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
3250 object_id: id,
3251 version: None,
3252 },
3253 }
3254 }
3255
3256 pub fn is_shared_object(&self) -> bool {
3257 matches!(self, Self::SharedMoveObject { .. })
3258 }
3259
3260 pub fn is_mutable(&self) -> bool {
3261 match self {
3262 Self::MovePackage(..) => false,
3263 Self::ImmOrOwnedMoveObject((_, _, _)) => true,
3264 Self::SharedMoveObject { mutable, .. } => *mutable,
3265 }
3266 }
3267
3268 pub fn left_union_with_checks(&mut self, other: &InputObjectKind) -> UserInputResult<()> {
3274 match self {
3275 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3276 fp_ensure!(
3277 self == other,
3278 UserInputError::InconsistentInput {
3279 object_id: other.object_id(),
3280 }
3281 );
3282 }
3283 InputObjectKind::SharedMoveObject {
3284 id,
3285 initial_shared_version,
3286 mutable,
3287 } => match other {
3288 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3289 fp_bail!(UserInputError::NotSharedObject)
3290 }
3291 InputObjectKind::SharedMoveObject {
3292 id: other_id,
3293 initial_shared_version: other_initial_shared_version,
3294 mutable: other_mutable,
3295 } => {
3296 fp_ensure!(id == other_id, UserInputError::SharedObjectIdMismatch);
3297 fp_ensure!(
3298 initial_shared_version == other_initial_shared_version,
3299 UserInputError::SharedObjectStartingVersionMismatch
3300 );
3301
3302 if !*mutable && *other_mutable {
3303 *mutable = *other_mutable;
3304 }
3305 }
3306 },
3307 }
3308
3309 Ok(())
3310 }
3311
3312 pub fn check_consistency_for_authentication(
3316 &self,
3317 other: &InputObjectKind,
3318 ) -> UserInputResult<()> {
3319 match self {
3320 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3321 fp_ensure!(
3322 self == other,
3323 UserInputError::InconsistentInput {
3324 object_id: self.object_id()
3325 }
3326 );
3327 }
3328 InputObjectKind::SharedMoveObject {
3329 id,
3330 initial_shared_version,
3331 mutable: _,
3332 } => match other {
3333 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3334 fp_bail!(UserInputError::InconsistentInput {
3335 object_id: self.object_id()
3336 })
3337 }
3338 InputObjectKind::SharedMoveObject {
3339 id: other_id,
3340 initial_shared_version: other_initial_shared_version,
3341 mutable: _,
3342 } => {
3343 fp_ensure!(
3344 id == other_id,
3345 UserInputError::InconsistentInput { object_id: *id }
3346 );
3347 fp_ensure!(
3348 initial_shared_version == other_initial_shared_version,
3349 UserInputError::InconsistentInput { object_id: *id }
3350 );
3351 }
3352 },
3353 }
3354
3355 Ok(())
3356 }
3357}
3358
3359#[derive(Clone, Debug)]
3363pub struct ObjectReadResult {
3364 pub input_object_kind: InputObjectKind,
3365 pub object: ObjectReadResultKind,
3366}
3367
3368#[derive(Clone, PartialEq)]
3369pub enum ObjectReadResultKind {
3370 Object(Object),
3371 DeletedSharedObject(SequenceNumber, TransactionDigest),
3374 CancelledTransactionSharedObject(SequenceNumber),
3376}
3377
3378impl std::fmt::Debug for ObjectReadResultKind {
3379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3380 match self {
3381 ObjectReadResultKind::Object(obj) => {
3382 write!(f, "Object({:?})", obj.compute_object_reference())
3383 }
3384 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
3385 write!(f, "DeletedSharedObject({seq}, {digest:?})")
3386 }
3387 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
3388 write!(f, "CancelledTransactionSharedObject({seq})")
3389 }
3390 }
3391 }
3392}
3393
3394impl From<Object> for ObjectReadResultKind {
3395 fn from(object: Object) -> Self {
3396 Self::Object(object)
3397 }
3398}
3399
3400impl ObjectReadResult {
3401 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
3402 if let (
3403 InputObjectKind::ImmOrOwnedMoveObject(_),
3404 ObjectReadResultKind::DeletedSharedObject(_, _),
3405 ) = (&input_object_kind, &object)
3406 {
3407 panic!("only shared objects can be DeletedSharedObject");
3408 }
3409
3410 if let (
3411 InputObjectKind::ImmOrOwnedMoveObject(_),
3412 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3413 ) = (&input_object_kind, &object)
3414 {
3415 panic!("only shared objects can be CancelledTransactionSharedObject");
3416 }
3417
3418 Self {
3419 input_object_kind,
3420 object,
3421 }
3422 }
3423
3424 pub fn id(&self) -> ObjectID {
3425 self.input_object_kind.object_id()
3426 }
3427
3428 pub fn as_object(&self) -> Option<&Object> {
3429 match &self.object {
3430 ObjectReadResultKind::Object(object) => Some(object),
3431 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
3432 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3433 }
3434 }
3435
3436 pub fn new_from_gas_object(gas: &Object) -> Self {
3437 let objref = gas.compute_object_reference();
3438 Self {
3439 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
3440 object: ObjectReadResultKind::Object(gas.clone()),
3441 }
3442 }
3443
3444 pub fn is_mutable(&self) -> bool {
3445 match (&self.input_object_kind, &self.object) {
3446 (InputObjectKind::MovePackage(_), _) => false,
3447 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
3448 !object.is_immutable()
3449 }
3450 (
3451 InputObjectKind::ImmOrOwnedMoveObject(_),
3452 ObjectReadResultKind::DeletedSharedObject(_, _),
3453 ) => unreachable!(),
3454 (
3455 InputObjectKind::ImmOrOwnedMoveObject(_),
3456 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3457 ) => unreachable!(),
3458 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
3459 }
3460 }
3461
3462 pub fn is_shared_object(&self) -> bool {
3463 self.input_object_kind.is_shared_object()
3464 }
3465
3466 pub fn is_deleted_shared_object(&self) -> bool {
3467 self.deletion_info().is_some()
3468 }
3469
3470 pub fn deletion_info(&self) -> Option<(SequenceNumber, TransactionDigest)> {
3471 match &self.object {
3472 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
3473 _ => None,
3474 }
3475 }
3476
3477 pub fn get_owned_objref(&self) -> Option<ObjectRef> {
3480 match (&self.input_object_kind, &self.object) {
3481 (InputObjectKind::MovePackage(_), _) => None,
3482 (
3483 InputObjectKind::ImmOrOwnedMoveObject(objref),
3484 ObjectReadResultKind::Object(object),
3485 ) => {
3486 if object.is_immutable() {
3487 None
3488 } else {
3489 Some(*objref)
3490 }
3491 }
3492 (
3493 InputObjectKind::ImmOrOwnedMoveObject(_),
3494 ObjectReadResultKind::DeletedSharedObject(_, _),
3495 ) => unreachable!(),
3496 (
3497 InputObjectKind::ImmOrOwnedMoveObject(_),
3498 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3499 ) => unreachable!(),
3500 (InputObjectKind::SharedMoveObject { .. }, _) => None,
3501 }
3502 }
3503
3504 pub fn is_owned(&self) -> bool {
3505 self.get_owned_objref().is_some()
3506 }
3507
3508 pub fn to_shared_input(&self) -> Option<SharedInput> {
3509 match self.input_object_kind {
3510 InputObjectKind::MovePackage(_) => None,
3511 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
3512 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
3513 ObjectReadResultKind::Object(obj) => {
3514 SharedInput::Existing(obj.compute_object_reference())
3515 }
3516 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
3517 SharedInput::Deleted((id, *seq, mutable, *digest))
3518 }
3519 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
3520 SharedInput::Cancelled((id, *seq))
3521 }
3522 }),
3523 }
3524 }
3525
3526 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
3527 match &self.object {
3528 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
3529 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
3530 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3531 }
3532 }
3533}
3534
3535#[derive(Clone)]
3536pub struct InputObjects {
3537 objects: Vec<ObjectReadResult>,
3538}
3539
3540impl std::fmt::Debug for InputObjects {
3541 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3542 f.debug_list().entries(self.objects.iter()).finish()
3543 }
3544}
3545
3546pub struct CheckedInputObjects(InputObjects);
3549
3550impl CheckedInputObjects {
3556 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3558 Self(inputs)
3559 }
3560
3561 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3563 Self(InputObjects::new(input_objects))
3564 }
3565
3566 pub fn new_for_replay(input_objects: InputObjects) -> Self {
3568 Self(input_objects)
3569 }
3570
3571 pub fn inner(&self) -> &InputObjects {
3572 &self.0
3573 }
3574
3575 pub fn into_inner(self) -> InputObjects {
3576 self.0
3577 }
3578}
3579
3580impl From<Vec<ObjectReadResult>> for InputObjects {
3581 fn from(objects: Vec<ObjectReadResult>) -> Self {
3582 Self::new(objects)
3583 }
3584}
3585
3586impl InputObjects {
3587 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
3588 Self { objects }
3589 }
3590
3591 pub fn len(&self) -> usize {
3592 self.objects.len()
3593 }
3594
3595 pub fn is_empty(&self) -> bool {
3596 self.objects.is_empty()
3597 }
3598
3599 pub fn contains_deleted_objects(&self) -> bool {
3600 self.objects
3601 .iter()
3602 .any(|obj| obj.is_deleted_shared_object())
3603 }
3604
3605 pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectID>, SequenceNumber)> {
3608 let mut contains_cancelled = false;
3609 let mut cancel_reason = None;
3610 let mut cancelled_objects = Vec::new();
3611 for obj in &self.objects {
3612 if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
3613 contains_cancelled = true;
3614 if version.is_congested() || version == SequenceNumber::RANDOMNESS_UNAVAILABLE {
3615 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
3617 cancel_reason = Some(version);
3618 cancelled_objects.push(obj.id());
3619 }
3620 }
3621 }
3622
3623 if !cancelled_objects.is_empty() {
3624 Some((
3625 cancelled_objects,
3626 cancel_reason
3627 .expect("there should be a cancel reason if there are cancelled objects"),
3628 ))
3629 } else {
3630 assert!(!contains_cancelled);
3631 None
3632 }
3633 }
3634
3635 pub fn filter_owned_objects(&self) -> Vec<ObjectRef> {
3636 let owned_objects: Vec<_> = self
3637 .objects
3638 .iter()
3639 .filter_map(|obj| obj.get_owned_objref())
3640 .collect();
3641
3642 trace!(
3643 num_mutable_objects = owned_objects.len(),
3644 "Checked locks and found mutable objects"
3645 );
3646
3647 owned_objects
3648 }
3649
3650 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
3651 self.objects
3652 .iter()
3653 .filter(|obj| obj.is_shared_object())
3654 .map(|obj| {
3655 obj.to_shared_input()
3656 .expect("already filtered for shared objects")
3657 })
3658 .collect()
3659 }
3660
3661 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3662 self.objects
3663 .iter()
3664 .filter_map(|obj| obj.get_previous_transaction())
3665 .collect()
3666 }
3667
3668 pub fn mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
3669 self.objects
3670 .iter()
3671 .filter_map(
3672 |ObjectReadResult {
3673 input_object_kind,
3674 object,
3675 }| match (input_object_kind, object) {
3676 (InputObjectKind::MovePackage(_), _) => None,
3677 (
3678 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3679 ObjectReadResultKind::Object(object),
3680 ) => {
3681 if object.is_immutable() {
3682 None
3683 } else {
3684 Some((object_ref.0, ((object_ref.1, object_ref.2), object.owner)))
3685 }
3686 }
3687 (
3688 InputObjectKind::ImmOrOwnedMoveObject(_),
3689 ObjectReadResultKind::DeletedSharedObject(_, _),
3690 ) => {
3691 unreachable!()
3692 }
3693 (
3694 InputObjectKind::SharedMoveObject { .. },
3695 ObjectReadResultKind::DeletedSharedObject(_, _),
3696 ) => None,
3697 (
3698 InputObjectKind::SharedMoveObject { mutable, .. },
3699 ObjectReadResultKind::Object(object),
3700 ) => {
3701 if *mutable {
3702 let oref = object.compute_object_reference();
3703 Some((oref.0, ((oref.1, oref.2), object.owner)))
3704 } else {
3705 None
3706 }
3707 }
3708 (
3709 InputObjectKind::ImmOrOwnedMoveObject(_),
3710 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3711 ) => {
3712 unreachable!()
3713 }
3714 (
3715 InputObjectKind::SharedMoveObject { .. },
3716 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3717 ) => None,
3718 },
3719 )
3720 .collect()
3721 }
3722
3723 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectRef]) -> SequenceNumber {
3727 let input_versions = self
3728 .objects
3729 .iter()
3730 .filter_map(|object| match &object.object {
3731 ObjectReadResultKind::Object(object) => {
3732 object.data.try_as_move().map(MoveObject::version)
3733 }
3734 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3735 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3736 })
3737 .chain(receiving_objects.iter().map(|object_ref| object_ref.1));
3738
3739 SequenceNumber::lamport_increment(input_versions)
3740 }
3741
3742 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3743 self.objects.iter().map(
3744 |ObjectReadResult {
3745 input_object_kind, ..
3746 }| input_object_kind,
3747 )
3748 }
3749
3750 pub fn into_object_map(self) -> BTreeMap<ObjectID, Object> {
3751 self.objects
3752 .into_iter()
3753 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3754 .collect()
3755 }
3756
3757 pub fn push(&mut self, object: ObjectReadResult) {
3758 self.objects.push(object);
3759 }
3760
3761 pub fn find_object_id_mut(&mut self, object_id: ObjectID) -> Option<&mut ObjectReadResult> {
3763 self.objects.iter_mut().find(|o| o.id() == object_id)
3764 }
3765
3766 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3767 self.objects.iter()
3768 }
3769
3770 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3771 self.objects.iter().filter_map(|o| o.as_object())
3772 }
3773}
3774
3775#[derive(Clone, Debug)]
3779pub enum ReceivingObjectReadResultKind {
3780 Object(Object),
3781 PreviouslyReceivedObject,
3783}
3784
3785impl ReceivingObjectReadResultKind {
3786 pub fn as_object(&self) -> Option<&Object> {
3787 match &self {
3788 Self::Object(object) => Some(object),
3789 Self::PreviouslyReceivedObject => None,
3790 }
3791 }
3792}
3793
3794pub struct ReceivingObjectReadResult {
3795 pub object_ref: ObjectRef,
3796 pub object: ReceivingObjectReadResultKind,
3797}
3798
3799impl ReceivingObjectReadResult {
3800 pub fn new(object_ref: ObjectRef, object: ReceivingObjectReadResultKind) -> Self {
3801 Self { object_ref, object }
3802 }
3803
3804 pub fn is_previously_received(&self) -> bool {
3805 matches!(
3806 self.object,
3807 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3808 )
3809 }
3810}
3811
3812impl From<Object> for ReceivingObjectReadResultKind {
3813 fn from(object: Object) -> Self {
3814 Self::Object(object)
3815 }
3816}
3817
3818pub struct ReceivingObjects {
3819 pub objects: Vec<ReceivingObjectReadResult>,
3820}
3821
3822impl ReceivingObjects {
3823 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3824 self.objects.iter()
3825 }
3826
3827 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3828 self.objects.iter().filter_map(|o| o.object.as_object())
3829 }
3830}
3831
3832impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3833 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3834 Self { objects }
3835 }
3836}
3837
3838impl Display for CertifiedTransaction {
3839 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3840 let mut writer = String::new();
3841 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3842 writeln!(
3843 writer,
3844 "Signed Authorities Bitmap : {:?}",
3845 self.auth_sig().signers_map
3846 )?;
3847 write!(writer, "{}", &self.data().intent_message().value.kind())?;
3848 write!(f, "{writer}")
3849 }
3850}
3851
3852#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3857pub enum TransactionKey {
3858 Digest(TransactionDigest),
3859 RandomnessRound(EpochId, RandomnessRound),
3860}
3861
3862impl TransactionKey {
3863 pub fn unwrap_digest(&self) -> &TransactionDigest {
3864 match self {
3865 TransactionKey::Digest(d) => d,
3866 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3867 }
3868 }
3869}