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