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