Skip to main content

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