iota_types/
transaction.rs

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