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