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