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