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