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 pub fn new(
3080 epoch: EpochId,
3081 transaction: VerifiedTransaction,
3082 authority: AuthorityName,
3083 secret: &dyn Signer<AuthoritySignature>,
3084 ) -> Self {
3085 Self::new_from_verified(SignedTransaction::new(
3086 epoch,
3087 transaction.into_inner().into_data(),
3088 secret,
3089 authority,
3090 ))
3091 }
3092}
3093
3094pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
3096pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
3097pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
3098
3099pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
3101pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
3102
3103impl Transaction {
3104 pub fn verify_signature_for_testing(
3105 &self,
3106 current_epoch: EpochId,
3107 verify_params: &VerifyParams,
3108 ) -> IotaResult {
3109 verify_sender_signed_data_message_signatures(
3110 self.data(),
3111 current_epoch,
3112 verify_params,
3113 Arc::new(VerifiedDigestCache::new_empty()),
3114 )
3115 }
3116
3117 pub fn try_into_verified_for_testing(
3118 self,
3119 current_epoch: EpochId,
3120 verify_params: &VerifyParams,
3121 ) -> IotaResult<VerifiedTransaction> {
3122 self.verify_signature_for_testing(current_epoch, verify_params)?;
3123 Ok(VerifiedTransaction::new_from_verified(self))
3124 }
3125}
3126
3127impl SignedTransaction {
3128 pub fn verify_signatures_authenticated_for_testing(
3129 &self,
3130 committee: &Committee,
3131 verify_params: &VerifyParams,
3132 ) -> IotaResult {
3133 verify_sender_signed_data_message_signatures(
3134 self.data(),
3135 committee.epoch(),
3136 verify_params,
3137 Arc::new(VerifiedDigestCache::new_empty()),
3138 )?;
3139
3140 self.auth_sig().verify_secure(
3141 self.data(),
3142 Intent::iota_app(IntentScope::SenderSignedTransaction),
3143 committee,
3144 )
3145 }
3146
3147 pub fn try_into_verified_for_testing(
3148 self,
3149 committee: &Committee,
3150 verify_params: &VerifyParams,
3151 ) -> IotaResult<VerifiedSignedTransaction> {
3152 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
3153 Ok(VerifiedSignedTransaction::new_from_verified(self))
3154 }
3155}
3156
3157pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3158
3159impl CertifiedTransaction {
3160 pub fn certificate_digest(&self) -> CertificateDigest {
3161 let mut digest = DefaultHash::default();
3162 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
3163 let hash = digest.finalize();
3164 CertificateDigest::new(hash.into())
3165 }
3166
3167 pub fn gas_price(&self) -> u64 {
3168 self.data().transaction_data().gas_price()
3169 }
3170
3171 pub fn verify_signatures_authenticated(
3174 &self,
3175 committee: &Committee,
3176 verify_params: &VerifyParams,
3177 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
3178 ) -> IotaResult {
3179 verify_sender_signed_data_message_signatures(
3180 self.data(),
3181 committee.epoch(),
3182 verify_params,
3183 zklogin_inputs_cache,
3184 )?;
3185 self.auth_sig().verify_secure(
3186 self.data(),
3187 Intent::iota_app(IntentScope::SenderSignedTransaction),
3188 committee,
3189 )
3190 }
3191
3192 pub fn try_into_verified_for_testing(
3193 self,
3194 committee: &Committee,
3195 verify_params: &VerifyParams,
3196 ) -> IotaResult<VerifiedCertificate> {
3197 self.verify_signatures_authenticated(
3198 committee,
3199 verify_params,
3200 Arc::new(VerifiedDigestCache::new_empty()),
3201 )?;
3202 Ok(VerifiedCertificate::new_from_verified(self))
3203 }
3204
3205 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
3206 self.auth_sig().verify_secure(
3207 self.data(),
3208 Intent::iota_app(IntentScope::SenderSignedTransaction),
3209 committee,
3210 )
3211 }
3212}
3213
3214pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3215pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3216
3217#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
3218pub enum InputObjectKind {
3219 MovePackage(ObjectID),
3221 ImmOrOwnedMoveObject(ObjectRef),
3223 SharedMoveObject {
3225 id: ObjectID,
3226 initial_shared_version: SequenceNumber,
3227 mutable: bool,
3228 },
3229}
3230
3231impl InputObjectKind {
3232 pub fn object_id(&self) -> ObjectID {
3233 match self {
3234 Self::MovePackage(id) => *id,
3235 Self::ImmOrOwnedMoveObject((id, _, _)) => *id,
3236 Self::SharedMoveObject { id, .. } => *id,
3237 }
3238 }
3239
3240 pub fn version(&self) -> Option<SequenceNumber> {
3241 match self {
3242 Self::MovePackage(..) => None,
3243 Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version),
3244 Self::SharedMoveObject { .. } => None,
3245 }
3246 }
3247
3248 pub fn object_not_found_error(&self) -> UserInputError {
3249 match *self {
3250 Self::MovePackage(package_id) => {
3251 UserInputError::DependentPackageNotFound { package_id }
3252 }
3253 Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound {
3254 object_id,
3255 version: Some(version),
3256 },
3257 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
3258 object_id: id,
3259 version: None,
3260 },
3261 }
3262 }
3263
3264 pub fn is_shared_object(&self) -> bool {
3265 matches!(self, Self::SharedMoveObject { .. })
3266 }
3267
3268 pub fn is_mutable(&self) -> bool {
3269 match self {
3270 Self::MovePackage(..) => false,
3271 Self::ImmOrOwnedMoveObject((_, _, _)) => true,
3272 Self::SharedMoveObject { mutable, .. } => *mutable,
3273 }
3274 }
3275
3276 pub fn left_union_with_checks(&mut self, other: &InputObjectKind) -> UserInputResult<()> {
3282 match self {
3283 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3284 fp_ensure!(
3285 self == other,
3286 UserInputError::InconsistentInput {
3287 object_id: other.object_id(),
3288 }
3289 );
3290 }
3291 InputObjectKind::SharedMoveObject {
3292 id,
3293 initial_shared_version,
3294 mutable,
3295 } => match other {
3296 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3297 fp_bail!(UserInputError::NotSharedObject)
3298 }
3299 InputObjectKind::SharedMoveObject {
3300 id: other_id,
3301 initial_shared_version: other_initial_shared_version,
3302 mutable: other_mutable,
3303 } => {
3304 fp_ensure!(id == other_id, UserInputError::SharedObjectIdMismatch);
3305 fp_ensure!(
3306 initial_shared_version == other_initial_shared_version,
3307 UserInputError::SharedObjectStartingVersionMismatch
3308 );
3309
3310 if !*mutable && *other_mutable {
3311 *mutable = *other_mutable;
3312 }
3313 }
3314 },
3315 }
3316
3317 Ok(())
3318 }
3319
3320 pub fn check_consistency_for_authentication(
3324 &self,
3325 other: &InputObjectKind,
3326 ) -> UserInputResult<()> {
3327 match self {
3328 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3329 fp_ensure!(
3330 self == other,
3331 UserInputError::InconsistentInput {
3332 object_id: self.object_id()
3333 }
3334 );
3335 }
3336 InputObjectKind::SharedMoveObject {
3337 id,
3338 initial_shared_version,
3339 mutable: _,
3340 } => match other {
3341 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
3342 fp_bail!(UserInputError::InconsistentInput {
3343 object_id: self.object_id()
3344 })
3345 }
3346 InputObjectKind::SharedMoveObject {
3347 id: other_id,
3348 initial_shared_version: other_initial_shared_version,
3349 mutable: _,
3350 } => {
3351 fp_ensure!(
3352 id == other_id,
3353 UserInputError::InconsistentInput { object_id: *id }
3354 );
3355 fp_ensure!(
3356 initial_shared_version == other_initial_shared_version,
3357 UserInputError::InconsistentInput { object_id: *id }
3358 );
3359 }
3360 },
3361 }
3362
3363 Ok(())
3364 }
3365}
3366
3367#[derive(Clone, Debug)]
3371pub struct ObjectReadResult {
3372 pub input_object_kind: InputObjectKind,
3373 pub object: ObjectReadResultKind,
3374}
3375
3376#[derive(Clone, PartialEq)]
3377pub enum ObjectReadResultKind {
3378 Object(Object),
3379 DeletedSharedObject(SequenceNumber, TransactionDigest),
3382 CancelledTransactionSharedObject(SequenceNumber),
3384}
3385
3386impl std::fmt::Debug for ObjectReadResultKind {
3387 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3388 match self {
3389 ObjectReadResultKind::Object(obj) => {
3390 write!(f, "Object({:?})", obj.compute_object_reference())
3391 }
3392 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
3393 write!(f, "DeletedSharedObject({seq}, {digest:?})")
3394 }
3395 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
3396 write!(f, "CancelledTransactionSharedObject({seq})")
3397 }
3398 }
3399 }
3400}
3401
3402impl From<Object> for ObjectReadResultKind {
3403 fn from(object: Object) -> Self {
3404 Self::Object(object)
3405 }
3406}
3407
3408impl ObjectReadResult {
3409 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
3410 if let (
3411 InputObjectKind::ImmOrOwnedMoveObject(_),
3412 ObjectReadResultKind::DeletedSharedObject(_, _),
3413 ) = (&input_object_kind, &object)
3414 {
3415 panic!("only shared objects can be DeletedSharedObject");
3416 }
3417
3418 if let (
3419 InputObjectKind::ImmOrOwnedMoveObject(_),
3420 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3421 ) = (&input_object_kind, &object)
3422 {
3423 panic!("only shared objects can be CancelledTransactionSharedObject");
3424 }
3425
3426 Self {
3427 input_object_kind,
3428 object,
3429 }
3430 }
3431
3432 pub fn id(&self) -> ObjectID {
3433 self.input_object_kind.object_id()
3434 }
3435
3436 pub fn as_object(&self) -> Option<&Object> {
3437 match &self.object {
3438 ObjectReadResultKind::Object(object) => Some(object),
3439 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
3440 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3441 }
3442 }
3443
3444 pub fn new_from_gas_object(gas: &Object) -> Self {
3445 let objref = gas.compute_object_reference();
3446 Self {
3447 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
3448 object: ObjectReadResultKind::Object(gas.clone()),
3449 }
3450 }
3451
3452 pub fn is_mutable(&self) -> bool {
3453 match (&self.input_object_kind, &self.object) {
3454 (InputObjectKind::MovePackage(_), _) => false,
3455 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
3456 !object.is_immutable()
3457 }
3458 (
3459 InputObjectKind::ImmOrOwnedMoveObject(_),
3460 ObjectReadResultKind::DeletedSharedObject(_, _),
3461 ) => unreachable!(),
3462 (
3463 InputObjectKind::ImmOrOwnedMoveObject(_),
3464 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3465 ) => unreachable!(),
3466 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
3467 }
3468 }
3469
3470 pub fn is_shared_object(&self) -> bool {
3471 self.input_object_kind.is_shared_object()
3472 }
3473
3474 pub fn is_deleted_shared_object(&self) -> bool {
3475 self.deletion_info().is_some()
3476 }
3477
3478 pub fn deletion_info(&self) -> Option<(SequenceNumber, TransactionDigest)> {
3479 match &self.object {
3480 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
3481 _ => None,
3482 }
3483 }
3484
3485 pub fn get_owned_objref(&self) -> Option<ObjectRef> {
3488 match (&self.input_object_kind, &self.object) {
3489 (InputObjectKind::MovePackage(_), _) => None,
3490 (
3491 InputObjectKind::ImmOrOwnedMoveObject(objref),
3492 ObjectReadResultKind::Object(object),
3493 ) => {
3494 if object.is_immutable() {
3495 None
3496 } else {
3497 Some(*objref)
3498 }
3499 }
3500 (
3501 InputObjectKind::ImmOrOwnedMoveObject(_),
3502 ObjectReadResultKind::DeletedSharedObject(_, _),
3503 ) => unreachable!(),
3504 (
3505 InputObjectKind::ImmOrOwnedMoveObject(_),
3506 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3507 ) => unreachable!(),
3508 (InputObjectKind::SharedMoveObject { .. }, _) => None,
3509 }
3510 }
3511
3512 pub fn is_owned(&self) -> bool {
3513 self.get_owned_objref().is_some()
3514 }
3515
3516 pub fn to_shared_input(&self) -> Option<SharedInput> {
3517 match self.input_object_kind {
3518 InputObjectKind::MovePackage(_) => None,
3519 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
3520 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
3521 ObjectReadResultKind::Object(obj) => {
3522 SharedInput::Existing(obj.compute_object_reference())
3523 }
3524 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
3525 SharedInput::Deleted((id, *seq, mutable, *digest))
3526 }
3527 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
3528 SharedInput::Cancelled((id, *seq))
3529 }
3530 }),
3531 }
3532 }
3533
3534 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
3535 match &self.object {
3536 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
3537 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
3538 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3539 }
3540 }
3541}
3542
3543#[derive(Clone)]
3544pub struct InputObjects {
3545 objects: Vec<ObjectReadResult>,
3546}
3547
3548impl std::fmt::Debug for InputObjects {
3549 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3550 f.debug_list().entries(self.objects.iter()).finish()
3551 }
3552}
3553
3554pub struct CheckedInputObjects(InputObjects);
3557
3558impl CheckedInputObjects {
3564 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3566 Self(inputs)
3567 }
3568
3569 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3571 Self(InputObjects::new(input_objects))
3572 }
3573
3574 pub fn new_for_replay(input_objects: InputObjects) -> Self {
3576 Self(input_objects)
3577 }
3578
3579 pub fn inner(&self) -> &InputObjects {
3580 &self.0
3581 }
3582
3583 pub fn into_inner(self) -> InputObjects {
3584 self.0
3585 }
3586}
3587
3588impl From<Vec<ObjectReadResult>> for InputObjects {
3589 fn from(objects: Vec<ObjectReadResult>) -> Self {
3590 Self::new(objects)
3591 }
3592}
3593
3594impl InputObjects {
3595 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
3596 Self { objects }
3597 }
3598
3599 pub fn len(&self) -> usize {
3600 self.objects.len()
3601 }
3602
3603 pub fn is_empty(&self) -> bool {
3604 self.objects.is_empty()
3605 }
3606
3607 pub fn contains_deleted_objects(&self) -> bool {
3608 self.objects
3609 .iter()
3610 .any(|obj| obj.is_deleted_shared_object())
3611 }
3612
3613 pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectID>, SequenceNumber)> {
3616 let mut contains_cancelled = false;
3617 let mut cancel_reason = None;
3618 let mut cancelled_objects = Vec::new();
3619 for obj in &self.objects {
3620 if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
3621 contains_cancelled = true;
3622 if version.is_congested() || version == SequenceNumber::RANDOMNESS_UNAVAILABLE {
3623 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
3625 cancel_reason = Some(version);
3626 cancelled_objects.push(obj.id());
3627 }
3628 }
3629 }
3630
3631 if !cancelled_objects.is_empty() {
3632 Some((
3633 cancelled_objects,
3634 cancel_reason
3635 .expect("there should be a cancel reason if there are cancelled objects"),
3636 ))
3637 } else {
3638 assert!(!contains_cancelled);
3639 None
3640 }
3641 }
3642
3643 pub fn filter_owned_objects(&self) -> Vec<ObjectRef> {
3644 let owned_objects: Vec<_> = self
3645 .objects
3646 .iter()
3647 .filter_map(|obj| obj.get_owned_objref())
3648 .collect();
3649
3650 trace!(
3651 num_mutable_objects = owned_objects.len(),
3652 "Checked locks and found mutable objects"
3653 );
3654
3655 owned_objects
3656 }
3657
3658 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
3659 self.objects
3660 .iter()
3661 .filter(|obj| obj.is_shared_object())
3662 .map(|obj| {
3663 obj.to_shared_input()
3664 .expect("already filtered for shared objects")
3665 })
3666 .collect()
3667 }
3668
3669 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3670 self.objects
3671 .iter()
3672 .filter_map(|obj| obj.get_previous_transaction())
3673 .collect()
3674 }
3675
3676 pub fn mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
3677 self.objects
3678 .iter()
3679 .filter_map(
3680 |ObjectReadResult {
3681 input_object_kind,
3682 object,
3683 }| match (input_object_kind, object) {
3684 (InputObjectKind::MovePackage(_), _) => None,
3685 (
3686 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3687 ObjectReadResultKind::Object(object),
3688 ) => {
3689 if object.is_immutable() {
3690 None
3691 } else {
3692 Some((object_ref.0, ((object_ref.1, object_ref.2), object.owner)))
3693 }
3694 }
3695 (
3696 InputObjectKind::ImmOrOwnedMoveObject(_),
3697 ObjectReadResultKind::DeletedSharedObject(_, _),
3698 ) => {
3699 unreachable!()
3700 }
3701 (
3702 InputObjectKind::SharedMoveObject { .. },
3703 ObjectReadResultKind::DeletedSharedObject(_, _),
3704 ) => None,
3705 (
3706 InputObjectKind::SharedMoveObject { mutable, .. },
3707 ObjectReadResultKind::Object(object),
3708 ) => {
3709 if *mutable {
3710 let oref = object.compute_object_reference();
3711 Some((oref.0, ((oref.1, oref.2), object.owner)))
3712 } else {
3713 None
3714 }
3715 }
3716 (
3717 InputObjectKind::ImmOrOwnedMoveObject(_),
3718 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3719 ) => {
3720 unreachable!()
3721 }
3722 (
3723 InputObjectKind::SharedMoveObject { .. },
3724 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3725 ) => None,
3726 },
3727 )
3728 .collect()
3729 }
3730
3731 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectRef]) -> SequenceNumber {
3735 let input_versions = self
3736 .objects
3737 .iter()
3738 .filter_map(|object| match &object.object {
3739 ObjectReadResultKind::Object(object) => {
3740 object.data.try_as_move().map(MoveObject::version)
3741 }
3742 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3743 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3744 })
3745 .chain(receiving_objects.iter().map(|object_ref| object_ref.1));
3746
3747 SequenceNumber::lamport_increment(input_versions)
3748 }
3749
3750 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3751 self.objects.iter().map(
3752 |ObjectReadResult {
3753 input_object_kind, ..
3754 }| input_object_kind,
3755 )
3756 }
3757
3758 pub fn into_object_map(self) -> BTreeMap<ObjectID, Object> {
3759 self.objects
3760 .into_iter()
3761 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3762 .collect()
3763 }
3764
3765 pub fn push(&mut self, object: ObjectReadResult) {
3766 self.objects.push(object);
3767 }
3768
3769 pub fn find_object_id_mut(&mut self, object_id: ObjectID) -> Option<&mut ObjectReadResult> {
3771 self.objects.iter_mut().find(|o| o.id() == object_id)
3772 }
3773
3774 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3775 self.objects.iter()
3776 }
3777
3778 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3779 self.objects.iter().filter_map(|o| o.as_object())
3780 }
3781}
3782
3783#[derive(Clone, Debug)]
3787pub enum ReceivingObjectReadResultKind {
3788 Object(Object),
3789 PreviouslyReceivedObject,
3791}
3792
3793impl ReceivingObjectReadResultKind {
3794 pub fn as_object(&self) -> Option<&Object> {
3795 match &self {
3796 Self::Object(object) => Some(object),
3797 Self::PreviouslyReceivedObject => None,
3798 }
3799 }
3800}
3801
3802pub struct ReceivingObjectReadResult {
3803 pub object_ref: ObjectRef,
3804 pub object: ReceivingObjectReadResultKind,
3805}
3806
3807impl ReceivingObjectReadResult {
3808 pub fn new(object_ref: ObjectRef, object: ReceivingObjectReadResultKind) -> Self {
3809 Self { object_ref, object }
3810 }
3811
3812 pub fn is_previously_received(&self) -> bool {
3813 matches!(
3814 self.object,
3815 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3816 )
3817 }
3818}
3819
3820impl From<Object> for ReceivingObjectReadResultKind {
3821 fn from(object: Object) -> Self {
3822 Self::Object(object)
3823 }
3824}
3825
3826pub struct ReceivingObjects {
3827 pub objects: Vec<ReceivingObjectReadResult>,
3828}
3829
3830impl ReceivingObjects {
3831 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3832 self.objects.iter()
3833 }
3834
3835 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3836 self.objects.iter().filter_map(|o| o.object.as_object())
3837 }
3838}
3839
3840impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3841 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3842 Self { objects }
3843 }
3844}
3845
3846impl Display for CertifiedTransaction {
3847 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3848 let mut writer = String::new();
3849 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3850 writeln!(
3851 writer,
3852 "Signed Authorities Bitmap : {:?}",
3853 self.auth_sig().signers_map
3854 )?;
3855 write!(writer, "{}", &self.data().intent_message().value.kind())?;
3856 write!(f, "{writer}")
3857 }
3858}
3859
3860#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3865pub enum TransactionKey {
3866 Digest(TransactionDigest),
3867 RandomnessRound(EpochId, RandomnessRound),
3868}
3869
3870impl TransactionKey {
3871 pub fn unwrap_digest(&self) -> &TransactionDigest {
3872 match self {
3873 TransactionKey::Digest(d) => d,
3874 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3875 }
3876 }
3877}