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