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