iota_types/
transaction.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6// zkLogin/AuthenticatorStateUpdate types are kept (deprecated) for
7// serialization compatibility only.
8
9use 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;
65// For some transactions we may either perform heavy operations or touch
66// objects that are storage expensive. That may happen (and often is the case)
67// because the object touched are set up in genesis and carry no storage cost
68// (and thus rebate) on first usage.
69pub 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    // contains no structs or objects
84    Pure(Vec<u8>),
85    // an object
86    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    // A Move object, either immutable, or owned mutable.
106    ImmOrOwnedObject(ObjectRef),
107    // A Move object that's shared.
108    // SharedObject::mutable controls whether caller asks for a mutable reference to shared
109    // object.
110    SharedObject {
111        id: ObjectID,
112        initial_shared_version: SequenceNumber,
113        mutable: bool,
114    },
115    // A Move object that can be received in this transaction.
116    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/// System transaction for advancing the epoch.
178#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
179pub struct ChangeEpoch {
180    /// The next (to become) epoch ID.
181    pub epoch: EpochId,
182    /// The protocol version in effect in the new epoch.
183    pub protocol_version: ProtocolVersion,
184    /// The total amount of gas charged for storage during the epoch.
185    pub storage_charge: u64,
186    /// The total amount of gas charged for computation during the epoch.
187    pub computation_charge: u64,
188    /// The amount of storage rebate refunded to the txn senders.
189    pub storage_rebate: u64,
190    /// The non-refundable storage fee.
191    pub non_refundable_storage_fee: u64,
192    /// Unix timestamp when epoch started
193    pub epoch_start_timestamp_ms: u64,
194    /// System packages (specifically framework and move stdlib) that are
195    /// written before the new epoch starts. This tracks framework upgrades
196    /// on chain. When executing the ChangeEpoch txn, the validator must
197    /// write out the modules below.  Modules are provided with the version they
198    /// will be upgraded to, their modules in serialized form (which include
199    /// their package ID), and a list of their transitive dependencies.
200    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
201}
202
203/// System transaction for advancing the epoch.
204/// This version includes the computation_charge_burned field for when
205/// protocol_defined_base_fee is enabled in the protocol config.
206#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
207pub struct ChangeEpochV2 {
208    /// The next (to become) epoch ID.
209    pub epoch: EpochId,
210    /// The protocol version in effect in the new epoch.
211    pub protocol_version: ProtocolVersion,
212    /// The total amount of gas charged for storage during the epoch.
213    pub storage_charge: u64,
214    /// The total amount of gas charged for computation during the epoch.
215    pub computation_charge: u64,
216    /// The burned component of the total computation/execution costs.
217    pub computation_charge_burned: u64,
218    /// The amount of storage rebate refunded to the txn senders.
219    pub storage_rebate: u64,
220    /// The non-refundable storage fee.
221    pub non_refundable_storage_fee: u64,
222    /// Unix timestamp when epoch started
223    pub epoch_start_timestamp_ms: u64,
224    /// System packages (specifically framework and move stdlib) that are
225    /// written before the new epoch starts. This tracks framework upgrades
226    /// on chain. When executing the ChangeEpochV2 txn, the validator must
227    /// write out the modules below.  Modules are provided with the version they
228    /// will be upgraded to, their modules in serialized form (which include
229    /// their package ID), and a list of their transitive dependencies.
230    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
231}
232
233/// System transaction for advancing the epoch.
234/// This version includes active validator indices that are eligible
235/// to take part in committee selection based on protocol version support.
236#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
237pub struct ChangeEpochV3 {
238    /// The next (to become) epoch ID.
239    pub epoch: EpochId,
240    /// The protocol version in effect in the new epoch.
241    pub protocol_version: ProtocolVersion,
242    /// The total amount of gas charged for storage during the epoch.
243    pub storage_charge: u64,
244    /// The total amount of gas charged for computation during the epoch.
245    pub computation_charge: u64,
246    /// The burned component of the total computation/execution costs.
247    pub computation_charge_burned: u64,
248    /// The amount of storage rebate refunded to the txn senders.
249    pub storage_rebate: u64,
250    /// The amount of storage rebate that is burnt due to the
251    /// gas_price. It's given that storage_rebate + non_refundable_storage_fee
252    /// is always equal to the storage_charge of the tx.
253    pub non_refundable_storage_fee: u64,
254    /// Unix timestamp from the start of the epoch as milliseconds
255    pub epoch_start_timestamp_ms: u64,
256    /// System packages (specifically framework and move stdlib) that are
257    /// written by the execution of this transaction. Validators must write
258    /// out the modules below.  Modules are provided with the version they
259    /// will be upgraded to, their modules in serialized form (which include
260    /// their package ID), and a list of their transitive dependencies.
261    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
262    /// Vector of active validator indices eligible to take part in committee
263    /// selection because they support the new, target protocol version.
264    pub eligible_active_validators: Vec<u64>,
265}
266/// System transaction for advancing the epoch.
267/// This version includes the scores field for when the score based rewards are
268/// enabled in the protocol config.
269#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
270pub struct ChangeEpochV4 {
271    /// The next (to become) epoch ID.
272    pub epoch: EpochId,
273    /// The protocol version in effect in the new epoch.
274    pub protocol_version: ProtocolVersion,
275    /// The total amount of gas charged for storage during the epoch.
276    pub storage_charge: u64,
277    /// The total amount of gas charged for computation during the epoch.
278    pub computation_charge: u64,
279    /// The burned component of the total computation/execution costs.
280    pub computation_charge_burned: u64,
281    /// The amount of storage rebate refunded to the txn senders.
282    pub storage_rebate: u64,
283    /// The non-refundable storage fee.
284    pub non_refundable_storage_fee: u64,
285    /// Unix timestamp when epoch started
286    pub epoch_start_timestamp_ms: u64,
287    /// System packages (specifically framework and move stdlib) that are
288    /// written before the new epoch starts. This tracks framework upgrades
289    /// on chain. When executing the ChangeEpochV4 txn, the validator must
290    /// write out the modules below.  Modules are provided with the version they
291    /// will be upgraded to, their modules in serialized form (which include
292    /// their package ID), and a list of their transitive dependencies.
293    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
294    /// Vector of active validator indices eligible to take part in committee
295    /// selection because they support the new, target protocol version.
296    pub eligible_active_validators: Vec<u64>,
297    /// Scores for the epoch being finalized. Each value corresponds to
298    /// an authority, ordered by the ending epoch's AuthorityIndex.
299    pub scores: Vec<u64>,
300    /// Whether to adjust validator rewards based on score.
301    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    /// Epoch of the randomness state update transaction
329    pub epoch: u64,
330    /// Randomness round of the update
331    pub randomness_round: RandomnessRound,
332    /// Updated random bytes
333    pub random_bytes: Vec<u8>,
334    /// The initial version of the randomness object that it was shared at.
335    pub randomness_obj_initial_shared_version: SequenceNumber,
336    // to version this struct, do not add new fields. Instead, add a RandomnessStateUpdateV2 to
337    // TransactionKind.
338}
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    /// A transaction that allows the interleaving of native commands and Move
350    /// calls
351    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 contains a list of transactions
358    /// that are allowed to run at the end of the epoch.
359    EndOfEpochTransaction(Vec<EndOfEpochTransactionKind>),
360
361    RandomnessStateUpdate(RandomnessStateUpdate),
362    // .. more transaction types go here
363    // TODO: When introducing `ConsensusCommitPrologueV2`, please add
364    // and use a new variant for `ConsensusDeterminedVersionAssignments`.
365    // See https://github.com/iotaledger/iota/issues/7692 and
366    // https://github.com/iotaledger/iota/pull/7697 for detail.
367}
368
369/// EndOfEpochTransactionKind
370#[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    // IMPORTANT: new enum variants should be added at the end to preserve serialization
377    // compatibility. DO NOT CHANGE THE ORDER OF EXISTING ENTRIES!
378}
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            // Receiving objects are not part of the input objects.
637            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        // unwrap safe because every u8 value is BCS-serializable
694        CallArg::Pure(bcs::to_bytes(&b).unwrap())
695    }
696}
697
698impl From<u8> for CallArg {
699    fn from(n: u8) -> Self {
700        // unwrap safe because every u8 value is BCS-serializable
701        CallArg::Pure(bcs::to_bytes(&n).unwrap())
702    }
703}
704
705impl From<u16> for CallArg {
706    fn from(n: u16) -> Self {
707        // unwrap safe because every u16 value is BCS-serializable
708        CallArg::Pure(bcs::to_bytes(&n).unwrap())
709    }
710}
711
712impl From<u32> for CallArg {
713    fn from(n: u32) -> Self {
714        // unwrap safe because every u32 value is BCS-serializable
715        CallArg::Pure(bcs::to_bytes(&n).unwrap())
716    }
717}
718
719impl From<u64> for CallArg {
720    fn from(n: u64) -> Self {
721        // unwrap safe because every u64 value is BCS-serializable
722        CallArg::Pure(bcs::to_bytes(&n).unwrap())
723    }
724}
725
726impl From<u128> for CallArg {
727    fn from(n: u128) -> Self {
728        // unwrap safe because every u128 value is BCS-serializable
729        CallArg::Pure(bcs::to_bytes(&n).unwrap())
730    }
731}
732
733impl From<&Vec<u8>> for CallArg {
734    fn from(v: &Vec<u8>) -> Self {
735        // unwrap safe because every vec<u8> value is BCS-serializable
736        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
762// Add package IDs, `ObjectID`, for types defined in modules.
763fn 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/// A series of commands where the results of one command can be used in future
786/// commands
787#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
788pub struct ProgrammableTransaction {
789    /// Input objects or primitive values
790    pub inputs: Vec<CallArg>,
791    /// The commands to be executed sequentially. A failure in any command will
792    /// result in the failure of the entire transaction.
793    pub commands: Vec<Command>,
794}
795
796/// A single command in a programmable transaction.
797#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
798pub enum Command {
799    /// A call to either an entry or a public Move function
800    MoveCall(Box<ProgrammableMoveCall>),
801    /// `(Vec<forall T:key+store. T>, address)`
802    /// It sends n-objects to the specified address. These objects must have
803    /// store (public transfer) and either the previous owner must be an
804    /// address or the object must be newly created.
805    TransferObjects(Vec<Argument>, Argument),
806    /// `(&mut Coin<T>, Vec<u64>)` -> `Vec<Coin<T>>`
807    /// It splits off some amounts into a new coins with those amounts
808    SplitCoins(Argument, Vec<Argument>),
809    /// `(&mut Coin<T>, Vec<Coin<T>>)`
810    /// It merges n-coins into the first coin
811    MergeCoins(Argument, Vec<Argument>),
812    /// Publishes a Move package. It takes the package bytes and a list of the
813    /// package's transitive dependencies to link against on-chain.
814    Publish(Vec<Vec<u8>>, Vec<ObjectID>),
815    /// `forall T: Vec<T> -> vector<T>`
816    /// Given n-values of the same type, it constructs a vector. For non objects
817    /// or an empty vector, the type input must be specified.
818    MakeMoveVec(Option<TypeInput>, Vec<Argument>),
819    /// Upgrades a Move package
820    /// Takes (in order):
821    /// 1. A vector of serialized modules for the package.
822    /// 2. A vector of object ids for the transitive dependencies of the new
823    ///    package.
824    /// 3. The object ID of the package being upgraded.
825    /// 4. An argument holding the `UpgradeTicket` that must have been produced
826    ///    from an earlier command in the same programmable transaction.
827    Upgrade(Vec<Vec<u8>>, Vec<ObjectID>, ObjectID, Argument),
828}
829
830/// An argument to a programmable transaction command
831#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
832pub enum Argument {
833    /// The gas coin. The gas coin can only be used by-ref, except for with
834    /// `TransferObjects`, which can use it by-value.
835    GasCoin,
836    /// One of the input objects or primitive values (from
837    /// `ProgrammableTransaction` inputs)
838    Input(u16),
839    /// The result of another command (from `ProgrammableTransaction` commands)
840    Result(u16),
841    /// Like a `Result` but it accesses a nested result. Currently, the only
842    /// usage of this is to access a value from a Move call with multiple
843    /// return values.
844    NestedResult(u16, u16),
845}
846
847/// The command for calling a Move function, either an entry function or a
848/// public function (which cannot return references).
849#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
850pub struct ProgrammableMoveCall {
851    /// The package containing the module and function.
852    pub package: ObjectID,
853    /// The specific module in the package containing the function.
854    pub module: String,
855    /// The function to be called.
856    pub function: String,
857    /// The type arguments to the function.
858    pub type_arguments: Vec<TypeInput>,
859    /// The arguments to the function.
860    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                // ty_opt.is_none() ==> !args.is_empty()
1003                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        // all objects, not just mutable, must be unique
1089        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        // do not duplicate packages referred to in commands
1094        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 randomness is used, it must be enabled by protocol config.
1150        // A command that uses Random can only be followed by TransferObjects or
1151        // MergeCoins.
1152        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    /// Merges another SharedInputObject into self.
1327    /// If there is a conflict in mutability, the resulting object will be
1328    /// mutable. Errors if the id or initial_shared_version do not match.
1329    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    /// present to make migrations to programmable transactions eaier.
1346    /// Will be removed
1347    pub fn programmable(pt: ProgrammableTransaction) -> Self {
1348        TransactionKind::ProgrammableTransaction(pt)
1349    }
1350
1351    pub fn is_system_tx(&self) -> bool {
1352        // Keep this as an exhaustive match so that we can't forget to update it.
1353        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    /// If this is advance epoch transaction, returns (total gas charged, total
1373    /// gas rebated). TODO: We should use GasCostSummary directly in
1374    /// ChangeEpoch struct, and return that directly.
1375    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    /// Returns an iterator of all shared input objects used by this
1402    /// transaction.
1403    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                // Deprecated: Authenticator state (JWK) is deprecated and
1415                // was never enabled. These transaction kinds are retained
1416                // only for BCS enum variant compatibility.
1417                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    /// Return the metadata of each of the input objects for the transaction.
1456    /// For a Move object, we attach the object reference;
1457    /// for a Move package, we provide the object id only since they never
1458    /// change on chain. TODO: use an iterator over references here instead
1459    /// of a Vec to avoid allocations.
1460    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                // Deprecated: Authenticator state (JWK) is deprecated and
1475                // was never enabled. These transaction kinds are retained
1476                // only for BCS enum variant compatibility.
1477                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                // Dedup since transactions may have an overlap in input objects.
1488                // Note: it's critical to ensure the order of inputs are deterministic.
1489                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        // Ensure that there are no duplicate inputs. This cannot be removed because:
1503        // In [`AuthorityState::check_locks`], we check that there are no duplicate
1504        // mutable input objects, which would have made this check here
1505        // unnecessary. However, we do plan to allow shared objects show up more
1506        // than once in multiple single transactions down the line. Once we have
1507        // that, we need check here to make sure the same shared object doesn't
1508        // show up more than once in the same single transaction.
1509        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            // All transaction kinds below are assumed to be system,
1520            // and no validity or limit checks are performed.
1521            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                // Deprecated: Authenticator state (JWK) is deprecated and
1531                // was never enabled. These transaction kinds are retained
1532                // only for BCS enum variant compatibility.
1533                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    /// number of commands, or 0 if it is a system transaction
1544    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    /// number of transactions, or 1 if it is a system transaction
1559    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    /// The transaction has no expiration
1629    None,
1630    /// Validators wont sign a transaction unless the expiration Epoch
1631    /// is greater than or equal to the current epoch
1632    Epoch(EpochId),
1633}
1634
1635#[enum_dispatch(TransactionDataAPI)]
1636#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1637pub enum TransactionData {
1638    V1(TransactionDataV1),
1639    // When new variants are introduced, it is important that we check version support
1640    // in the validity_check function based on the protocol config.
1641}
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 transaction kind if a system transaction
1654        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                // If the capability is owned by an object, then the module defining the owning
1968                // object gets to decide how the upgrade capability should be used.
1969                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    /// Checks if the transaction data contains the `Random` object as an
2044    /// input.
2045    ///
2046    /// IMPORTANT: This function does not check shared objects associated with
2047    /// `MoveAuthenticator` signatures. To check those objects as well, use the
2048    /// corresponding function from `SenderSignedData`.
2049    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    // Note: this implies that SingleTransactionKind itself must be versioned, so
2065    // that it can be shared across versions. This will be easy to do since it
2066    // is already an enum.
2067    fn kind(&self) -> &TransactionKind;
2068
2069    // Used by programmable_transaction_builder
2070    fn kind_mut(&mut self) -> &mut TransactionKind;
2071
2072    // kind is moved out of often enough that this is worth it to special case.
2073    fn into_kind(self) -> TransactionKind;
2074
2075    /// Transaction signer and Gas owner
2076    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    /// Checks if the transaction data contains at least one shared object.
2091    ///
2092    /// IMPORTANT: This function does not check shared objects associated with
2093    /// `MoveAuthenticator` signatures. To check those objects as well, use the
2094    /// corresponding function from `SenderSignedData`.
2095    fn contains_shared_object(&self) -> bool;
2096
2097    /// Returns a list of the transaction data shared input objects.
2098    ///
2099    /// IMPORTANT: This function does not return shared objects associated with
2100    /// `MoveAuthenticator` signatures. To check those objects as well, use the
2101    /// corresponding function from `SenderSignedData`.
2102    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    /// Check if the transaction is compliant with sponsorship.
2115    fn check_sponsorship(&self) -> UserInputResult;
2116
2117    fn is_system_tx(&self) -> bool;
2118    fn is_genesis_tx(&self) -> bool;
2119
2120    /// returns true if the transaction is one that is specially sequenced to
2121    /// run at the very end of the epoch
2122    fn is_end_of_epoch_tx(&self) -> bool;
2123
2124    /// Check if the transaction is sponsored (namely gas owner != sender)
2125    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    // This should be used in testing only.
2132    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    /// Transaction signer and Gas owner
2153    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    // Keep all the logic for validity here, we need this for dry run where the gas
2227    // may not be provided and created "on the fly"
2228    #[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    /// Check if the transaction is sponsored (namely gas owner != sender)
2235    fn is_sponsored_tx(&self) -> bool {
2236        self.gas_owner() != self.sender
2237    }
2238
2239    /// Check if the transaction is compliant with sponsorship.
2240    fn check_sponsorship(&self) -> UserInputResult {
2241        // Not a sponsored transaction, nothing to check
2242        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    /// A list of signatures signed by all transaction participants.
2285    /// 1. non participant signature must not be present.
2286    /// 2. signature order does not matter.
2287    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    // This function does not check validity of the signature
2387    // or perform any de-dup checks.
2388    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    // used cross-crate, so cannot be #[cfg(test)]
2422    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    /// Validate untrusted user transaction, including its size, input count,
2474    /// command count, etc.
2475    /// Returns the certificate serialised bytes size.
2476    pub fn validity_check(
2477        &self,
2478        config: &ProtocolConfig,
2479        epoch: EpochId,
2480    ) -> Result<usize, IotaError> {
2481        // Check that the features used by the user signatures are enabled on the
2482        // network.
2483        self.check_user_signature_protocol_compatibility(config)?;
2484
2485        // CRITICAL!!
2486        // Users cannot send system transactions.
2487        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        // Checks to see if the transaction has expired
2498        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        // Enforce overall transaction size limit.
2506        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    /// Returns all unique input objects including those from
2554    /// `MoveAuthenticator`s if any for reading.
2555    ///
2556    /// Although some shared objects(with a different mutability flag, for
2557    /// example) can be duplicated in the transaction and authenticators, we
2558    /// load them independently to make it possible to analyze the inputs in
2559    /// the transaction checkers.
2560    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    /// Splits the provided input objects into groups:
2577    /// 1. Input objects required by the transaction itself; may contain
2578    ///    duplicates if an IOTA coin is used both as an input and a gas coin.
2579    /// 2. A list of input objects required by each `MoveAuthenticator`(
2580    ///    including the object to authenticate) + the object to authenticate.
2581    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    /// Checks if `SenderSignedData` contains at least one shared object.
2649    /// This function checks shared objects from the `MoveAuthenticator`s if
2650    /// any.
2651    pub fn contains_shared_object(&self) -> bool {
2652        !self.shared_input_objects().is_empty()
2653    }
2654
2655    /// Returns an iterator over all shared input objects related to this
2656    /// transaction, including those from `MoveAuthenticator`s if any.
2657    ///
2658    /// If a shared object appears with the same version but different
2659    /// mutability, only one instance which is mutable is returned.
2660    ///
2661    /// Panics if there are shared objects with the same ID but different
2662    /// initial versions.
2663    pub fn shared_input_objects(&self) -> Vec<SharedInputObject> {
2664        // Vector is used to preserve the order of input objects.
2665        let mut input_objects = self.transaction_data().shared_input_objects();
2666
2667        // Add Move authenticator shared objects if any.
2668        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    /// Returns an iterator over all input objects related to this
2689    /// transaction, including those from the `MoveAuthenticator`s if any.
2690    ///
2691    /// If an IOTA coin is used both as an input and as a gas coin, it will
2692    /// appear two times in the returned iterator.
2693    ///
2694    /// If a shared object appears both in the transaction and authenticator
2695    /// with different mutability, only one instance which is mutable is
2696    /// returned.
2697    ///
2698    /// Shared objects with the same ID but different versions are not allowed.
2699    pub fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>> {
2700        // Can contain duplicates in case of using the same IOTA coin as an input and as
2701        // a gas coin.
2702        let mut input_objects = self.transaction_data().input_objects()?;
2703
2704        // Add the `MoveAuthenticator` shared objects if any.
2705        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    /// Checks if `SenderSignedData` contains the `Random` object as an
2725    /// input.
2726    /// This function checks shared objects from the `MoveAuthenticator`s if
2727    /// any.
2728    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        // Check each `MoveAuthenticator` validity.
2738        authenticators
2739            .iter()
2740            .try_for_each(|authenticator| authenticator.validity_check(config))?;
2741
2742        // Additional checks when `MoveAuthenticators` are present.
2743        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        // Get the input objects from the transaction data kind to skip the gas coins.
2787        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    /// Computes the tx digest that encodes the Rust type prefix from Signable
2820    /// trait.
2821    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    // Returns the primary key for this transaction.
2836    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    // Returns non-Digest keys that could be used to refer to this transaction.
2846    //
2847    // At the moment this returns a single Option for efficiency, but if more key
2848    // types are added, the return type could change to Vec<TransactionKey>.
2849    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    // TODO: Rename this function and above to make it clearer.
2884    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    /// Returns the Base64 encoded tx_bytes
2902    /// and a list of Base64 encoded [enum GenericSignature].
2903    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 is reserved for when we have multi commits per round.
2934            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    /// Use signing key to create a signed object.
2984    #[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
3000/// A transaction that is signed by a sender but not yet by an authority.
3001pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
3002pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
3003pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
3004
3005/// A transaction that is signed by a sender and also by an authority.
3006pub 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    // TODO: Eventually we should remove all calls to verify_signature
3063    // and make sure they all call verify to avoid repeated verifications.
3064    #[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    // A Move package, must be immutable.
3102    MovePackage(ObjectID),
3103    // A Move object, either immutable, or owned mutable.
3104    ImmOrOwnedMoveObject(ObjectRef),
3105    // A Move object that's shared and mutable.
3106    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    /// Merges another InputObjectKind into self.
3159    ///
3160    /// For shared objects, if either is mutable, the result is mutable. Fails
3161    /// if the IDs or initial versions do not match.
3162    /// For non-shared objects, fails if they are not equal.
3163    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    /// Checks that `self` and `other` are equal for non-shared objects.
3203    /// For shared objects, checks that IDs and initial versions match while
3204    /// mutability can be different.
3205    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/// The result of reading an object for execution. Because shared objects may be
3250/// deleted, one possible result of reading a shared object is that
3251/// ObjectReadResultKind::Deleted is returned.
3252#[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    // The version of the object that the transaction intended to read, and the digest of the tx
3262    // that deleted it.
3263    DeletedSharedObject(SequenceNumber, TransactionDigest),
3264    // A shared object in a cancelled transaction. The sequence number embeds cancellation reason.
3265    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    /// Return the object ref iff the object is an owned object (i.e. not
3368    /// shared, not immutable).
3369    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
3436// An InputObjects new-type that has been verified by iota-transaction-checks,
3437// and can be safely passed to execution.
3438pub struct CheckedInputObjects(InputObjects);
3439
3440// DO NOT CALL outside of iota-transaction-checks, genesis, or replay.
3441//
3442// CheckedInputObjects should really be defined in iota-transaction-checks so
3443// that we can make public construction impossible. But we can't do that because
3444// it would result in circular dependencies.
3445impl CheckedInputObjects {
3446    // Only called by iota-transaction-checks.
3447    pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3448        Self(inputs)
3449    }
3450
3451    // Only called when building the genesis transaction
3452    pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3453        Self(InputObjects::new(input_objects))
3454    }
3455
3456    // Only called from the replay tool.
3457    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    // Returns IDs of objects responsible for a transaction being cancelled, and the
3496    // corresponding reason for cancellation.
3497    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                    // Verify we don't have multiple cancellation reasons.
3506                    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    /// The version to set on objects created by the computation that `self` is
3614    /// input to. Guaranteed to be strictly greater than the versions of all
3615    /// input objects and objects received in the transaction.
3616    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    // If it contains then it returns the ObjectReadResult
3652    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// Result of attempting to read a receiving object (currently only at signing
3666// time). Because an object may have been previously received and deleted, the
3667// result may be ReceivingObjectReadResultKind::PreviouslyReceivedObject.
3668#[derive(Clone, Debug)]
3669pub enum ReceivingObjectReadResultKind {
3670    Object(Object),
3671    // The object was received by some other transaction, and we were not able to read it
3672    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/// TransactionKey uniquely identifies a transaction across all epochs.
3743/// Note that a single transaction may have multiple keys, for example a
3744/// RandomnessStateUpdate could be identified by both `Digest` and
3745/// `RandomnessRound`.
3746#[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}