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