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