sui_types/
transaction.rs

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