Skip to main content

iota_types/
transaction.rs

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