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