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