iota_types/
transaction.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6use std::{
7    collections::{BTreeMap, BTreeSet, HashSet},
8    fmt::{Debug, Display, Formatter, Write},
9    hash::Hash,
10    iter,
11    iter::once,
12    sync::Arc,
13};
14
15use anyhow::bail;
16use enum_dispatch::enum_dispatch;
17use fastcrypto::{encoding::Base64, hash::HashFunction};
18use iota_protocol_config::ProtocolConfig;
19use itertools::Either;
20use move_core_types::{
21    ident_str,
22    identifier::{self, Identifier},
23    language_storage::TypeTag,
24};
25use nonempty::{NonEmpty, nonempty};
26use serde::{Deserialize, Serialize};
27use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
28use strum::IntoStaticStr;
29use tap::Pipe;
30use tracing::trace;
31
32use super::{base_types::*, error::*};
33use crate::{
34    IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION,
35    IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION, IOTA_FRAMEWORK_PACKAGE_ID,
36    IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID,
37    IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
38    authenticator_state::ActiveJwk,
39    committee::{Committee, EpochId, ProtocolVersion},
40    crypto::{
41        AuthoritySignInfo, AuthoritySignInfoTrait, AuthoritySignature,
42        AuthorityStrongQuorumSignInfo, DefaultHash, Ed25519IotaSignature, EmptySignInfo,
43        IotaSignatureInner, RandomnessRound, Signature, Signer, ToFromBytes, default_hash,
44    },
45    digests::{
46        CertificateDigest, ConsensusCommitDigest, SenderSignedDataDigest, ZKLoginInputsDigest,
47    },
48    event::Event,
49    execution::SharedInput,
50    message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
51    messages_checkpoint::CheckpointTimestamp,
52    messages_consensus::{ConsensusCommitPrologueV1, ConsensusDeterminedVersionAssignments},
53    object::{MoveObject, Object, Owner},
54    programmable_transaction_builder::ProgrammableTransactionBuilder,
55    signature::{GenericSignature, VerifyParams},
56    signature_verification::{VerifiedDigestCache, verify_sender_signed_data_message_signatures},
57    type_input::TypeInput,
58};
59
60pub const TEST_ONLY_GAS_UNIT_FOR_TRANSFER: u64 = 10_000;
61pub const TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS: u64 = 50_000;
62pub const TEST_ONLY_GAS_UNIT_FOR_PUBLISH: u64 = 50_000;
63pub const TEST_ONLY_GAS_UNIT_FOR_STAKING: u64 = 50_000;
64pub const TEST_ONLY_GAS_UNIT_FOR_GENERIC: u64 = 50_000;
65pub const TEST_ONLY_GAS_UNIT_FOR_SPLIT_COIN: u64 = 10_000;
66// For some transactions we may either perform heavy operations or touch
67// objects that are storage expensive. That may happen (and often is the case)
68// because the object touched are set up in genesis and carry no storage cost
69// (and thus rebate) on first usage.
70pub const TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE: u64 = 5_000_000;
71
72pub const GAS_PRICE_FOR_SYSTEM_TX: u64 = 1;
73
74pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = 1000;
75
76const BLOCKED_MOVE_FUNCTIONS: [(ObjectID, &str, &str); 0] = [];
77
78#[cfg(test)]
79#[path = "unit_tests/messages_tests.rs"]
80mod messages_tests;
81
82#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
83pub enum CallArg {
84    // contains no structs or objects
85    Pure(Vec<u8>),
86    // an object
87    Object(ObjectArg),
88}
89
90impl CallArg {
91    pub const IOTA_SYSTEM_MUT: Self = Self::Object(ObjectArg::IOTA_SYSTEM_MUT);
92    pub const CLOCK_IMM: Self = Self::Object(ObjectArg::SharedObject {
93        id: IOTA_CLOCK_OBJECT_ID,
94        initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
95        mutable: false,
96    });
97    pub const CLOCK_MUT: Self = Self::Object(ObjectArg::SharedObject {
98        id: IOTA_CLOCK_OBJECT_ID,
99        initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
100        mutable: true,
101    });
102    pub const AUTHENTICATOR_MUT: Self = Self::Object(ObjectArg::SharedObject {
103        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
104        initial_shared_version: IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION,
105        mutable: true,
106    });
107}
108
109#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
110pub enum ObjectArg {
111    // A Move object, either immutable, or owned mutable.
112    ImmOrOwnedObject(ObjectRef),
113    // A Move object that's shared.
114    // SharedObject::mutable controls whether caller asks for a mutable reference to shared
115    // object.
116    SharedObject {
117        id: ObjectID,
118        initial_shared_version: SequenceNumber,
119        mutable: bool,
120    },
121    // A Move object that can be received in this transaction.
122    Receiving(ObjectRef),
123}
124
125fn type_input_validity_check(
126    tag: &TypeInput,
127    config: &ProtocolConfig,
128    starting_count: &mut usize,
129) -> UserInputResult<()> {
130    let mut stack = vec![(tag, 1)];
131    while let Some((tag, depth)) = stack.pop() {
132        *starting_count += 1;
133        fp_ensure!(
134            *starting_count < config.max_type_arguments() as usize,
135            UserInputError::SizeLimitExceeded {
136                limit: "maximum type arguments in a call transaction".to_string(),
137                value: config.max_type_arguments().to_string()
138            }
139        );
140        fp_ensure!(
141            depth < config.max_type_argument_depth(),
142            UserInputError::SizeLimitExceeded {
143                limit: "maximum type argument depth in a call transaction".to_string(),
144                value: config.max_type_argument_depth().to_string()
145            }
146        );
147        match tag {
148            TypeInput::Bool
149            | TypeInput::U8
150            | TypeInput::U64
151            | TypeInput::U128
152            | TypeInput::Address
153            | TypeInput::Signer
154            | TypeInput::U16
155            | TypeInput::U32
156            | TypeInput::U256 => (),
157            TypeInput::Vector(t) => {
158                stack.push((t, depth + 1));
159            }
160            TypeInput::Struct(s) => {
161                let next_depth = depth + 1;
162                if config.validate_identifier_inputs() {
163                    fp_ensure!(
164                        identifier::is_valid(&s.module),
165                        UserInputError::InvalidIdentifier {
166                            error: s.module.clone()
167                        }
168                    );
169                    fp_ensure!(
170                        identifier::is_valid(&s.name),
171                        UserInputError::InvalidIdentifier {
172                            error: s.name.clone()
173                        }
174                    );
175                }
176                stack.extend(s.type_params.iter().map(|t| (t, next_depth)));
177            }
178        }
179    }
180    Ok(())
181}
182
183/// System transaction for advancing the epoch.
184#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
185pub struct ChangeEpoch {
186    /// The next (to become) epoch ID.
187    pub epoch: EpochId,
188    /// The protocol version in effect in the new epoch.
189    pub protocol_version: ProtocolVersion,
190    /// The total amount of gas charged for storage during the epoch.
191    pub storage_charge: u64,
192    /// The total amount of gas charged for computation during the epoch.
193    pub computation_charge: u64,
194    /// The amount of storage rebate refunded to the txn senders.
195    pub storage_rebate: u64,
196    /// The non-refundable storage fee.
197    pub non_refundable_storage_fee: u64,
198    /// Unix timestamp when epoch started
199    pub epoch_start_timestamp_ms: u64,
200    /// System packages (specifically framework and move stdlib) that are
201    /// written before the new epoch starts. This tracks framework upgrades
202    /// on chain. When executing the ChangeEpoch txn, the validator must
203    /// write out the modules below.  Modules are provided with the version they
204    /// will be upgraded to, their modules in serialized form (which include
205    /// their package ID), and a list of their transitive dependencies.
206    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
207}
208
209/// System transaction for advancing the epoch.
210/// This version includes the computation_charge_burned field for when
211/// protocol_defined_base_fee is enabled in the protocol config.
212#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
213pub struct ChangeEpochV2 {
214    /// The next (to become) epoch ID.
215    pub epoch: EpochId,
216    /// The protocol version in effect in the new epoch.
217    pub protocol_version: ProtocolVersion,
218    /// The total amount of gas charged for storage during the epoch.
219    pub storage_charge: u64,
220    /// The total amount of gas charged for computation during the epoch.
221    pub computation_charge: u64,
222    /// The burned component of the total computation/execution costs.
223    pub computation_charge_burned: u64,
224    /// The amount of storage rebate refunded to the txn senders.
225    pub storage_rebate: u64,
226    /// The non-refundable storage fee.
227    pub non_refundable_storage_fee: u64,
228    /// Unix timestamp when epoch started
229    pub epoch_start_timestamp_ms: u64,
230    /// System packages (specifically framework and move stdlib) that are
231    /// written before the new epoch starts. This tracks framework upgrades
232    /// on chain. When executing the ChangeEpochV2 txn, the validator must
233    /// write out the modules below.  Modules are provided with the version they
234    /// will be upgraded to, their modules in serialized form (which include
235    /// their package ID), and a list of their transitive dependencies.
236    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
237}
238
239/// System transaction for advancing the epoch.
240/// This version includes active validator indices that are eligible
241/// to take part in committee selection based on protocol version support.
242#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
243pub struct ChangeEpochV3 {
244    /// The next (to become) epoch ID.
245    pub epoch: EpochId,
246    /// The protocol version in effect in the new epoch.
247    pub protocol_version: ProtocolVersion,
248    /// The total amount of gas charged for storage during the epoch.
249    pub storage_charge: u64,
250    /// The total amount of gas charged for computation during the epoch.
251    pub computation_charge: u64,
252    /// The burned component of the total computation/execution costs.
253    pub computation_charge_burned: u64,
254    /// The amount of storage rebate refunded to the txn senders.
255    pub storage_rebate: u64,
256    /// The amount of storage rebate that is burnt due to the
257    /// gas_price. It's given that storage_rebate + non_refundable_storage_fee
258    /// is always equal to the storage_charge of the tx.
259    pub non_refundable_storage_fee: u64,
260    /// Unix timestamp from the start of the epoch as milliseconds
261    pub epoch_start_timestamp_ms: u64,
262    /// System packages (specifically framework and move stdlib) that are
263    /// written by the execution of this transaction. Validators must write
264    /// out the modules below.  Modules are provided with the version they
265    /// will be upgraded to, their modules in serialized form (which include
266    /// their package ID), and a list of their transitive dependencies.
267    pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
268    /// Vector of active validator indices eligible to take part in committee
269    /// selection because they support the new, target protocol version.
270    pub eligible_active_validators: Vec<u64>,
271}
272
273#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
274pub struct GenesisTransaction {
275    pub objects: Vec<GenesisObject>,
276    pub events: Vec<Event>,
277}
278
279#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
280pub enum GenesisObject {
281    RawObject {
282        data: crate::object::Data,
283        owner: crate::object::Owner,
284    },
285}
286
287impl GenesisObject {
288    pub fn id(&self) -> ObjectID {
289        match self {
290            GenesisObject::RawObject { data, .. } => data.id(),
291        }
292    }
293}
294
295#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
296pub struct AuthenticatorStateExpire {
297    /// expire JWKs that have a lower epoch than this
298    pub min_epoch: u64,
299    /// The initial version of the authenticator object that it was shared at.
300    pub authenticator_obj_initial_shared_version: SequenceNumber,
301}
302
303impl AuthenticatorStateExpire {
304    pub fn authenticator_obj_initial_shared_version(&self) -> SequenceNumber {
305        self.authenticator_obj_initial_shared_version
306    }
307}
308
309#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
310pub struct AuthenticatorStateUpdateV1 {
311    /// Epoch of the authenticator state update transaction
312    pub epoch: u64,
313    /// Consensus round of the authenticator state update
314    pub round: u64,
315    /// newly active jwks
316    pub new_active_jwks: Vec<ActiveJwk>,
317    /// The initial version of the authenticator object that it was shared at.
318    pub authenticator_obj_initial_shared_version: SequenceNumber,
319    // to version this struct, do not add new fields. Instead, add a AuthenticatorStateUpdateV2 to
320    // TransactionKind.
321}
322
323impl AuthenticatorStateUpdateV1 {
324    pub fn authenticator_obj_initial_shared_version(&self) -> SequenceNumber {
325        self.authenticator_obj_initial_shared_version
326    }
327}
328
329#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
330pub struct RandomnessStateUpdate {
331    /// Epoch of the randomness state update transaction
332    pub epoch: u64,
333    /// Randomness round of the update
334    pub randomness_round: RandomnessRound,
335    /// Updated random bytes
336    pub random_bytes: Vec<u8>,
337    /// The initial version of the randomness object that it was shared at.
338    pub randomness_obj_initial_shared_version: SequenceNumber,
339    // to version this struct, do not add new fields. Instead, add a RandomnessStateUpdateV2 to
340    // TransactionKind.
341}
342
343impl RandomnessStateUpdate {
344    pub fn randomness_obj_initial_shared_version(&self) -> SequenceNumber {
345        self.randomness_obj_initial_shared_version
346    }
347}
348
349#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)]
350pub enum TransactionKind {
351    /// A transaction that allows the interleaving of native commands and Move
352    /// calls
353    ProgrammableTransaction(ProgrammableTransaction),
354    /// A system transaction that will update epoch information on-chain.
355    /// It will only ever be executed once in an epoch.
356    /// The argument is the next epoch number, which is critical
357    /// because it ensures that this transaction has a unique digest.
358    /// This will eventually be translated to a Move call during execution.
359    /// It also doesn't require/use a gas object.
360    /// A validator will not sign a transaction of this kind from outside. It
361    /// only signs internally during epoch changes.
362    Genesis(GenesisTransaction),
363    ConsensusCommitPrologueV1(ConsensusCommitPrologueV1),
364    AuthenticatorStateUpdateV1(AuthenticatorStateUpdateV1),
365
366    /// EndOfEpochTransaction contains a list of transactions
367    /// that are allowed to run at the end of the epoch.
368    EndOfEpochTransaction(Vec<EndOfEpochTransactionKind>),
369
370    RandomnessStateUpdate(RandomnessStateUpdate),
371    // .. more transaction types go here
372    // TODO: When introducing `ConsensusCommitPrologueV2`, please add
373    // and use a new variant for `ConsensusDeterminedVersionAssignments`.
374    // See https://github.com/iotaledger/iota/issues/7692 and
375    // https://github.com/iotaledger/iota/pull/7697 for detail.
376}
377
378/// EndOfEpochTransactionKind
379#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)]
380pub enum EndOfEpochTransactionKind {
381    ChangeEpoch(ChangeEpoch),
382    ChangeEpochV2(ChangeEpochV2),
383    ChangeEpochV3(ChangeEpochV3),
384    AuthenticatorStateCreate,
385    AuthenticatorStateExpire(AuthenticatorStateExpire),
386}
387
388impl EndOfEpochTransactionKind {
389    pub fn new_change_epoch(
390        next_epoch: EpochId,
391        protocol_version: ProtocolVersion,
392        storage_charge: u64,
393        computation_charge: u64,
394        storage_rebate: u64,
395        non_refundable_storage_fee: u64,
396        epoch_start_timestamp_ms: u64,
397        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
398    ) -> Self {
399        Self::ChangeEpoch(ChangeEpoch {
400            epoch: next_epoch,
401            protocol_version,
402            storage_charge,
403            computation_charge,
404            storage_rebate,
405            non_refundable_storage_fee,
406            epoch_start_timestamp_ms,
407            system_packages,
408        })
409    }
410
411    pub fn new_change_epoch_v2(
412        next_epoch: EpochId,
413        protocol_version: ProtocolVersion,
414        storage_charge: u64,
415        computation_charge: u64,
416        computation_charge_burned: u64,
417        storage_rebate: u64,
418        non_refundable_storage_fee: u64,
419        epoch_start_timestamp_ms: u64,
420        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
421    ) -> Self {
422        Self::ChangeEpochV2(ChangeEpochV2 {
423            epoch: next_epoch,
424            protocol_version,
425            storage_charge,
426            computation_charge,
427            computation_charge_burned,
428            storage_rebate,
429            non_refundable_storage_fee,
430            epoch_start_timestamp_ms,
431            system_packages,
432        })
433    }
434
435    pub fn new_change_epoch_v3(
436        next_epoch: EpochId,
437        protocol_version: ProtocolVersion,
438        storage_charge: u64,
439        computation_charge: u64,
440        computation_charge_burned: u64,
441        storage_rebate: u64,
442        non_refundable_storage_fee: u64,
443        epoch_start_timestamp_ms: u64,
444        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
445        eligible_active_validators: Vec<u64>,
446    ) -> Self {
447        Self::ChangeEpochV3(ChangeEpochV3 {
448            epoch: next_epoch,
449            protocol_version,
450            storage_charge,
451            computation_charge,
452            computation_charge_burned,
453            storage_rebate,
454            non_refundable_storage_fee,
455            epoch_start_timestamp_ms,
456            system_packages,
457            eligible_active_validators,
458        })
459    }
460
461    pub fn new_authenticator_state_expire(
462        min_epoch: u64,
463        authenticator_obj_initial_shared_version: SequenceNumber,
464    ) -> Self {
465        Self::AuthenticatorStateExpire(AuthenticatorStateExpire {
466            min_epoch,
467            authenticator_obj_initial_shared_version,
468        })
469    }
470
471    pub fn new_authenticator_state_create() -> Self {
472        Self::AuthenticatorStateCreate
473    }
474
475    fn input_objects(&self) -> Vec<InputObjectKind> {
476        match self {
477            Self::ChangeEpoch(_) => {
478                vec![InputObjectKind::SharedMoveObject {
479                    id: IOTA_SYSTEM_STATE_OBJECT_ID,
480                    initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
481                    mutable: true,
482                }]
483            }
484            Self::ChangeEpochV2(_) => {
485                vec![InputObjectKind::SharedMoveObject {
486                    id: IOTA_SYSTEM_STATE_OBJECT_ID,
487                    initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
488                    mutable: true,
489                }]
490            }
491            Self::ChangeEpochV3(_) => {
492                vec![InputObjectKind::SharedMoveObject {
493                    id: IOTA_SYSTEM_STATE_OBJECT_ID,
494                    initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
495                    mutable: true,
496                }]
497            }
498            Self::AuthenticatorStateCreate => vec![],
499            Self::AuthenticatorStateExpire(expire) => {
500                vec![InputObjectKind::SharedMoveObject {
501                    id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
502                    initial_shared_version: expire.authenticator_obj_initial_shared_version(),
503                    mutable: true,
504                }]
505            }
506        }
507    }
508
509    fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
510        match self {
511            Self::ChangeEpoch(_) => {
512                Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
513            }
514            Self::ChangeEpochV2(_) => {
515                Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
516            }
517            Self::ChangeEpochV3(_) => {
518                Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
519            }
520            Self::AuthenticatorStateExpire(expire) => Either::Left(
521                vec![SharedInputObject {
522                    id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
523                    initial_shared_version: expire.authenticator_obj_initial_shared_version(),
524                    mutable: true,
525                }]
526                .into_iter(),
527            ),
528            Self::AuthenticatorStateCreate => Either::Right(iter::empty()),
529        }
530    }
531
532    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
533        match self {
534            Self::ChangeEpoch(_) => {
535                if config.protocol_defined_base_fee() {
536                    return Err(UserInputError::Unsupported(
537                        "protocol defined base fee not supported".to_string(),
538                    ));
539                }
540                if config.select_committee_from_eligible_validators() {
541                    return Err(UserInputError::Unsupported(
542                        "selecting committee only among validators supporting the protocol version not supported".to_string(),
543                    ));
544                }
545            }
546            Self::ChangeEpochV2(_) => {
547                if !config.protocol_defined_base_fee() {
548                    return Err(UserInputError::Unsupported(
549                        "protocol defined base fee required".to_string(),
550                    ));
551                }
552                if config.select_committee_from_eligible_validators() {
553                    return Err(UserInputError::Unsupported(
554                        "selecting committee only among validators supporting the protocol version not supported".to_string(),
555                    ));
556                }
557            }
558            Self::ChangeEpochV3(_) => {
559                if !config.protocol_defined_base_fee() {
560                    return Err(UserInputError::Unsupported(
561                        "protocol defined base fee required".to_string(),
562                    ));
563                }
564                if !config.select_committee_from_eligible_validators() {
565                    return Err(UserInputError::Unsupported(
566                        "selecting committee only among validators supporting the protocol version required".to_string(),
567                    ));
568                }
569            }
570            Self::AuthenticatorStateCreate | Self::AuthenticatorStateExpire(_) => {
571                if !config.enable_jwk_consensus_updates() {
572                    return Err(UserInputError::Unsupported(
573                        "authenticator state updates not enabled".to_string(),
574                    ));
575                }
576            }
577        }
578        Ok(())
579    }
580}
581
582impl CallArg {
583    fn input_objects(&self) -> Vec<InputObjectKind> {
584        match self {
585            CallArg::Pure(_) => vec![],
586            CallArg::Object(ObjectArg::ImmOrOwnedObject(object_ref)) => {
587                vec![InputObjectKind::ImmOrOwnedMoveObject(*object_ref)]
588            }
589            CallArg::Object(ObjectArg::SharedObject {
590                id,
591                initial_shared_version,
592                mutable,
593            }) => {
594                let id = *id;
595                let initial_shared_version = *initial_shared_version;
596                let mutable = *mutable;
597                vec![InputObjectKind::SharedMoveObject {
598                    id,
599                    initial_shared_version,
600                    mutable,
601                }]
602            }
603            // Receiving objects are not part of the input objects.
604            CallArg::Object(ObjectArg::Receiving(_)) => vec![],
605        }
606    }
607
608    fn receiving_objects(&self) -> Vec<ObjectRef> {
609        match self {
610            CallArg::Pure(_) => vec![],
611            CallArg::Object(o) => match o {
612                ObjectArg::ImmOrOwnedObject(_) => vec![],
613                ObjectArg::SharedObject { .. } => vec![],
614                ObjectArg::Receiving(obj_ref) => vec![*obj_ref],
615            },
616        }
617    }
618
619    pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
620        match self {
621            CallArg::Pure(p) => {
622                fp_ensure!(
623                    p.len() < config.max_pure_argument_size() as usize,
624                    UserInputError::SizeLimitExceeded {
625                        limit: "maximum pure argument size".to_string(),
626                        value: config.max_pure_argument_size().to_string()
627                    }
628                );
629            }
630            CallArg::Object(o) => match o {
631                ObjectArg::Receiving(_)
632                | ObjectArg::ImmOrOwnedObject(_)
633                | ObjectArg::SharedObject { .. } => (),
634            },
635        }
636        Ok(())
637    }
638}
639
640impl From<bool> for CallArg {
641    fn from(b: bool) -> Self {
642        // unwrap safe because every u8 value is BCS-serializable
643        CallArg::Pure(bcs::to_bytes(&b).unwrap())
644    }
645}
646
647impl From<u8> for CallArg {
648    fn from(n: u8) -> Self {
649        // unwrap safe because every u8 value is BCS-serializable
650        CallArg::Pure(bcs::to_bytes(&n).unwrap())
651    }
652}
653
654impl From<u16> for CallArg {
655    fn from(n: u16) -> Self {
656        // unwrap safe because every u16 value is BCS-serializable
657        CallArg::Pure(bcs::to_bytes(&n).unwrap())
658    }
659}
660
661impl From<u32> for CallArg {
662    fn from(n: u32) -> Self {
663        // unwrap safe because every u32 value is BCS-serializable
664        CallArg::Pure(bcs::to_bytes(&n).unwrap())
665    }
666}
667
668impl From<u64> for CallArg {
669    fn from(n: u64) -> Self {
670        // unwrap safe because every u64 value is BCS-serializable
671        CallArg::Pure(bcs::to_bytes(&n).unwrap())
672    }
673}
674
675impl From<u128> for CallArg {
676    fn from(n: u128) -> Self {
677        // unwrap safe because every u128 value is BCS-serializable
678        CallArg::Pure(bcs::to_bytes(&n).unwrap())
679    }
680}
681
682impl From<&Vec<u8>> for CallArg {
683    fn from(v: &Vec<u8>) -> Self {
684        // unwrap safe because every vec<u8> value is BCS-serializable
685        CallArg::Pure(bcs::to_bytes(v).unwrap())
686    }
687}
688
689impl From<ObjectRef> for CallArg {
690    fn from(obj: ObjectRef) -> Self {
691        CallArg::Object(ObjectArg::ImmOrOwnedObject(obj))
692    }
693}
694
695impl ObjectArg {
696    pub const IOTA_SYSTEM_MUT: Self = Self::SharedObject {
697        id: IOTA_SYSTEM_STATE_OBJECT_ID,
698        initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
699        mutable: true,
700    };
701
702    pub fn id(&self) -> ObjectID {
703        match self {
704            ObjectArg::Receiving((id, _, _))
705            | ObjectArg::ImmOrOwnedObject((id, _, _))
706            | ObjectArg::SharedObject { id, .. } => *id,
707        }
708    }
709}
710
711// Add package IDs, `ObjectID`, for types defined in modules.
712fn add_type_input_packages(packages: &mut BTreeSet<ObjectID>, type_argument: &TypeInput) {
713    let mut stack = vec![type_argument];
714    while let Some(cur) = stack.pop() {
715        match cur {
716            TypeInput::Bool
717            | TypeInput::U8
718            | TypeInput::U64
719            | TypeInput::U128
720            | TypeInput::Address
721            | TypeInput::Signer
722            | TypeInput::U16
723            | TypeInput::U32
724            | TypeInput::U256 => (),
725            TypeInput::Vector(inner) => stack.push(inner),
726            TypeInput::Struct(struct_tag) => {
727                packages.insert(struct_tag.address.into());
728                stack.extend(struct_tag.type_params.iter())
729            }
730        }
731    }
732}
733
734/// A series of commands where the results of one command can be used in future
735/// commands
736#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
737pub struct ProgrammableTransaction {
738    /// Input objects or primitive values
739    pub inputs: Vec<CallArg>,
740    /// The commands to be executed sequentially. A failure in any command will
741    /// result in the failure of the entire transaction.
742    pub commands: Vec<Command>,
743}
744
745/// A single command in a programmable transaction.
746#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
747pub enum Command {
748    /// A call to either an entry or a public Move function
749    MoveCall(Box<ProgrammableMoveCall>),
750    /// `(Vec<forall T:key+store. T>, address)`
751    /// It sends n-objects to the specified address. These objects must have
752    /// store (public transfer) and either the previous owner must be an
753    /// address or the object must be newly created.
754    TransferObjects(Vec<Argument>, Argument),
755    /// `(&mut Coin<T>, Vec<u64>)` -> `Vec<Coin<T>>`
756    /// It splits off some amounts into a new coins with those amounts
757    SplitCoins(Argument, Vec<Argument>),
758    /// `(&mut Coin<T>, Vec<Coin<T>>)`
759    /// It merges n-coins into the first coin
760    MergeCoins(Argument, Vec<Argument>),
761    /// Publishes a Move package. It takes the package bytes and a list of the
762    /// package's transitive dependencies to link against on-chain.
763    Publish(Vec<Vec<u8>>, Vec<ObjectID>),
764    /// `forall T: Vec<T> -> vector<T>`
765    /// Given n-values of the same type, it constructs a vector. For non objects
766    /// or an empty vector, the type input must be specified.
767    MakeMoveVec(Option<TypeInput>, Vec<Argument>),
768    /// Upgrades a Move package
769    /// Takes (in order):
770    /// 1. A vector of serialized modules for the package.
771    /// 2. A vector of object ids for the transitive dependencies of the new
772    ///    package.
773    /// 3. The object ID of the package being upgraded.
774    /// 4. An argument holding the `UpgradeTicket` that must have been produced
775    ///    from an earlier command in the same programmable transaction.
776    Upgrade(Vec<Vec<u8>>, Vec<ObjectID>, ObjectID, Argument),
777}
778
779/// An argument to a programmable transaction command
780#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
781pub enum Argument {
782    /// The gas coin. The gas coin can only be used by-ref, except for with
783    /// `TransferObjects`, which can use it by-value.
784    GasCoin,
785    /// One of the input objects or primitive values (from
786    /// `ProgrammableTransaction` inputs)
787    Input(u16),
788    /// The result of another command (from `ProgrammableTransaction` commands)
789    Result(u16),
790    /// Like a `Result` but it accesses a nested result. Currently, the only
791    /// usage of this is to access a value from a Move call with multiple
792    /// return values.
793    NestedResult(u16, u16),
794}
795
796/// The command for calling a Move function, either an entry function or a
797/// public function (which cannot return references).
798#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
799pub struct ProgrammableMoveCall {
800    /// The package containing the module and function.
801    pub package: ObjectID,
802    /// The specific module in the package containing the function.
803    pub module: String,
804    /// The function to be called.
805    pub function: String,
806    /// The type arguments to the function.
807    pub type_arguments: Vec<TypeInput>,
808    /// The arguments to the function.
809    pub arguments: Vec<Argument>,
810}
811
812impl ProgrammableMoveCall {
813    fn input_objects(&self) -> Vec<InputObjectKind> {
814        let ProgrammableMoveCall {
815            package,
816            type_arguments,
817            ..
818        } = self;
819        let mut packages = BTreeSet::from([*package]);
820        for type_argument in type_arguments {
821            add_type_input_packages(&mut packages, type_argument)
822        }
823        packages
824            .into_iter()
825            .map(InputObjectKind::MovePackage)
826            .collect()
827    }
828
829    pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
830        let is_blocked = BLOCKED_MOVE_FUNCTIONS.contains(&(
831            self.package,
832            self.module.as_str(),
833            self.function.as_str(),
834        ));
835        fp_ensure!(!is_blocked, UserInputError::BlockedMoveFunction);
836        let mut type_arguments_count = 0;
837        for tag in &self.type_arguments {
838            type_input_validity_check(tag, config, &mut type_arguments_count)?;
839        }
840        fp_ensure!(
841            self.arguments.len() < config.max_arguments() as usize,
842            UserInputError::SizeLimitExceeded {
843                limit: "maximum arguments in a move call".to_string(),
844                value: config.max_arguments().to_string()
845            }
846        );
847        if config.validate_identifier_inputs() {
848            fp_ensure!(
849                identifier::is_valid(&self.module),
850                UserInputError::InvalidIdentifier {
851                    error: self.module.clone()
852                }
853            );
854            fp_ensure!(
855                identifier::is_valid(&self.function),
856                UserInputError::InvalidIdentifier {
857                    error: self.module.clone()
858                }
859            );
860        }
861        Ok(())
862    }
863
864    fn is_input_arg_used(&self, arg: u16) -> bool {
865        self.arguments
866            .iter()
867            .any(|a| matches!(a, Argument::Input(inp) if *inp == arg))
868    }
869}
870
871impl Command {
872    pub fn move_call(
873        package: ObjectID,
874        module: Identifier,
875        function: Identifier,
876        type_arguments: Vec<TypeTag>,
877        arguments: Vec<Argument>,
878    ) -> Self {
879        let module = module.to_string();
880        let function = function.to_string();
881        let type_arguments = type_arguments.into_iter().map(TypeInput::from).collect();
882        Command::MoveCall(Box::new(ProgrammableMoveCall {
883            package,
884            module,
885            function,
886            type_arguments,
887            arguments,
888        }))
889    }
890
891    pub fn make_move_vec(ty: Option<TypeTag>, args: Vec<Argument>) -> Self {
892        Command::MakeMoveVec(ty.map(TypeInput::from), args)
893    }
894
895    fn input_objects(&self) -> Vec<InputObjectKind> {
896        match self {
897            Command::Upgrade(_, deps, package_id, _) => deps
898                .iter()
899                .map(|id| InputObjectKind::MovePackage(*id))
900                .chain(Some(InputObjectKind::MovePackage(*package_id)))
901                .collect(),
902            Command::Publish(_, deps) => deps
903                .iter()
904                .map(|id| InputObjectKind::MovePackage(*id))
905                .collect(),
906            Command::MoveCall(c) => c.input_objects(),
907            Command::MakeMoveVec(Some(t), _) => {
908                let mut packages = BTreeSet::new();
909                add_type_input_packages(&mut packages, t);
910                packages
911                    .into_iter()
912                    .map(InputObjectKind::MovePackage)
913                    .collect()
914            }
915            Command::MakeMoveVec(None, _)
916            | Command::TransferObjects(_, _)
917            | Command::SplitCoins(_, _)
918            | Command::MergeCoins(_, _) => vec![],
919        }
920    }
921
922    fn non_system_packages_to_be_published(&self) -> Option<&Vec<Vec<u8>>> {
923        match self {
924            Command::Upgrade(v, _, _, _) => Some(v),
925            Command::Publish(v, _) => Some(v),
926            Command::MoveCall(_)
927            | Command::TransferObjects(_, _)
928            | Command::SplitCoins(_, _)
929            | Command::MergeCoins(_, _)
930            | Command::MakeMoveVec(_, _) => None,
931        }
932    }
933
934    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
935        match self {
936            Command::MoveCall(call) => call.validity_check(config)?,
937            Command::TransferObjects(args, _)
938            | Command::MergeCoins(_, args)
939            | Command::SplitCoins(_, args) => {
940                fp_ensure!(!args.is_empty(), UserInputError::EmptyCommandInput);
941                fp_ensure!(
942                    args.len() < config.max_arguments() as usize,
943                    UserInputError::SizeLimitExceeded {
944                        limit: "maximum arguments in a programmable transaction command"
945                            .to_string(),
946                        value: config.max_arguments().to_string()
947                    }
948                );
949            }
950            Command::MakeMoveVec(ty_opt, args) => {
951                // ty_opt.is_none() ==> !args.is_empty()
952                fp_ensure!(
953                    ty_opt.is_some() || !args.is_empty(),
954                    UserInputError::EmptyCommandInput
955                );
956                if let Some(ty) = ty_opt {
957                    let mut type_arguments_count = 0;
958                    type_input_validity_check(ty, config, &mut type_arguments_count)?;
959                }
960                fp_ensure!(
961                    args.len() < config.max_arguments() as usize,
962                    UserInputError::SizeLimitExceeded {
963                        limit: "maximum arguments in a programmable transaction command"
964                            .to_string(),
965                        value: config.max_arguments().to_string()
966                    }
967                );
968            }
969            Command::Publish(modules, deps) | Command::Upgrade(modules, deps, _, _) => {
970                fp_ensure!(!modules.is_empty(), UserInputError::EmptyCommandInput);
971                fp_ensure!(
972                    modules.len() < config.max_modules_in_publish() as usize,
973                    UserInputError::SizeLimitExceeded {
974                        limit: "maximum modules in a programmable transaction upgrade command"
975                            .to_string(),
976                        value: config.max_modules_in_publish().to_string()
977                    }
978                );
979                if let Some(max_package_dependencies) = config.max_package_dependencies_as_option()
980                {
981                    fp_ensure!(
982                        deps.len() < max_package_dependencies as usize,
983                        UserInputError::SizeLimitExceeded {
984                            limit: "maximum package dependencies".to_string(),
985                            value: max_package_dependencies.to_string()
986                        }
987                    );
988                };
989            }
990        };
991        Ok(())
992    }
993
994    fn is_input_arg_used(&self, input_arg: u16) -> bool {
995        match self {
996            Command::MoveCall(c) => c.is_input_arg_used(input_arg),
997            Command::TransferObjects(args, arg)
998            | Command::MergeCoins(arg, args)
999            | Command::SplitCoins(arg, args) => args
1000                .iter()
1001                .chain(once(arg))
1002                .any(|a| matches!(a, Argument::Input(inp) if *inp == input_arg)),
1003            Command::MakeMoveVec(_, args) => args
1004                .iter()
1005                .any(|a| matches!(a, Argument::Input(inp) if *inp == input_arg)),
1006            Command::Upgrade(_, _, _, arg) => {
1007                matches!(arg, Argument::Input(inp) if *inp == input_arg)
1008            }
1009            Command::Publish(_, _) => false,
1010        }
1011    }
1012}
1013
1014pub fn write_sep<T: Display>(
1015    f: &mut Formatter<'_>,
1016    items: impl IntoIterator<Item = T>,
1017    sep: &str,
1018) -> std::fmt::Result {
1019    let mut xs = items.into_iter();
1020    let Some(x) = xs.next() else {
1021        return Ok(());
1022    };
1023    write!(f, "{x}")?;
1024    for x in xs {
1025        write!(f, "{sep}{x}")?;
1026    }
1027    Ok(())
1028}
1029
1030impl ProgrammableTransaction {
1031    pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1032        let ProgrammableTransaction { inputs, commands } = self;
1033        let input_arg_objects = inputs
1034            .iter()
1035            .flat_map(|arg| arg.input_objects())
1036            .collect::<Vec<_>>();
1037        // all objects, not just mutable, must be unique
1038        let mut used = HashSet::new();
1039        if !input_arg_objects.iter().all(|o| used.insert(o.object_id())) {
1040            return Err(UserInputError::DuplicateObjectRefInput);
1041        }
1042        // do not duplicate packages referred to in commands
1043        let command_input_objects: BTreeSet<InputObjectKind> = commands
1044            .iter()
1045            .flat_map(|command| command.input_objects())
1046            .collect();
1047        Ok(input_arg_objects
1048            .into_iter()
1049            .chain(command_input_objects)
1050            .collect())
1051    }
1052
1053    fn receiving_objects(&self) -> Vec<ObjectRef> {
1054        let ProgrammableTransaction { inputs, .. } = self;
1055        inputs
1056            .iter()
1057            .flat_map(|arg| arg.receiving_objects())
1058            .collect()
1059    }
1060
1061    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1062        let ProgrammableTransaction { inputs, commands } = self;
1063        fp_ensure!(
1064            commands.len() < config.max_programmable_tx_commands() as usize,
1065            UserInputError::SizeLimitExceeded {
1066                limit: "maximum commands in a programmable transaction".to_string(),
1067                value: config.max_programmable_tx_commands().to_string()
1068            }
1069        );
1070        let total_inputs = self.input_objects()?.len() + self.receiving_objects().len();
1071        fp_ensure!(
1072            total_inputs <= config.max_input_objects() as usize,
1073            UserInputError::SizeLimitExceeded {
1074                limit: "maximum input + receiving objects in a transaction".to_string(),
1075                value: config.max_input_objects().to_string()
1076            }
1077        );
1078        for input in inputs {
1079            input.validity_check(config)?
1080        }
1081        if let Some(max_publish_commands) = config.max_publish_or_upgrade_per_ptb_as_option() {
1082            let publish_count = commands
1083                .iter()
1084                .filter(|c| matches!(c, Command::Publish(_, _) | Command::Upgrade(_, _, _, _)))
1085                .count() as u64;
1086            fp_ensure!(
1087                publish_count <= max_publish_commands,
1088                UserInputError::MaxPublishCountExceeded {
1089                    max_publish_commands,
1090                    publish_count,
1091                }
1092            );
1093        }
1094        for command in commands {
1095            command.validity_check(config)?;
1096        }
1097
1098        // If randomness is used, it must be enabled by protocol config.
1099        // A command that uses Random can only be followed by TransferObjects or
1100        // MergeCoins.
1101        if let Some(random_index) = inputs.iter().position(|obj| {
1102            matches!(obj, CallArg::Object(ObjectArg::SharedObject { id, .. }) if *id == IOTA_RANDOMNESS_STATE_OBJECT_ID)
1103        }) {
1104            let mut used_random_object = false;
1105            let random_index = random_index.try_into().unwrap();
1106            for command in commands {
1107                if !used_random_object {
1108                    used_random_object = command.is_input_arg_used(random_index);
1109                } else {
1110                    fp_ensure!(
1111                        matches!(
1112                            command,
1113                            Command::TransferObjects(_, _) | Command::MergeCoins(_, _)
1114                        ),
1115                        UserInputError::PostRandomCommandRestrictions
1116                    );
1117                }
1118            }
1119        }
1120
1121        Ok(())
1122    }
1123
1124    fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
1125        self.inputs
1126            .iter()
1127            .filter_map(|arg| match arg {
1128                CallArg::Pure(_)
1129                | CallArg::Object(ObjectArg::Receiving(_))
1130                | CallArg::Object(ObjectArg::ImmOrOwnedObject(_)) => None,
1131                CallArg::Object(ObjectArg::SharedObject {
1132                    id,
1133                    initial_shared_version,
1134                    mutable,
1135                }) => Some(vec![SharedInputObject {
1136                    id: *id,
1137                    initial_shared_version: *initial_shared_version,
1138                    mutable: *mutable,
1139                }]),
1140            })
1141            .flatten()
1142    }
1143
1144    fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)> {
1145        self.commands
1146            .iter()
1147            .filter_map(|command| match command {
1148                Command::MoveCall(m) => Some((&m.package, m.module.as_str(), m.function.as_str())),
1149                _ => None,
1150            })
1151            .collect()
1152    }
1153
1154    pub fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>> + '_ {
1155        self.commands
1156            .iter()
1157            .filter_map(|q| q.non_system_packages_to_be_published())
1158    }
1159}
1160
1161impl Display for Argument {
1162    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1163        match self {
1164            Argument::GasCoin => write!(f, "GasCoin"),
1165            Argument::Input(i) => write!(f, "Input({i})"),
1166            Argument::Result(i) => write!(f, "Result({i})"),
1167            Argument::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
1168        }
1169    }
1170}
1171
1172impl Display for ProgrammableMoveCall {
1173    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1174        let ProgrammableMoveCall {
1175            package,
1176            module,
1177            function,
1178            type_arguments,
1179            arguments,
1180        } = self;
1181        write!(f, "{package}::{module}::{function}")?;
1182        if !type_arguments.is_empty() {
1183            write!(f, "<")?;
1184            write_sep(f, type_arguments, ",")?;
1185            write!(f, ">")?;
1186        }
1187        write!(f, "(")?;
1188        write_sep(f, arguments, ",")?;
1189        write!(f, ")")
1190    }
1191}
1192
1193impl Display for Command {
1194    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1195        match self {
1196            Command::MoveCall(p) => {
1197                write!(f, "MoveCall({p})")
1198            }
1199            Command::MakeMoveVec(ty_opt, elems) => {
1200                write!(f, "MakeMoveVec(")?;
1201                if let Some(ty) = ty_opt {
1202                    write!(f, "Some{ty}")?;
1203                } else {
1204                    write!(f, "None")?;
1205                }
1206                write!(f, ",[")?;
1207                write_sep(f, elems, ",")?;
1208                write!(f, "])")
1209            }
1210            Command::TransferObjects(objs, addr) => {
1211                write!(f, "TransferObjects([")?;
1212                write_sep(f, objs, ",")?;
1213                write!(f, "],{addr})")
1214            }
1215            Command::SplitCoins(coin, amounts) => {
1216                write!(f, "SplitCoins({coin}")?;
1217                write_sep(f, amounts, ",")?;
1218                write!(f, ")")
1219            }
1220            Command::MergeCoins(target, coins) => {
1221                write!(f, "MergeCoins({target},")?;
1222                write_sep(f, coins, ",")?;
1223                write!(f, ")")
1224            }
1225            Command::Publish(_bytes, deps) => {
1226                write!(f, "Publish(_,")?;
1227                write_sep(f, deps, ",")?;
1228                write!(f, ")")
1229            }
1230            Command::Upgrade(_bytes, deps, current_package_id, ticket) => {
1231                write!(f, "Upgrade(_,")?;
1232                write_sep(f, deps, ",")?;
1233                write!(f, ", {current_package_id}")?;
1234                write!(f, ", {ticket}")?;
1235                write!(f, ")")
1236            }
1237        }
1238    }
1239}
1240
1241impl Display for ProgrammableTransaction {
1242    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1243        let ProgrammableTransaction { inputs, commands } = self;
1244        writeln!(f, "Inputs: {inputs:?}")?;
1245        writeln!(f, "Commands: [")?;
1246        for c in commands {
1247            writeln!(f, "  {c},")?;
1248        }
1249        writeln!(f, "]")
1250    }
1251}
1252
1253#[derive(Debug, PartialEq, Eq)]
1254pub struct SharedInputObject {
1255    pub id: ObjectID,
1256    pub initial_shared_version: SequenceNumber,
1257    pub mutable: bool,
1258}
1259
1260impl SharedInputObject {
1261    pub const IOTA_SYSTEM_OBJ: Self = Self {
1262        id: IOTA_SYSTEM_STATE_OBJECT_ID,
1263        initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
1264        mutable: true,
1265    };
1266
1267    pub fn id(&self) -> ObjectID {
1268        self.id
1269    }
1270
1271    pub fn into_id_and_version(self) -> (ObjectID, SequenceNumber) {
1272        (self.id, self.initial_shared_version)
1273    }
1274}
1275
1276impl TransactionKind {
1277    /// present to make migrations to programmable transactions eaier.
1278    /// Will be removed
1279    pub fn programmable(pt: ProgrammableTransaction) -> Self {
1280        TransactionKind::ProgrammableTransaction(pt)
1281    }
1282
1283    pub fn is_system_tx(&self) -> bool {
1284        // Keep this as an exhaustive match so that we can't forget to update it.
1285        match self {
1286            TransactionKind::Genesis(_)
1287            | TransactionKind::ConsensusCommitPrologueV1(_)
1288            | TransactionKind::AuthenticatorStateUpdateV1(_)
1289            | TransactionKind::RandomnessStateUpdate(_)
1290            | TransactionKind::EndOfEpochTransaction(_) => true,
1291            TransactionKind::ProgrammableTransaction(_) => false,
1292        }
1293    }
1294
1295    pub fn is_end_of_epoch_tx(&self) -> bool {
1296        matches!(self, TransactionKind::EndOfEpochTransaction(_))
1297    }
1298
1299    /// If this is advance epoch transaction, returns (total gas charged, total
1300    /// gas rebated). TODO: We should use GasCostSummary directly in
1301    /// ChangeEpoch struct, and return that directly.
1302    pub fn get_advance_epoch_tx_gas_summary(&self) -> Option<(u64, u64)> {
1303        match self {
1304            Self::EndOfEpochTransaction(txns) => {
1305                match txns.last().expect("at least one end-of-epoch txn required") {
1306                    EndOfEpochTransactionKind::ChangeEpoch(e) => {
1307                        Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1308                    }
1309                    EndOfEpochTransactionKind::ChangeEpochV2(e) => {
1310                        Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1311                    }
1312                    EndOfEpochTransactionKind::ChangeEpochV3(e) => {
1313                        Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1314                    }
1315                    _ => panic!("final end-of-epoch txn must be ChangeEpoch"),
1316                }
1317            }
1318            _ => None,
1319        }
1320    }
1321
1322    pub fn contains_shared_object(&self) -> bool {
1323        self.shared_input_objects().next().is_some()
1324    }
1325
1326    /// Returns an iterator of all shared input objects used by this
1327    /// transaction.
1328    pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
1329        match &self {
1330            Self::ConsensusCommitPrologueV1(_) => {
1331                Either::Left(Either::Left(iter::once(SharedInputObject {
1332                    id: IOTA_CLOCK_OBJECT_ID,
1333                    initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
1334                    mutable: true,
1335                })))
1336            }
1337            Self::AuthenticatorStateUpdateV1(update) => {
1338                Either::Left(Either::Left(iter::once(SharedInputObject {
1339                    id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1340                    initial_shared_version: update.authenticator_obj_initial_shared_version,
1341                    mutable: true,
1342                })))
1343            }
1344            Self::RandomnessStateUpdate(update) => {
1345                Either::Left(Either::Left(iter::once(SharedInputObject {
1346                    id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1347                    initial_shared_version: update.randomness_obj_initial_shared_version,
1348                    mutable: true,
1349                })))
1350            }
1351            Self::EndOfEpochTransaction(txns) => Either::Left(Either::Right(
1352                txns.iter().flat_map(|txn| txn.shared_input_objects()),
1353            )),
1354            Self::ProgrammableTransaction(pt) => {
1355                Either::Right(Either::Left(pt.shared_input_objects()))
1356            }
1357            _ => Either::Right(Either::Right(iter::empty())),
1358        }
1359    }
1360
1361    fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)> {
1362        match &self {
1363            Self::ProgrammableTransaction(pt) => pt.move_calls(),
1364            _ => vec![],
1365        }
1366    }
1367
1368    pub fn receiving_objects(&self) -> Vec<ObjectRef> {
1369        match &self {
1370            TransactionKind::Genesis(_)
1371            | TransactionKind::ConsensusCommitPrologueV1(_)
1372            | TransactionKind::AuthenticatorStateUpdateV1(_)
1373            | TransactionKind::RandomnessStateUpdate(_)
1374            | TransactionKind::EndOfEpochTransaction(_) => vec![],
1375            TransactionKind::ProgrammableTransaction(pt) => pt.receiving_objects(),
1376        }
1377    }
1378
1379    /// Return the metadata of each of the input objects for the transaction.
1380    /// For a Move object, we attach the object reference;
1381    /// for a Move package, we provide the object id only since they never
1382    /// change on chain. TODO: use an iterator over references here instead
1383    /// of a Vec to avoid allocations.
1384    pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1385        let input_objects = match &self {
1386            Self::Genesis(_) => {
1387                vec![]
1388            }
1389            Self::ConsensusCommitPrologueV1(_) => {
1390                vec![InputObjectKind::SharedMoveObject {
1391                    id: IOTA_CLOCK_OBJECT_ID,
1392                    initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
1393                    mutable: true,
1394                }]
1395            }
1396            Self::AuthenticatorStateUpdateV1(update) => {
1397                vec![InputObjectKind::SharedMoveObject {
1398                    id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1399                    initial_shared_version: update.authenticator_obj_initial_shared_version(),
1400                    mutable: true,
1401                }]
1402            }
1403            Self::RandomnessStateUpdate(update) => {
1404                vec![InputObjectKind::SharedMoveObject {
1405                    id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1406                    initial_shared_version: update.randomness_obj_initial_shared_version(),
1407                    mutable: true,
1408                }]
1409            }
1410            Self::EndOfEpochTransaction(txns) => {
1411                // Dedup since transactions may have an overlap in input objects.
1412                // Note: it's critical to ensure the order of inputs are deterministic.
1413                let before_dedup: Vec<_> =
1414                    txns.iter().flat_map(|txn| txn.input_objects()).collect();
1415                let mut has_seen = HashSet::new();
1416                let mut after_dedup = vec![];
1417                for obj in before_dedup {
1418                    if has_seen.insert(obj) {
1419                        after_dedup.push(obj);
1420                    }
1421                }
1422                after_dedup
1423            }
1424            Self::ProgrammableTransaction(p) => return p.input_objects(),
1425        };
1426        // Ensure that there are no duplicate inputs. This cannot be removed because:
1427        // In [`AuthorityState::check_locks`], we check that there are no duplicate
1428        // mutable input objects, which would have made this check here
1429        // unnecessary. However, we do plan to allow shared objects show up more
1430        // than once in multiple single transactions down the line. Once we have
1431        // that, we need check here to make sure the same shared object doesn't
1432        // show up more than once in the same single transaction.
1433        let mut used = HashSet::new();
1434        if !input_objects.iter().all(|o| used.insert(o.object_id())) {
1435            return Err(UserInputError::DuplicateObjectRefInput);
1436        }
1437        Ok(input_objects)
1438    }
1439
1440    pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1441        match self {
1442            TransactionKind::ProgrammableTransaction(p) => p.validity_check(config)?,
1443            // All transaction kinds below are assumed to be system,
1444            // and no validity or limit checks are performed.
1445            TransactionKind::Genesis(_) | TransactionKind::ConsensusCommitPrologueV1(_) => (),
1446            TransactionKind::EndOfEpochTransaction(txns) => {
1447                for tx in txns {
1448                    tx.validity_check(config)?;
1449                }
1450            }
1451
1452            TransactionKind::AuthenticatorStateUpdateV1(_) => {
1453                if !config.enable_jwk_consensus_updates() {
1454                    return Err(UserInputError::Unsupported(
1455                        "authenticator state updates not enabled".to_string(),
1456                    ));
1457                }
1458            }
1459            TransactionKind::RandomnessStateUpdate(_) => (),
1460        };
1461        Ok(())
1462    }
1463
1464    /// number of commands, or 0 if it is a system transaction
1465    pub fn num_commands(&self) -> usize {
1466        match self {
1467            TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1468            _ => 0,
1469        }
1470    }
1471
1472    pub fn iter_commands(&self) -> impl Iterator<Item = &Command> {
1473        match self {
1474            TransactionKind::ProgrammableTransaction(pt) => pt.commands.iter(),
1475            _ => [].iter(),
1476        }
1477    }
1478
1479    /// number of transactions, or 1 if it is a system transaction
1480    pub fn tx_count(&self) -> usize {
1481        match self {
1482            TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1483            _ => 1,
1484        }
1485    }
1486
1487    pub fn name(&self) -> &'static str {
1488        match self {
1489            Self::Genesis(_) => "Genesis",
1490            Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
1491            Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
1492            Self::AuthenticatorStateUpdateV1(_) => "AuthenticatorStateUpdateV1",
1493            Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
1494            Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
1495        }
1496    }
1497}
1498
1499impl Display for TransactionKind {
1500    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1501        let mut writer = String::new();
1502        match &self {
1503            Self::Genesis(_) => {
1504                writeln!(writer, "Transaction Kind : Genesis")?;
1505            }
1506            Self::ConsensusCommitPrologueV1(p) => {
1507                writeln!(writer, "Transaction Kind : Consensus Commit Prologue V1")?;
1508                writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1509                writeln!(writer, "Consensus Digest: {}", p.consensus_commit_digest)?;
1510                writeln!(
1511                    writer,
1512                    "Consensus determined version assignment: {:?}",
1513                    p.consensus_determined_version_assignments
1514                )?;
1515            }
1516            Self::ProgrammableTransaction(p) => {
1517                writeln!(writer, "Transaction Kind : Programmable")?;
1518                write!(writer, "{p}")?;
1519            }
1520            Self::AuthenticatorStateUpdateV1(_) => {
1521                writeln!(writer, "Transaction Kind : Authenticator State Update")?;
1522            }
1523            Self::RandomnessStateUpdate(_) => {
1524                writeln!(writer, "Transaction Kind : Randomness State Update")?;
1525            }
1526            Self::EndOfEpochTransaction(_) => {
1527                writeln!(writer, "Transaction Kind : End of Epoch Transaction")?;
1528            }
1529        }
1530        write!(f, "{writer}")
1531    }
1532}
1533
1534#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1535pub struct GasData {
1536    pub payment: Vec<ObjectRef>,
1537    pub owner: IotaAddress,
1538    pub price: u64,
1539    pub budget: u64,
1540}
1541
1542#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
1543pub enum TransactionExpiration {
1544    /// The transaction has no expiration
1545    None,
1546    /// Validators wont sign a transaction unless the expiration Epoch
1547    /// is greater than or equal to the current epoch
1548    Epoch(EpochId),
1549}
1550
1551#[enum_dispatch(TransactionDataAPI)]
1552#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1553pub enum TransactionData {
1554    V1(TransactionDataV1),
1555    // When new variants are introduced, it is important that we check version support
1556    // in the validity_check function based on the protocol config.
1557}
1558
1559#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1560pub struct TransactionDataV1 {
1561    pub kind: TransactionKind,
1562    pub sender: IotaAddress,
1563    pub gas_data: GasData,
1564    pub expiration: TransactionExpiration,
1565}
1566
1567impl TransactionData {
1568    fn new_system_transaction(kind: TransactionKind) -> Self {
1569        // assert transaction kind if a system transaction
1570        assert!(kind.is_system_tx());
1571        let sender = IotaAddress::default();
1572        TransactionData::V1(TransactionDataV1 {
1573            kind,
1574            sender,
1575            gas_data: GasData {
1576                price: GAS_PRICE_FOR_SYSTEM_TX,
1577                owner: sender,
1578                payment: vec![(ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN)],
1579                budget: 0,
1580            },
1581            expiration: TransactionExpiration::None,
1582        })
1583    }
1584
1585    pub fn new(
1586        kind: TransactionKind,
1587        sender: IotaAddress,
1588        gas_payment: ObjectRef,
1589        gas_budget: u64,
1590        gas_price: u64,
1591    ) -> Self {
1592        TransactionData::V1(TransactionDataV1 {
1593            kind,
1594            sender,
1595            gas_data: GasData {
1596                price: gas_price,
1597                owner: sender,
1598                payment: vec![gas_payment],
1599                budget: gas_budget,
1600            },
1601            expiration: TransactionExpiration::None,
1602        })
1603    }
1604
1605    pub fn new_with_gas_coins(
1606        kind: TransactionKind,
1607        sender: IotaAddress,
1608        gas_payment: Vec<ObjectRef>,
1609        gas_budget: u64,
1610        gas_price: u64,
1611    ) -> Self {
1612        Self::new_with_gas_coins_allow_sponsor(
1613            kind,
1614            sender,
1615            gas_payment,
1616            gas_budget,
1617            gas_price,
1618            sender,
1619        )
1620    }
1621
1622    pub fn new_with_gas_coins_allow_sponsor(
1623        kind: TransactionKind,
1624        sender: IotaAddress,
1625        gas_payment: Vec<ObjectRef>,
1626        gas_budget: u64,
1627        gas_price: u64,
1628        gas_sponsor: IotaAddress,
1629    ) -> Self {
1630        TransactionData::V1(TransactionDataV1 {
1631            kind,
1632            sender,
1633            gas_data: GasData {
1634                price: gas_price,
1635                owner: gas_sponsor,
1636                payment: gas_payment,
1637                budget: gas_budget,
1638            },
1639            expiration: TransactionExpiration::None,
1640        })
1641    }
1642
1643    pub fn new_with_gas_data(
1644        kind: TransactionKind,
1645        sender: IotaAddress,
1646        gas_data: GasData,
1647    ) -> Self {
1648        TransactionData::V1(TransactionDataV1 {
1649            kind,
1650            sender,
1651            gas_data,
1652            expiration: TransactionExpiration::None,
1653        })
1654    }
1655
1656    pub fn new_move_call(
1657        sender: IotaAddress,
1658        package: ObjectID,
1659        module: Identifier,
1660        function: Identifier,
1661        type_arguments: Vec<TypeTag>,
1662        gas_payment: ObjectRef,
1663        arguments: Vec<CallArg>,
1664        gas_budget: u64,
1665        gas_price: u64,
1666    ) -> anyhow::Result<Self> {
1667        Self::new_move_call_with_gas_coins(
1668            sender,
1669            package,
1670            module,
1671            function,
1672            type_arguments,
1673            vec![gas_payment],
1674            arguments,
1675            gas_budget,
1676            gas_price,
1677        )
1678    }
1679
1680    pub fn new_move_call_with_gas_coins(
1681        sender: IotaAddress,
1682        package: ObjectID,
1683        module: Identifier,
1684        function: Identifier,
1685        type_arguments: Vec<TypeTag>,
1686        gas_payment: Vec<ObjectRef>,
1687        arguments: Vec<CallArg>,
1688        gas_budget: u64,
1689        gas_price: u64,
1690    ) -> anyhow::Result<Self> {
1691        let pt = {
1692            let mut builder = ProgrammableTransactionBuilder::new();
1693            builder.move_call(package, module, function, type_arguments, arguments)?;
1694            builder.finish()
1695        };
1696        Ok(Self::new_programmable(
1697            sender,
1698            gas_payment,
1699            pt,
1700            gas_budget,
1701            gas_price,
1702        ))
1703    }
1704
1705    pub fn new_transfer(
1706        recipient: IotaAddress,
1707        object_ref: ObjectRef,
1708        sender: IotaAddress,
1709        gas_payment: ObjectRef,
1710        gas_budget: u64,
1711        gas_price: u64,
1712    ) -> Self {
1713        let pt = {
1714            let mut builder = ProgrammableTransactionBuilder::new();
1715            builder.transfer_object(recipient, object_ref).unwrap();
1716            builder.finish()
1717        };
1718        Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1719    }
1720
1721    pub fn new_transfer_iota(
1722        recipient: IotaAddress,
1723        sender: IotaAddress,
1724        amount: Option<u64>,
1725        gas_payment: ObjectRef,
1726        gas_budget: u64,
1727        gas_price: u64,
1728    ) -> Self {
1729        Self::new_transfer_iota_allow_sponsor(
1730            recipient,
1731            sender,
1732            amount,
1733            gas_payment,
1734            gas_budget,
1735            gas_price,
1736            sender,
1737        )
1738    }
1739
1740    pub fn new_transfer_iota_allow_sponsor(
1741        recipient: IotaAddress,
1742        sender: IotaAddress,
1743        amount: Option<u64>,
1744        gas_payment: ObjectRef,
1745        gas_budget: u64,
1746        gas_price: u64,
1747        gas_sponsor: IotaAddress,
1748    ) -> Self {
1749        let pt = {
1750            let mut builder = ProgrammableTransactionBuilder::new();
1751            builder.transfer_iota(recipient, amount);
1752            builder.finish()
1753        };
1754        Self::new_programmable_allow_sponsor(
1755            sender,
1756            vec![gas_payment],
1757            pt,
1758            gas_budget,
1759            gas_price,
1760            gas_sponsor,
1761        )
1762    }
1763
1764    pub fn new_pay(
1765        sender: IotaAddress,
1766        coins: Vec<ObjectRef>,
1767        recipients: Vec<IotaAddress>,
1768        amounts: Vec<u64>,
1769        gas_payment: ObjectRef,
1770        gas_budget: u64,
1771        gas_price: u64,
1772    ) -> anyhow::Result<Self> {
1773        let pt = {
1774            let mut builder = ProgrammableTransactionBuilder::new();
1775            builder.pay(coins, recipients, amounts)?;
1776            builder.finish()
1777        };
1778        Ok(Self::new_programmable(
1779            sender,
1780            vec![gas_payment],
1781            pt,
1782            gas_budget,
1783            gas_price,
1784        ))
1785    }
1786
1787    pub fn new_pay_iota(
1788        sender: IotaAddress,
1789        mut coins: Vec<ObjectRef>,
1790        recipients: Vec<IotaAddress>,
1791        amounts: Vec<u64>,
1792        gas_payment: ObjectRef,
1793        gas_budget: u64,
1794        gas_price: u64,
1795    ) -> anyhow::Result<Self> {
1796        coins.insert(0, gas_payment);
1797        let pt = {
1798            let mut builder = ProgrammableTransactionBuilder::new();
1799            builder.pay_iota(recipients, amounts)?;
1800            builder.finish()
1801        };
1802        Ok(Self::new_programmable(
1803            sender, coins, pt, gas_budget, gas_price,
1804        ))
1805    }
1806
1807    pub fn new_pay_all_iota(
1808        sender: IotaAddress,
1809        mut coins: Vec<ObjectRef>,
1810        recipient: IotaAddress,
1811        gas_payment: ObjectRef,
1812        gas_budget: u64,
1813        gas_price: u64,
1814    ) -> Self {
1815        coins.insert(0, gas_payment);
1816        let pt = {
1817            let mut builder = ProgrammableTransactionBuilder::new();
1818            builder.pay_all_iota(recipient);
1819            builder.finish()
1820        };
1821        Self::new_programmable(sender, coins, pt, gas_budget, gas_price)
1822    }
1823
1824    pub fn new_split_coin(
1825        sender: IotaAddress,
1826        coin: ObjectRef,
1827        amounts: Vec<u64>,
1828        gas_payment: ObjectRef,
1829        gas_budget: u64,
1830        gas_price: u64,
1831    ) -> Self {
1832        let pt = {
1833            let mut builder = ProgrammableTransactionBuilder::new();
1834            builder.split_coin(sender, coin, amounts);
1835            builder.finish()
1836        };
1837        Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1838    }
1839
1840    pub fn new_module(
1841        sender: IotaAddress,
1842        gas_payment: ObjectRef,
1843        modules: Vec<Vec<u8>>,
1844        dep_ids: Vec<ObjectID>,
1845        gas_budget: u64,
1846        gas_price: u64,
1847    ) -> Self {
1848        let pt = {
1849            let mut builder = ProgrammableTransactionBuilder::new();
1850            let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
1851            builder.transfer_arg(sender, upgrade_cap);
1852            builder.finish()
1853        };
1854        Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1855    }
1856
1857    pub fn new_upgrade(
1858        sender: IotaAddress,
1859        gas_payment: ObjectRef,
1860        package_id: ObjectID,
1861        modules: Vec<Vec<u8>>,
1862        dep_ids: Vec<ObjectID>,
1863        (upgrade_capability, capability_owner): (ObjectRef, Owner),
1864        upgrade_policy: u8,
1865        digest: Vec<u8>,
1866        gas_budget: u64,
1867        gas_price: u64,
1868    ) -> anyhow::Result<Self> {
1869        let pt = {
1870            let mut builder = ProgrammableTransactionBuilder::new();
1871            let capability_arg = match capability_owner {
1872                Owner::AddressOwner(_) => ObjectArg::ImmOrOwnedObject(upgrade_capability),
1873                Owner::Shared {
1874                    initial_shared_version,
1875                } => ObjectArg::SharedObject {
1876                    id: upgrade_capability.0,
1877                    initial_shared_version,
1878                    mutable: true,
1879                },
1880                Owner::Immutable => {
1881                    bail!("Upgrade capability is stored immutably and cannot be used for upgrades");
1882                }
1883                // If the capability is owned by an object, then the module defining the owning
1884                // object gets to decide how the upgrade capability should be used.
1885                Owner::ObjectOwner(_) => {
1886                    bail!("Upgrade capability controlled by object");
1887                }
1888            };
1889            builder.obj(capability_arg).unwrap();
1890            let upgrade_arg = builder.pure(upgrade_policy).unwrap();
1891            let digest_arg = builder.pure(digest).unwrap();
1892            let upgrade_ticket = builder.programmable_move_call(
1893                IOTA_FRAMEWORK_PACKAGE_ID,
1894                ident_str!("package").to_owned(),
1895                ident_str!("authorize_upgrade").to_owned(),
1896                vec![],
1897                vec![Argument::Input(0), upgrade_arg, digest_arg],
1898            );
1899            let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
1900
1901            builder.programmable_move_call(
1902                IOTA_FRAMEWORK_PACKAGE_ID,
1903                ident_str!("package").to_owned(),
1904                ident_str!("commit_upgrade").to_owned(),
1905                vec![],
1906                vec![Argument::Input(0), upgrade_receipt],
1907            );
1908
1909            builder.finish()
1910        };
1911        Ok(Self::new_programmable(
1912            sender,
1913            vec![gas_payment],
1914            pt,
1915            gas_budget,
1916            gas_price,
1917        ))
1918    }
1919
1920    pub fn new_programmable(
1921        sender: IotaAddress,
1922        gas_payment: Vec<ObjectRef>,
1923        pt: ProgrammableTransaction,
1924        gas_budget: u64,
1925        gas_price: u64,
1926    ) -> Self {
1927        Self::new_programmable_allow_sponsor(sender, gas_payment, pt, gas_budget, gas_price, sender)
1928    }
1929
1930    pub fn new_programmable_allow_sponsor(
1931        sender: IotaAddress,
1932        gas_payment: Vec<ObjectRef>,
1933        pt: ProgrammableTransaction,
1934        gas_budget: u64,
1935        gas_price: u64,
1936        sponsor: IotaAddress,
1937    ) -> Self {
1938        let kind = TransactionKind::ProgrammableTransaction(pt);
1939        Self::new_with_gas_coins_allow_sponsor(
1940            kind,
1941            sender,
1942            gas_payment,
1943            gas_budget,
1944            gas_price,
1945            sponsor,
1946        )
1947    }
1948
1949    pub fn message_version(&self) -> u64 {
1950        match self {
1951            TransactionData::V1(_) => 1,
1952        }
1953    }
1954
1955    pub fn execution_parts(&self) -> (TransactionKind, IotaAddress, Vec<ObjectRef>) {
1956        (
1957            self.kind().clone(),
1958            self.sender(),
1959            self.gas_data().payment.clone(),
1960        )
1961    }
1962
1963    pub fn uses_randomness(&self) -> bool {
1964        self.shared_input_objects()
1965            .iter()
1966            .any(|obj| obj.id() == IOTA_RANDOMNESS_STATE_OBJECT_ID)
1967    }
1968
1969    pub fn digest(&self) -> TransactionDigest {
1970        TransactionDigest::new(default_hash(self))
1971    }
1972}
1973
1974#[enum_dispatch]
1975pub trait TransactionDataAPI {
1976    fn sender(&self) -> IotaAddress;
1977
1978    // Note: this implies that SingleTransactionKind itself must be versioned, so
1979    // that it can be shared across versions. This will be easy to do since it
1980    // is already an enum.
1981    fn kind(&self) -> &TransactionKind;
1982
1983    // Used by programmable_transaction_builder
1984    fn kind_mut(&mut self) -> &mut TransactionKind;
1985
1986    // kind is moved out of often enough that this is worth it to special case.
1987    fn into_kind(self) -> TransactionKind;
1988
1989    /// Transaction signer and Gas owner
1990    fn signers(&self) -> NonEmpty<IotaAddress>;
1991
1992    fn gas_data(&self) -> &GasData;
1993
1994    fn gas_owner(&self) -> IotaAddress;
1995
1996    fn gas(&self) -> &[ObjectRef];
1997
1998    fn gas_price(&self) -> u64;
1999
2000    fn gas_budget(&self) -> u64;
2001
2002    fn expiration(&self) -> &TransactionExpiration;
2003
2004    fn contains_shared_object(&self) -> bool;
2005
2006    fn shared_input_objects(&self) -> Vec<SharedInputObject>;
2007
2008    fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)>;
2009
2010    fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
2011
2012    fn receiving_objects(&self) -> Vec<ObjectRef>;
2013
2014    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
2015
2016    fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult;
2017
2018    /// Check if the transaction is compliant with sponsorship.
2019    fn check_sponsorship(&self) -> UserInputResult;
2020
2021    fn is_system_tx(&self) -> bool;
2022    fn is_genesis_tx(&self) -> bool;
2023
2024    /// returns true if the transaction is one that is specially sequenced to
2025    /// run at the very end of the epoch
2026    fn is_end_of_epoch_tx(&self) -> bool;
2027
2028    /// Check if the transaction is sponsored (namely gas owner != sender)
2029    fn is_sponsored_tx(&self) -> bool;
2030
2031    fn sender_mut_for_testing(&mut self) -> &mut IotaAddress;
2032
2033    fn gas_data_mut(&mut self) -> &mut GasData;
2034
2035    // This should be used in testing only.
2036    fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration;
2037}
2038
2039impl TransactionDataAPI for TransactionDataV1 {
2040    fn sender(&self) -> IotaAddress {
2041        self.sender
2042    }
2043
2044    fn kind(&self) -> &TransactionKind {
2045        &self.kind
2046    }
2047
2048    fn kind_mut(&mut self) -> &mut TransactionKind {
2049        &mut self.kind
2050    }
2051
2052    fn into_kind(self) -> TransactionKind {
2053        self.kind
2054    }
2055
2056    /// Transaction signer and Gas owner
2057    fn signers(&self) -> NonEmpty<IotaAddress> {
2058        let mut signers = nonempty![self.sender];
2059        if self.gas_owner() != self.sender {
2060            signers.push(self.gas_owner());
2061        }
2062        signers
2063    }
2064
2065    fn gas_data(&self) -> &GasData {
2066        &self.gas_data
2067    }
2068
2069    fn gas_owner(&self) -> IotaAddress {
2070        self.gas_data.owner
2071    }
2072
2073    fn gas(&self) -> &[ObjectRef] {
2074        &self.gas_data.payment
2075    }
2076
2077    fn gas_price(&self) -> u64 {
2078        self.gas_data.price
2079    }
2080
2081    fn gas_budget(&self) -> u64 {
2082        self.gas_data.budget
2083    }
2084
2085    fn expiration(&self) -> &TransactionExpiration {
2086        &self.expiration
2087    }
2088
2089    fn contains_shared_object(&self) -> bool {
2090        self.kind.shared_input_objects().next().is_some()
2091    }
2092
2093    fn shared_input_objects(&self) -> Vec<SharedInputObject> {
2094        self.kind.shared_input_objects().collect()
2095    }
2096
2097    fn move_calls(&self) -> Vec<(&ObjectID, &str, &str)> {
2098        self.kind.move_calls()
2099    }
2100
2101    fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
2102        let mut inputs = self.kind.input_objects()?;
2103
2104        if !self.kind.is_system_tx() {
2105            inputs.extend(
2106                self.gas()
2107                    .iter()
2108                    .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
2109            );
2110        }
2111        Ok(inputs)
2112    }
2113
2114    fn receiving_objects(&self) -> Vec<ObjectRef> {
2115        self.kind.receiving_objects()
2116    }
2117
2118    fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
2119        fp_ensure!(!self.gas().is_empty(), UserInputError::MissingGasPayment);
2120        fp_ensure!(
2121            self.gas().len() < config.max_gas_payment_objects() as usize,
2122            UserInputError::SizeLimitExceeded {
2123                limit: "maximum number of gas payment objects".to_string(),
2124                value: config.max_gas_payment_objects().to_string()
2125            }
2126        );
2127        self.validity_check_no_gas_check(config)
2128    }
2129
2130    // Keep all the logic for validity here, we need this for dry run where the gas
2131    // may not be provided and created "on the fly"
2132    fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
2133        self.kind().validity_check(config)?;
2134        self.check_sponsorship()
2135    }
2136
2137    /// Check if the transaction is sponsored (namely gas owner != sender)
2138    fn is_sponsored_tx(&self) -> bool {
2139        self.gas_owner() != self.sender
2140    }
2141
2142    /// Check if the transaction is compliant with sponsorship.
2143    fn check_sponsorship(&self) -> UserInputResult {
2144        // Not a sponsored transaction, nothing to check
2145        if self.gas_owner() == self.sender() {
2146            return Ok(());
2147        }
2148        if matches!(&self.kind, TransactionKind::ProgrammableTransaction(_)) {
2149            return Ok(());
2150        }
2151        Err(UserInputError::UnsupportedSponsoredTransactionKind)
2152    }
2153
2154    fn is_end_of_epoch_tx(&self) -> bool {
2155        matches!(self.kind, TransactionKind::EndOfEpochTransaction(_))
2156    }
2157
2158    fn is_system_tx(&self) -> bool {
2159        self.kind.is_system_tx()
2160    }
2161
2162    fn is_genesis_tx(&self) -> bool {
2163        matches!(self.kind, TransactionKind::Genesis(_))
2164    }
2165
2166    fn sender_mut_for_testing(&mut self) -> &mut IotaAddress {
2167        &mut self.sender
2168    }
2169
2170    fn gas_data_mut(&mut self) -> &mut GasData {
2171        &mut self.gas_data
2172    }
2173
2174    fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
2175        &mut self.expiration
2176    }
2177}
2178
2179impl TransactionDataV1 {}
2180
2181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
2182pub struct SenderSignedData(SizeOneVec<SenderSignedTransaction>);
2183
2184#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2185pub struct SenderSignedTransaction {
2186    pub intent_message: IntentMessage<TransactionData>,
2187    /// A list of signatures signed by all transaction participants.
2188    /// 1. non participant signature must not be present.
2189    /// 2. signature order does not matter.
2190    pub tx_signatures: Vec<GenericSignature>,
2191}
2192
2193impl Serialize for SenderSignedTransaction {
2194    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2195    where
2196        S: serde::Serializer,
2197    {
2198        #[derive(Serialize)]
2199        #[serde(rename = "SenderSignedTransaction")]
2200        struct SignedTxn<'a> {
2201            intent_message: &'a IntentMessage<TransactionData>,
2202            tx_signatures: &'a Vec<GenericSignature>,
2203        }
2204
2205        if self.intent_message().intent != Intent::iota_transaction() {
2206            return Err(serde::ser::Error::custom("invalid Intent for Transaction"));
2207        }
2208
2209        let txn = SignedTxn {
2210            intent_message: self.intent_message(),
2211            tx_signatures: &self.tx_signatures,
2212        };
2213        txn.serialize(serializer)
2214    }
2215}
2216
2217impl<'de> Deserialize<'de> for SenderSignedTransaction {
2218    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2219    where
2220        D: serde::Deserializer<'de>,
2221    {
2222        #[derive(Deserialize)]
2223        #[serde(rename = "SenderSignedTransaction")]
2224        struct SignedTxn {
2225            intent_message: IntentMessage<TransactionData>,
2226            tx_signatures: Vec<GenericSignature>,
2227        }
2228
2229        let SignedTxn {
2230            intent_message,
2231            tx_signatures,
2232        } = Deserialize::deserialize(deserializer)?;
2233
2234        if intent_message.intent != Intent::iota_transaction() {
2235            return Err(serde::de::Error::custom("invalid Intent for Transaction"));
2236        }
2237
2238        Ok(Self {
2239            intent_message,
2240            tx_signatures,
2241        })
2242    }
2243}
2244
2245impl SenderSignedTransaction {
2246    pub(crate) fn get_signer_sig_mapping(
2247        &self,
2248    ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2249        let mut mapping = BTreeMap::new();
2250        for sig in &self.tx_signatures {
2251            let address = sig.try_into()?;
2252            mapping.insert(address, sig);
2253        }
2254        Ok(mapping)
2255    }
2256
2257    pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2258        &self.intent_message
2259    }
2260}
2261
2262impl SenderSignedData {
2263    pub fn new(tx_data: TransactionData, tx_signatures: Vec<GenericSignature>) -> Self {
2264        Self(SizeOneVec::new(SenderSignedTransaction {
2265            intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2266            tx_signatures,
2267        }))
2268    }
2269
2270    pub fn new_from_sender_signature(tx_data: TransactionData, tx_signature: Signature) -> Self {
2271        Self(SizeOneVec::new(SenderSignedTransaction {
2272            intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2273            tx_signatures: vec![tx_signature.into()],
2274        }))
2275    }
2276
2277    pub fn inner(&self) -> &SenderSignedTransaction {
2278        self.0.element()
2279    }
2280
2281    pub fn into_inner(self) -> SenderSignedTransaction {
2282        self.0.into_inner()
2283    }
2284
2285    pub fn inner_mut(&mut self) -> &mut SenderSignedTransaction {
2286        self.0.element_mut()
2287    }
2288
2289    // This function does not check validity of the signature
2290    // or perform any de-dup checks.
2291    pub fn add_signature(&mut self, new_signature: Signature) {
2292        self.inner_mut().tx_signatures.push(new_signature.into());
2293    }
2294
2295    pub(crate) fn get_signer_sig_mapping(
2296        &self,
2297    ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2298        self.inner().get_signer_sig_mapping()
2299    }
2300
2301    pub fn transaction_data(&self) -> &TransactionData {
2302        &self.intent_message().value
2303    }
2304
2305    pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2306        self.inner().intent_message()
2307    }
2308
2309    pub fn tx_signatures(&self) -> &[GenericSignature] {
2310        &self.inner().tx_signatures
2311    }
2312
2313    pub fn has_zklogin_sig(&self) -> bool {
2314        self.tx_signatures().iter().any(|sig| sig.is_zklogin())
2315    }
2316
2317    pub fn has_upgraded_multisig(&self) -> bool {
2318        self.tx_signatures()
2319            .iter()
2320            .any(|sig| sig.is_upgraded_multisig())
2321    }
2322
2323    #[cfg(test)]
2324    pub fn intent_message_mut_for_testing(&mut self) -> &mut IntentMessage<TransactionData> {
2325        &mut self.inner_mut().intent_message
2326    }
2327
2328    // used cross-crate, so cannot be #[cfg(test)]
2329    pub fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<GenericSignature> {
2330        &mut self.inner_mut().tx_signatures
2331    }
2332
2333    pub fn full_message_digest(&self) -> SenderSignedDataDigest {
2334        let mut digest = DefaultHash::default();
2335        bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2336        let hash = digest.finalize();
2337        SenderSignedDataDigest::new(hash.into())
2338    }
2339
2340    pub fn serialized_size(&self) -> IotaResult<usize> {
2341        bcs::serialized_size(self).map_err(|e| IotaError::TransactionSerialization {
2342            error: e.to_string(),
2343        })
2344    }
2345
2346    fn check_user_signature_protocol_compatibility(&self, config: &ProtocolConfig) -> IotaResult {
2347        for sig in &self.inner().tx_signatures {
2348            match sig {
2349                GenericSignature::ZkLoginAuthenticator(_) => {
2350                    if !config.zklogin_auth() {
2351                        return Err(IotaError::UserInput {
2352                            error: UserInputError::Unsupported(
2353                                "zklogin is not enabled on this network".to_string(),
2354                            ),
2355                        });
2356                    }
2357                }
2358                GenericSignature::PasskeyAuthenticator(_) => {
2359                    if !config.passkey_auth() {
2360                        return Err(IotaError::UserInput {
2361                            error: UserInputError::Unsupported(
2362                                "passkey is not enabled on this network".to_string(),
2363                            ),
2364                        });
2365                    }
2366                }
2367                GenericSignature::Signature(_) | GenericSignature::MultiSig(_) => (),
2368            }
2369        }
2370
2371        Ok(())
2372    }
2373
2374    /// Validate untrusted user transaction, including its size, input count,
2375    /// command count, etc.
2376    /// Returns the certificate serialised bytes size.
2377    pub fn validity_check(
2378        &self,
2379        config: &ProtocolConfig,
2380        epoch: EpochId,
2381    ) -> Result<usize, IotaError> {
2382        // Check that the features used by the user signatures are enabled on the
2383        // network.
2384        self.check_user_signature_protocol_compatibility(config)?;
2385
2386        // CRITICAL!!
2387        // Users cannot send system transactions.
2388        let tx_data = &self.transaction_data();
2389        fp_ensure!(
2390            !tx_data.is_system_tx(),
2391            IotaError::UserInput {
2392                error: UserInputError::Unsupported(
2393                    "SenderSignedData must not contain system transaction".to_string()
2394                )
2395            }
2396        );
2397
2398        // Checks to see if the transaction has expired
2399        if match &tx_data.expiration() {
2400            TransactionExpiration::None => false,
2401            TransactionExpiration::Epoch(exp_poch) => *exp_poch < epoch,
2402        } {
2403            return Err(IotaError::TransactionExpired);
2404        }
2405
2406        // Enforce overall transaction size limit.
2407        let tx_size = self.serialized_size()?;
2408        let max_tx_size_bytes = config.max_tx_size_bytes();
2409        fp_ensure!(
2410            tx_size as u64 <= max_tx_size_bytes,
2411            IotaError::UserInput {
2412                error: UserInputError::SizeLimitExceeded {
2413                    limit: format!(
2414                        "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
2415                    ),
2416                    value: tx_size.to_string(),
2417                }
2418            }
2419        );
2420
2421        tx_data
2422            .validity_check(config)
2423            .map_err(Into::<IotaError>::into)?;
2424
2425        Ok(tx_size)
2426    }
2427}
2428
2429impl Message for SenderSignedData {
2430    type DigestType = TransactionDigest;
2431    const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2432
2433    /// Computes the tx digest that encodes the Rust type prefix from Signable
2434    /// trait.
2435    fn digest(&self) -> Self::DigestType {
2436        self.intent_message().value.digest()
2437    }
2438}
2439
2440impl<S> Envelope<SenderSignedData, S> {
2441    pub fn sender_address(&self) -> IotaAddress {
2442        self.data().intent_message().value.sender()
2443    }
2444
2445    pub fn gas(&self) -> &[ObjectRef] {
2446        self.data().intent_message().value.gas()
2447    }
2448
2449    pub fn contains_shared_object(&self) -> bool {
2450        self.shared_input_objects().next().is_some()
2451    }
2452
2453    pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
2454        self.data()
2455            .inner()
2456            .intent_message
2457            .value
2458            .shared_input_objects()
2459            .into_iter()
2460    }
2461
2462    // Returns the primary key for this transaction.
2463    pub fn key(&self) -> TransactionKey {
2464        match &self.data().intent_message().value.kind() {
2465            TransactionKind::RandomnessStateUpdate(rsu) => {
2466                TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2467            }
2468            _ => TransactionKey::Digest(*self.digest()),
2469        }
2470    }
2471
2472    // Returns non-Digest keys that could be used to refer to this transaction.
2473    //
2474    // At the moment this returns a single Option for efficiency, but if more key
2475    // types are added, the return type could change to Vec<TransactionKey>.
2476    pub fn non_digest_key(&self) -> Option<TransactionKey> {
2477        match &self.data().intent_message().value.kind() {
2478            TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2479                rsu.epoch,
2480                rsu.randomness_round,
2481            )),
2482            _ => None,
2483        }
2484    }
2485
2486    pub fn is_system_tx(&self) -> bool {
2487        self.data().intent_message().value.is_system_tx()
2488    }
2489
2490    pub fn is_sponsored_tx(&self) -> bool {
2491        self.data().intent_message().value.is_sponsored_tx()
2492    }
2493}
2494
2495impl Transaction {
2496    pub fn from_data_and_signer(
2497        data: TransactionData,
2498        signers: Vec<&dyn Signer<Signature>>,
2499    ) -> Self {
2500        let signatures = {
2501            let intent_msg = IntentMessage::new(Intent::iota_transaction(), &data);
2502            signers
2503                .into_iter()
2504                .map(|s| Signature::new_secure(&intent_msg, s))
2505                .collect()
2506        };
2507        Self::from_data(data, signatures)
2508    }
2509
2510    // TODO: Rename this function and above to make it clearer.
2511    pub fn from_data(data: TransactionData, signatures: Vec<Signature>) -> Self {
2512        Self::from_generic_sig_data(data, signatures.into_iter().map(|s| s.into()).collect())
2513    }
2514
2515    pub fn signature_from_signer(
2516        data: TransactionData,
2517        intent: Intent,
2518        signer: &dyn Signer<Signature>,
2519    ) -> Signature {
2520        let intent_msg = IntentMessage::new(intent, data);
2521        Signature::new_secure(&intent_msg, signer)
2522    }
2523
2524    pub fn from_generic_sig_data(data: TransactionData, signatures: Vec<GenericSignature>) -> Self {
2525        Self::new(SenderSignedData::new(data, signatures))
2526    }
2527
2528    /// Returns the Base64 encoded tx_bytes
2529    /// and a list of Base64 encoded [enum GenericSignature].
2530    pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2531        (
2532            Base64::from_bytes(&bcs::to_bytes(&self.data().intent_message().value).unwrap()),
2533            self.data()
2534                .inner()
2535                .tx_signatures
2536                .iter()
2537                .map(|s| Base64::from_bytes(s.as_ref()))
2538                .collect(),
2539        )
2540    }
2541}
2542
2543impl VerifiedTransaction {
2544    pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2545        GenesisTransaction { objects, events }
2546            .pipe(TransactionKind::Genesis)
2547            .pipe(Self::new_system_transaction)
2548    }
2549
2550    pub fn new_consensus_commit_prologue_v1(
2551        epoch: u64,
2552        round: u64,
2553        commit_timestamp_ms: CheckpointTimestamp,
2554        consensus_commit_digest: ConsensusCommitDigest,
2555        cancelled_txn_version_assignment: Vec<(TransactionDigest, Vec<(ObjectID, SequenceNumber)>)>,
2556    ) -> Self {
2557        ConsensusCommitPrologueV1 {
2558            epoch,
2559            round,
2560            // sub_dag_index is reserved for when we have multi commits per round.
2561            sub_dag_index: None,
2562            commit_timestamp_ms,
2563            consensus_commit_digest,
2564            consensus_determined_version_assignments:
2565                ConsensusDeterminedVersionAssignments::CancelledTransactions(
2566                    cancelled_txn_version_assignment,
2567                ),
2568        }
2569        .pipe(TransactionKind::ConsensusCommitPrologueV1)
2570        .pipe(Self::new_system_transaction)
2571    }
2572
2573    pub fn new_authenticator_state_update(
2574        epoch: u64,
2575        round: u64,
2576        new_active_jwks: Vec<ActiveJwk>,
2577        authenticator_obj_initial_shared_version: SequenceNumber,
2578    ) -> Self {
2579        AuthenticatorStateUpdateV1 {
2580            epoch,
2581            round,
2582            new_active_jwks,
2583            authenticator_obj_initial_shared_version,
2584        }
2585        .pipe(TransactionKind::AuthenticatorStateUpdateV1)
2586        .pipe(Self::new_system_transaction)
2587    }
2588
2589    pub fn new_randomness_state_update(
2590        epoch: u64,
2591        randomness_round: RandomnessRound,
2592        random_bytes: Vec<u8>,
2593        randomness_obj_initial_shared_version: SequenceNumber,
2594    ) -> Self {
2595        RandomnessStateUpdate {
2596            epoch,
2597            randomness_round,
2598            random_bytes,
2599            randomness_obj_initial_shared_version,
2600        }
2601        .pipe(TransactionKind::RandomnessStateUpdate)
2602        .pipe(Self::new_system_transaction)
2603    }
2604
2605    pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
2606        TransactionKind::EndOfEpochTransaction(txns).pipe(Self::new_system_transaction)
2607    }
2608
2609    fn new_system_transaction(system_transaction: TransactionKind) -> Self {
2610        system_transaction
2611            .pipe(TransactionData::new_system_transaction)
2612            .pipe(|data| {
2613                SenderSignedData::new_from_sender_signature(
2614                    data,
2615                    Ed25519IotaSignature::from_bytes(&[0; Ed25519IotaSignature::LENGTH])
2616                        .unwrap()
2617                        .into(),
2618                )
2619            })
2620            .pipe(Transaction::new)
2621            .pipe(Self::new_from_verified)
2622    }
2623}
2624
2625impl VerifiedSignedTransaction {
2626    /// Use signing key to create a signed object.
2627    pub fn new(
2628        epoch: EpochId,
2629        transaction: VerifiedTransaction,
2630        authority: AuthorityName,
2631        secret: &dyn Signer<AuthoritySignature>,
2632    ) -> Self {
2633        Self::new_from_verified(SignedTransaction::new(
2634            epoch,
2635            transaction.into_inner().into_data(),
2636            secret,
2637            authority,
2638        ))
2639    }
2640}
2641
2642/// A transaction that is signed by a sender but not yet by an authority.
2643pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
2644pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
2645pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
2646
2647/// A transaction that is signed by a sender and also by an authority.
2648pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
2649pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
2650
2651impl Transaction {
2652    pub fn verify_signature_for_testing(
2653        &self,
2654        current_epoch: EpochId,
2655        verify_params: &VerifyParams,
2656    ) -> IotaResult {
2657        verify_sender_signed_data_message_signatures(
2658            self.data(),
2659            current_epoch,
2660            verify_params,
2661            Arc::new(VerifiedDigestCache::new_empty()),
2662        )
2663    }
2664
2665    pub fn try_into_verified_for_testing(
2666        self,
2667        current_epoch: EpochId,
2668        verify_params: &VerifyParams,
2669    ) -> IotaResult<VerifiedTransaction> {
2670        self.verify_signature_for_testing(current_epoch, verify_params)?;
2671        Ok(VerifiedTransaction::new_from_verified(self))
2672    }
2673}
2674
2675impl SignedTransaction {
2676    pub fn verify_signatures_authenticated_for_testing(
2677        &self,
2678        committee: &Committee,
2679        verify_params: &VerifyParams,
2680    ) -> IotaResult {
2681        verify_sender_signed_data_message_signatures(
2682            self.data(),
2683            committee.epoch(),
2684            verify_params,
2685            Arc::new(VerifiedDigestCache::new_empty()),
2686        )?;
2687
2688        self.auth_sig().verify_secure(
2689            self.data(),
2690            Intent::iota_app(IntentScope::SenderSignedTransaction),
2691            committee,
2692        )
2693    }
2694
2695    pub fn try_into_verified_for_testing(
2696        self,
2697        committee: &Committee,
2698        verify_params: &VerifyParams,
2699    ) -> IotaResult<VerifiedSignedTransaction> {
2700        self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
2701        Ok(VerifiedSignedTransaction::new_from_verified(self))
2702    }
2703}
2704
2705pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2706
2707impl CertifiedTransaction {
2708    pub fn certificate_digest(&self) -> CertificateDigest {
2709        let mut digest = DefaultHash::default();
2710        bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2711        let hash = digest.finalize();
2712        CertificateDigest::new(hash.into())
2713    }
2714
2715    pub fn gas_price(&self) -> u64 {
2716        self.data().transaction_data().gas_price()
2717    }
2718
2719    // TODO: Eventually we should remove all calls to verify_signature
2720    // and make sure they all call verify to avoid repeated verifications.
2721    pub fn verify_signatures_authenticated(
2722        &self,
2723        committee: &Committee,
2724        verify_params: &VerifyParams,
2725        zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
2726    ) -> IotaResult {
2727        verify_sender_signed_data_message_signatures(
2728            self.data(),
2729            committee.epoch(),
2730            verify_params,
2731            zklogin_inputs_cache,
2732        )?;
2733        self.auth_sig().verify_secure(
2734            self.data(),
2735            Intent::iota_app(IntentScope::SenderSignedTransaction),
2736            committee,
2737        )
2738    }
2739
2740    pub fn try_into_verified_for_testing(
2741        self,
2742        committee: &Committee,
2743        verify_params: &VerifyParams,
2744    ) -> IotaResult<VerifiedCertificate> {
2745        self.verify_signatures_authenticated(
2746            committee,
2747            verify_params,
2748            Arc::new(VerifiedDigestCache::new_empty()),
2749        )?;
2750        Ok(VerifiedCertificate::new_from_verified(self))
2751    }
2752
2753    pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
2754        self.auth_sig().verify_secure(
2755            self.data(),
2756            Intent::iota_app(IntentScope::SenderSignedTransaction),
2757            committee,
2758        )
2759    }
2760}
2761
2762pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2763pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2764
2765#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
2766pub enum InputObjectKind {
2767    // A Move package, must be immutable.
2768    MovePackage(ObjectID),
2769    // A Move object, either immutable, or owned mutable.
2770    ImmOrOwnedMoveObject(ObjectRef),
2771    // A Move object that's shared and mutable.
2772    SharedMoveObject {
2773        id: ObjectID,
2774        initial_shared_version: SequenceNumber,
2775        mutable: bool,
2776    },
2777}
2778
2779impl InputObjectKind {
2780    pub fn object_id(&self) -> ObjectID {
2781        match self {
2782            Self::MovePackage(id) => *id,
2783            Self::ImmOrOwnedMoveObject((id, _, _)) => *id,
2784            Self::SharedMoveObject { id, .. } => *id,
2785        }
2786    }
2787
2788    pub fn version(&self) -> Option<SequenceNumber> {
2789        match self {
2790            Self::MovePackage(..) => None,
2791            Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version),
2792            Self::SharedMoveObject { .. } => None,
2793        }
2794    }
2795
2796    pub fn object_not_found_error(&self) -> UserInputError {
2797        match *self {
2798            Self::MovePackage(package_id) => {
2799                UserInputError::DependentPackageNotFound { package_id }
2800            }
2801            Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound {
2802                object_id,
2803                version: Some(version),
2804            },
2805            Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
2806                object_id: id,
2807                version: None,
2808            },
2809        }
2810    }
2811
2812    pub fn is_shared_object(&self) -> bool {
2813        matches!(self, Self::SharedMoveObject { .. })
2814    }
2815
2816    pub fn is_mutable(&self) -> bool {
2817        match self {
2818            Self::MovePackage(..) => false,
2819            Self::ImmOrOwnedMoveObject((_, _, _)) => true,
2820            Self::SharedMoveObject { mutable, .. } => *mutable,
2821        }
2822    }
2823}
2824
2825/// The result of reading an object for execution. Because shared objects may be
2826/// deleted, one possible result of reading a shared object is that
2827/// ObjectReadResultKind::Deleted is returned.
2828#[derive(Clone, Debug)]
2829pub struct ObjectReadResult {
2830    pub input_object_kind: InputObjectKind,
2831    pub object: ObjectReadResultKind,
2832}
2833
2834#[derive(Clone)]
2835pub enum ObjectReadResultKind {
2836    Object(Object),
2837    // The version of the object that the transaction intended to read, and the digest of the tx
2838    // that deleted it.
2839    DeletedSharedObject(SequenceNumber, TransactionDigest),
2840    // A shared object in a cancelled transaction. The sequence number embeds cancellation reason.
2841    CancelledTransactionSharedObject(SequenceNumber),
2842}
2843
2844impl std::fmt::Debug for ObjectReadResultKind {
2845    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2846        match self {
2847            ObjectReadResultKind::Object(obj) => {
2848                write!(f, "Object({:?})", obj.compute_object_reference())
2849            }
2850            ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2851                write!(f, "DeletedSharedObject({seq}, {digest:?})")
2852            }
2853            ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2854                write!(f, "CancelledTransactionSharedObject({seq})")
2855            }
2856        }
2857    }
2858}
2859
2860impl From<Object> for ObjectReadResultKind {
2861    fn from(object: Object) -> Self {
2862        Self::Object(object)
2863    }
2864}
2865
2866impl ObjectReadResult {
2867    pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
2868        if let (
2869            InputObjectKind::ImmOrOwnedMoveObject(_),
2870            ObjectReadResultKind::DeletedSharedObject(_, _),
2871        ) = (&input_object_kind, &object)
2872        {
2873            panic!("only shared objects can be DeletedSharedObject");
2874        }
2875
2876        if let (
2877            InputObjectKind::ImmOrOwnedMoveObject(_),
2878            ObjectReadResultKind::CancelledTransactionSharedObject(_),
2879        ) = (&input_object_kind, &object)
2880        {
2881            panic!("only shared objects can be CancelledTransactionSharedObject");
2882        }
2883
2884        Self {
2885            input_object_kind,
2886            object,
2887        }
2888    }
2889
2890    pub fn id(&self) -> ObjectID {
2891        self.input_object_kind.object_id()
2892    }
2893
2894    pub fn as_object(&self) -> Option<&Object> {
2895        match &self.object {
2896            ObjectReadResultKind::Object(object) => Some(object),
2897            ObjectReadResultKind::DeletedSharedObject(_, _) => None,
2898            ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2899        }
2900    }
2901
2902    pub fn new_from_gas_object(gas: &Object) -> Self {
2903        let objref = gas.compute_object_reference();
2904        Self {
2905            input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
2906            object: ObjectReadResultKind::Object(gas.clone()),
2907        }
2908    }
2909
2910    pub fn is_mutable(&self) -> bool {
2911        match (&self.input_object_kind, &self.object) {
2912            (InputObjectKind::MovePackage(_), _) => false,
2913            (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
2914                !object.is_immutable()
2915            }
2916            (
2917                InputObjectKind::ImmOrOwnedMoveObject(_),
2918                ObjectReadResultKind::DeletedSharedObject(_, _),
2919            ) => unreachable!(),
2920            (
2921                InputObjectKind::ImmOrOwnedMoveObject(_),
2922                ObjectReadResultKind::CancelledTransactionSharedObject(_),
2923            ) => unreachable!(),
2924            (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
2925        }
2926    }
2927
2928    pub fn is_shared_object(&self) -> bool {
2929        self.input_object_kind.is_shared_object()
2930    }
2931
2932    pub fn is_deleted_shared_object(&self) -> bool {
2933        self.deletion_info().is_some()
2934    }
2935
2936    pub fn deletion_info(&self) -> Option<(SequenceNumber, TransactionDigest)> {
2937        match &self.object {
2938            ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
2939            _ => None,
2940        }
2941    }
2942
2943    /// Return the object ref iff the object is an owned object (i.e. not
2944    /// shared, not immutable).
2945    pub fn get_owned_objref(&self) -> Option<ObjectRef> {
2946        match (&self.input_object_kind, &self.object) {
2947            (InputObjectKind::MovePackage(_), _) => None,
2948            (
2949                InputObjectKind::ImmOrOwnedMoveObject(objref),
2950                ObjectReadResultKind::Object(object),
2951            ) => {
2952                if object.is_immutable() {
2953                    None
2954                } else {
2955                    Some(*objref)
2956                }
2957            }
2958            (
2959                InputObjectKind::ImmOrOwnedMoveObject(_),
2960                ObjectReadResultKind::DeletedSharedObject(_, _),
2961            ) => unreachable!(),
2962            (
2963                InputObjectKind::ImmOrOwnedMoveObject(_),
2964                ObjectReadResultKind::CancelledTransactionSharedObject(_),
2965            ) => unreachable!(),
2966            (InputObjectKind::SharedMoveObject { .. }, _) => None,
2967        }
2968    }
2969
2970    pub fn is_owned(&self) -> bool {
2971        self.get_owned_objref().is_some()
2972    }
2973
2974    pub fn to_shared_input(&self) -> Option<SharedInput> {
2975        match self.input_object_kind {
2976            InputObjectKind::MovePackage(_) => None,
2977            InputObjectKind::ImmOrOwnedMoveObject(_) => None,
2978            InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
2979                ObjectReadResultKind::Object(obj) => {
2980                    SharedInput::Existing(obj.compute_object_reference())
2981                }
2982                ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2983                    SharedInput::Deleted((id, *seq, mutable, *digest))
2984                }
2985                ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2986                    SharedInput::Cancelled((id, *seq))
2987                }
2988            }),
2989        }
2990    }
2991
2992    pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
2993        match &self.object {
2994            ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
2995            ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
2996            ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2997        }
2998    }
2999}
3000
3001#[derive(Clone)]
3002pub struct InputObjects {
3003    objects: Vec<ObjectReadResult>,
3004}
3005
3006impl std::fmt::Debug for InputObjects {
3007    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3008        f.debug_list().entries(self.objects.iter()).finish()
3009    }
3010}
3011
3012// An InputObjects new-type that has been verified by iota-transaction-checks,
3013// and can be safely passed to execution.
3014pub struct CheckedInputObjects(InputObjects);
3015
3016// DO NOT CALL outside of iota-transaction-checks, genesis, or replay.
3017//
3018// CheckedInputObjects should really be defined in iota-transaction-checks so
3019// that we can make public construction impossible. But we can't do that because
3020// it would result in circular dependencies.
3021impl CheckedInputObjects {
3022    // Only called by iota-transaction-checks.
3023    pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3024        Self(inputs)
3025    }
3026
3027    // Only called when building the genesis transaction
3028    pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3029        Self(InputObjects::new(input_objects))
3030    }
3031
3032    // Only called from the replay tool.
3033    pub fn new_for_replay(input_objects: InputObjects) -> Self {
3034        Self(input_objects)
3035    }
3036
3037    pub fn inner(&self) -> &InputObjects {
3038        &self.0
3039    }
3040
3041    pub fn into_inner(self) -> InputObjects {
3042        self.0
3043    }
3044}
3045
3046impl From<Vec<ObjectReadResult>> for InputObjects {
3047    fn from(objects: Vec<ObjectReadResult>) -> Self {
3048        Self::new(objects)
3049    }
3050}
3051
3052impl InputObjects {
3053    pub fn new(objects: Vec<ObjectReadResult>) -> Self {
3054        Self { objects }
3055    }
3056
3057    pub fn len(&self) -> usize {
3058        self.objects.len()
3059    }
3060
3061    pub fn is_empty(&self) -> bool {
3062        self.objects.is_empty()
3063    }
3064
3065    pub fn contains_deleted_objects(&self) -> bool {
3066        self.objects
3067            .iter()
3068            .any(|obj| obj.is_deleted_shared_object())
3069    }
3070
3071    // Returns IDs of objects responsible for a transaction being cancelled, and the
3072    // corresponding reason for cancellation.
3073    pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectID>, SequenceNumber)> {
3074        let mut contains_cancelled = false;
3075        let mut cancel_reason = None;
3076        let mut cancelled_objects = Vec::new();
3077        for obj in &self.objects {
3078            if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
3079                contains_cancelled = true;
3080                if version.is_congested() || version == SequenceNumber::RANDOMNESS_UNAVAILABLE {
3081                    // Verify we don't have multiple cancellation reasons.
3082                    assert!(cancel_reason.is_none() || cancel_reason == Some(version));
3083                    cancel_reason = Some(version);
3084                    cancelled_objects.push(obj.id());
3085                }
3086            }
3087        }
3088
3089        if !cancelled_objects.is_empty() {
3090            Some((
3091                cancelled_objects,
3092                cancel_reason
3093                    .expect("there should be a cancel reason if there are cancelled objects"),
3094            ))
3095        } else {
3096            assert!(!contains_cancelled);
3097            None
3098        }
3099    }
3100
3101    pub fn filter_owned_objects(&self) -> Vec<ObjectRef> {
3102        let owned_objects: Vec<_> = self
3103            .objects
3104            .iter()
3105            .filter_map(|obj| obj.get_owned_objref())
3106            .collect();
3107
3108        trace!(
3109            num_mutable_objects = owned_objects.len(),
3110            "Checked locks and found mutable objects"
3111        );
3112
3113        owned_objects
3114    }
3115
3116    pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
3117        self.objects
3118            .iter()
3119            .filter(|obj| obj.is_shared_object())
3120            .map(|obj| {
3121                obj.to_shared_input()
3122                    .expect("already filtered for shared objects")
3123            })
3124            .collect()
3125    }
3126
3127    pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3128        self.objects
3129            .iter()
3130            .filter_map(|obj| obj.get_previous_transaction())
3131            .collect()
3132    }
3133
3134    pub fn mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
3135        self.objects
3136            .iter()
3137            .filter_map(
3138                |ObjectReadResult {
3139                     input_object_kind,
3140                     object,
3141                 }| match (input_object_kind, object) {
3142                    (InputObjectKind::MovePackage(_), _) => None,
3143                    (
3144                        InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3145                        ObjectReadResultKind::Object(object),
3146                    ) => {
3147                        if object.is_immutable() {
3148                            None
3149                        } else {
3150                            Some((object_ref.0, ((object_ref.1, object_ref.2), object.owner)))
3151                        }
3152                    }
3153                    (
3154                        InputObjectKind::ImmOrOwnedMoveObject(_),
3155                        ObjectReadResultKind::DeletedSharedObject(_, _),
3156                    ) => {
3157                        unreachable!()
3158                    }
3159                    (
3160                        InputObjectKind::SharedMoveObject { .. },
3161                        ObjectReadResultKind::DeletedSharedObject(_, _),
3162                    ) => None,
3163                    (
3164                        InputObjectKind::SharedMoveObject { mutable, .. },
3165                        ObjectReadResultKind::Object(object),
3166                    ) => {
3167                        if *mutable {
3168                            let oref = object.compute_object_reference();
3169                            Some((oref.0, ((oref.1, oref.2), object.owner)))
3170                        } else {
3171                            None
3172                        }
3173                    }
3174                    (
3175                        InputObjectKind::ImmOrOwnedMoveObject(_),
3176                        ObjectReadResultKind::CancelledTransactionSharedObject(_),
3177                    ) => {
3178                        unreachable!()
3179                    }
3180                    (
3181                        InputObjectKind::SharedMoveObject { .. },
3182                        ObjectReadResultKind::CancelledTransactionSharedObject(_),
3183                    ) => None,
3184                },
3185            )
3186            .collect()
3187    }
3188
3189    /// The version to set on objects created by the computation that `self` is
3190    /// input to. Guaranteed to be strictly greater than the versions of all
3191    /// input objects and objects received in the transaction.
3192    pub fn lamport_timestamp(&self, receiving_objects: &[ObjectRef]) -> SequenceNumber {
3193        let input_versions = self
3194            .objects
3195            .iter()
3196            .filter_map(|object| match &object.object {
3197                ObjectReadResultKind::Object(object) => {
3198                    object.data.try_as_move().map(MoveObject::version)
3199                }
3200                ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3201                ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3202            })
3203            .chain(receiving_objects.iter().map(|object_ref| object_ref.1));
3204
3205        SequenceNumber::lamport_increment(input_versions)
3206    }
3207
3208    pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3209        self.objects.iter().map(
3210            |ObjectReadResult {
3211                 input_object_kind, ..
3212             }| input_object_kind,
3213        )
3214    }
3215
3216    pub fn into_object_map(self) -> BTreeMap<ObjectID, Object> {
3217        self.objects
3218            .into_iter()
3219            .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3220            .collect()
3221    }
3222
3223    pub fn push(&mut self, object: ObjectReadResult) {
3224        self.objects.push(object);
3225    }
3226
3227    pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3228        self.objects.iter()
3229    }
3230
3231    pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3232        self.objects.iter().filter_map(|o| o.as_object())
3233    }
3234}
3235
3236// Result of attempting to read a receiving object (currently only at signing
3237// time). Because an object may have been previously received and deleted, the
3238// result may be ReceivingObjectReadResultKind::PreviouslyReceivedObject.
3239#[derive(Clone, Debug)]
3240pub enum ReceivingObjectReadResultKind {
3241    Object(Object),
3242    // The object was received by some other transaction, and we were not able to read it
3243    PreviouslyReceivedObject,
3244}
3245
3246impl ReceivingObjectReadResultKind {
3247    pub fn as_object(&self) -> Option<&Object> {
3248        match &self {
3249            Self::Object(object) => Some(object),
3250            Self::PreviouslyReceivedObject => None,
3251        }
3252    }
3253}
3254
3255pub struct ReceivingObjectReadResult {
3256    pub object_ref: ObjectRef,
3257    pub object: ReceivingObjectReadResultKind,
3258}
3259
3260impl ReceivingObjectReadResult {
3261    pub fn new(object_ref: ObjectRef, object: ReceivingObjectReadResultKind) -> Self {
3262        Self { object_ref, object }
3263    }
3264
3265    pub fn is_previously_received(&self) -> bool {
3266        matches!(
3267            self.object,
3268            ReceivingObjectReadResultKind::PreviouslyReceivedObject
3269        )
3270    }
3271}
3272
3273impl From<Object> for ReceivingObjectReadResultKind {
3274    fn from(object: Object) -> Self {
3275        Self::Object(object)
3276    }
3277}
3278
3279pub struct ReceivingObjects {
3280    pub objects: Vec<ReceivingObjectReadResult>,
3281}
3282
3283impl ReceivingObjects {
3284    pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3285        self.objects.iter()
3286    }
3287
3288    pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3289        self.objects.iter().filter_map(|o| o.object.as_object())
3290    }
3291}
3292
3293impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3294    fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3295        Self { objects }
3296    }
3297}
3298
3299impl Display for CertifiedTransaction {
3300    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3301        let mut writer = String::new();
3302        writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3303        writeln!(
3304            writer,
3305            "Signed Authorities Bitmap : {:?}",
3306            self.auth_sig().signers_map
3307        )?;
3308        write!(writer, "{}", &self.data().intent_message().value.kind())?;
3309        write!(f, "{writer}")
3310    }
3311}
3312
3313/// TransactionKey uniquely identifies a transaction across all epochs.
3314/// Note that a single transaction may have multiple keys, for example a
3315/// RandomnessStateUpdate could be identified by both `Digest` and
3316/// `RandomnessRound`.
3317#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3318pub enum TransactionKey {
3319    Digest(TransactionDigest),
3320    RandomnessRound(EpochId, RandomnessRound),
3321}
3322
3323impl TransactionKey {
3324    pub fn unwrap_digest(&self) -> &TransactionDigest {
3325        match self {
3326            TransactionKey::Digest(d) => d,
3327            _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3328        }
3329    }
3330}