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