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 an accumulator barrier settlement transaction, returns its
1552    /// `AccumulatorSettlement` transaction key by extracting epoch and
1553    /// checkpoint_height from the prologue call arguments.
1554    pub fn accumulator_barrier_settlement_key(&self) -> Option<TransactionKey> {
1555        let TransactionKind::ProgrammableSystemTransaction(pt) = self else {
1556            return None;
1557        };
1558        let has_mutable_acc_root = pt.inputs.iter().any(|input| {
1559            matches!(
1560                input,
1561                CallArg::Object(ObjectArg::SharedObject {
1562                    id,
1563                    mutability: SharedObjectMutability::Mutable,
1564                    ..
1565                }) if *id == SUI_ACCUMULATOR_ROOT_OBJECT_ID
1566            )
1567        });
1568        if !has_mutable_acc_root {
1569            return None;
1570        }
1571        // The prologue embeds epoch as Input(1) and checkpoint_height as Input(2),
1572        // both as BCS-encoded u64 pure values.
1573        let epoch = pt.inputs.get(1).and_then(|arg| match arg {
1574            CallArg::Pure(bytes) => bcs::from_bytes::<u64>(bytes).ok(),
1575            _ => None,
1576        })?;
1577        let checkpoint_height = pt.inputs.get(2).and_then(|arg| match arg {
1578            CallArg::Pure(bytes) => bcs::from_bytes::<u64>(bytes).ok(),
1579            _ => None,
1580        })?;
1581        Some(TransactionKey::AccumulatorSettlement(
1582            epoch,
1583            checkpoint_height,
1584        ))
1585    }
1586
1587    /// If this is advance epoch transaction, returns (total gas charged, total gas rebated).
1588    /// TODO: We should use GasCostSummary directly in ChangeEpoch struct, and return that
1589    /// directly.
1590    pub fn get_advance_epoch_tx_gas_summary(&self) -> Option<(u64, u64)> {
1591        let e = match self {
1592            Self::ChangeEpoch(e) => e,
1593            Self::EndOfEpochTransaction(txns) => {
1594                if let EndOfEpochTransactionKind::ChangeEpoch(e) =
1595                    txns.last().expect("at least one end-of-epoch txn required")
1596                {
1597                    e
1598                } else {
1599                    panic!("final end-of-epoch txn must be ChangeEpoch")
1600                }
1601            }
1602            _ => return None,
1603        };
1604
1605        Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1606    }
1607
1608    /// Returns an iterator of all shared input objects used by this transaction.
1609    /// It covers both Call and ChangeEpoch transaction kind, because both makes Move calls.
1610    pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
1611        match &self {
1612            Self::ChangeEpoch(_) => {
1613                Either::Left(Either::Left(iter::once(SharedInputObject::SUI_SYSTEM_OBJ)))
1614            }
1615
1616            Self::ConsensusCommitPrologue(_)
1617            | Self::ConsensusCommitPrologueV2(_)
1618            | Self::ConsensusCommitPrologueV3(_)
1619            | Self::ConsensusCommitPrologueV4(_) => {
1620                Either::Left(Either::Left(iter::once(SharedInputObject {
1621                    id: SUI_CLOCK_OBJECT_ID,
1622                    initial_shared_version: SUI_CLOCK_OBJECT_SHARED_VERSION,
1623                    mutability: SharedObjectMutability::Mutable,
1624                })))
1625            }
1626            Self::AuthenticatorStateUpdate(update) => {
1627                Either::Left(Either::Left(iter::once(SharedInputObject {
1628                    id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1629                    initial_shared_version: update.authenticator_obj_initial_shared_version,
1630                    mutability: SharedObjectMutability::Mutable,
1631                })))
1632            }
1633            Self::RandomnessStateUpdate(update) => {
1634                Either::Left(Either::Left(iter::once(SharedInputObject {
1635                    id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1636                    initial_shared_version: update.randomness_obj_initial_shared_version,
1637                    mutability: SharedObjectMutability::Mutable,
1638                })))
1639            }
1640            Self::EndOfEpochTransaction(txns) => Either::Left(Either::Right(
1641                txns.iter().flat_map(|txn| txn.shared_input_objects()),
1642            )),
1643            Self::ProgrammableTransaction(pt) | Self::ProgrammableSystemTransaction(pt) => {
1644                Either::Right(Either::Left(pt.shared_input_objects()))
1645            }
1646            Self::Genesis(_) => Either::Right(Either::Right(iter::empty())),
1647        }
1648    }
1649
1650    fn move_calls(&self) -> Vec<(usize, &ObjectID, &str, &str)> {
1651        match &self {
1652            Self::ProgrammableTransaction(pt) => pt.move_calls(),
1653            _ => vec![],
1654        }
1655    }
1656
1657    pub fn receiving_objects(&self) -> Vec<ObjectRef> {
1658        match &self {
1659            TransactionKind::ChangeEpoch(_)
1660            | TransactionKind::Genesis(_)
1661            | TransactionKind::ConsensusCommitPrologue(_)
1662            | TransactionKind::ConsensusCommitPrologueV2(_)
1663            | TransactionKind::ConsensusCommitPrologueV3(_)
1664            | TransactionKind::ConsensusCommitPrologueV4(_)
1665            | TransactionKind::AuthenticatorStateUpdate(_)
1666            | TransactionKind::RandomnessStateUpdate(_)
1667            | TransactionKind::EndOfEpochTransaction(_)
1668            | TransactionKind::ProgrammableSystemTransaction(_) => vec![],
1669            TransactionKind::ProgrammableTransaction(pt) => pt.receiving_objects(),
1670        }
1671    }
1672
1673    /// Return the metadata of each of the input objects for the transaction.
1674    /// For a Move object, we attach the object reference;
1675    /// for a Move package, we provide the object id only since they never change on chain.
1676    /// TODO: use an iterator over references here instead of a Vec to avoid allocations.
1677    pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1678        let input_objects = match &self {
1679            Self::ChangeEpoch(_) => {
1680                vec![InputObjectKind::SharedMoveObject {
1681                    id: SUI_SYSTEM_STATE_OBJECT_ID,
1682                    initial_shared_version: SUI_SYSTEM_STATE_OBJECT_SHARED_VERSION,
1683                    mutability: SharedObjectMutability::Mutable,
1684                }]
1685            }
1686            Self::Genesis(_) => {
1687                vec![]
1688            }
1689            Self::ConsensusCommitPrologue(_)
1690            | Self::ConsensusCommitPrologueV2(_)
1691            | Self::ConsensusCommitPrologueV3(_)
1692            | Self::ConsensusCommitPrologueV4(_) => {
1693                vec![InputObjectKind::SharedMoveObject {
1694                    id: SUI_CLOCK_OBJECT_ID,
1695                    initial_shared_version: SUI_CLOCK_OBJECT_SHARED_VERSION,
1696                    mutability: SharedObjectMutability::Mutable,
1697                }]
1698            }
1699            Self::AuthenticatorStateUpdate(update) => {
1700                vec![InputObjectKind::SharedMoveObject {
1701                    id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1702                    initial_shared_version: update.authenticator_obj_initial_shared_version(),
1703                    mutability: SharedObjectMutability::Mutable,
1704                }]
1705            }
1706            Self::RandomnessStateUpdate(update) => {
1707                vec![InputObjectKind::SharedMoveObject {
1708                    id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1709                    initial_shared_version: update.randomness_obj_initial_shared_version(),
1710                    mutability: SharedObjectMutability::Mutable,
1711                }]
1712            }
1713            Self::EndOfEpochTransaction(txns) => {
1714                // Dedup since transactions may have a overlap in input objects.
1715                // Note: it's critical to ensure the order of inputs are deterministic.
1716                let before_dedup: Vec<_> =
1717                    txns.iter().flat_map(|txn| txn.input_objects()).collect();
1718                let mut has_seen = HashSet::new();
1719                let mut after_dedup = vec![];
1720                for obj in before_dedup {
1721                    if has_seen.insert(obj) {
1722                        after_dedup.push(obj);
1723                    }
1724                }
1725                after_dedup
1726            }
1727            Self::ProgrammableTransaction(p) | Self::ProgrammableSystemTransaction(p) => {
1728                return p.input_objects();
1729            }
1730        };
1731        // Ensure that there are no duplicate inputs. This cannot be removed because:
1732        // In [`AuthorityState::check_locks`], we check that there are no duplicate mutable
1733        // input objects, which would have made this check here unnecessary. However we
1734        // do plan to allow shared objects show up more than once in multiple single
1735        // transactions down the line. Once we have that, we need check here to make sure
1736        // the same shared object doesn't show up more than once in the same single
1737        // transaction.
1738        let mut used = HashSet::new();
1739        if !input_objects.iter().all(|o| used.insert(o.object_id())) {
1740            return Err(UserInputError::DuplicateObjectRefInput);
1741        }
1742        Ok(input_objects)
1743    }
1744
1745    fn get_funds_withdrawals<'a>(&'a self) -> impl Iterator<Item = &'a FundsWithdrawalArg> + 'a {
1746        let TransactionKind::ProgrammableTransaction(pt) = &self else {
1747            return Either::Left(iter::empty());
1748        };
1749        Either::Right(pt.inputs.iter().filter_map(|input| {
1750            if let CallArg::FundsWithdrawal(withdraw) = input {
1751                Some(withdraw)
1752            } else {
1753                None
1754            }
1755        }))
1756    }
1757
1758    pub fn get_coin_reservation_obj_refs(&self) -> impl Iterator<Item = ObjectRef> + '_ {
1759        let TransactionKind::ProgrammableTransaction(pt) = &self else {
1760            return Either::Left(iter::empty());
1761        };
1762        Either::Right(pt.inputs.iter().filter_map(|input| {
1763            if let CallArg::Object(ObjectArg::ImmOrOwnedObject(obj_ref)) = input {
1764                if ParsedDigest::is_coin_reservation_digest(&obj_ref.2) {
1765                    Some(*obj_ref)
1766                } else {
1767                    None
1768                }
1769            } else {
1770                None
1771            }
1772        }))
1773    }
1774
1775    pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1776        match self {
1777            TransactionKind::ProgrammableTransaction(p) => p.validity_check(config)?,
1778            // All transactiond kinds below are assumed to be system,
1779            // and no validity or limit checks are performed.
1780            TransactionKind::ChangeEpoch(_)
1781            | TransactionKind::Genesis(_)
1782            | TransactionKind::ConsensusCommitPrologue(_) => (),
1783            TransactionKind::ConsensusCommitPrologueV2(_) => {
1784                if !config.include_consensus_digest_in_prologue() {
1785                    return Err(UserInputError::Unsupported(
1786                        "ConsensusCommitPrologueV2 is not supported".to_string(),
1787                    ));
1788                }
1789            }
1790            TransactionKind::ConsensusCommitPrologueV3(_) => {
1791                if !config.record_consensus_determined_version_assignments_in_prologue() {
1792                    return Err(UserInputError::Unsupported(
1793                        "ConsensusCommitPrologueV3 is not supported".to_string(),
1794                    ));
1795                }
1796            }
1797            TransactionKind::ConsensusCommitPrologueV4(_) => {
1798                if !config.record_additional_state_digest_in_prologue() {
1799                    return Err(UserInputError::Unsupported(
1800                        "ConsensusCommitPrologueV4 is not supported".to_string(),
1801                    ));
1802                }
1803            }
1804            TransactionKind::EndOfEpochTransaction(txns) => {
1805                if !config.end_of_epoch_transaction_supported() {
1806                    return Err(UserInputError::Unsupported(
1807                        "EndOfEpochTransaction is not supported".to_string(),
1808                    ));
1809                }
1810
1811                for tx in txns {
1812                    tx.validity_check(config)?;
1813                }
1814            }
1815
1816            TransactionKind::AuthenticatorStateUpdate(_) => {
1817                if !config.enable_jwk_consensus_updates() {
1818                    return Err(UserInputError::Unsupported(
1819                        "authenticator state updates not enabled".to_string(),
1820                    ));
1821                }
1822            }
1823            TransactionKind::RandomnessStateUpdate(_) => {
1824                if !config.random_beacon() {
1825                    return Err(UserInputError::Unsupported(
1826                        "randomness state updates not enabled".to_string(),
1827                    ));
1828                }
1829            }
1830            TransactionKind::ProgrammableSystemTransaction(_) => {
1831                if !config.enable_accumulators() {
1832                    return Err(UserInputError::Unsupported(
1833                        "accumulators not enabled".to_string(),
1834                    ));
1835                }
1836            }
1837        };
1838        Ok(())
1839    }
1840
1841    /// number of commands, or 0 if it is a system transaction
1842    pub fn num_commands(&self) -> usize {
1843        match self {
1844            TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1845            _ => 0,
1846        }
1847    }
1848
1849    pub fn iter_commands(&self) -> impl Iterator<Item = &Command> {
1850        match self {
1851            TransactionKind::ProgrammableTransaction(pt) => pt.commands.iter(),
1852            _ => [].iter(),
1853        }
1854    }
1855
1856    /// number of transactions, or 1 if it is a system transaction
1857    pub fn tx_count(&self) -> usize {
1858        match self {
1859            TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1860            _ => 1,
1861        }
1862    }
1863
1864    pub fn name(&self) -> &'static str {
1865        match self {
1866            Self::ChangeEpoch(_) => "ChangeEpoch",
1867            Self::Genesis(_) => "Genesis",
1868            Self::ConsensusCommitPrologue(_) => "ConsensusCommitPrologue",
1869            Self::ConsensusCommitPrologueV2(_) => "ConsensusCommitPrologueV2",
1870            Self::ConsensusCommitPrologueV3(_) => "ConsensusCommitPrologueV3",
1871            Self::ConsensusCommitPrologueV4(_) => "ConsensusCommitPrologueV4",
1872            Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
1873            Self::ProgrammableSystemTransaction(_) => "ProgrammableSystemTransaction",
1874            Self::AuthenticatorStateUpdate(_) => "AuthenticatorStateUpdate",
1875            Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
1876            Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
1877        }
1878    }
1879}
1880
1881impl Display for TransactionKind {
1882    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1883        let mut writer = String::new();
1884        match &self {
1885            Self::ChangeEpoch(e) => {
1886                writeln!(writer, "Transaction Kind : Epoch Change")?;
1887                writeln!(writer, "New epoch ID : {}", e.epoch)?;
1888                writeln!(writer, "Storage gas reward : {}", e.storage_charge)?;
1889                writeln!(writer, "Computation gas reward : {}", e.computation_charge)?;
1890                writeln!(writer, "Storage rebate : {}", e.storage_rebate)?;
1891                writeln!(writer, "Timestamp : {}", e.epoch_start_timestamp_ms)?;
1892            }
1893            Self::Genesis(_) => {
1894                writeln!(writer, "Transaction Kind : Genesis")?;
1895            }
1896            Self::ConsensusCommitPrologue(p) => {
1897                writeln!(writer, "Transaction Kind : Consensus Commit Prologue")?;
1898                writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1899            }
1900            Self::ConsensusCommitPrologueV2(p) => {
1901                writeln!(writer, "Transaction Kind : Consensus Commit Prologue V2")?;
1902                writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1903                writeln!(writer, "Consensus Digest: {}", p.consensus_commit_digest)?;
1904            }
1905            Self::ConsensusCommitPrologueV3(p) => {
1906                writeln!(writer, "Transaction Kind : Consensus Commit Prologue V3")?;
1907                writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1908                writeln!(writer, "Consensus Digest: {}", p.consensus_commit_digest)?;
1909                writeln!(
1910                    writer,
1911                    "Consensus determined version assignment: {:?}",
1912                    p.consensus_determined_version_assignments
1913                )?;
1914            }
1915            Self::ConsensusCommitPrologueV4(p) => {
1916                writeln!(writer, "Transaction Kind : Consensus Commit Prologue V4")?;
1917                writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1918                writeln!(writer, "Consensus Digest: {}", p.consensus_commit_digest)?;
1919                writeln!(
1920                    writer,
1921                    "Consensus determined version assignment: {:?}",
1922                    p.consensus_determined_version_assignments
1923                )?;
1924                writeln!(
1925                    writer,
1926                    "Additional State Digest: {}",
1927                    p.additional_state_digest
1928                )?;
1929            }
1930            Self::ProgrammableTransaction(p) => {
1931                writeln!(writer, "Transaction Kind : Programmable")?;
1932                write!(writer, "{p}")?;
1933            }
1934            Self::ProgrammableSystemTransaction(p) => {
1935                writeln!(writer, "Transaction Kind : Programmable System")?;
1936                write!(writer, "{p}")?;
1937            }
1938            Self::AuthenticatorStateUpdate(_) => {
1939                writeln!(writer, "Transaction Kind : Authenticator State Update")?;
1940            }
1941            Self::RandomnessStateUpdate(_) => {
1942                writeln!(writer, "Transaction Kind : Randomness State Update")?;
1943            }
1944            Self::EndOfEpochTransaction(_) => {
1945                writeln!(writer, "Transaction Kind : End of Epoch Transaction")?;
1946            }
1947        }
1948        write!(f, "{}", writer)
1949    }
1950}
1951
1952#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1953pub struct GasData {
1954    pub payment: Vec<ObjectRef>,
1955    pub owner: SuiAddress,
1956    pub price: u64,
1957    pub budget: u64,
1958}
1959
1960impl GasData {
1961    pub fn is_unmetered(&self) -> bool {
1962        self.payment.len() == 1
1963            && self.payment[0].0 == ObjectID::ZERO
1964            && self.payment[0].1 == SequenceNumber::default()
1965            && self.payment[0].2 == ObjectDigest::MIN
1966    }
1967}
1968
1969pub fn is_gas_paid_from_address_balance(
1970    gas_data: &GasData,
1971    transaction_kind: &TransactionKind,
1972) -> bool {
1973    gas_data.payment.is_empty()
1974        && matches!(
1975            transaction_kind,
1976            TransactionKind::ProgrammableTransaction(_)
1977        )
1978}
1979
1980#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
1981pub enum TransactionExpiration {
1982    /// The transaction has no expiration
1983    None,
1984    /// Validators wont sign a transaction unless the expiration Epoch
1985    /// is greater than or equal to the current epoch
1986    Epoch(EpochId),
1987    /// ValidDuring enables gas payments from address balances.
1988    ///
1989    /// When transactions use address balances for gas payment instead of explicit gas coins,
1990    /// we lose the natural transaction uniqueness and replay prevention that comes from
1991    /// mutation of gas coin objects.
1992    ///
1993    /// By bounding expiration and providing a nonce, validators must only retain
1994    /// executed digests for the maximum possible expiry range to differentiate
1995    /// retries from unique transactions with otherwise identical inputs.
1996    ValidDuring {
1997        /// Transaction invalid before this epoch. Must equal current epoch.
1998        min_epoch: Option<EpochId>,
1999        /// Transaction expires after this epoch. Must equal current epoch
2000        max_epoch: Option<EpochId>,
2001        /// Future support for sub-epoch timing (not yet implemented)
2002        min_timestamp: Option<u64>,
2003        /// Future support for sub-epoch timing (not yet implemented)
2004        max_timestamp: Option<u64>,
2005        /// Network identifier to prevent cross-chain replay
2006        chain: ChainIdentifier,
2007        /// User-provided uniqueness identifier to differentiate otherwise identical transactions
2008        nonce: u32,
2009    },
2010}
2011
2012#[enum_dispatch(TransactionDataAPI)]
2013#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
2014pub enum TransactionData {
2015    V1(TransactionDataV1),
2016    // When new variants are introduced, it is important that we check version support
2017    // in the validity_check function based on the protocol config.
2018}
2019
2020#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
2021pub struct TransactionDataV1 {
2022    pub kind: TransactionKind,
2023    pub sender: SuiAddress,
2024    pub gas_data: GasData,
2025    pub expiration: TransactionExpiration,
2026}
2027
2028impl TransactionData {
2029    pub fn as_v1(&self) -> &TransactionDataV1 {
2030        match self {
2031            TransactionData::V1(v1) => v1,
2032        }
2033    }
2034    fn new_system_transaction(kind: TransactionKind) -> Self {
2035        // assert transaction kind if a system transaction
2036        assert!(kind.is_system_tx());
2037        let sender = SuiAddress::default();
2038        TransactionData::V1(TransactionDataV1 {
2039            kind,
2040            sender,
2041            gas_data: GasData {
2042                price: GAS_PRICE_FOR_SYSTEM_TX,
2043                owner: sender,
2044                payment: vec![(ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN)],
2045                budget: 0,
2046            },
2047            expiration: TransactionExpiration::None,
2048        })
2049    }
2050
2051    pub fn new(
2052        kind: TransactionKind,
2053        sender: SuiAddress,
2054        gas_payment: ObjectRef,
2055        gas_budget: u64,
2056        gas_price: u64,
2057    ) -> Self {
2058        TransactionData::V1(TransactionDataV1 {
2059            kind,
2060            sender,
2061            gas_data: GasData {
2062                price: gas_price,
2063                owner: sender,
2064                payment: vec![gas_payment],
2065                budget: gas_budget,
2066            },
2067            expiration: TransactionExpiration::None,
2068        })
2069    }
2070
2071    pub fn new_with_gas_coins(
2072        kind: TransactionKind,
2073        sender: SuiAddress,
2074        gas_payment: Vec<ObjectRef>,
2075        gas_budget: u64,
2076        gas_price: u64,
2077    ) -> Self {
2078        Self::new_with_gas_coins_allow_sponsor(
2079            kind,
2080            sender,
2081            gas_payment,
2082            gas_budget,
2083            gas_price,
2084            sender,
2085        )
2086    }
2087
2088    pub fn new_with_gas_coins_allow_sponsor(
2089        kind: TransactionKind,
2090        sender: SuiAddress,
2091        gas_payment: Vec<ObjectRef>,
2092        gas_budget: u64,
2093        gas_price: u64,
2094        gas_sponsor: SuiAddress,
2095    ) -> Self {
2096        TransactionData::V1(TransactionDataV1 {
2097            kind,
2098            sender,
2099            gas_data: GasData {
2100                price: gas_price,
2101                owner: gas_sponsor,
2102                payment: gas_payment,
2103                budget: gas_budget,
2104            },
2105            expiration: TransactionExpiration::None,
2106        })
2107    }
2108
2109    pub fn new_with_gas_data(kind: TransactionKind, sender: SuiAddress, gas_data: GasData) -> Self {
2110        TransactionData::V1(TransactionDataV1 {
2111            kind,
2112            sender,
2113            gas_data,
2114            expiration: TransactionExpiration::None,
2115        })
2116    }
2117
2118    pub fn new_move_call(
2119        sender: SuiAddress,
2120        package: ObjectID,
2121        module: Identifier,
2122        function: Identifier,
2123        type_arguments: Vec<TypeTag>,
2124        gas_payment: ObjectRef,
2125        arguments: Vec<CallArg>,
2126        gas_budget: u64,
2127        gas_price: u64,
2128    ) -> anyhow::Result<Self> {
2129        Self::new_move_call_with_gas_coins(
2130            sender,
2131            package,
2132            module,
2133            function,
2134            type_arguments,
2135            vec![gas_payment],
2136            arguments,
2137            gas_budget,
2138            gas_price,
2139        )
2140    }
2141
2142    pub fn new_move_call_with_gas_coins(
2143        sender: SuiAddress,
2144        package: ObjectID,
2145        module: Identifier,
2146        function: Identifier,
2147        type_arguments: Vec<TypeTag>,
2148        gas_payment: Vec<ObjectRef>,
2149        arguments: Vec<CallArg>,
2150        gas_budget: u64,
2151        gas_price: u64,
2152    ) -> anyhow::Result<Self> {
2153        let pt = {
2154            let mut builder = ProgrammableTransactionBuilder::new();
2155            builder.move_call(package, module, function, type_arguments, arguments)?;
2156            builder.finish()
2157        };
2158        Ok(Self::new_programmable(
2159            sender,
2160            gas_payment,
2161            pt,
2162            gas_budget,
2163            gas_price,
2164        ))
2165    }
2166
2167    pub fn new_transfer(
2168        recipient: SuiAddress,
2169        full_object_ref: FullObjectRef,
2170        sender: SuiAddress,
2171        gas_payment: ObjectRef,
2172        gas_budget: u64,
2173        gas_price: u64,
2174    ) -> Self {
2175        let pt = {
2176            let mut builder = ProgrammableTransactionBuilder::new();
2177            builder.transfer_object(recipient, full_object_ref).unwrap();
2178            builder.finish()
2179        };
2180        Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
2181    }
2182
2183    pub fn new_transfer_sui(
2184        recipient: SuiAddress,
2185        sender: SuiAddress,
2186        amount: Option<u64>,
2187        gas_payment: ObjectRef,
2188        gas_budget: u64,
2189        gas_price: u64,
2190    ) -> Self {
2191        Self::new_transfer_sui_allow_sponsor(
2192            recipient,
2193            sender,
2194            amount,
2195            gas_payment,
2196            gas_budget,
2197            gas_price,
2198            sender,
2199        )
2200    }
2201
2202    pub fn new_transfer_sui_allow_sponsor(
2203        recipient: SuiAddress,
2204        sender: SuiAddress,
2205        amount: Option<u64>,
2206        gas_payment: ObjectRef,
2207        gas_budget: u64,
2208        gas_price: u64,
2209        gas_sponsor: SuiAddress,
2210    ) -> Self {
2211        let pt = {
2212            let mut builder = ProgrammableTransactionBuilder::new();
2213            builder.transfer_sui(recipient, amount);
2214            builder.finish()
2215        };
2216        Self::new_programmable_allow_sponsor(
2217            sender,
2218            vec![gas_payment],
2219            pt,
2220            gas_budget,
2221            gas_price,
2222            gas_sponsor,
2223        )
2224    }
2225
2226    pub fn new_pay(
2227        sender: SuiAddress,
2228        coins: Vec<ObjectRef>,
2229        recipients: Vec<SuiAddress>,
2230        amounts: Vec<u64>,
2231        gas_payment: ObjectRef,
2232        gas_budget: u64,
2233        gas_price: u64,
2234    ) -> anyhow::Result<Self> {
2235        let pt = {
2236            let mut builder = ProgrammableTransactionBuilder::new();
2237            builder.pay(coins, recipients, amounts)?;
2238            builder.finish()
2239        };
2240        Ok(Self::new_programmable(
2241            sender,
2242            vec![gas_payment],
2243            pt,
2244            gas_budget,
2245            gas_price,
2246        ))
2247    }
2248
2249    pub fn new_pay_sui(
2250        sender: SuiAddress,
2251        mut coins: Vec<ObjectRef>,
2252        recipients: Vec<SuiAddress>,
2253        amounts: Vec<u64>,
2254        gas_payment: ObjectRef,
2255        gas_budget: u64,
2256        gas_price: u64,
2257    ) -> anyhow::Result<Self> {
2258        coins.insert(0, gas_payment);
2259        let pt = {
2260            let mut builder = ProgrammableTransactionBuilder::new();
2261            builder.pay_sui(recipients, amounts)?;
2262            builder.finish()
2263        };
2264        Ok(Self::new_programmable(
2265            sender, coins, pt, gas_budget, gas_price,
2266        ))
2267    }
2268
2269    pub fn new_pay_all_sui(
2270        sender: SuiAddress,
2271        mut coins: Vec<ObjectRef>,
2272        recipient: SuiAddress,
2273        gas_payment: ObjectRef,
2274        gas_budget: u64,
2275        gas_price: u64,
2276    ) -> Self {
2277        coins.insert(0, gas_payment);
2278        let pt = {
2279            let mut builder = ProgrammableTransactionBuilder::new();
2280            builder.pay_all_sui(recipient);
2281            builder.finish()
2282        };
2283        Self::new_programmable(sender, coins, pt, gas_budget, gas_price)
2284    }
2285
2286    pub fn new_split_coin(
2287        sender: SuiAddress,
2288        coin: ObjectRef,
2289        amounts: Vec<u64>,
2290        gas_payment: ObjectRef,
2291        gas_budget: u64,
2292        gas_price: u64,
2293    ) -> Self {
2294        let pt = {
2295            let mut builder = ProgrammableTransactionBuilder::new();
2296            builder.split_coin(sender, coin, amounts);
2297            builder.finish()
2298        };
2299        Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
2300    }
2301
2302    pub fn new_module(
2303        sender: SuiAddress,
2304        gas_payment: ObjectRef,
2305        modules: Vec<Vec<u8>>,
2306        dep_ids: Vec<ObjectID>,
2307        gas_budget: u64,
2308        gas_price: u64,
2309    ) -> Self {
2310        let pt = {
2311            let mut builder = ProgrammableTransactionBuilder::new();
2312            let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
2313            builder.transfer_arg(sender, upgrade_cap);
2314            builder.finish()
2315        };
2316        Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
2317    }
2318
2319    pub fn new_upgrade(
2320        sender: SuiAddress,
2321        gas_payment: ObjectRef,
2322        package_id: ObjectID,
2323        modules: Vec<Vec<u8>>,
2324        dep_ids: Vec<ObjectID>,
2325        (upgrade_capability, capability_owner): (ObjectRef, Owner),
2326        upgrade_policy: u8,
2327        digest: Vec<u8>,
2328        gas_budget: u64,
2329        gas_price: u64,
2330    ) -> anyhow::Result<Self> {
2331        let pt = {
2332            let mut builder = ProgrammableTransactionBuilder::new();
2333            let capability_arg = match capability_owner {
2334                Owner::AddressOwner(_) => ObjectArg::ImmOrOwnedObject(upgrade_capability),
2335                Owner::Shared {
2336                    initial_shared_version,
2337                }
2338                | Owner::ConsensusAddressOwner {
2339                    start_version: initial_shared_version,
2340                    ..
2341                } => ObjectArg::SharedObject {
2342                    id: upgrade_capability.0,
2343                    initial_shared_version,
2344                    mutability: SharedObjectMutability::Mutable,
2345                },
2346                Owner::Immutable => {
2347                    return Err(anyhow::anyhow!(
2348                        "Upgrade capability is stored immutably and cannot be used for upgrades"
2349                    ));
2350                }
2351                // If the capability is owned by an object, then the module defining the owning
2352                // object gets to decide how the upgrade capability should be used.
2353                Owner::ObjectOwner(_) => {
2354                    return Err(anyhow::anyhow!("Upgrade capability controlled by object"));
2355                }
2356            };
2357            builder.obj(capability_arg).unwrap();
2358            let upgrade_arg = builder.pure(upgrade_policy).unwrap();
2359            let digest_arg = builder.pure(digest).unwrap();
2360            let upgrade_ticket = builder.programmable_move_call(
2361                SUI_FRAMEWORK_PACKAGE_ID,
2362                ident_str!("package").to_owned(),
2363                ident_str!("authorize_upgrade").to_owned(),
2364                vec![],
2365                vec![Argument::Input(0), upgrade_arg, digest_arg],
2366            );
2367            let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
2368
2369            builder.programmable_move_call(
2370                SUI_FRAMEWORK_PACKAGE_ID,
2371                ident_str!("package").to_owned(),
2372                ident_str!("commit_upgrade").to_owned(),
2373                vec![],
2374                vec![Argument::Input(0), upgrade_receipt],
2375            );
2376
2377            builder.finish()
2378        };
2379        Ok(Self::new_programmable(
2380            sender,
2381            vec![gas_payment],
2382            pt,
2383            gas_budget,
2384            gas_price,
2385        ))
2386    }
2387
2388    pub fn new_programmable(
2389        sender: SuiAddress,
2390        gas_payment: Vec<ObjectRef>,
2391        pt: ProgrammableTransaction,
2392        gas_budget: u64,
2393        gas_price: u64,
2394    ) -> Self {
2395        Self::new_programmable_allow_sponsor(sender, gas_payment, pt, gas_budget, gas_price, sender)
2396    }
2397
2398    pub fn new_programmable_allow_sponsor(
2399        sender: SuiAddress,
2400        gas_payment: Vec<ObjectRef>,
2401        pt: ProgrammableTransaction,
2402        gas_budget: u64,
2403        gas_price: u64,
2404        sponsor: SuiAddress,
2405    ) -> Self {
2406        let kind = TransactionKind::ProgrammableTransaction(pt);
2407        Self::new_with_gas_coins_allow_sponsor(
2408            kind,
2409            sender,
2410            gas_payment,
2411            gas_budget,
2412            gas_price,
2413            sponsor,
2414        )
2415    }
2416
2417    pub fn new_programmable_with_address_balance_gas(
2418        sender: SuiAddress,
2419        pt: ProgrammableTransaction,
2420        gas_budget: u64,
2421        gas_price: u64,
2422        chain_identifier: ChainIdentifier,
2423        current_epoch: EpochId,
2424        nonce: u32,
2425    ) -> Self {
2426        TransactionData::V1(TransactionDataV1 {
2427            kind: TransactionKind::ProgrammableTransaction(pt),
2428            sender,
2429            gas_data: GasData {
2430                payment: vec![],
2431                owner: sender,
2432                price: gas_price,
2433                budget: gas_budget,
2434            },
2435            expiration: TransactionExpiration::ValidDuring {
2436                min_epoch: Some(current_epoch),
2437                max_epoch: Some(current_epoch + 1),
2438                min_timestamp: None,
2439                max_timestamp: None,
2440                chain: chain_identifier,
2441                nonce,
2442            },
2443        })
2444    }
2445
2446    pub fn message_version(&self) -> u64 {
2447        match self {
2448            TransactionData::V1(_) => 1,
2449        }
2450    }
2451
2452    pub fn execution_parts(&self) -> (TransactionKind, SuiAddress, GasData) {
2453        (self.kind().clone(), self.sender(), self.gas_data().clone())
2454    }
2455
2456    pub fn uses_randomness(&self) -> bool {
2457        self.kind()
2458            .shared_input_objects()
2459            .any(|obj| obj.id() == SUI_RANDOMNESS_STATE_OBJECT_ID)
2460    }
2461
2462    pub fn digest(&self) -> TransactionDigest {
2463        TransactionDigest::new(default_hash(self))
2464    }
2465}
2466
2467#[enum_dispatch]
2468pub trait TransactionDataAPI {
2469    fn sender(&self) -> SuiAddress;
2470
2471    // Note: this implies that SingleTransactionKind itself must be versioned, so that it can be
2472    // shared across versions. This will be easy to do since it is already an enum.
2473    fn kind(&self) -> &TransactionKind;
2474
2475    // Used by programmable_transaction_builder
2476    fn kind_mut(&mut self) -> &mut TransactionKind;
2477
2478    // kind is moved out of often enough that this is worth it to special case.
2479    fn into_kind(self) -> TransactionKind;
2480
2481    /// Transaction signer and Gas owner
2482    fn required_signers(&self) -> NonEmpty<SuiAddress>;
2483
2484    fn gas_data(&self) -> &GasData;
2485
2486    fn gas_owner(&self) -> SuiAddress;
2487
2488    fn gas(&self) -> &[ObjectRef];
2489
2490    fn gas_price(&self) -> u64;
2491
2492    fn gas_budget(&self) -> u64;
2493
2494    fn expiration(&self) -> &TransactionExpiration;
2495
2496    fn expiration_mut(&mut self) -> &mut TransactionExpiration;
2497
2498    fn move_calls(&self) -> Vec<(usize, &ObjectID, &str, &str)>;
2499
2500    fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
2501
2502    fn shared_input_objects(&self) -> Vec<SharedInputObject>;
2503
2504    fn receiving_objects(&self) -> Vec<ObjectRef>;
2505
2506    // Dependency (input, package & receiving) objects that already have a version,
2507    // and do not require version assignment from consensus.
2508    // Returns move objects, package objects and receiving objects.
2509    fn fastpath_dependency_objects(
2510        &self,
2511    ) -> UserInputResult<(Vec<ObjectRef>, Vec<ObjectID>, Vec<ObjectRef>)>;
2512
2513    /// Processes funds withdraws and returns a map from funds account object ID to total
2514    /// reserved amount. This method aggregates all withdraw operations for the same account by
2515    /// merging their reservations. Each account object ID is derived from the type parameter of
2516    /// each withdraw operation.
2517    ///
2518    /// This method is used at signing time, and can reject a transaction if it contains
2519    /// invalid reservations.
2520    fn process_funds_withdrawals_for_signing(
2521        &self,
2522        chain_identifier: ChainIdentifier,
2523        coin_resolver: &dyn CoinReservationResolverTrait,
2524    ) -> UserInputResult<BTreeMap<AccumulatorObjId, u64>>;
2525
2526    /// Like `process_funds_withdrawals_for_signing`, but must only be called on a certified
2527    /// transaction, i.e. one that is known to be valid.
2528    fn process_funds_withdrawals_for_execution(
2529        &self,
2530        chain_identifier: ChainIdentifier,
2531    ) -> BTreeMap<AccumulatorObjId, u64>;
2532
2533    // A cheap way to quickly check if the transaction has funds withdraws.
2534    fn has_funds_withdrawals(&self) -> bool;
2535
2536    // Get all the funds withdrawals args in the transaction.
2537    fn get_funds_withdrawals(&self) -> Vec<FundsWithdrawalArg>;
2538
2539    fn coin_reservation_obj_refs(
2540        &self,
2541        chain_identifier: ChainIdentifier,
2542    ) -> Vec<ParsedObjectRefWithdrawal>;
2543
2544    fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> SuiResult;
2545
2546    fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult;
2547
2548    /// Check if the transaction is compliant with sponsorship.
2549    fn check_sponsorship(&self) -> UserInputResult;
2550
2551    fn is_system_tx(&self) -> bool;
2552    fn is_genesis_tx(&self) -> bool;
2553
2554    /// returns true if the transaction is one that is specially sequenced to run at the very end
2555    /// of the epoch
2556    fn is_end_of_epoch_tx(&self) -> bool;
2557
2558    fn is_consensus_commit_prologue(&self) -> bool;
2559
2560    /// Check if the transaction is sponsored (namely gas owner != sender)
2561    fn is_sponsored_tx(&self) -> bool;
2562
2563    fn is_gas_paid_from_address_balance(&self) -> bool;
2564
2565    fn sender_mut_for_testing(&mut self) -> &mut SuiAddress;
2566
2567    fn gas_data_mut(&mut self) -> &mut GasData;
2568
2569    // This should be used in testing only.
2570    fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration;
2571}
2572
2573impl TransactionDataAPI for TransactionDataV1 {
2574    fn sender(&self) -> SuiAddress {
2575        self.sender
2576    }
2577
2578    fn kind(&self) -> &TransactionKind {
2579        &self.kind
2580    }
2581
2582    fn kind_mut(&mut self) -> &mut TransactionKind {
2583        &mut self.kind
2584    }
2585
2586    fn into_kind(self) -> TransactionKind {
2587        self.kind
2588    }
2589
2590    /// Transaction signer and Gas owner
2591    fn required_signers(&self) -> NonEmpty<SuiAddress> {
2592        let mut signers = nonempty![self.sender];
2593        if self.gas_owner() != self.sender {
2594            signers.push(self.gas_owner());
2595        }
2596        signers
2597    }
2598
2599    fn gas_data(&self) -> &GasData {
2600        &self.gas_data
2601    }
2602
2603    fn gas_owner(&self) -> SuiAddress {
2604        self.gas_data.owner
2605    }
2606
2607    fn gas(&self) -> &[ObjectRef] {
2608        &self.gas_data.payment
2609    }
2610
2611    fn gas_price(&self) -> u64 {
2612        self.gas_data.price
2613    }
2614
2615    fn gas_budget(&self) -> u64 {
2616        self.gas_data.budget
2617    }
2618
2619    fn expiration(&self) -> &TransactionExpiration {
2620        &self.expiration
2621    }
2622
2623    fn expiration_mut(&mut self) -> &mut TransactionExpiration {
2624        &mut self.expiration
2625    }
2626
2627    fn move_calls(&self) -> Vec<(usize, &ObjectID, &str, &str)> {
2628        self.kind.move_calls()
2629    }
2630
2631    fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
2632        let mut inputs = self.kind.input_objects()?;
2633
2634        if !self.kind.is_system_tx() {
2635            inputs.extend(
2636                self.gas()
2637                    .iter()
2638                    .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
2639            );
2640        }
2641        Ok(inputs)
2642    }
2643
2644    fn shared_input_objects(&self) -> Vec<SharedInputObject> {
2645        self.kind.shared_input_objects().collect()
2646    }
2647
2648    fn receiving_objects(&self) -> Vec<ObjectRef> {
2649        self.kind.receiving_objects()
2650    }
2651
2652    fn fastpath_dependency_objects(
2653        &self,
2654    ) -> UserInputResult<(Vec<ObjectRef>, Vec<ObjectID>, Vec<ObjectRef>)> {
2655        let mut move_objects = vec![];
2656        let mut packages = vec![];
2657        let mut receiving_objects = vec![];
2658        self.input_objects()?.iter().for_each(|o| match o {
2659            InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
2660                move_objects.push(*object_ref);
2661            }
2662            InputObjectKind::MovePackage(package_id) => {
2663                packages.push(*package_id);
2664            }
2665            InputObjectKind::SharedMoveObject { .. } => {}
2666        });
2667        self.receiving_objects().iter().for_each(|object_ref| {
2668            receiving_objects.push(*object_ref);
2669        });
2670        Ok((move_objects, packages, receiving_objects))
2671    }
2672
2673    fn process_funds_withdrawals_for_signing(
2674        &self,
2675        chain_identifier: ChainIdentifier,
2676        coin_resolver: &dyn CoinReservationResolverTrait,
2677    ) -> UserInputResult<BTreeMap<AccumulatorObjId, u64>> {
2678        let mut withdraws = self.get_funds_withdrawals();
2679
2680        for withdraw in self.parsed_coin_reservations(chain_identifier) {
2681            let withdrawal_arg = coin_resolver.resolve_funds_withdrawal(self.sender(), withdraw)?;
2682            withdraws.push(withdrawal_arg);
2683        }
2684
2685        withdraws.extend(self.get_funds_withdrawal_for_gas_payment());
2686
2687        // Accumulate all withdraws per account.
2688        let mut withdraw_map: BTreeMap<_, 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 account_address = withdraw.owner_for_withdrawal(self);
2698            let account_id =
2699                AccumulatorValue::get_field_id(account_address, &withdraw.type_arg.to_type_tag())
2700                    .map_err(|e| UserInputError::InvalidWithdrawReservation {
2701                    error: e.to_string(),
2702                })?;
2703
2704            let current_amount = withdraw_map.entry(account_id).or_default();
2705            *current_amount = current_amount.checked_add(reserved_amount).ok_or(
2706                UserInputError::InvalidWithdrawReservation {
2707                    error: "Balance withdraw reservation overflow".to_string(),
2708                },
2709            )?;
2710        }
2711
2712        Ok(withdraw_map)
2713    }
2714
2715    fn process_funds_withdrawals_for_execution(
2716        &self,
2717        chain_identifier: ChainIdentifier,
2718    ) -> BTreeMap<AccumulatorObjId, u64> {
2719        let mut withdraws = self.get_funds_withdrawals();
2720
2721        withdraws.extend(self.get_funds_withdrawal_for_gas_payment());
2722
2723        // Accumulate all withdraws per account.
2724        let mut withdraw_map: BTreeMap<AccumulatorObjId, u64> = BTreeMap::new();
2725        for withdraw in withdraws {
2726            let reserved_amount = match &withdraw.reservation {
2727                Reservation::MaxAmountU64(amount) => {
2728                    assert!(*amount > 0, "verified in validity check");
2729                    *amount
2730                }
2731            };
2732
2733            let withdrawal_owner = withdraw.owner_for_withdrawal(self);
2734
2735            // unwrap checked at signing time
2736            let account_id =
2737                AccumulatorValue::get_field_id(withdrawal_owner, &withdraw.type_arg.to_type_tag())
2738                    .unwrap();
2739
2740            let value = withdraw_map.entry(account_id).or_default();
2741            // overflow checked at signing time
2742            *value = value.checked_add(reserved_amount).unwrap();
2743        }
2744
2745        // It is not necessarily possible to construct a FundsWithdrawalArg for coin reservations, because
2746        // the accumulator object may not exist any more. This is okay, as the scheduler will simply
2747        // cancel the transaction if there are no funds available.
2748        for obj in self.coin_reservation_obj_refs() {
2749            // unwrap safe because of signing time checks
2750            let parsed = ParsedObjectRefWithdrawal::parse(&obj, chain_identifier).unwrap();
2751            let value = withdraw_map
2752                // new_unchecked is safe because we verify that this is a valid accumulator object id
2753                // at signing time
2754                // The underlying object may have been deleted by now - this is okay. We don't need type information
2755                // here, we only need the accumulator object id.
2756                .entry(AccumulatorObjId::new_unchecked(parsed.unmasked_object_id))
2757                .or_default();
2758            // overflow checked at signing time
2759            *value = value.checked_add(parsed.reservation_amount()).unwrap();
2760        }
2761
2762        withdraw_map
2763    }
2764
2765    fn has_funds_withdrawals(&self) -> bool {
2766        if self.is_gas_paid_from_address_balance() {
2767            return true;
2768        }
2769        if let TransactionKind::ProgrammableTransaction(pt) = &self.kind {
2770            for input in &pt.inputs {
2771                if matches!(input, CallArg::FundsWithdrawal(_)) {
2772                    return true;
2773                }
2774            }
2775        }
2776        if self.coin_reservation_obj_refs().next().is_some() {
2777            return true;
2778        }
2779        false
2780    }
2781
2782    fn get_funds_withdrawals(&self) -> Vec<FundsWithdrawalArg> {
2783        self.kind.get_funds_withdrawals().cloned().collect()
2784    }
2785
2786    fn coin_reservation_obj_refs(
2787        &self,
2788        chain_identifier: ChainIdentifier,
2789    ) -> Vec<ParsedObjectRefWithdrawal> {
2790        self.coin_reservation_obj_refs()
2791            .filter_map(|obj_ref| ParsedObjectRefWithdrawal::parse(&obj_ref, chain_identifier))
2792            .collect()
2793    }
2794
2795    fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> SuiResult {
2796        let config = context.config;
2797
2798        // Checks to see if the transaction has expired
2799        match self.expiration() {
2800            TransactionExpiration::None => (), // always valid
2801            TransactionExpiration::Epoch(max_epoch) => {
2802                if context.epoch > *max_epoch {
2803                    return Err(SuiErrorKind::TransactionExpired.into());
2804                }
2805            }
2806            TransactionExpiration::ValidDuring {
2807                min_epoch,
2808                max_epoch,
2809                min_timestamp,
2810                max_timestamp,
2811                chain,
2812                nonce: _,
2813            } => {
2814                if min_timestamp.is_some() || max_timestamp.is_some() {
2815                    return Err(UserInputError::Unsupported(
2816                        "Timestamp-based transaction expiration is not yet supported".to_string(),
2817                    )
2818                    .into());
2819                }
2820
2821                // TODO: these checks can be loosened in the case where the transaction is not stateless,
2822                // i.e. contains AddressOwned inputs.
2823                match (min_epoch, max_epoch) {
2824                    (Some(min), Some(max)) => {
2825                        if config.enable_multi_epoch_transaction_expiration() {
2826                            if !(*max == *min || *max == min.saturating_add(1)) {
2827                                return Err(UserInputError::Unsupported(
2828                                    "max_epoch must be at most min_epoch + 1".to_string(),
2829                                )
2830                                .into());
2831                            }
2832                        } else if min != max {
2833                            return Err(UserInputError::Unsupported(
2834                                "min_epoch must equal max_epoch".to_string(),
2835                            )
2836                            .into());
2837                        }
2838                    }
2839                    _ => {
2840                        return Err(UserInputError::Unsupported(
2841                            "Both min_epoch and max_epoch must be specified".to_string(),
2842                        )
2843                        .into());
2844                    }
2845                }
2846
2847                if *chain != context.chain_identifier {
2848                    return Err(UserInputError::InvalidChainId {
2849                        provided: format!("{:?}", chain),
2850                        expected: format!("{:?}", context.chain_identifier),
2851                    }
2852                    .into());
2853                }
2854
2855                if let Some(min) = min_epoch
2856                    && context.epoch < *min
2857                {
2858                    return Err(SuiErrorKind::TransactionExpired.into());
2859                }
2860                if let Some(max) = max_epoch
2861                    && context.epoch > *max
2862                {
2863                    return Err(SuiErrorKind::TransactionExpired.into());
2864                }
2865            }
2866        }
2867
2868        if self.has_funds_withdrawals() {
2869            // TODO: this check is incorrect, we should only require this if there are zero owned
2870            // inputs
2871            fp_ensure!(
2872                !self.gas().is_empty() || config.enable_address_balance_gas_payments(),
2873                UserInputError::MissingGasPayment.into()
2874            );
2875
2876            fp_ensure!(
2877                config.enable_accumulators(),
2878                UserInputError::Unsupported("Address balance withdraw is not enabled".to_string())
2879                    .into()
2880            );
2881
2882            // TODO(address-balances): Use a protocol config parameter for max_withdraws.
2883            let max_withdraws = 10;
2884            let mut num_reservations = 0;
2885
2886            for withdraw in self.kind.get_funds_withdrawals() {
2887                num_reservations += 1;
2888                match withdraw.withdraw_from {
2889                    WithdrawFrom::Sender => (),
2890                    WithdrawFrom::Sponsor => {
2891                        return Err(UserInputError::InvalidWithdrawReservation {
2892                            error: "Explicit sponsor withdrawals are not yet supported".to_string(),
2893                        }
2894                        .into());
2895                    }
2896                }
2897
2898                match withdraw.reservation {
2899                    Reservation::MaxAmountU64(amount) => {
2900                        fp_ensure!(
2901                            amount > 0,
2902                            UserInputError::InvalidWithdrawReservation {
2903                                error: "Balance withdraw reservation amount must be non-zero"
2904                                    .to_string(),
2905                            }
2906                            .into()
2907                        );
2908                    }
2909                };
2910            }
2911
2912            for parsed in self.parsed_coin_reservations(context.chain_identifier) {
2913                num_reservations += 1;
2914                // coin reservations are valid for the current and next epoch, just as transactions that
2915                // specify a TransactionDuring are.
2916                // TODO: this check can be skipped if the transaction contains any address owned inputs.
2917                if parsed.epoch_id() != context.epoch && parsed.epoch_id() + 1 != context.epoch {
2918                    return Err(SuiErrorKind::TransactionExpired.into());
2919                }
2920                if parsed.reservation_amount() == 0 {
2921                    return Err(UserInputError::InvalidWithdrawReservation {
2922                        error: "Balance withdraw reservation amount must be non-zero".to_string(),
2923                    }
2924                    .into());
2925                }
2926            }
2927
2928            fp_ensure!(
2929                num_reservations <= max_withdraws,
2930                UserInputError::InvalidWithdrawReservation {
2931                    error: format!(
2932                        "Maximum number of balance withdraw reservations is {max_withdraws}"
2933                    ),
2934                }
2935                .into()
2936            );
2937        }
2938
2939        if config.enable_accumulators()
2940            && config.enable_address_balance_gas_payments()
2941            && self.is_gas_paid_from_address_balance()
2942        {
2943            if config.address_balance_gas_check_rgp_at_signing() {
2944                fp_ensure!(
2945                    self.gas_data.price >= context.reference_gas_price,
2946                    UserInputError::GasPriceUnderRGP {
2947                        gas_price: self.gas_data.price,
2948                        reference_gas_price: context.reference_gas_price,
2949                    }
2950                    .into()
2951                );
2952            }
2953
2954            match self.expiration() {
2955                TransactionExpiration::None => {
2956                    // To avoid changing error behavior unnecessarily, we flag this as a missing gas payment error
2957                    // instead of a missing expiration error.
2958                    return Err(UserInputError::MissingGasPayment.into());
2959                }
2960                TransactionExpiration::Epoch(_) => {
2961                    return Err(UserInputError::InvalidExpiration {
2962                        error: "Address balance gas payments require ValidDuring expiration"
2963                            .to_string(),
2964                    }
2965                    .into());
2966                }
2967                TransactionExpiration::ValidDuring { .. } => {}
2968            }
2969        } else {
2970            fp_ensure!(
2971                !self.gas().is_empty(),
2972                UserInputError::MissingGasPayment.into()
2973            );
2974        }
2975
2976        let gas_len = self.gas().len();
2977        let max_gas_objects = config.max_gas_payment_objects() as usize;
2978
2979        let within_limit = if config.correct_gas_payment_limit_check() {
2980            gas_len <= max_gas_objects
2981        } else {
2982            gas_len < max_gas_objects
2983        };
2984
2985        fp_ensure!(
2986            within_limit,
2987            UserInputError::SizeLimitExceeded {
2988                limit: "maximum number of gas payment objects".to_string(),
2989                value: config.max_gas_payment_objects().to_string()
2990            }
2991            .into()
2992        );
2993
2994        for (_, _, gas_digest) in self.gas().iter().copied() {
2995            fp_ensure!(
2996                ParsedDigest::try_from(gas_digest).is_err(),
2997                // This is not the most appropriate error, but we can't introduce a new one
2998                // since the point here is to achieve backward compatibility.
2999                UserInputError::GasObjectNotOwnedObject {
3000                    owner: Owner::AddressOwner(self.sender)
3001                }
3002                .into()
3003            );
3004        }
3005
3006        if !self.is_system_tx() {
3007            fp_ensure!(
3008                !check_for_gas_price_too_high(config.gas_model_version())
3009                    || self.gas_data.price < config.max_gas_price(),
3010                UserInputError::GasPriceTooHigh {
3011                    max_gas_price: config.max_gas_price(),
3012                }
3013                .into()
3014            );
3015            let cost_table = SuiCostTable::new(config, self.gas_data.price);
3016
3017            fp_ensure!(
3018                self.gas_data.budget <= cost_table.max_gas_budget,
3019                UserInputError::GasBudgetTooHigh {
3020                    gas_budget: self.gas_data().budget,
3021                    max_budget: cost_table.max_gas_budget,
3022                }
3023                .into()
3024            );
3025            fp_ensure!(
3026                self.gas_data.budget >= cost_table.min_transaction_cost,
3027                UserInputError::GasBudgetTooLow {
3028                    gas_budget: self.gas_data.budget,
3029                    min_budget: cost_table.min_transaction_cost,
3030                }
3031                .into()
3032            );
3033        }
3034
3035        self.validity_check_no_gas_check(config)?;
3036        Ok(())
3037    }
3038
3039    // Keep all the logic for validity here, we need this for dry run where the gas
3040    // may not be provided and created "on the fly"
3041    fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
3042        self.kind().validity_check(config)?;
3043        self.check_sponsorship()
3044    }
3045
3046    /// Check if the transaction is sponsored (namely gas owner != sender)
3047    fn is_sponsored_tx(&self) -> bool {
3048        self.gas_owner() != self.sender
3049    }
3050
3051    fn is_gas_paid_from_address_balance(&self) -> bool {
3052        is_gas_paid_from_address_balance(&self.gas_data, &self.kind)
3053    }
3054
3055    /// Check if the transaction is compliant with sponsorship.
3056    fn check_sponsorship(&self) -> UserInputResult {
3057        // Not a sponsored transaction, nothing to check
3058        if self.gas_owner() == self.sender() {
3059            return Ok(());
3060        }
3061        if matches!(&self.kind, TransactionKind::ProgrammableTransaction(_)) {
3062            return Ok(());
3063        }
3064        Err(UserInputError::UnsupportedSponsoredTransactionKind)
3065    }
3066
3067    fn is_end_of_epoch_tx(&self) -> bool {
3068        matches!(
3069            self.kind,
3070            TransactionKind::ChangeEpoch(_) | TransactionKind::EndOfEpochTransaction(_)
3071        )
3072    }
3073
3074    fn is_consensus_commit_prologue(&self) -> bool {
3075        match &self.kind {
3076            TransactionKind::ConsensusCommitPrologue(_)
3077            | TransactionKind::ConsensusCommitPrologueV2(_)
3078            | TransactionKind::ConsensusCommitPrologueV3(_)
3079            | TransactionKind::ConsensusCommitPrologueV4(_) => true,
3080
3081            TransactionKind::ProgrammableTransaction(_)
3082            | TransactionKind::ProgrammableSystemTransaction(_)
3083            | TransactionKind::ChangeEpoch(_)
3084            | TransactionKind::Genesis(_)
3085            | TransactionKind::AuthenticatorStateUpdate(_)
3086            | TransactionKind::EndOfEpochTransaction(_)
3087            | TransactionKind::RandomnessStateUpdate(_) => false,
3088        }
3089    }
3090
3091    fn is_system_tx(&self) -> bool {
3092        self.kind.is_system_tx()
3093    }
3094
3095    fn is_genesis_tx(&self) -> bool {
3096        matches!(self.kind, TransactionKind::Genesis(_))
3097    }
3098
3099    fn sender_mut_for_testing(&mut self) -> &mut SuiAddress {
3100        &mut self.sender
3101    }
3102
3103    fn gas_data_mut(&mut self) -> &mut GasData {
3104        &mut self.gas_data
3105    }
3106
3107    fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
3108        &mut self.expiration
3109    }
3110}
3111
3112impl TransactionDataV1 {
3113    fn get_funds_withdrawal_for_gas_payment(&self) -> Option<FundsWithdrawalArg> {
3114        if self.is_gas_paid_from_address_balance() {
3115            Some(if self.sender() != self.gas_owner() {
3116                FundsWithdrawalArg::balance_from_sponsor(self.gas_data().budget, GAS::type_tag())
3117            } else {
3118                FundsWithdrawalArg::balance_from_sender(self.gas_data().budget, GAS::type_tag())
3119            })
3120        } else {
3121            None
3122        }
3123    }
3124
3125    fn coin_reservation_obj_refs(&self) -> impl Iterator<Item = ObjectRef> {
3126        // TODO(address-balances): add gas coin obj refs
3127        self.kind.get_coin_reservation_obj_refs()
3128    }
3129
3130    fn parsed_coin_reservations(
3131        &self,
3132        chain_identifier: ChainIdentifier,
3133    ) -> impl Iterator<Item = ParsedObjectRefWithdrawal> {
3134        self.coin_reservation_obj_refs().map(move |obj_ref| {
3135            ParsedObjectRefWithdrawal::parse(&obj_ref, chain_identifier).unwrap()
3136        })
3137    }
3138}
3139
3140pub struct TxValidityCheckContext<'a> {
3141    pub config: &'a ProtocolConfig,
3142    pub epoch: EpochId,
3143    pub chain_identifier: ChainIdentifier,
3144    pub reference_gas_price: u64,
3145}
3146
3147impl<'a> TxValidityCheckContext<'a> {
3148    pub fn from_cfg_for_testing(config: &'a ProtocolConfig) -> Self {
3149        Self {
3150            config,
3151            epoch: 0,
3152            chain_identifier: ChainIdentifier::default(),
3153            reference_gas_price: 1000,
3154        }
3155    }
3156}
3157
3158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
3159pub struct SenderSignedData(SizeOneVec<SenderSignedTransaction>);
3160
3161#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3162pub struct SenderSignedTransaction {
3163    pub intent_message: IntentMessage<TransactionData>,
3164    /// A list of signatures signed by all transaction participants.
3165    /// 1. non participant signature must not be present.
3166    /// 2. signature order does not matter.
3167    pub tx_signatures: Vec<GenericSignature>,
3168}
3169
3170impl Serialize for SenderSignedTransaction {
3171    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3172    where
3173        S: serde::Serializer,
3174    {
3175        #[derive(Serialize)]
3176        #[serde(rename = "SenderSignedTransaction")]
3177        struct SignedTxn<'a> {
3178            intent_message: &'a IntentMessage<TransactionData>,
3179            tx_signatures: &'a Vec<GenericSignature>,
3180        }
3181
3182        if self.intent_message().intent != Intent::sui_transaction() {
3183            return Err(serde::ser::Error::custom("invalid Intent for Transaction"));
3184        }
3185
3186        let txn = SignedTxn {
3187            intent_message: self.intent_message(),
3188            tx_signatures: &self.tx_signatures,
3189        };
3190        txn.serialize(serializer)
3191    }
3192}
3193
3194impl<'de> Deserialize<'de> for SenderSignedTransaction {
3195    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3196    where
3197        D: serde::Deserializer<'de>,
3198    {
3199        #[derive(Deserialize)]
3200        #[serde(rename = "SenderSignedTransaction")]
3201        struct SignedTxn {
3202            intent_message: IntentMessage<TransactionData>,
3203            tx_signatures: Vec<GenericSignature>,
3204        }
3205
3206        let SignedTxn {
3207            intent_message,
3208            tx_signatures,
3209        } = Deserialize::deserialize(deserializer)?;
3210
3211        if intent_message.intent != Intent::sui_transaction() {
3212            return Err(serde::de::Error::custom("invalid Intent for Transaction"));
3213        }
3214
3215        Ok(Self {
3216            intent_message,
3217            tx_signatures,
3218        })
3219    }
3220}
3221
3222impl SenderSignedTransaction {
3223    /// Returns a mapping from signer address to the signature and its index in `tx_signatures`.
3224    pub(crate) fn get_signer_sig_mapping(
3225        &self,
3226        verify_legacy_zklogin_address: bool,
3227    ) -> SuiResult<BTreeMap<SuiAddress, (u8, &GenericSignature)>> {
3228        let mut mapping = BTreeMap::new();
3229        for (idx, sig) in self.tx_signatures.iter().enumerate() {
3230            if verify_legacy_zklogin_address && let GenericSignature::ZkLoginAuthenticator(z) = sig
3231            {
3232                // Try deriving the address from the legacy padded way.
3233                mapping.insert(SuiAddress::try_from_padded(&z.inputs)?, (idx as u8, sig));
3234            }
3235            let address = sig.try_into()?;
3236            mapping.insert(address, (idx as u8, sig));
3237        }
3238        Ok(mapping)
3239    }
3240
3241    pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
3242        &self.intent_message
3243    }
3244}
3245
3246impl SenderSignedData {
3247    pub fn new(tx_data: TransactionData, tx_signatures: Vec<GenericSignature>) -> Self {
3248        Self(SizeOneVec::new(SenderSignedTransaction {
3249            intent_message: IntentMessage::new(Intent::sui_transaction(), tx_data),
3250            tx_signatures,
3251        }))
3252    }
3253
3254    pub fn new_from_sender_signature(tx_data: TransactionData, tx_signature: Signature) -> Self {
3255        Self(SizeOneVec::new(SenderSignedTransaction {
3256            intent_message: IntentMessage::new(Intent::sui_transaction(), tx_data),
3257            tx_signatures: vec![tx_signature.into()],
3258        }))
3259    }
3260
3261    pub fn inner(&self) -> &SenderSignedTransaction {
3262        self.0.element()
3263    }
3264
3265    pub fn into_inner(self) -> SenderSignedTransaction {
3266        self.0.into_inner()
3267    }
3268
3269    pub fn inner_mut(&mut self) -> &mut SenderSignedTransaction {
3270        self.0.element_mut()
3271    }
3272
3273    // This function does not check validity of the signature
3274    // or perform any de-dup checks.
3275    pub fn add_signature(&mut self, new_signature: Signature) {
3276        self.inner_mut().tx_signatures.push(new_signature.into());
3277    }
3278
3279    pub(crate) fn get_signer_sig_mapping(
3280        &self,
3281        verify_legacy_zklogin_address: bool,
3282    ) -> SuiResult<BTreeMap<SuiAddress, (u8, &GenericSignature)>> {
3283        self.inner()
3284            .get_signer_sig_mapping(verify_legacy_zklogin_address)
3285    }
3286
3287    pub fn transaction_data(&self) -> &TransactionData {
3288        &self.intent_message().value
3289    }
3290
3291    pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
3292        self.inner().intent_message()
3293    }
3294
3295    pub fn tx_signatures(&self) -> &[GenericSignature] {
3296        &self.inner().tx_signatures
3297    }
3298
3299    pub fn has_zklogin_sig(&self) -> bool {
3300        self.tx_signatures().iter().any(|sig| sig.is_zklogin())
3301    }
3302
3303    pub fn has_upgraded_multisig(&self) -> bool {
3304        self.tx_signatures()
3305            .iter()
3306            .any(|sig| sig.is_upgraded_multisig())
3307    }
3308
3309    #[cfg(test)]
3310    pub fn intent_message_mut_for_testing(&mut self) -> &mut IntentMessage<TransactionData> {
3311        &mut self.inner_mut().intent_message
3312    }
3313
3314    // used cross-crate, so cannot be #[cfg(test)]
3315    pub fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<GenericSignature> {
3316        &mut self.inner_mut().tx_signatures
3317    }
3318
3319    /// Includes alias_versions to ensure cache invalidation when aliases change.
3320    pub fn full_message_digest_with_alias_versions(
3321        &self,
3322        alias_versions: &Vec<(SuiAddress, Option<SequenceNumber>)>,
3323    ) -> SenderSignedDataDigest {
3324        let mut digest = DefaultHash::default();
3325        bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
3326        bcs::serialize_into(&mut digest, alias_versions).expect("serialization should not fail");
3327        let hash = digest.finalize();
3328        SenderSignedDataDigest::new(hash.into())
3329    }
3330
3331    pub fn serialized_size(&self) -> SuiResult<usize> {
3332        bcs::serialized_size(self).map_err(|e| {
3333            SuiErrorKind::TransactionSerializationError {
3334                error: e.to_string(),
3335            }
3336            .into()
3337        })
3338    }
3339
3340    fn check_user_signature_protocol_compatibility(&self, config: &ProtocolConfig) -> SuiResult {
3341        for sig in &self.inner().tx_signatures {
3342            match sig {
3343                GenericSignature::MultiSig(_) => {
3344                    if !config.supports_upgraded_multisig() {
3345                        return Err(SuiErrorKind::UserInputError {
3346                            error: UserInputError::Unsupported(
3347                                "upgraded multisig format not enabled on this network".to_string(),
3348                            ),
3349                        }
3350                        .into());
3351                    }
3352                }
3353                GenericSignature::ZkLoginAuthenticator(_) => {
3354                    if !config.zklogin_auth() {
3355                        return Err(SuiErrorKind::UserInputError {
3356                            error: UserInputError::Unsupported(
3357                                "zklogin is not enabled on this network".to_string(),
3358                            ),
3359                        }
3360                        .into());
3361                    }
3362                }
3363                GenericSignature::PasskeyAuthenticator(_) => {
3364                    if !config.passkey_auth() {
3365                        return Err(SuiErrorKind::UserInputError {
3366                            error: UserInputError::Unsupported(
3367                                "passkey is not enabled on this network".to_string(),
3368                            ),
3369                        }
3370                        .into());
3371                    }
3372                }
3373                GenericSignature::Signature(_) | GenericSignature::MultiSigLegacy(_) => (),
3374            }
3375        }
3376
3377        Ok(())
3378    }
3379
3380    /// Validate untrusted user transaction, including its size, input count, command count, etc.
3381    /// Returns the certificate serialised bytes size.
3382    pub fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, SuiError> {
3383        // Check that the features used by the user signatures are enabled on the network.
3384        self.check_user_signature_protocol_compatibility(context.config)?;
3385
3386        // TODO: The following checks can be moved to TransactionData, if we pass context into it.
3387
3388        // CRITICAL!!
3389        // Users cannot send system transactions.
3390        let tx_data = &self.transaction_data();
3391        fp_ensure!(
3392            !tx_data.is_system_tx(),
3393            SuiErrorKind::UserInputError {
3394                error: UserInputError::Unsupported(
3395                    "SenderSignedData must not contain system transaction".to_string()
3396                )
3397            }
3398            .into()
3399        );
3400
3401        // Enforce overall transaction size limit.
3402        let tx_size = self.serialized_size()?;
3403        let max_tx_size_bytes = context.config.max_tx_size_bytes();
3404        fp_ensure!(
3405            tx_size as u64 <= max_tx_size_bytes,
3406            SuiErrorKind::UserInputError {
3407                error: UserInputError::SizeLimitExceeded {
3408                    limit: format!(
3409                        "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
3410                    ),
3411                    value: tx_size.to_string(),
3412                }
3413            }
3414            .into()
3415        );
3416
3417        tx_data.validity_check(context)?;
3418
3419        Ok(tx_size)
3420    }
3421}
3422
3423impl Message for SenderSignedData {
3424    type DigestType = TransactionDigest;
3425    const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
3426
3427    /// Computes the tx digest that encodes the Rust type prefix from Signable trait.
3428    fn digest(&self) -> Self::DigestType {
3429        self.intent_message().value.digest()
3430    }
3431}
3432
3433impl<S> Envelope<SenderSignedData, S> {
3434    pub fn sender_address(&self) -> SuiAddress {
3435        self.data().intent_message().value.sender()
3436    }
3437
3438    pub fn gas_owner(&self) -> SuiAddress {
3439        self.data().intent_message().value.gas_owner()
3440    }
3441
3442    pub fn gas(&self) -> &[ObjectRef] {
3443        self.data().intent_message().value.gas()
3444    }
3445
3446    pub fn is_consensus_tx(&self) -> bool {
3447        self.transaction_data().has_funds_withdrawals()
3448            || self.shared_input_objects().next().is_some()
3449    }
3450
3451    pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
3452        self.data()
3453            .inner()
3454            .intent_message
3455            .value
3456            .shared_input_objects()
3457            .into_iter()
3458    }
3459
3460    // Returns the primary key for this transaction.
3461    pub fn key(&self) -> TransactionKey {
3462        match &self.data().intent_message().value.kind() {
3463            TransactionKind::RandomnessStateUpdate(rsu) => {
3464                TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
3465            }
3466            _ => TransactionKey::Digest(*self.digest()),
3467        }
3468    }
3469
3470    // Returns non-Digest keys that could be used to refer to this transaction.
3471    //
3472    // At the moment this returns a single Option for efficiency, but if more key types are added,
3473    // the return type could change to Vec<TransactionKey>.
3474    pub fn non_digest_key(&self) -> Option<TransactionKey> {
3475        match &self.data().intent_message().value.kind() {
3476            TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
3477                rsu.epoch,
3478                rsu.randomness_round,
3479            )),
3480            _ => None,
3481        }
3482    }
3483
3484    pub fn is_system_tx(&self) -> bool {
3485        self.data().intent_message().value.is_system_tx()
3486    }
3487
3488    pub fn is_sponsored_tx(&self) -> bool {
3489        self.data().intent_message().value.is_sponsored_tx()
3490    }
3491}
3492
3493impl Transaction {
3494    pub fn from_data_and_signer(
3495        data: TransactionData,
3496        signers: Vec<&dyn Signer<Signature>>,
3497    ) -> Self {
3498        let signatures = {
3499            let intent_msg = IntentMessage::new(Intent::sui_transaction(), &data);
3500            signers
3501                .into_iter()
3502                .map(|s| Signature::new_secure(&intent_msg, s))
3503                .collect()
3504        };
3505        Self::from_data(data, signatures)
3506    }
3507
3508    // TODO: Rename this function and above to make it clearer.
3509    pub fn from_data(data: TransactionData, signatures: Vec<Signature>) -> Self {
3510        Self::from_generic_sig_data(data, signatures.into_iter().map(|s| s.into()).collect())
3511    }
3512
3513    pub fn signature_from_signer(
3514        data: TransactionData,
3515        intent: Intent,
3516        signer: &dyn Signer<Signature>,
3517    ) -> Signature {
3518        let intent_msg = IntentMessage::new(intent, data);
3519        Signature::new_secure(&intent_msg, signer)
3520    }
3521
3522    pub fn from_generic_sig_data(data: TransactionData, signatures: Vec<GenericSignature>) -> Self {
3523        Self::new(SenderSignedData::new(data, signatures))
3524    }
3525
3526    /// Returns the Base64 encoded tx_bytes
3527    /// and a list of Base64 encoded [enum GenericSignature].
3528    pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
3529        (
3530            Base64::from_bytes(&bcs::to_bytes(&self.data().intent_message().value).unwrap()),
3531            self.data()
3532                .inner()
3533                .tx_signatures
3534                .iter()
3535                .map(|s| Base64::from_bytes(s.as_ref()))
3536                .collect(),
3537        )
3538    }
3539}
3540
3541impl VerifiedTransaction {
3542    pub fn new_change_epoch(
3543        next_epoch: EpochId,
3544        protocol_version: ProtocolVersion,
3545        storage_charge: u64,
3546        computation_charge: u64,
3547        storage_rebate: u64,
3548        non_refundable_storage_fee: u64,
3549        epoch_start_timestamp_ms: u64,
3550        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
3551    ) -> Self {
3552        ChangeEpoch {
3553            epoch: next_epoch,
3554            protocol_version,
3555            storage_charge,
3556            computation_charge,
3557            storage_rebate,
3558            non_refundable_storage_fee,
3559            epoch_start_timestamp_ms,
3560            system_packages,
3561        }
3562        .pipe(TransactionKind::ChangeEpoch)
3563        .pipe(Self::new_system_transaction)
3564    }
3565
3566    pub fn new_genesis_transaction(objects: Vec<GenesisObject>) -> Self {
3567        GenesisTransaction { objects }
3568            .pipe(TransactionKind::Genesis)
3569            .pipe(Self::new_system_transaction)
3570    }
3571
3572    pub fn new_consensus_commit_prologue(
3573        epoch: u64,
3574        round: u64,
3575        commit_timestamp_ms: CheckpointTimestamp,
3576    ) -> Self {
3577        ConsensusCommitPrologue {
3578            epoch,
3579            round,
3580            commit_timestamp_ms,
3581        }
3582        .pipe(TransactionKind::ConsensusCommitPrologue)
3583        .pipe(Self::new_system_transaction)
3584    }
3585
3586    pub fn new_consensus_commit_prologue_v2(
3587        epoch: u64,
3588        round: u64,
3589        commit_timestamp_ms: CheckpointTimestamp,
3590        consensus_commit_digest: ConsensusCommitDigest,
3591    ) -> Self {
3592        ConsensusCommitPrologueV2 {
3593            epoch,
3594            round,
3595            commit_timestamp_ms,
3596            consensus_commit_digest,
3597        }
3598        .pipe(TransactionKind::ConsensusCommitPrologueV2)
3599        .pipe(Self::new_system_transaction)
3600    }
3601
3602    pub fn new_consensus_commit_prologue_v3(
3603        epoch: u64,
3604        round: u64,
3605        commit_timestamp_ms: CheckpointTimestamp,
3606        consensus_commit_digest: ConsensusCommitDigest,
3607        consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments,
3608    ) -> Self {
3609        ConsensusCommitPrologueV3 {
3610            epoch,
3611            round,
3612            // sub_dag_index is reserved for when we have multi commits per round.
3613            sub_dag_index: None,
3614            commit_timestamp_ms,
3615            consensus_commit_digest,
3616            consensus_determined_version_assignments,
3617        }
3618        .pipe(TransactionKind::ConsensusCommitPrologueV3)
3619        .pipe(Self::new_system_transaction)
3620    }
3621
3622    pub fn new_consensus_commit_prologue_v4(
3623        epoch: u64,
3624        round: u64,
3625        commit_timestamp_ms: CheckpointTimestamp,
3626        consensus_commit_digest: ConsensusCommitDigest,
3627        consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments,
3628        additional_state_digest: AdditionalConsensusStateDigest,
3629    ) -> Self {
3630        ConsensusCommitPrologueV4 {
3631            epoch,
3632            round,
3633            // sub_dag_index is reserved for when we have multi commits per round.
3634            sub_dag_index: None,
3635            commit_timestamp_ms,
3636            consensus_commit_digest,
3637            consensus_determined_version_assignments,
3638            additional_state_digest,
3639        }
3640        .pipe(TransactionKind::ConsensusCommitPrologueV4)
3641        .pipe(Self::new_system_transaction)
3642    }
3643
3644    pub fn new_authenticator_state_update(
3645        epoch: u64,
3646        round: u64,
3647        new_active_jwks: Vec<ActiveJwk>,
3648        authenticator_obj_initial_shared_version: SequenceNumber,
3649    ) -> Self {
3650        AuthenticatorStateUpdate {
3651            epoch,
3652            round,
3653            new_active_jwks,
3654            authenticator_obj_initial_shared_version,
3655        }
3656        .pipe(TransactionKind::AuthenticatorStateUpdate)
3657        .pipe(Self::new_system_transaction)
3658    }
3659
3660    pub fn new_randomness_state_update(
3661        epoch: u64,
3662        randomness_round: RandomnessRound,
3663        random_bytes: Vec<u8>,
3664        randomness_obj_initial_shared_version: SequenceNumber,
3665    ) -> Self {
3666        RandomnessStateUpdate {
3667            epoch,
3668            randomness_round,
3669            random_bytes,
3670            randomness_obj_initial_shared_version,
3671        }
3672        .pipe(TransactionKind::RandomnessStateUpdate)
3673        .pipe(Self::new_system_transaction)
3674    }
3675
3676    pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
3677        TransactionKind::EndOfEpochTransaction(txns).pipe(Self::new_system_transaction)
3678    }
3679
3680    pub fn new_system_transaction(system_transaction: TransactionKind) -> Self {
3681        system_transaction
3682            .pipe(TransactionData::new_system_transaction)
3683            .pipe(|data| {
3684                SenderSignedData::new_from_sender_signature(
3685                    data,
3686                    Ed25519SuiSignature::from_bytes(&[0; Ed25519SuiSignature::LENGTH])
3687                        .unwrap()
3688                        .into(),
3689                )
3690            })
3691            .pipe(Transaction::new)
3692            .pipe(Self::new_from_verified)
3693    }
3694}
3695
3696impl VerifiedSignedTransaction {
3697    /// Use signing key to create a signed object.
3698    pub fn new(
3699        epoch: EpochId,
3700        transaction: VerifiedTransaction,
3701        authority: AuthorityName,
3702        secret: &dyn Signer<AuthoritySignature>,
3703    ) -> Self {
3704        Self::new_from_verified(SignedTransaction::new(
3705            epoch,
3706            transaction.into_inner().into_data(),
3707            secret,
3708            authority,
3709        ))
3710    }
3711}
3712
3713/// A transaction that is signed by a sender but not yet by an authority.
3714pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
3715pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
3716pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
3717
3718/// A transaction that is signed by a sender and also by an authority.
3719pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
3720pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
3721
3722impl Transaction {
3723    pub fn verify_signature_for_testing(
3724        &self,
3725        current_epoch: EpochId,
3726        verify_params: &VerifyParams,
3727    ) -> SuiResult {
3728        verify_sender_signed_data_message_signatures(
3729            self.data(),
3730            current_epoch,
3731            verify_params,
3732            Arc::new(VerifiedDigestCache::new_empty()),
3733            vec![],
3734        )?;
3735        Ok(())
3736    }
3737
3738    pub fn try_into_verified_for_testing(
3739        self,
3740        current_epoch: EpochId,
3741        verify_params: &VerifyParams,
3742    ) -> SuiResult<VerifiedTransaction> {
3743        self.verify_signature_for_testing(current_epoch, verify_params)?;
3744        Ok(VerifiedTransaction::new_from_verified(self))
3745    }
3746}
3747
3748impl SignedTransaction {
3749    pub fn verify_signatures_authenticated_for_testing(
3750        &self,
3751        committee: &Committee,
3752        verify_params: &VerifyParams,
3753    ) -> SuiResult {
3754        verify_sender_signed_data_message_signatures(
3755            self.data(),
3756            committee.epoch(),
3757            verify_params,
3758            Arc::new(VerifiedDigestCache::new_empty()),
3759            vec![],
3760        )?;
3761
3762        self.auth_sig().verify_secure(
3763            self.data(),
3764            Intent::sui_app(IntentScope::SenderSignedTransaction),
3765            committee,
3766        )
3767    }
3768
3769    pub fn try_into_verified_for_testing(
3770        self,
3771        committee: &Committee,
3772        verify_params: &VerifyParams,
3773    ) -> SuiResult<VerifiedSignedTransaction> {
3774        self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
3775        Ok(VerifiedSignedTransaction::new_from_verified(self))
3776    }
3777}
3778
3779pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3780
3781impl CertifiedTransaction {
3782    pub fn certificate_digest(&self) -> CertificateDigest {
3783        let mut digest = DefaultHash::default();
3784        bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
3785        let hash = digest.finalize();
3786        CertificateDigest::new(hash.into())
3787    }
3788
3789    pub fn gas_price(&self) -> u64 {
3790        self.data().transaction_data().gas_price()
3791    }
3792
3793    // TODO: Eventually we should remove all calls to verify_signature
3794    // and make sure they all call verify to avoid repeated verifications.
3795    pub fn verify_signatures_authenticated(
3796        &self,
3797        committee: &Committee,
3798        verify_params: &VerifyParams,
3799        zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
3800    ) -> SuiResult {
3801        verify_sender_signed_data_message_signatures(
3802            self.data(),
3803            committee.epoch(),
3804            verify_params,
3805            zklogin_inputs_cache,
3806            vec![],
3807        )?;
3808        self.auth_sig().verify_secure(
3809            self.data(),
3810            Intent::sui_app(IntentScope::SenderSignedTransaction),
3811            committee,
3812        )
3813    }
3814
3815    pub fn try_into_verified_for_testing(
3816        self,
3817        committee: &Committee,
3818        verify_params: &VerifyParams,
3819    ) -> SuiResult<VerifiedCertificate> {
3820        self.verify_signatures_authenticated(
3821            committee,
3822            verify_params,
3823            Arc::new(VerifiedDigestCache::new_empty()),
3824        )?;
3825        Ok(VerifiedCertificate::new_from_verified(self))
3826    }
3827
3828    pub fn verify_committee_sigs_only(&self, committee: &Committee) -> SuiResult {
3829        self.auth_sig().verify_secure(
3830            self.data(),
3831            Intent::sui_app(IntentScope::SenderSignedTransaction),
3832            committee,
3833        )
3834    }
3835}
3836
3837pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3838pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
3839
3840#[derive(Clone, Debug, Serialize, Deserialize)]
3841pub struct WithAliases<T>(
3842    T,
3843    #[serde(with = "nonempty_as_vec")] NonEmpty<(u8, Option<SequenceNumber>)>,
3844);
3845
3846impl<T> WithAliases<T> {
3847    pub fn new(tx: T, aliases: NonEmpty<(u8, Option<SequenceNumber>)>) -> Self {
3848        Self(tx, aliases)
3849    }
3850
3851    pub fn tx(&self) -> &T {
3852        &self.0
3853    }
3854
3855    pub fn aliases(&self) -> &NonEmpty<(u8, Option<SequenceNumber>)> {
3856        &self.1
3857    }
3858
3859    pub fn into_tx(self) -> T {
3860        self.0
3861    }
3862
3863    pub fn into_aliases(self) -> NonEmpty<(u8, Option<SequenceNumber>)> {
3864        self.1
3865    }
3866
3867    pub fn into_inner(self) -> (T, NonEmpty<(u8, Option<SequenceNumber>)>) {
3868        (self.0, self.1)
3869    }
3870}
3871
3872impl<T: Message, S> WithAliases<VerifiedEnvelope<T, S>> {
3873    /// Analogous to VerifiedEnvelope::serializable.
3874    pub fn serializable(self) -> WithAliases<TrustedEnvelope<T, S>> {
3875        WithAliases(self.0.serializable(), self.1)
3876    }
3877}
3878
3879impl<S> WithAliases<Envelope<SenderSignedData, S>> {
3880    /// Creates a WithAliases where each required signer is mapped to its corresponding
3881    /// signature index (assuming 1:1 correspondence) with no alias object version.
3882    pub fn no_aliases(tx: Envelope<SenderSignedData, S>) -> Self {
3883        let required_signers = tx.intent_message().value.required_signers();
3884        assert_eq!(required_signers.len(), tx.tx_signatures().len());
3885        let no_aliases = required_signers
3886            .iter()
3887            .enumerate()
3888            .map(|(idx, _)| (idx as u8, None))
3889            .collect::<Vec<_>>();
3890        Self::new(
3891            tx,
3892            NonEmpty::from_vec(no_aliases).expect("must have at least one required_signer"),
3893        )
3894    }
3895}
3896
3897impl<S> WithAliases<VerifiedEnvelope<SenderSignedData, S>> {
3898    /// Creates a WithAliases where each required signer is mapped to its corresponding
3899    /// signature index (assuming 1:1 correspondence) with no alias object version.
3900    pub fn no_aliases(tx: VerifiedEnvelope<SenderSignedData, S>) -> Self {
3901        let required_signers = tx.intent_message().value.required_signers();
3902        assert_eq!(required_signers.len(), tx.tx_signatures().len());
3903        let no_aliases = required_signers
3904            .iter()
3905            .enumerate()
3906            .map(|(idx, _)| (idx as u8, None))
3907            .collect::<Vec<_>>();
3908        Self::new(
3909            tx,
3910            NonEmpty::from_vec(no_aliases).expect("must have at least one required_signer"),
3911        )
3912    }
3913}
3914
3915pub type TransactionWithAliases = WithAliases<Transaction>;
3916pub type VerifiedTransactionWithAliases = WithAliases<VerifiedTransaction>;
3917pub type TrustedTransactionWithAliases = WithAliases<TrustedTransaction>;
3918
3919/// Deprecated version of WithAliases that uses SuiAddress instead of u8.
3920/// This is needed to read data from deferred_transactions_with_aliases_v2 table
3921/// which was written with the old format before the type was changed.
3922// TODO: Delete this after all production networks are on the latest table.
3923#[derive(Clone, Debug, Serialize, Deserialize)]
3924pub struct DeprecatedWithAliases<T>(
3925    T,
3926    #[serde(with = "nonempty_as_vec")] NonEmpty<(SuiAddress, Option<SequenceNumber>)>,
3927);
3928
3929impl<T> DeprecatedWithAliases<T> {
3930    pub fn into_inner(self) -> (T, NonEmpty<(SuiAddress, Option<SequenceNumber>)>) {
3931        (self.0, self.1)
3932    }
3933}
3934
3935impl<T: Message, S> From<WithAliases<VerifiedEnvelope<T, S>>> for WithAliases<Envelope<T, S>> {
3936    fn from(value: WithAliases<VerifiedEnvelope<T, S>>) -> Self {
3937        Self(value.0.into(), value.1)
3938    }
3939}
3940
3941impl<T: Message, S> From<WithAliases<TrustedEnvelope<T, S>>>
3942    for WithAliases<VerifiedEnvelope<T, S>>
3943{
3944    fn from(value: WithAliases<TrustedEnvelope<T, S>>) -> Self {
3945        Self(value.0.into(), value.1)
3946    }
3947}
3948
3949mod nonempty_as_vec {
3950    use super::*;
3951    use serde::{Deserialize, Deserializer, Serialize, Serializer};
3952
3953    pub fn serialize<S, T>(value: &NonEmpty<T>, serializer: S) -> Result<S::Ok, S::Error>
3954    where
3955        S: Serializer,
3956        T: Serialize,
3957    {
3958        let vec: Vec<&T> = value.iter().collect();
3959        vec.serialize(serializer)
3960    }
3961
3962    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<NonEmpty<T>, D::Error>
3963    where
3964        D: Deserializer<'de>,
3965        T: Deserialize<'de> + Clone,
3966    {
3967        use serde::de::{SeqAccess, Visitor};
3968        use std::fmt;
3969        use std::marker::PhantomData;
3970
3971        struct NonEmptyVisitor<T>(PhantomData<T>);
3972
3973        impl<'de, T> Visitor<'de> for NonEmptyVisitor<T>
3974        where
3975            T: Deserialize<'de> + Clone,
3976        {
3977            type Value = NonEmpty<T>;
3978
3979            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
3980                formatter.write_str("a non-empty sequence")
3981            }
3982
3983            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
3984            where
3985                A: SeqAccess<'de>,
3986            {
3987                let head = seq
3988                    .next_element()?
3989                    .ok_or_else(|| serde::de::Error::custom("empty vector"))?;
3990
3991                let mut tail = Vec::new();
3992                while let Some(elem) = seq.next_element()? {
3993                    tail.push(elem);
3994                }
3995
3996                Ok(NonEmpty { head, tail })
3997            }
3998        }
3999
4000        deserializer.deserialize_seq(NonEmptyVisitor(PhantomData))
4001    }
4002}
4003
4004// =============================================================================
4005// TransactionWithClaims - Generalized claim system for consensus messages
4006// =============================================================================
4007
4008/// Claims that can be attached to a transaction for consensus validation.
4009/// Each claim type represents a piece of information that:
4010/// 1. The submitting validator includes in the consensus message
4011/// 2. Voting validators verify before accepting
4012/// 3. The consensus handler can use deterministically
4013#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
4014pub enum TransactionClaim {
4015    /// DEPRECATED. Do not use.
4016    #[deprecated(note = "Use AddressAliasesV2")]
4017    AddressAliases(
4018        #[serde(with = "nonempty_as_vec")] NonEmpty<(SuiAddress, Option<SequenceNumber>)>,
4019    ),
4020
4021    /// Object IDs that are claimed to be immutable.
4022    /// Used to filter out immutable objects from lock acquisition in consensus handler.
4023    ImmutableInputObjects(Vec<ObjectID>),
4024
4025    /// Address aliases used for signature verification.
4026    /// Length must equal the number of `required_signers`. Each element maps the corresponding
4027    /// signer to the signature index and alias object version (if any) used to verify it.
4028    AddressAliasesV2(#[serde(with = "nonempty_as_vec")] NonEmpty<(u8, Option<SequenceNumber>)>),
4029}
4030
4031/// A transaction with attached claims that have been verified by voting validators.
4032#[derive(Clone, Debug, Serialize, Deserialize)]
4033pub struct TransactionWithClaims<T> {
4034    tx: T,
4035    claims: Vec<TransactionClaim>,
4036}
4037
4038impl<T> TransactionWithClaims<T> {
4039    pub fn new(tx: T, claims: Vec<TransactionClaim>) -> Self {
4040        Self { tx, claims }
4041    }
4042
4043    /// Create from a transaction with only address aliases.
4044    pub fn from_aliases(tx: T, aliases: NonEmpty<(u8, Option<SequenceNumber>)>) -> Self {
4045        Self {
4046            tx,
4047            claims: vec![TransactionClaim::AddressAliasesV2(aliases)],
4048        }
4049    }
4050
4051    /// Creates from a transaction without any aliases attached.
4052    pub fn no_aliases(tx: T) -> Self {
4053        Self { tx, claims: vec![] }
4054    }
4055
4056    pub fn tx(&self) -> &T {
4057        &self.tx
4058    }
4059
4060    pub fn into_tx(self) -> T {
4061        self.tx
4062    }
4063
4064    /// Get the address aliases V2 claim. Differentiate between empty and not present for validation.
4065    pub fn aliases(&self) -> Option<NonEmpty<(u8, Option<SequenceNumber>)>> {
4066        self.claims
4067            .iter()
4068            .find_map(|c| match c {
4069                TransactionClaim::AddressAliasesV2(aliases) => Some(aliases),
4070                _ => None,
4071            })
4072            .cloned()
4073    }
4074
4075    // TODO: Remove once `fix_checkpoint_signature_mapping` flag is enabled in testnet.
4076    #[allow(deprecated)]
4077    pub fn aliases_v1(&self) -> Option<NonEmpty<(SuiAddress, Option<SequenceNumber>)>> {
4078        self.claims
4079            .iter()
4080            .find_map(|c| match c {
4081                TransactionClaim::AddressAliases(aliases) => Some(aliases),
4082                _ => None,
4083            })
4084            .cloned()
4085    }
4086
4087    /// Get the immutable input objects claim. Returns empty vector if not present.
4088    pub fn get_immutable_objects(&self) -> Vec<ObjectID> {
4089        self.claims
4090            .iter()
4091            .find_map(|c| match c {
4092                TransactionClaim::ImmutableInputObjects(objs) => Some(objs.clone()),
4093                _ => None,
4094            })
4095            .unwrap_or_default()
4096    }
4097}
4098
4099pub type PlainTransactionWithClaims = TransactionWithClaims<Transaction>;
4100
4101/// Convert from `WithAliases<VerifiedEnvelope>` to `TransactionWithClaims<Envelope>`.
4102/// Used when feature flag is off to convert existing WithAliases to the new type.
4103impl<T: Message, S> From<WithAliases<VerifiedEnvelope<T, S>>>
4104    for TransactionWithClaims<Envelope<T, S>>
4105{
4106    fn from(value: WithAliases<VerifiedEnvelope<T, S>>) -> Self {
4107        let (tx, aliases) = value.into_inner();
4108        Self::from_aliases(tx.into(), aliases)
4109    }
4110}
4111
4112#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
4113pub enum InputObjectKind {
4114    // A Move package, must be immutable.
4115    MovePackage(ObjectID),
4116    // A Move object, either immutable, or owned mutable.
4117    ImmOrOwnedMoveObject(ObjectRef),
4118    // A Move object that's shared and mutable.
4119    SharedMoveObject {
4120        id: ObjectID,
4121        initial_shared_version: SequenceNumber,
4122        mutability: SharedObjectMutability,
4123    },
4124}
4125
4126#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
4127pub enum SharedObjectMutability {
4128    // The "classic" mutable/immutable modes.
4129    Immutable,
4130    Mutable,
4131    // Non-exclusive write is used to allow multiple transactions to
4132    // simultaneously add disjoint dynamic fields to an object.
4133    // (Currently only used by settlement transactions).
4134    NonExclusiveWrite,
4135}
4136
4137impl SharedObjectMutability {
4138    pub fn is_exclusive(&self) -> bool {
4139        match self {
4140            SharedObjectMutability::Mutable => true,
4141            SharedObjectMutability::Immutable => false,
4142            SharedObjectMutability::NonExclusiveWrite => false,
4143        }
4144    }
4145}
4146
4147impl InputObjectKind {
4148    pub fn object_id(&self) -> ObjectID {
4149        self.full_object_id().id()
4150    }
4151
4152    pub fn full_object_id(&self) -> FullObjectID {
4153        match self {
4154            Self::MovePackage(id) => FullObjectID::Fastpath(*id),
4155            Self::ImmOrOwnedMoveObject((id, _, _)) => FullObjectID::Fastpath(*id),
4156            Self::SharedMoveObject {
4157                id,
4158                initial_shared_version,
4159                ..
4160            } => FullObjectID::Consensus((*id, *initial_shared_version)),
4161        }
4162    }
4163
4164    pub fn version(&self) -> Option<SequenceNumber> {
4165        match self {
4166            Self::MovePackage(..) => None,
4167            Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version),
4168            Self::SharedMoveObject { .. } => None,
4169        }
4170    }
4171
4172    pub fn object_not_found_error(&self) -> UserInputError {
4173        match *self {
4174            Self::MovePackage(package_id) => {
4175                UserInputError::DependentPackageNotFound { package_id }
4176            }
4177            Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound {
4178                object_id,
4179                version: Some(version),
4180            },
4181            Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
4182                object_id: id,
4183                version: None,
4184            },
4185        }
4186    }
4187
4188    pub fn is_shared_object(&self) -> bool {
4189        matches!(self, Self::SharedMoveObject { .. })
4190    }
4191}
4192
4193/// The result of reading an object for execution. Because shared objects may be deleted, one
4194/// possible result of reading a shared object is that ObjectReadResultKind::Deleted is returned.
4195#[derive(Clone, Debug)]
4196pub struct ObjectReadResult {
4197    pub input_object_kind: InputObjectKind,
4198    pub object: ObjectReadResultKind,
4199}
4200
4201#[derive(Clone)]
4202pub enum ObjectReadResultKind {
4203    Object(Object),
4204    // The version of the object that the transaction intended to read, and the digest of the tx
4205    // that removed it from consensus.
4206    ObjectConsensusStreamEnded(SequenceNumber, TransactionDigest),
4207    // A shared object in a cancelled transaction. The sequence number embeds cancellation reason.
4208    CancelledTransactionSharedObject(SequenceNumber),
4209}
4210
4211impl ObjectReadResultKind {
4212    pub fn is_cancelled(&self) -> bool {
4213        matches!(
4214            self,
4215            ObjectReadResultKind::CancelledTransactionSharedObject(_)
4216        )
4217    }
4218
4219    pub fn version(&self) -> SequenceNumber {
4220        match self {
4221            ObjectReadResultKind::Object(object) => object.version(),
4222            ObjectReadResultKind::ObjectConsensusStreamEnded(seq, _) => *seq,
4223            ObjectReadResultKind::CancelledTransactionSharedObject(seq) => *seq,
4224        }
4225    }
4226}
4227
4228impl std::fmt::Debug for ObjectReadResultKind {
4229    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4230        match self {
4231            ObjectReadResultKind::Object(obj) => {
4232                write!(f, "Object({:?})", obj.compute_object_reference())
4233            }
4234            ObjectReadResultKind::ObjectConsensusStreamEnded(seq, digest) => {
4235                write!(f, "ObjectConsensusStreamEnded({}, {:?})", seq, digest)
4236            }
4237            ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
4238                write!(f, "CancelledTransactionSharedObject({})", seq)
4239            }
4240        }
4241    }
4242}
4243
4244impl From<Object> for ObjectReadResultKind {
4245    fn from(object: Object) -> Self {
4246        Self::Object(object)
4247    }
4248}
4249
4250impl ObjectReadResult {
4251    pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
4252        if let (
4253            InputObjectKind::ImmOrOwnedMoveObject(_),
4254            ObjectReadResultKind::ObjectConsensusStreamEnded(_, _),
4255        ) = (&input_object_kind, &object)
4256        {
4257            panic!("only consensus objects can be ObjectConsensusStreamEnded");
4258        }
4259
4260        if let (
4261            InputObjectKind::ImmOrOwnedMoveObject(_),
4262            ObjectReadResultKind::CancelledTransactionSharedObject(_),
4263        ) = (&input_object_kind, &object)
4264        {
4265            panic!("only consensus objects can be CancelledTransactionSharedObject");
4266        }
4267
4268        Self {
4269            input_object_kind,
4270            object,
4271        }
4272    }
4273
4274    pub fn id(&self) -> ObjectID {
4275        self.input_object_kind.object_id()
4276    }
4277
4278    pub fn as_object(&self) -> Option<&Object> {
4279        match &self.object {
4280            ObjectReadResultKind::Object(object) => Some(object),
4281            ObjectReadResultKind::ObjectConsensusStreamEnded(_, _) => None,
4282            ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
4283        }
4284    }
4285
4286    pub fn new_from_gas_object(gas: &Object) -> Self {
4287        let objref = gas.compute_object_reference();
4288        Self {
4289            input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
4290            object: ObjectReadResultKind::Object(gas.clone()),
4291        }
4292    }
4293
4294    pub fn is_mutable(&self) -> bool {
4295        match (&self.input_object_kind, &self.object) {
4296            (InputObjectKind::MovePackage(_), _) => false,
4297            (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
4298                !object.is_immutable()
4299            }
4300            (
4301                InputObjectKind::ImmOrOwnedMoveObject(_),
4302                ObjectReadResultKind::ObjectConsensusStreamEnded(_, _),
4303            ) => unreachable!(),
4304            (
4305                InputObjectKind::ImmOrOwnedMoveObject(_),
4306                ObjectReadResultKind::CancelledTransactionSharedObject(_),
4307            ) => unreachable!(),
4308            (InputObjectKind::SharedMoveObject { mutability, .. }, _) => match mutability {
4309                SharedObjectMutability::Mutable => true,
4310                SharedObjectMutability::Immutable => false,
4311                SharedObjectMutability::NonExclusiveWrite => false,
4312            },
4313        }
4314    }
4315
4316    pub fn is_shared_object(&self) -> bool {
4317        self.input_object_kind.is_shared_object()
4318    }
4319
4320    pub fn is_consensus_stream_ended(&self) -> bool {
4321        self.consensus_stream_end_info().is_some()
4322    }
4323
4324    pub fn consensus_stream_end_info(&self) -> Option<(SequenceNumber, TransactionDigest)> {
4325        match &self.object {
4326            ObjectReadResultKind::ObjectConsensusStreamEnded(v, tx) => Some((*v, *tx)),
4327            _ => None,
4328        }
4329    }
4330
4331    /// Return the object ref iff the object is an owned object (i.e. not shared, not immutable).
4332    pub fn get_owned_objref(&self) -> Option<ObjectRef> {
4333        match (&self.input_object_kind, &self.object) {
4334            (InputObjectKind::MovePackage(_), _) => None,
4335            (
4336                InputObjectKind::ImmOrOwnedMoveObject(objref),
4337                ObjectReadResultKind::Object(object),
4338            ) => {
4339                if object.is_immutable() {
4340                    None
4341                } else {
4342                    Some(*objref)
4343                }
4344            }
4345            (
4346                InputObjectKind::ImmOrOwnedMoveObject(_),
4347                ObjectReadResultKind::ObjectConsensusStreamEnded(_, _),
4348            ) => unreachable!(),
4349            (
4350                InputObjectKind::ImmOrOwnedMoveObject(_),
4351                ObjectReadResultKind::CancelledTransactionSharedObject(_),
4352            ) => unreachable!(),
4353            (InputObjectKind::SharedMoveObject { .. }, _) => None,
4354        }
4355    }
4356
4357    pub fn is_owned(&self) -> bool {
4358        self.get_owned_objref().is_some()
4359    }
4360
4361    pub fn to_shared_input(&self) -> Option<SharedInput> {
4362        match self.input_object_kind {
4363            InputObjectKind::MovePackage(_) => None,
4364            InputObjectKind::ImmOrOwnedMoveObject(_) => None,
4365            InputObjectKind::SharedMoveObject { id, mutability, .. } => Some(match &self.object {
4366                ObjectReadResultKind::Object(obj) => {
4367                    SharedInput::Existing(obj.compute_object_reference())
4368                }
4369                ObjectReadResultKind::ObjectConsensusStreamEnded(seq, digest) => {
4370                    SharedInput::ConsensusStreamEnded((id, *seq, mutability, *digest))
4371                }
4372                ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
4373                    SharedInput::Cancelled((id, *seq))
4374                }
4375            }),
4376        }
4377    }
4378
4379    pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
4380        match &self.object {
4381            ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
4382            ObjectReadResultKind::ObjectConsensusStreamEnded(_, digest) => Some(*digest),
4383            ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
4384        }
4385    }
4386}
4387
4388#[derive(Clone)]
4389pub struct InputObjects {
4390    objects: Vec<ObjectReadResult>,
4391}
4392
4393impl std::fmt::Debug for InputObjects {
4394    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4395        f.debug_list().entries(self.objects.iter()).finish()
4396    }
4397}
4398
4399// An InputObjects new-type that has been verified by sui-transaction-checks, and can be
4400// safely passed to execution.
4401#[derive(Clone)]
4402pub struct CheckedInputObjects(InputObjects);
4403
4404// DO NOT CALL outside of sui-transaction-checks, genesis, or replay.
4405//
4406// CheckedInputObjects should really be defined in sui-transaction-checks so that we can
4407// make public construction impossible. But we can't do that because it would result in circular
4408// dependencies.
4409impl CheckedInputObjects {
4410    // Only called by sui-transaction-checks.
4411    pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
4412        Self(inputs)
4413    }
4414
4415    // Only called when building the genesis transaction
4416    pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
4417        Self(InputObjects::new(input_objects))
4418    }
4419
4420    // Only called from the replay tool.
4421    pub fn new_for_replay(input_objects: InputObjects) -> Self {
4422        Self(input_objects)
4423    }
4424
4425    pub fn inner(&self) -> &InputObjects {
4426        &self.0
4427    }
4428
4429    pub fn into_inner(self) -> InputObjects {
4430        self.0
4431    }
4432}
4433
4434impl From<Vec<ObjectReadResult>> for InputObjects {
4435    fn from(objects: Vec<ObjectReadResult>) -> Self {
4436        Self::new(objects)
4437    }
4438}
4439
4440impl InputObjects {
4441    pub fn new(objects: Vec<ObjectReadResult>) -> Self {
4442        Self { objects }
4443    }
4444
4445    pub fn len(&self) -> usize {
4446        self.objects.len()
4447    }
4448
4449    pub fn is_empty(&self) -> bool {
4450        self.objects.is_empty()
4451    }
4452
4453    pub fn contains_consensus_stream_ended_objects(&self) -> bool {
4454        self.objects
4455            .iter()
4456            .any(|obj| obj.is_consensus_stream_ended())
4457    }
4458
4459    // Returns IDs of objects responsible for a transaction being cancelled, and the corresponding
4460    // reason for cancellation.
4461    pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectID>, SequenceNumber)> {
4462        let mut contains_cancelled = false;
4463        let mut cancel_reason = None;
4464        let mut cancelled_objects = Vec::new();
4465        for obj in &self.objects {
4466            if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
4467                contains_cancelled = true;
4468                if version == SequenceNumber::CONGESTED
4469                    || version == SequenceNumber::RANDOMNESS_UNAVAILABLE
4470                {
4471                    // Verify we don't have multiple cancellation reasons.
4472                    assert!(cancel_reason.is_none() || cancel_reason == Some(version));
4473                    cancel_reason = Some(version);
4474                    cancelled_objects.push(obj.id());
4475                }
4476            }
4477        }
4478
4479        if !cancelled_objects.is_empty() {
4480            Some((
4481                cancelled_objects,
4482                cancel_reason
4483                    .expect("there should be a cancel reason if there are cancelled objects"),
4484            ))
4485        } else {
4486            assert!(!contains_cancelled);
4487            None
4488        }
4489    }
4490
4491    pub fn filter_owned_objects(&self) -> Vec<ObjectRef> {
4492        let owned_objects: Vec<_> = self
4493            .objects
4494            .iter()
4495            .filter_map(|obj| obj.get_owned_objref())
4496            .collect();
4497
4498        trace!(
4499            num_mutable_objects = owned_objects.len(),
4500            "Checked locks and found mutable objects"
4501        );
4502
4503        owned_objects
4504    }
4505
4506    pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
4507        self.objects
4508            .iter()
4509            .filter(|obj| obj.is_shared_object())
4510            .map(|obj| {
4511                obj.to_shared_input()
4512                    .expect("already filtered for shared objects")
4513            })
4514            .collect()
4515    }
4516
4517    pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
4518        self.objects
4519            .iter()
4520            .filter_map(|obj| obj.get_previous_transaction())
4521            .collect()
4522    }
4523
4524    /// All inputs that will be directly mutated by the transaction. This does
4525    /// not include SharedObjectMutability::NonExclusiveWrite inputs.
4526    pub fn exclusive_mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
4527        self.mutables_with_input_kinds()
4528            .filter_map(|(id, (version, owner, kind))| match kind {
4529                InputObjectKind::SharedMoveObject { mutability, .. } => match mutability {
4530                    SharedObjectMutability::Mutable => Some((id, (version, owner))),
4531                    SharedObjectMutability::Immutable => None,
4532                    SharedObjectMutability::NonExclusiveWrite => None,
4533                },
4534                _ => Some((id, (version, owner))),
4535            })
4536            .collect()
4537    }
4538
4539    pub fn non_exclusive_input_objects(&self) -> BTreeMap<ObjectID, Object> {
4540        self.objects
4541            .iter()
4542            .filter_map(|read_result| {
4543                match (read_result.as_object(), read_result.input_object_kind) {
4544                    (
4545                        Some(object),
4546                        InputObjectKind::SharedMoveObject {
4547                            mutability: SharedObjectMutability::NonExclusiveWrite,
4548                            ..
4549                        },
4550                    ) => Some((read_result.id(), object.clone())),
4551                    _ => None,
4552                }
4553            })
4554            .collect()
4555    }
4556
4557    /// All inputs that can be taken as &mut T, which includes both
4558    /// SharedObjectMutability::Mutable and SharedObjectMutability::NonExclusiveWrite inputs.
4559    pub fn all_mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
4560        self.mutables_with_input_kinds()
4561            .filter_map(|(id, (version, owner, kind))| match kind {
4562                InputObjectKind::SharedMoveObject { mutability, .. } => match mutability {
4563                    SharedObjectMutability::Mutable => Some((id, (version, owner))),
4564                    SharedObjectMutability::Immutable => None,
4565                    SharedObjectMutability::NonExclusiveWrite => Some((id, (version, owner))),
4566                },
4567                _ => Some((id, (version, owner))),
4568            })
4569            .collect()
4570    }
4571
4572    fn mutables_with_input_kinds(
4573        &self,
4574    ) -> impl Iterator<Item = (ObjectID, (VersionDigest, Owner, InputObjectKind))> + '_ {
4575        self.objects.iter().filter_map(
4576            |ObjectReadResult {
4577                 input_object_kind,
4578                 object,
4579             }| match (input_object_kind, object) {
4580                (InputObjectKind::MovePackage(_), _) => None,
4581                (
4582                    InputObjectKind::ImmOrOwnedMoveObject(object_ref),
4583                    ObjectReadResultKind::Object(object),
4584                ) => {
4585                    if object.is_immutable() {
4586                        None
4587                    } else {
4588                        Some((
4589                            object_ref.0,
4590                            (
4591                                (object_ref.1, object_ref.2),
4592                                object.owner.clone(),
4593                                *input_object_kind,
4594                            ),
4595                        ))
4596                    }
4597                }
4598                (
4599                    InputObjectKind::ImmOrOwnedMoveObject(_),
4600                    ObjectReadResultKind::ObjectConsensusStreamEnded(_, _),
4601                ) => {
4602                    unreachable!()
4603                }
4604                (
4605                    InputObjectKind::SharedMoveObject { .. },
4606                    ObjectReadResultKind::ObjectConsensusStreamEnded(_, _),
4607                ) => None,
4608                (
4609                    InputObjectKind::SharedMoveObject { mutability, .. },
4610                    ObjectReadResultKind::Object(object),
4611                ) => match *mutability {
4612                    SharedObjectMutability::Mutable => {
4613                        let oref = object.compute_object_reference();
4614                        Some((
4615                            oref.0,
4616                            ((oref.1, oref.2), object.owner.clone(), *input_object_kind),
4617                        ))
4618                    }
4619                    SharedObjectMutability::Immutable => None,
4620                    SharedObjectMutability::NonExclusiveWrite => {
4621                        let oref = object.compute_object_reference();
4622                        Some((
4623                            oref.0,
4624                            ((oref.1, oref.2), object.owner.clone(), *input_object_kind),
4625                        ))
4626                    }
4627                },
4628                (
4629                    InputObjectKind::ImmOrOwnedMoveObject(_),
4630                    ObjectReadResultKind::CancelledTransactionSharedObject(_),
4631                ) => {
4632                    unreachable!()
4633                }
4634                (
4635                    InputObjectKind::SharedMoveObject { .. },
4636                    ObjectReadResultKind::CancelledTransactionSharedObject(_),
4637                ) => None,
4638            },
4639        )
4640    }
4641
4642    /// The version to set on objects created by the computation that `self` is input to.
4643    /// Guaranteed to be strictly greater than the versions of all input objects and objects
4644    /// received in the transaction.
4645    pub fn lamport_timestamp(&self, receiving_objects: &[ObjectRef]) -> SequenceNumber {
4646        let input_versions = self
4647            .objects
4648            .iter()
4649            .filter_map(|object| match &object.object {
4650                ObjectReadResultKind::Object(object) => {
4651                    object.data.try_as_move().map(MoveObject::version)
4652                }
4653                ObjectReadResultKind::ObjectConsensusStreamEnded(v, _) => Some(*v),
4654                ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
4655            })
4656            .chain(receiving_objects.iter().map(|object_ref| object_ref.1));
4657
4658        SequenceNumber::lamport_increment(input_versions)
4659    }
4660
4661    pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
4662        self.objects.iter().map(
4663            |ObjectReadResult {
4664                 input_object_kind, ..
4665             }| input_object_kind,
4666        )
4667    }
4668
4669    pub fn consensus_stream_ended_objects(&self) -> BTreeMap<ObjectID, SequenceNumber> {
4670        self.objects
4671            .iter()
4672            .filter_map(|obj| {
4673                if let InputObjectKind::SharedMoveObject {
4674                    id,
4675                    initial_shared_version,
4676                    ..
4677                } = obj.input_object_kind
4678                {
4679                    obj.is_consensus_stream_ended()
4680                        .then_some((id, initial_shared_version))
4681                } else {
4682                    None
4683                }
4684            })
4685            .collect()
4686    }
4687
4688    pub fn into_object_map(self) -> BTreeMap<ObjectID, Object> {
4689        self.objects
4690            .into_iter()
4691            .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
4692            .collect()
4693    }
4694
4695    pub fn push(&mut self, object: ObjectReadResult) {
4696        self.objects.push(object);
4697    }
4698
4699    pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
4700        self.objects.iter()
4701    }
4702
4703    pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
4704        self.objects.iter().filter_map(|o| o.as_object())
4705    }
4706
4707    pub fn non_exclusive_mutable_inputs(
4708        &self,
4709    ) -> impl Iterator<Item = (ObjectID, SequenceNumber)> + '_ {
4710        self.objects.iter().filter_map(
4711            |ObjectReadResult {
4712                 input_object_kind,
4713                 object,
4714             }| match input_object_kind {
4715                // TODO: this is not exercised yet since settlement transactions cannot be
4716                // cancelled, but if/when we expose non-exclusive writes to users,
4717                // a cancelled transaction should not be considered to have done any writes.
4718                InputObjectKind::SharedMoveObject {
4719                    id,
4720                    mutability: SharedObjectMutability::NonExclusiveWrite,
4721                    ..
4722                } if !object.is_cancelled() => Some((*id, object.version())),
4723                _ => None,
4724            },
4725        )
4726    }
4727}
4728
4729// Result of attempting to read a receiving object (currently only at signing time).
4730// Because an object may have been previously received and deleted, the result may be
4731// ReceivingObjectReadResultKind::PreviouslyReceivedObject.
4732#[derive(Clone, Debug)]
4733pub enum ReceivingObjectReadResultKind {
4734    Object(Object),
4735    // The object was received by some other transaction, and we were not able to read it
4736    PreviouslyReceivedObject,
4737}
4738
4739impl ReceivingObjectReadResultKind {
4740    pub fn as_object(&self) -> Option<&Object> {
4741        match &self {
4742            Self::Object(object) => Some(object),
4743            Self::PreviouslyReceivedObject => None,
4744        }
4745    }
4746}
4747
4748pub struct ReceivingObjectReadResult {
4749    pub object_ref: ObjectRef,
4750    pub object: ReceivingObjectReadResultKind,
4751}
4752
4753impl ReceivingObjectReadResult {
4754    pub fn new(object_ref: ObjectRef, object: ReceivingObjectReadResultKind) -> Self {
4755        Self { object_ref, object }
4756    }
4757
4758    pub fn is_previously_received(&self) -> bool {
4759        matches!(
4760            self.object,
4761            ReceivingObjectReadResultKind::PreviouslyReceivedObject
4762        )
4763    }
4764}
4765
4766impl From<Object> for ReceivingObjectReadResultKind {
4767    fn from(object: Object) -> Self {
4768        Self::Object(object)
4769    }
4770}
4771
4772pub struct ReceivingObjects {
4773    pub objects: Vec<ReceivingObjectReadResult>,
4774}
4775
4776impl ReceivingObjects {
4777    pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
4778        self.objects.iter()
4779    }
4780
4781    pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
4782        self.objects.iter().filter_map(|o| o.object.as_object())
4783    }
4784}
4785
4786impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
4787    fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
4788        Self { objects }
4789    }
4790}
4791
4792impl Display for CertifiedTransaction {
4793    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
4794        let mut writer = String::new();
4795        writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
4796        writeln!(
4797            writer,
4798            "Signed Authorities Bitmap : {:?}",
4799            self.auth_sig().signers_map
4800        )?;
4801        write!(writer, "{}", &self.data().intent_message().value.kind())?;
4802        write!(f, "{}", writer)
4803    }
4804}
4805
4806/// TransactionKey uniquely identifies a transaction across all epochs.
4807/// Note that a single transaction may have multiple keys, for example a RandomnessStateUpdate
4808/// could be identified by both `Digest` and `RandomnessRound`.
4809#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
4810pub enum TransactionKey {
4811    Digest(TransactionDigest),
4812    RandomnessRound(EpochId, RandomnessRound),
4813    AccumulatorSettlement(EpochId, u64 /* checkpoint height */),
4814    ConsensusCommitPrologue(EpochId, u64 /* round */, u32 /* sub_dag_index */),
4815}
4816
4817impl TransactionKey {
4818    pub fn unwrap_digest(&self) -> &TransactionDigest {
4819        match self {
4820            TransactionKey::Digest(d) => d,
4821            _ => panic!("called unwrap_digest on a non-Digest TransactionKey: {self:?}"),
4822        }
4823    }
4824
4825    pub fn as_digest(&self) -> Option<&TransactionDigest> {
4826        match self {
4827            TransactionKey::Digest(d) => Some(d),
4828            _ => None,
4829        }
4830    }
4831}