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 Address, Argument, CancelledTransaction, CertificateDigest, Command, ConsensusCommitDigest,
21 ConsensusCommitPrologueV1, ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind,
22 Event, GasPayment, GenesisObject, GenesisTransaction, Identifier, Input, MakeMoveVector,
23 MergeCoins, MoveAuthenticator, MoveCall, MoveStruct, ObjectDigest, ObjectId, ObjectReference,
24 Owner, ProgrammableTransaction, Publish, RandomnessRound, RandomnessStateUpdate,
25 SharedObjectReference, SplitCoins, TransactionDigest, TransactionExpiration, TransactionKind,
26 TransferObjects, TypeTag, Upgrade, UserSignature, Version,
27 crypto::{Intent, IntentMessage, IntentScope},
28};
29pub use iota_sdk_types::{
30 SenderSignedTransaction as SenderSignedData, Transaction as TransactionData,
31 TransactionV1 as TransactionDataV1,
32};
33use itertools::Either;
34use nonempty::{NonEmpty, nonempty};
35use serde::{Deserialize, Serialize};
36use tap::Pipe;
37use tracing::{instrument, trace};
38
39use super::{base_types::*, error::*};
40use crate::{
41 IOTA_CLOCK_OBJECT_SHARED_VERSION, IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
42 committee::{Committee, EpochId},
43 crypto::{
44 AuthoritySignInfo, AuthoritySignInfoTrait, AuthoritySignature,
45 AuthorityStrongQuorumSignInfo, DefaultHash, EmptySignInfo, IotaKeyPair, IotaSignature,
46 Signature, Signer, zero_ed25519_signature,
47 },
48 execution::SharedInput,
49 message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
50 messages_checkpoint::CheckpointTimestamp,
51 move_authenticator::MoveAuthenticatorExt,
52 object::Object,
53 programmable_transaction_builder::ProgrammableTransactionBuilder,
54 signature::VerifyParams,
55 signature_verification::verify_sender_signed_data_message_signatures,
56};
57
58pub const TEST_ONLY_GAS_UNIT_FOR_TRANSFER: u64 = 10_000;
59pub const TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS: u64 = 50_000;
60pub const TEST_ONLY_GAS_UNIT_FOR_PUBLISH: u64 = 50_000;
61pub const TEST_ONLY_GAS_UNIT_FOR_STAKING: u64 = 50_000;
62pub const TEST_ONLY_GAS_UNIT_FOR_GENERIC: u64 = 50_000;
63pub const TEST_ONLY_GAS_UNIT_FOR_SPLIT_COIN: u64 = 10_000;
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(SharedObjectReference {
283 object_id,
284 initial_shared_version,
285 mutable,
286 }) => Some(InputObjectKind::SharedMoveObject {
287 id: *object_id,
288 initial_shared_version: *initial_shared_version,
289 mutable: *mutable,
290 }),
291 CallArg::Pure(_) | CallArg::Receiving(_) => None,
292 _ => unimplemented!("a new CallArg enum variant was added and needs to be handled"),
293 }
294 }
295
296 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
297 match self {
298 CallArg::Pure(bytes) => {
299 fp_ensure!(
300 bytes.len() < config.max_pure_argument_size() as usize,
301 UserInputError::SizeLimitExceeded {
302 limit: "maximum pure argument size".to_string(),
303 value: config.max_pure_argument_size().to_string()
304 }
305 );
306 }
307 CallArg::ImmutableOrOwned(_) | CallArg::Shared(_) | CallArg::Receiving(_) => {
308 }
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<ObjectReference>;
574 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
575 fn shared_input_objects(&self) -> impl Iterator<Item = SharedObjectReference>;
576 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)>;
577 fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>>;
578}
579
580impl ProgrammableTransactionExt for ProgrammableTransaction {
581 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
582 let ProgrammableTransaction { inputs, commands } = self;
583 let input_arg_objects = inputs
584 .iter()
585 .filter_map(|arg| arg.input_object_kind())
586 .collect::<Vec<_>>();
587 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<ObjectReference> {
604 let ProgrammableTransaction { inputs, .. } = self;
605 inputs
606 .iter()
607 .filter_map(|arg| arg.as_opt_receiving().copied())
608 .collect()
609 }
610
611 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
612 let ProgrammableTransaction { inputs, commands } = self;
613 fp_ensure!(
614 commands.len() < config.max_programmable_tx_commands() as usize,
615 UserInputError::SizeLimitExceeded {
616 limit: "maximum commands in a programmable transaction".to_string(),
617 value: config.max_programmable_tx_commands().to_string()
618 }
619 );
620 let total_inputs = self.input_objects()?.len() + self.receiving_objects().len();
621 fp_ensure!(
622 total_inputs <= config.max_input_objects() as usize,
623 UserInputError::SizeLimitExceeded {
624 limit: "maximum input + receiving objects in a transaction".to_string(),
625 value: config.max_input_objects().to_string()
626 }
627 );
628 for input in inputs {
629 input.validity_check(config)?
630 }
631 if let Some(max_publish_commands) = config.max_publish_or_upgrade_per_ptb_as_option() {
632 let publish_count = commands
633 .iter()
634 .filter(|c| c.is_publish() || c.is_upgrade())
635 .count() as u64;
636 fp_ensure!(
637 publish_count <= max_publish_commands,
638 UserInputError::MaxPublishCountExceeded {
639 max_publish_commands,
640 publish_count,
641 }
642 );
643 }
644 for command in commands {
645 command.validity_check(config)?;
646 }
647
648 if let Some(random_index) = inputs.iter().position(|obj| {
652 matches!(obj, CallArg::Shared(SharedObjectReference { object_id, .. }) if *object_id == ObjectId::RANDOMNESS_STATE)
653 }) {
654 let mut used_random_object = false;
655 let random_index = random_index.try_into().unwrap();
656 for command in commands {
657 if !used_random_object {
658 used_random_object = command.is_input_arg_used(random_index);
659 } else {
660 fp_ensure!(
661 command.is_transfer_objects() || command.is_merge_coins(),
662 UserInputError::PostRandomCommandRestrictions
663 );
664 }
665 }
666 }
667
668 Ok(())
669 }
670
671 fn shared_input_objects(&self) -> impl Iterator<Item = SharedObjectReference> {
672 self.inputs.iter().filter_map(|arg| match arg {
673 CallArg::Shared(shared) => Some(*shared),
674 CallArg::Pure(_) | CallArg::Receiving(_) | CallArg::ImmutableOrOwned(_) => None,
675 _ => unimplemented!("a new CallArg enum variant was added and needs to be handled"),
676 })
677 }
678
679 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
680 self.commands
681 .iter()
682 .filter_map(|command| match command {
683 Command::MoveCall(m) => Some((&m.package, m.module.as_str(), m.function.as_str())),
684 _ => None,
685 })
686 .collect()
687 }
688
689 fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>> {
690 self.commands
691 .iter()
692 .filter_map(|q| q.non_system_packages_to_be_published())
693 }
694}
695
696fn left_union_shared_input_objects(
700 this: &mut SharedObjectReference,
701 other: &SharedObjectReference,
702) -> UserInputResult<()> {
703 fp_ensure!(
704 this.object_id == other.object_id,
705 UserInputError::SharedObjectIdMismatch
706 );
707 fp_ensure!(
708 this.initial_shared_version == other.initial_shared_version,
709 UserInputError::SharedObjectStartingVersionMismatch
710 );
711
712 if !this.mutable && other.mutable {
713 this.mutable = other.mutable;
714 }
715
716 Ok(())
717}
718
719mod transaction_kind_ext {
720 pub trait Sealed {}
721 impl Sealed for super::TransactionKind {}
722}
723
724pub trait TransactionKindExt: Sized + transaction_kind_ext::Sealed {
725 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 = SharedObjectReference> + '_;
734 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)>;
737 fn receiving_objects(&self) -> Vec<ObjectReference>;
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 = SharedObjectReference> + '_ {
784 match &self {
785 Self::ConsensusCommitPrologueV1(_) => Either::Left(Either::Left(iter::once(
786 SharedObjectReference::new(ObjectId::CLOCK, IOTA_CLOCK_OBJECT_SHARED_VERSION, true),
787 ))),
788 #[allow(deprecated)]
789 Self::AuthenticatorStateUpdateV1Deprecated => {
790 Either::Right(Either::Right(iter::empty()))
794 }
795 Self::RandomnessStateUpdate(update) => {
796 Either::Left(Either::Left(iter::once(SharedObjectReference::new(
797 ObjectId::RANDOMNESS_STATE,
798 update.randomness_obj_initial_shared_version,
799 true,
800 ))))
801 }
802 Self::EndOfEpoch(txns) => Either::Left(Either::Right(
803 txns.iter().flat_map(|txn| txn.shared_input_objects()),
804 )),
805 Self::Programmable(pt) => Either::Right(Either::Left(pt.shared_input_objects())),
806 _ => Either::Right(Either::Right(iter::empty())),
807 }
808 }
809
810 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
811 match &self {
812 Self::Programmable(pt) => pt.move_calls(),
813 _ => vec![],
814 }
815 }
816
817 fn receiving_objects(&self) -> Vec<ObjectReference> {
818 match &self {
819 #[allow(deprecated)]
820 TransactionKind::Genesis(_)
821 | TransactionKind::ConsensusCommitPrologueV1(_)
822 | TransactionKind::AuthenticatorStateUpdateV1Deprecated
823 | TransactionKind::RandomnessStateUpdate(_)
824 | TransactionKind::EndOfEpoch(_) => vec![],
825 TransactionKind::Programmable(pt) => pt.receiving_objects(),
826 _ => unimplemented!(
827 "a new TransactionKind enum variant was added and needs to be handled"
828 ),
829 }
830 }
831
832 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
833 let input_objects = match &self {
834 Self::Genesis(_) => {
835 vec![]
836 }
837 Self::ConsensusCommitPrologueV1(_) => {
838 vec![InputObjectKind::SharedMoveObject {
839 id: ObjectId::CLOCK,
840 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
841 mutable: true,
842 }]
843 }
844 #[allow(deprecated)]
845 Self::AuthenticatorStateUpdateV1Deprecated => {
846 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) -> Address;
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<Address>;
973
974 fn gas_data(&self) -> &GasPayment;
977
978 fn gas_owner(&self) -> Address;
980
981 fn gas(&self) -> &[ObjectReference];
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<SharedObjectReference>;
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<ObjectReference>;
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 Address;
1036
1037 fn gas_data_mut(&mut self) -> &mut GasPayment;
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: Address,
1053 gas_payment: ObjectReference,
1054 gas_budget: u64,
1055 gas_price: u64,
1056 ) -> TransactionData;
1057
1058 fn new_with_gas_coins(
1061 kind: TransactionKind,
1062 sender: Address,
1063 gas_payment: Vec<ObjectReference>,
1064 gas_budget: u64,
1065 gas_price: u64,
1066 ) -> TransactionData;
1067
1068 fn new_with_gas_coins_allow_sponsor(
1072 kind: TransactionKind,
1073 sender: Address,
1074 gas_payment: Vec<ObjectReference>,
1075 gas_budget: u64,
1076 gas_price: u64,
1077 gas_sponsor: Address,
1078 ) -> TransactionData;
1079
1080 fn new_with_gas_data(
1082 kind: TransactionKind,
1083 sender: Address,
1084 gas_data: GasPayment,
1085 ) -> TransactionData;
1086
1087 fn new_move_call(
1090 sender: Address,
1091 package: ObjectId,
1092 module: Identifier,
1093 function: Identifier,
1094 type_arguments: Vec<TypeTag>,
1095 gas_payment: ObjectReference,
1096 arguments: Vec<CallArg>,
1097 gas_budget: u64,
1098 gas_price: u64,
1099 ) -> anyhow::Result<TransactionData>;
1100
1101 fn new_move_call_with_gas_coins(
1104 sender: Address,
1105 package: ObjectId,
1106 module: Identifier,
1107 function: Identifier,
1108 type_arguments: Vec<TypeTag>,
1109 gas_payment: Vec<ObjectReference>,
1110 arguments: Vec<CallArg>,
1111 gas_budget: u64,
1112 gas_price: u64,
1113 ) -> anyhow::Result<TransactionData>;
1114
1115 fn new_transfer(
1117 recipient: Address,
1118 object_ref: ObjectReference,
1119 sender: Address,
1120 gas_payment: ObjectReference,
1121 gas_budget: u64,
1122 gas_price: u64,
1123 ) -> TransactionData;
1124
1125 fn new_transfer_iota(
1129 recipient: Address,
1130 sender: Address,
1131 amount: Option<u64>,
1132 gas_payment: ObjectReference,
1133 gas_budget: u64,
1134 gas_price: u64,
1135 ) -> TransactionData;
1136
1137 fn new_transfer_iota_allow_sponsor(
1141 recipient: Address,
1142 sender: Address,
1143 amount: Option<u64>,
1144 gas_payment: ObjectReference,
1145 gas_budget: u64,
1146 gas_price: u64,
1147 gas_sponsor: Address,
1148 ) -> TransactionData;
1149
1150 fn new_pay(
1154 sender: Address,
1155 coins: Vec<ObjectReference>,
1156 recipients: Vec<Address>,
1157 amounts: Vec<u64>,
1158 gas_payment: ObjectReference,
1159 gas_budget: u64,
1160 gas_price: u64,
1161 ) -> anyhow::Result<TransactionData>;
1162
1163 fn new_pay_iota(
1167 sender: Address,
1168 coins: Vec<ObjectReference>,
1169 recipients: Vec<Address>,
1170 amounts: Vec<u64>,
1171 gas_payment: ObjectReference,
1172 gas_budget: u64,
1173 gas_price: u64,
1174 ) -> anyhow::Result<TransactionData>;
1175
1176 fn new_pay_all_iota(
1179 sender: Address,
1180 coins: Vec<ObjectReference>,
1181 recipient: Address,
1182 gas_payment: ObjectReference,
1183 gas_budget: u64,
1184 gas_price: u64,
1185 ) -> TransactionData;
1186
1187 fn new_split_coin(
1190 sender: Address,
1191 coin: ObjectReference,
1192 amounts: Vec<u64>,
1193 gas_payment: ObjectReference,
1194 gas_budget: u64,
1195 gas_price: u64,
1196 ) -> TransactionData;
1197
1198 fn new_module(
1200 sender: Address,
1201 gas_payment: ObjectReference,
1202 modules: Vec<Vec<u8>>,
1203 dep_ids: Vec<ObjectId>,
1204 gas_budget: u64,
1205 gas_price: u64,
1206 ) -> TransactionData;
1207
1208 fn new_upgrade(
1211 sender: Address,
1212 gas_payment: ObjectReference,
1213 package_id: ObjectId,
1214 modules: Vec<Vec<u8>>,
1215 dep_ids: Vec<ObjectId>,
1216 upgrade_capability_and_owner: (ObjectReference, Owner),
1217 upgrade_policy: u8,
1218 digest: Vec<u8>,
1219 gas_budget: u64,
1220 gas_price: u64,
1221 ) -> anyhow::Result<TransactionData>;
1222
1223 fn new_programmable(
1226 sender: Address,
1227 gas_payment: Vec<ObjectReference>,
1228 pt: ProgrammableTransaction,
1229 gas_budget: u64,
1230 gas_price: u64,
1231 ) -> TransactionData;
1232
1233 fn new_programmable_allow_sponsor(
1236 sender: Address,
1237 gas_payment: Vec<ObjectReference>,
1238 pt: ProgrammableTransaction,
1239 gas_budget: u64,
1240 gas_price: u64,
1241 sponsor: Address,
1242 ) -> TransactionData;
1243
1244 fn message_version(&self) -> u64;
1246
1247 fn execution_parts(&self) -> (TransactionKind, Address, GasPayment);
1250}
1251
1252impl TransactionDataAPI for TransactionData {
1253 fn sender(&self) -> Address {
1254 match self {
1255 Self::V1(v1) => v1.sender,
1256 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1257 }
1258 }
1259
1260 fn kind(&self) -> &TransactionKind {
1261 match self {
1262 Self::V1(v1) => &v1.kind,
1263 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1264 }
1265 }
1266
1267 fn kind_mut(&mut self) -> &mut TransactionKind {
1268 match self {
1269 Self::V1(v1) => &mut v1.kind,
1270 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1271 }
1272 }
1273
1274 fn into_kind(self) -> TransactionKind {
1275 match self {
1276 Self::V1(v1) => v1.kind,
1277 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1278 }
1279 }
1280
1281 fn signers(&self) -> NonEmpty<Address> {
1282 let mut signers = nonempty![self.sender()];
1283 if self.gas_owner() != self.sender() {
1284 signers.push(self.gas_owner());
1285 }
1286 signers
1287 }
1288
1289 fn gas_data(&self) -> &GasPayment {
1290 match self {
1291 Self::V1(v1) => &v1.gas_payment,
1292 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1293 }
1294 }
1295
1296 fn gas_owner(&self) -> Address {
1297 self.gas_data().owner
1298 }
1299
1300 fn gas(&self) -> &[ObjectReference] {
1301 &self.gas_data().objects
1302 }
1303
1304 fn gas_price(&self) -> u64 {
1305 self.gas_data().price
1306 }
1307
1308 fn gas_budget(&self) -> u64 {
1309 self.gas_data().budget
1310 }
1311
1312 fn expiration(&self) -> &TransactionExpiration {
1313 match self {
1314 Self::V1(v1) => &v1.expiration,
1315 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1316 }
1317 }
1318
1319 fn shared_input_objects(&self) -> Vec<SharedObjectReference> {
1320 self.kind().shared_input_objects().collect()
1321 }
1322
1323 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
1324 self.kind().move_calls()
1325 }
1326
1327 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1328 let mut inputs = self.kind().input_objects()?;
1329
1330 if !self.kind().is_system() {
1331 inputs.extend(
1332 self.gas()
1333 .iter()
1334 .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
1335 );
1336 }
1337 Ok(inputs)
1338 }
1339
1340 fn receiving_objects(&self) -> Vec<ObjectReference> {
1341 self.kind().receiving_objects()
1342 }
1343
1344 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1345 fp_ensure!(!self.gas().is_empty(), UserInputError::MissingGasPayment);
1346 fp_ensure!(
1347 self.gas().len() < config.max_gas_payment_objects() as usize,
1348 UserInputError::SizeLimitExceeded {
1349 limit: "maximum number of gas payment objects".to_string(),
1350 value: config.max_gas_payment_objects().to_string()
1351 }
1352 );
1353 self.validity_check_no_gas_check(config)
1354 }
1355
1356 #[instrument(level = "trace", skip_all)]
1357 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
1358 self.kind().validity_check(config)?;
1359 self.check_sponsorship()
1360 }
1361
1362 fn is_sponsored_tx(&self) -> bool {
1363 self.gas_owner() != self.sender()
1364 }
1365
1366 fn check_sponsorship(&self) -> UserInputResult {
1367 if self.gas_owner() == self.sender() {
1368 return Ok(());
1369 }
1370 if matches!(self.kind(), TransactionKind::Programmable(_)) {
1371 return Ok(());
1372 }
1373 Err(UserInputError::UnsupportedSponsoredTransactionKind)
1374 }
1375
1376 fn is_end_of_epoch_tx(&self) -> bool {
1377 matches!(self.kind(), TransactionKind::EndOfEpoch(_))
1378 }
1379
1380 fn is_system_tx(&self) -> bool {
1381 self.kind().is_system()
1382 }
1383
1384 fn is_genesis_tx(&self) -> bool {
1385 matches!(self.kind(), TransactionKind::Genesis(_))
1386 }
1387
1388 fn sender_mut_for_testing(&mut self) -> &mut Address {
1389 match self {
1390 Self::V1(v1) => &mut v1.sender,
1391 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1392 }
1393 }
1394
1395 fn gas_data_mut(&mut self) -> &mut GasPayment {
1396 match self {
1397 Self::V1(v1) => &mut v1.gas_payment,
1398 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1399 }
1400 }
1401
1402 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
1403 match self {
1404 Self::V1(v1) => &mut v1.expiration,
1405 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1406 }
1407 }
1408
1409 fn new_system_transaction(kind: TransactionKind) -> TransactionData {
1410 assert!(kind.is_system());
1411 let sender = Address::ZERO;
1412 TransactionData::V1(TransactionDataV1 {
1413 kind,
1414 sender,
1415 gas_payment: GasPayment {
1416 price: GAS_PRICE_FOR_SYSTEM_TX,
1417 owner: sender,
1418 objects: vec![ObjectReference::new(
1419 ObjectId::ZERO,
1420 Version::default(),
1421 ObjectDigest::MIN,
1422 )],
1423 budget: 0,
1424 },
1425 expiration: TransactionExpiration::None,
1426 })
1427 }
1428
1429 fn new(
1430 kind: TransactionKind,
1431 sender: Address,
1432 gas_payment: ObjectReference,
1433 gas_budget: u64,
1434 gas_price: u64,
1435 ) -> TransactionData {
1436 TransactionData::V1(TransactionDataV1 {
1437 kind,
1438 sender,
1439 gas_payment: GasPayment {
1440 price: gas_price,
1441 owner: sender,
1442 objects: vec![gas_payment],
1443 budget: gas_budget,
1444 },
1445 expiration: TransactionExpiration::None,
1446 })
1447 }
1448
1449 fn new_with_gas_coins(
1450 kind: TransactionKind,
1451 sender: Address,
1452 gas_payment: Vec<ObjectReference>,
1453 gas_budget: u64,
1454 gas_price: u64,
1455 ) -> TransactionData {
1456 TransactionData::new_with_gas_coins_allow_sponsor(
1457 kind,
1458 sender,
1459 gas_payment,
1460 gas_budget,
1461 gas_price,
1462 sender,
1463 )
1464 }
1465
1466 fn new_with_gas_coins_allow_sponsor(
1467 kind: TransactionKind,
1468 sender: Address,
1469 gas_payment: Vec<ObjectReference>,
1470 gas_budget: u64,
1471 gas_price: u64,
1472 gas_sponsor: Address,
1473 ) -> TransactionData {
1474 TransactionData::V1(TransactionDataV1 {
1475 kind,
1476 sender,
1477 gas_payment: GasPayment {
1478 price: gas_price,
1479 owner: gas_sponsor,
1480 objects: gas_payment,
1481 budget: gas_budget,
1482 },
1483 expiration: TransactionExpiration::None,
1484 })
1485 }
1486
1487 fn new_with_gas_data(
1488 kind: TransactionKind,
1489 sender: Address,
1490 gas_data: GasPayment,
1491 ) -> TransactionData {
1492 TransactionData::V1(TransactionDataV1 {
1493 kind,
1494 sender,
1495 gas_payment: gas_data,
1496 expiration: TransactionExpiration::None,
1497 })
1498 }
1499
1500 fn new_move_call(
1501 sender: Address,
1502 package: ObjectId,
1503 module: Identifier,
1504 function: Identifier,
1505 type_arguments: Vec<TypeTag>,
1506 gas_payment: ObjectReference,
1507 arguments: Vec<CallArg>,
1508 gas_budget: u64,
1509 gas_price: u64,
1510 ) -> anyhow::Result<TransactionData> {
1511 TransactionData::new_move_call_with_gas_coins(
1512 sender,
1513 package,
1514 module,
1515 function,
1516 type_arguments,
1517 vec![gas_payment],
1518 arguments,
1519 gas_budget,
1520 gas_price,
1521 )
1522 }
1523
1524 fn new_move_call_with_gas_coins(
1525 sender: Address,
1526 package: ObjectId,
1527 module: Identifier,
1528 function: Identifier,
1529 type_arguments: Vec<TypeTag>,
1530 gas_payment: Vec<ObjectReference>,
1531 arguments: Vec<CallArg>,
1532 gas_budget: u64,
1533 gas_price: u64,
1534 ) -> anyhow::Result<TransactionData> {
1535 let pt = {
1536 let mut builder = ProgrammableTransactionBuilder::new();
1537 builder.move_call(package, module, function, type_arguments, arguments)?;
1538 builder.finish()
1539 };
1540 Ok(TransactionData::new_programmable(
1541 sender,
1542 gas_payment,
1543 pt,
1544 gas_budget,
1545 gas_price,
1546 ))
1547 }
1548
1549 fn new_transfer(
1550 recipient: Address,
1551 object_ref: ObjectReference,
1552 sender: Address,
1553 gas_payment: ObjectReference,
1554 gas_budget: u64,
1555 gas_price: u64,
1556 ) -> TransactionData {
1557 let pt = {
1558 let mut builder = ProgrammableTransactionBuilder::new();
1559 builder.transfer_object(recipient, object_ref).unwrap();
1560 builder.finish()
1561 };
1562 TransactionData::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1563 }
1564
1565 fn new_transfer_iota(
1566 recipient: Address,
1567 sender: Address,
1568 amount: Option<u64>,
1569 gas_payment: ObjectReference,
1570 gas_budget: u64,
1571 gas_price: u64,
1572 ) -> TransactionData {
1573 TransactionData::new_transfer_iota_allow_sponsor(
1574 recipient,
1575 sender,
1576 amount,
1577 gas_payment,
1578 gas_budget,
1579 gas_price,
1580 sender,
1581 )
1582 }
1583
1584 fn new_transfer_iota_allow_sponsor(
1585 recipient: Address,
1586 sender: Address,
1587 amount: Option<u64>,
1588 gas_payment: ObjectReference,
1589 gas_budget: u64,
1590 gas_price: u64,
1591 gas_sponsor: Address,
1592 ) -> TransactionData {
1593 let pt = {
1594 let mut builder = ProgrammableTransactionBuilder::new();
1595 builder.transfer_iota(recipient, amount);
1596 builder.finish()
1597 };
1598 TransactionData::new_programmable_allow_sponsor(
1599 sender,
1600 vec![gas_payment],
1601 pt,
1602 gas_budget,
1603 gas_price,
1604 gas_sponsor,
1605 )
1606 }
1607
1608 fn new_pay(
1609 sender: Address,
1610 coins: Vec<ObjectReference>,
1611 recipients: Vec<Address>,
1612 amounts: Vec<u64>,
1613 gas_payment: ObjectReference,
1614 gas_budget: u64,
1615 gas_price: u64,
1616 ) -> anyhow::Result<TransactionData> {
1617 let pt = {
1618 let mut builder = ProgrammableTransactionBuilder::new();
1619 builder.pay(coins, recipients, amounts)?;
1620 builder.finish()
1621 };
1622 Ok(TransactionData::new_programmable(
1623 sender,
1624 vec![gas_payment],
1625 pt,
1626 gas_budget,
1627 gas_price,
1628 ))
1629 }
1630
1631 fn new_pay_iota(
1632 sender: Address,
1633 mut coins: Vec<ObjectReference>,
1634 recipients: Vec<Address>,
1635 amounts: Vec<u64>,
1636 gas_payment: ObjectReference,
1637 gas_budget: u64,
1638 gas_price: u64,
1639 ) -> anyhow::Result<TransactionData> {
1640 coins.insert(0, gas_payment);
1641 let pt = {
1642 let mut builder = ProgrammableTransactionBuilder::new();
1643 builder.pay_iota(recipients, amounts)?;
1644 builder.finish()
1645 };
1646 Ok(TransactionData::new_programmable(
1647 sender, coins, pt, gas_budget, gas_price,
1648 ))
1649 }
1650
1651 fn new_pay_all_iota(
1652 sender: Address,
1653 mut coins: Vec<ObjectReference>,
1654 recipient: Address,
1655 gas_payment: ObjectReference,
1656 gas_budget: u64,
1657 gas_price: u64,
1658 ) -> TransactionData {
1659 coins.insert(0, gas_payment);
1660 let pt = {
1661 let mut builder = ProgrammableTransactionBuilder::new();
1662 builder.pay_all_iota(recipient);
1663 builder.finish()
1664 };
1665 TransactionData::new_programmable(sender, coins, pt, gas_budget, gas_price)
1666 }
1667
1668 fn new_split_coin(
1669 sender: Address,
1670 coin: ObjectReference,
1671 amounts: Vec<u64>,
1672 gas_payment: ObjectReference,
1673 gas_budget: u64,
1674 gas_price: u64,
1675 ) -> TransactionData {
1676 let pt = {
1677 let mut builder = ProgrammableTransactionBuilder::new();
1678 builder.split_coin(sender, coin, amounts);
1679 builder.finish()
1680 };
1681 TransactionData::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1682 }
1683
1684 fn new_module(
1685 sender: Address,
1686 gas_payment: ObjectReference,
1687 modules: Vec<Vec<u8>>,
1688 dep_ids: Vec<ObjectId>,
1689 gas_budget: u64,
1690 gas_price: u64,
1691 ) -> TransactionData {
1692 let pt = {
1693 let mut builder = ProgrammableTransactionBuilder::new();
1694 let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
1695 builder.transfer_arg(sender, upgrade_cap);
1696 builder.finish()
1697 };
1698 TransactionData::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1699 }
1700
1701 fn new_upgrade(
1702 sender: Address,
1703 gas_payment: ObjectReference,
1704 package_id: ObjectId,
1705 modules: Vec<Vec<u8>>,
1706 dep_ids: Vec<ObjectId>,
1707 (upgrade_capability, capability_owner): (ObjectReference, Owner),
1708 upgrade_policy: u8,
1709 digest: Vec<u8>,
1710 gas_budget: u64,
1711 gas_price: u64,
1712 ) -> anyhow::Result<TransactionData> {
1713 let pt = {
1714 let mut builder = ProgrammableTransactionBuilder::new();
1715 let capability_arg = match capability_owner {
1716 Owner::Address(_) => CallArg::ImmutableOrOwned(upgrade_capability),
1717 Owner::Shared(initial_shared_version) => {
1718 CallArg::Shared(SharedObjectReference::new(
1719 upgrade_capability.object_id,
1720 initial_shared_version,
1721 true,
1722 ))
1723 }
1724 Owner::Immutable => {
1725 bail!("Upgrade capability is stored immutably and cannot be used for upgrades");
1726 }
1727 Owner::Object(_) => {
1728 bail!("Upgrade capability controlled by object");
1729 }
1730 _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
1731 };
1732 builder.obj(capability_arg).unwrap();
1733 let upgrade_arg = builder.pure(upgrade_policy).unwrap();
1734 let digest_arg = builder.pure(digest).unwrap();
1735 let upgrade_ticket = builder.programmable_move_call(
1736 ObjectId::FRAMEWORK,
1737 Identifier::PACKAGE_MODULE,
1738 Identifier::from_static("authorize_upgrade"),
1739 vec![],
1740 vec![Argument::Input(0), upgrade_arg, digest_arg],
1741 );
1742 let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
1743
1744 builder.programmable_move_call(
1745 ObjectId::FRAMEWORK,
1746 Identifier::PACKAGE_MODULE,
1747 Identifier::from_static("commit_upgrade"),
1748 vec![],
1749 vec![Argument::Input(0), upgrade_receipt],
1750 );
1751
1752 builder.finish()
1753 };
1754 Ok(TransactionData::new_programmable(
1755 sender,
1756 vec![gas_payment],
1757 pt,
1758 gas_budget,
1759 gas_price,
1760 ))
1761 }
1762
1763 fn new_programmable(
1764 sender: Address,
1765 gas_payment: Vec<ObjectReference>,
1766 pt: ProgrammableTransaction,
1767 gas_budget: u64,
1768 gas_price: u64,
1769 ) -> TransactionData {
1770 TransactionData::new_programmable_allow_sponsor(
1771 sender,
1772 gas_payment,
1773 pt,
1774 gas_budget,
1775 gas_price,
1776 sender,
1777 )
1778 }
1779
1780 fn new_programmable_allow_sponsor(
1781 sender: Address,
1782 gas_payment: Vec<ObjectReference>,
1783 pt: ProgrammableTransaction,
1784 gas_budget: u64,
1785 gas_price: u64,
1786 sponsor: Address,
1787 ) -> TransactionData {
1788 let kind = TransactionKind::Programmable(pt);
1789 TransactionData::new_with_gas_coins_allow_sponsor(
1790 kind,
1791 sender,
1792 gas_payment,
1793 gas_budget,
1794 gas_price,
1795 sponsor,
1796 )
1797 }
1798
1799 fn message_version(&self) -> u64 {
1800 match self {
1801 TransactionData::V1(_) => 1,
1802 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1803 }
1804 }
1805
1806 fn execution_parts(&self) -> (TransactionKind, Address, GasPayment) {
1807 (self.kind().clone(), self.sender(), self.gas_data().clone())
1808 }
1809}
1810
1811pub struct TxValidityCheckContext<'a> {
1812 pub config: &'a ProtocolConfig,
1813 pub epoch: EpochId,
1814}
1815
1816pub fn merge_authenticator_input_objects<'a>(
1823 move_authenticators: impl IntoIterator<Item = &'a MoveAuthenticator>,
1824 input_objects: &mut Vec<InputObjectKind>,
1825) -> UserInputResult<()> {
1826 for move_authenticator in move_authenticators {
1827 for auth_object in move_authenticator.input_objects() {
1828 let entry = input_objects
1829 .iter_mut()
1830 .find(|o| o.object_id() == auth_object.object_id());
1831
1832 match entry {
1833 None => input_objects.push(auth_object),
1834 Some(existing) => existing.left_union_with_checks(&auth_object)?,
1835 }
1836 }
1837 }
1838 Ok(())
1839}
1840
1841pub trait SenderSignedTransactionAPI {
1849 fn new_from_sender_signature(
1851 tx_data: TransactionData,
1852 tx_signature: Signature,
1853 ) -> SenderSignedData;
1854
1855 fn add_signature(&mut self, new_signature: Signature);
1858
1859 fn get_signer_sig_mapping(&self) -> IotaResult<BTreeMap<Address, &UserSignature>>;
1862
1863 fn has_multisig(&self) -> bool;
1865
1866 fn transaction_mut_for_testing(&mut self) -> &mut TransactionData;
1868
1869 fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<UserSignature>;
1871
1872 fn serialized_size(&self) -> IotaResult<usize>;
1874
1875 fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, IotaError>;
1879
1880 fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>>;
1888
1889 fn split_input_objects_into_groups_for_reading(
1895 &self,
1896 input_objects: InputObjects,
1897 ) -> IotaResult<(InputObjects, Vec<(InputObjects, ObjectReadResult)>)>;
1898
1899 fn contains_shared_object(&self) -> bool;
1903
1904 fn shared_input_objects(&self) -> Vec<SharedObjectReference>;
1913
1914 fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>>;
1926
1927 fn uses_randomness(&self) -> bool;
1932}
1933
1934impl SenderSignedTransactionAPI for SenderSignedData {
1935 fn new_from_sender_signature(
1936 tx_data: TransactionData,
1937 tx_signature: Signature,
1938 ) -> SenderSignedData {
1939 Self::new(tx_data, vec![tx_signature.into()])
1940 }
1941
1942 fn add_signature(&mut self, new_signature: Signature) {
1943 self.0.signatures.push(new_signature.into());
1944 }
1945
1946 fn get_signer_sig_mapping(&self) -> IotaResult<BTreeMap<Address, &UserSignature>> {
1947 let mut mapping = BTreeMap::new();
1948 for sig in &self.0.signatures {
1949 let address = sig.derive_address();
1950 mapping.insert(address, sig);
1951 }
1952 Ok(mapping)
1953 }
1954
1955 fn has_multisig(&self) -> bool {
1956 self.signatures().iter().any(|sig| sig.is_multisig())
1957 }
1958
1959 fn transaction_mut_for_testing(&mut self) -> &mut TransactionData {
1960 &mut self.0.transaction
1961 }
1962
1963 fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<UserSignature> {
1964 &mut self.0.signatures
1965 }
1966
1967 fn serialized_size(&self) -> IotaResult<usize> {
1968 bcs::serialized_size(self).map_err(|e| IotaError::TransactionSerialization {
1969 error: e.to_string(),
1970 })
1971 }
1972
1973 fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, IotaError> {
1974 check_user_signature_protocol_compatibility(self, context.config)?;
1977
1978 let tx = self.transaction();
1981 fp_ensure!(
1982 !tx.is_system_tx(),
1983 IotaError::UserInput {
1984 error: UserInputError::Unsupported(
1985 "SenderSignedData must not contain system transaction".to_string()
1986 )
1987 }
1988 );
1989
1990 if match &tx.expiration() {
1992 TransactionExpiration::None => false,
1993 TransactionExpiration::Epoch(exp_poch) => *exp_poch < context.epoch,
1994 _ => unimplemented!(
1995 "a new TransactionExpiration enum variant was added and needs to be handled"
1996 ),
1997 } {
1998 return Err(IotaError::TransactionExpired);
1999 }
2000
2001 let tx_size = self.serialized_size()?;
2003 let max_tx_size_bytes = context.config.max_tx_size_bytes();
2004 fp_ensure!(
2005 tx_size as u64 <= max_tx_size_bytes,
2006 IotaError::UserInput {
2007 error: UserInputError::SizeLimitExceeded {
2008 limit: format!(
2009 "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
2010 ),
2011 value: tx_size.to_string(),
2012 }
2013 }
2014 );
2015
2016 tx.validity_check(context.config)
2017 .map_err(Into::<IotaError>::into)?;
2018
2019 move_authenticators_validity_check(self, context.config)?;
2020
2021 Ok(tx_size)
2022 }
2023
2024 fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>> {
2025 let mut input_objects_set = self
2026 .transaction()
2027 .input_objects()?
2028 .into_iter()
2029 .collect::<HashSet<_>>();
2030
2031 self.move_authenticators()
2032 .into_iter()
2033 .for_each(|authenticator| {
2034 input_objects_set.extend(authenticator.input_objects());
2035 });
2036
2037 Ok(input_objects_set.into_iter().collect::<Vec<_>>())
2038 }
2039
2040 fn split_input_objects_into_groups_for_reading(
2041 &self,
2042 input_objects: InputObjects,
2043 ) -> IotaResult<(InputObjects, Vec<(InputObjects, ObjectReadResult)>)> {
2044 let input_objects_map = input_objects
2045 .iter()
2046 .map(|o| (&o.input_object_kind, o))
2047 .collect::<HashMap<_, _>>();
2048
2049 let tx_input_objects = self
2050 .transaction()
2051 .input_objects()?
2052 .iter()
2053 .map(|k| {
2054 input_objects_map
2055 .get(k)
2056 .map(|&r| r.clone())
2057 .expect("All transaction input objects are expected to be present")
2058 })
2059 .collect::<Vec<_>>()
2060 .into();
2061
2062 let per_authenticator_inputs =
2063 self.move_authenticators()
2064 .into_iter()
2065 .map(|move_authenticator| {
2066 let authenticator_input_objects = move_authenticator
2067 .input_objects()
2068 .iter()
2069 .map(|k| {
2070 input_objects_map.get(k).map(|&r| r.clone()).expect(
2071 "All authenticator input objects are expected to be present",
2072 )
2073 })
2074 .collect::<Vec<_>>()
2075 .into();
2076
2077 let account_objects = move_authenticator
2078 .object_to_authenticate()
2079 .input_object_kind()
2080 .iter()
2081 .map(|k| {
2082 input_objects_map
2083 .get(k)
2084 .map(|&r| r.clone())
2085 .expect("Account object is expected to be present")
2086 })
2087 .collect::<Vec<_>>();
2088
2089 debug_assert!(
2090 account_objects.len() == 1,
2091 "Only one account object must be loaded"
2092 );
2093
2094 (
2095 authenticator_input_objects,
2096 account_objects
2097 .into_iter()
2098 .next()
2099 .expect("Account object is expected to be present"),
2100 )
2101 })
2102 .collect();
2103
2104 Ok((tx_input_objects, per_authenticator_inputs))
2105 }
2106
2107 fn contains_shared_object(&self) -> bool {
2108 !self.shared_input_objects().is_empty()
2109 }
2110
2111 fn shared_input_objects(&self) -> Vec<SharedObjectReference> {
2112 let mut input_objects = self.transaction().shared_input_objects();
2114
2115 self.move_authenticators()
2117 .into_iter()
2118 .for_each(|move_authenticator| {
2119 for auth_shared_object in move_authenticator.shared_objects() {
2120 let entry = input_objects
2121 .iter_mut()
2122 .find(|o| o.object_id == auth_shared_object.object_id);
2123
2124 match entry {
2125 None => input_objects.push(auth_shared_object),
2126 Some(existing) => {
2127 left_union_shared_input_objects(existing, &auth_shared_object)
2128 .expect("union of shared objects should not fail")
2129 }
2130 }
2131 }
2132 });
2133
2134 input_objects
2135 }
2136
2137 fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>> {
2138 let mut input_objects = self.transaction().input_objects()?;
2141
2142 merge_authenticator_input_objects(self.move_authenticators(), &mut input_objects)?;
2144
2145 Ok(input_objects)
2146 }
2147
2148 fn uses_randomness(&self) -> bool {
2149 self.shared_input_objects()
2150 .iter()
2151 .any(|obj| obj.object_id == ObjectId::RANDOMNESS_STATE)
2152 }
2153}
2154
2155fn check_user_signature_protocol_compatibility(
2156 data: &SenderSignedData,
2157 config: &ProtocolConfig,
2158) -> IotaResult {
2159 for sig in data.signatures() {
2160 match sig {
2161 UserSignature::PasskeyAuthenticator(_) => {
2162 if !config.passkey_auth() {
2163 return Err(IotaError::UserInput {
2164 error: UserInputError::Unsupported(
2165 "passkey is not enabled on this network".to_string(),
2166 ),
2167 });
2168 }
2169 }
2170 UserSignature::MoveAuthenticator(_) => {
2171 if !config.enable_move_authentication() {
2172 return Err(IotaError::UserInput {
2173 error: UserInputError::Unsupported(
2174 "`Move authentication` is not enabled on this network".to_string(),
2175 ),
2176 });
2177 }
2178 }
2179 UserSignature::Simple(_) | UserSignature::Multisig(_) => (),
2180 _ => {
2181 unimplemented!("a new UserSignature variant was added and needs to be handled")
2182 }
2183 }
2184 }
2185
2186 Ok(())
2187}
2188
2189fn move_authenticators_validity_check(
2190 data: &SenderSignedData,
2191 config: &ProtocolConfig,
2192) -> IotaResult {
2193 let authenticators = data.move_authenticators();
2194
2195 authenticators
2197 .iter()
2198 .try_for_each(|authenticator| authenticator.validity_check(config))?;
2199
2200 let authenticators_num = authenticators.len();
2202 if authenticators_num > 0 {
2203 let tx = data.transaction();
2204
2205 fp_ensure!(
2206 tx.kind().is_programmable(),
2207 UserInputError::Unsupported(
2208 "SenderSignedData with MoveAuthenticator must be a programmable transaction"
2209 .to_string(),
2210 )
2211 .into()
2212 );
2213
2214 if !config.enable_move_authentication_for_sponsor() {
2215 fp_ensure!(
2216 authenticators_num == 1,
2217 UserInputError::Unsupported(
2218 "SenderSignedData with more than one MoveAuthenticator is not supported"
2219 .to_string(),
2220 )
2221 .into()
2222 );
2223
2224 fp_ensure!(
2225 data.sender_move_authenticator().is_some(),
2226 UserInputError::Unsupported(
2227 "SenderSignedData can have MoveAuthenticator only for the sender".to_string(),
2228 )
2229 .into()
2230 );
2231 }
2232
2233 check_move_authenticators_input_consistency(tx, &authenticators)?;
2234 }
2235
2236 Ok(())
2237}
2238
2239fn check_move_authenticators_input_consistency(
2240 tx_data: &TransactionData,
2241 authenticators: &[&MoveAuthenticator],
2242) -> IotaResult {
2243 let mut checked_inputs = tx_data
2245 .kind()
2246 .input_objects()?
2247 .into_iter()
2248 .map(|o| (o.object_id(), o))
2249 .collect::<HashMap<_, _>>();
2250
2251 authenticators.iter().try_for_each(|authenticator| {
2252 authenticator
2253 .input_objects()
2254 .iter()
2255 .try_for_each(|auth_input_object| {
2256 match checked_inputs.get(&auth_input_object.object_id()) {
2257 Some(existing) => {
2258 auth_input_object.check_consistency_for_authentication(existing)?
2259 }
2260 None => {
2261 checked_inputs.insert(auth_input_object.object_id(), *auth_input_object);
2262 }
2263 };
2264
2265 Ok(())
2266 })
2267 })
2268}
2269
2270impl Message for SenderSignedData {
2271 type DigestType = TransactionDigest;
2272 const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2273
2274 fn digest(&self) -> Self::DigestType {
2277 self.transaction().digest()
2278 }
2279}
2280
2281impl<S> Envelope<SenderSignedData, S> {
2282 pub fn sender_address(&self) -> Address {
2283 self.data().transaction().sender()
2284 }
2285
2286 pub fn gas(&self) -> &[ObjectReference] {
2287 self.data().transaction().gas()
2288 }
2289
2290 pub fn key(&self) -> TransactionKey {
2292 match &self.data().transaction().kind() {
2293 TransactionKind::RandomnessStateUpdate(rsu) => {
2294 TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2295 }
2296 _ => TransactionKey::Digest(*self.digest()),
2297 }
2298 }
2299
2300 pub fn non_digest_key(&self) -> Option<TransactionKey> {
2305 match &self.data().transaction().kind() {
2306 TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2307 rsu.epoch,
2308 rsu.randomness_round,
2309 )),
2310 _ => None,
2311 }
2312 }
2313
2314 pub fn is_system_tx(&self) -> bool {
2315 self.data().transaction().is_system_tx()
2316 }
2317
2318 pub fn is_sponsored_tx(&self) -> bool {
2319 self.data().transaction().is_sponsored_tx()
2320 }
2321}
2322
2323impl Transaction {
2324 pub fn from_data_and_signer(
2325 data: TransactionData,
2326 signers: Vec<impl Into<IotaKeyPair>>,
2327 ) -> Self {
2328 let signatures = {
2329 let intent_msg = data.intent_message();
2330 signers
2331 .into_iter()
2332 .map(|s| Signature::new_secure(&intent_msg, s))
2333 .collect()
2334 };
2335 Self::from_data(data, signatures)
2336 }
2337
2338 pub fn from_data(data: TransactionData, signatures: Vec<Signature>) -> Self {
2340 Self::from_user_sig_data(data, signatures.into_iter().map(|s| s.into()).collect())
2341 }
2342
2343 pub fn signature_from_signer(
2344 data: TransactionData,
2345 intent: Intent,
2346 signer: impl Into<IotaKeyPair>,
2347 ) -> Signature {
2348 let intent_msg = IntentMessage::new(intent, data);
2349 Signature::new_secure(&intent_msg, signer)
2350 }
2351
2352 pub fn from_user_sig_data(data: TransactionData, signatures: Vec<UserSignature>) -> Self {
2353 Self::new(SenderSignedData::new(data, signatures))
2354 }
2355
2356 pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2359 (
2360 Base64::from_bytes(&bcs::to_bytes(self.data().transaction()).unwrap()),
2361 self.data()
2362 .signatures()
2363 .iter()
2364 .map(|s| Base64::from_bytes(&s.to_bytes()))
2365 .collect(),
2366 )
2367 }
2368}
2369
2370impl VerifiedTransaction {
2371 pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2372 GenesisTransaction { objects, events }
2373 .pipe(TransactionKind::Genesis)
2374 .pipe(Self::new_system_transaction)
2375 }
2376
2377 pub fn new_consensus_commit_prologue_v1(
2378 epoch: u64,
2379 round: u64,
2380 commit_timestamp_ms: CheckpointTimestamp,
2381 consensus_commit_digest: ConsensusCommitDigest,
2382 cancelled_transactions: Vec<CancelledTransaction>,
2383 ) -> Self {
2384 ConsensusCommitPrologueV1 {
2385 epoch,
2386 round,
2387 sub_dag_index: None,
2389 commit_timestamp_ms,
2390 consensus_commit_digest,
2391 consensus_determined_version_assignments:
2392 ConsensusDeterminedVersionAssignments::CancelledTransactions {
2393 cancelled_transactions,
2394 },
2395 }
2396 .pipe(TransactionKind::ConsensusCommitPrologueV1)
2397 .pipe(Self::new_system_transaction)
2398 }
2399
2400 pub fn new_randomness_state_update(
2401 epoch: u64,
2402 randomness_round: RandomnessRound,
2403 random_bytes: Vec<u8>,
2404 randomness_obj_initial_shared_version: Version,
2405 ) -> Self {
2406 RandomnessStateUpdate {
2407 epoch,
2408 randomness_round,
2409 random_bytes,
2410 randomness_obj_initial_shared_version,
2411 }
2412 .pipe(TransactionKind::RandomnessStateUpdate)
2413 .pipe(Self::new_system_transaction)
2414 }
2415
2416 pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
2417 TransactionKind::EndOfEpoch(txns).pipe(Self::new_system_transaction)
2418 }
2419
2420 fn new_system_transaction(system_transaction: TransactionKind) -> Self {
2421 system_transaction
2422 .pipe(TransactionData::new_system_transaction)
2423 .pipe(|data| {
2424 SenderSignedData::new_from_sender_signature(data, zero_ed25519_signature())
2425 })
2426 .pipe(Transaction::new)
2427 .pipe(Self::new_from_verified)
2428 }
2429}
2430
2431impl VerifiedSignedTransaction {
2432 #[instrument(level = "trace", skip_all)]
2434 pub fn new(
2435 epoch: EpochId,
2436 transaction: VerifiedTransaction,
2437 authority: AuthorityName,
2438 secret: &dyn Signer<AuthoritySignature>,
2439 ) -> Self {
2440 Self::new_from_verified(SignedTransaction::new(
2441 epoch,
2442 transaction.into_inner().into_data(),
2443 secret,
2444 authority,
2445 ))
2446 }
2447}
2448
2449pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
2451pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
2452pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
2453
2454pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
2456pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
2457
2458impl Transaction {
2459 pub fn verify_signature_for_testing(&self, verify_params: &VerifyParams) -> IotaResult {
2460 verify_sender_signed_data_message_signatures(self.data(), verify_params)
2461 }
2462
2463 pub fn try_into_verified_for_testing(
2464 self,
2465 verify_params: &VerifyParams,
2466 ) -> IotaResult<VerifiedTransaction> {
2467 self.verify_signature_for_testing(verify_params)?;
2468 Ok(VerifiedTransaction::new_from_verified(self))
2469 }
2470
2471 pub fn gas_price(&self) -> u64 {
2472 self.data().transaction().gas_price()
2473 }
2474}
2475
2476impl SignedTransaction {
2477 pub fn verify_signatures_authenticated_for_testing(
2478 &self,
2479 committee: &Committee,
2480 verify_params: &VerifyParams,
2481 ) -> IotaResult {
2482 verify_sender_signed_data_message_signatures(self.data(), verify_params)?;
2483
2484 self.auth_sig().verify_secure(
2485 self.data(),
2486 Intent::iota_app(IntentScope::SenderSignedTransaction),
2487 committee,
2488 )
2489 }
2490
2491 pub fn try_into_verified_for_testing(
2492 self,
2493 committee: &Committee,
2494 verify_params: &VerifyParams,
2495 ) -> IotaResult<VerifiedSignedTransaction> {
2496 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
2497 Ok(VerifiedSignedTransaction::new_from_verified(self))
2498 }
2499}
2500
2501pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2502
2503impl CertifiedTransaction {
2504 pub fn certificate_digest(&self) -> CertificateDigest {
2505 let mut digest = DefaultHash::default();
2506 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2507 let hash = digest.finalize();
2508 CertificateDigest::new(hash.into())
2509 }
2510
2511 pub fn gas_price(&self) -> u64 {
2512 self.data().transaction().gas_price()
2513 }
2514
2515 #[instrument(level = "trace", skip_all)]
2518 pub fn verify_signatures_authenticated(
2519 &self,
2520 committee: &Committee,
2521 verify_params: &VerifyParams,
2522 ) -> IotaResult {
2523 verify_sender_signed_data_message_signatures(self.data(), verify_params)?;
2524 self.auth_sig().verify_secure(
2525 self.data(),
2526 Intent::iota_app(IntentScope::SenderSignedTransaction),
2527 committee,
2528 )
2529 }
2530
2531 pub fn try_into_verified_for_testing(
2532 self,
2533 committee: &Committee,
2534 verify_params: &VerifyParams,
2535 ) -> IotaResult<VerifiedCertificate> {
2536 self.verify_signatures_authenticated(committee, verify_params)?;
2537 Ok(VerifiedCertificate::new_from_verified(self))
2538 }
2539
2540 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
2541 self.auth_sig().verify_secure(
2542 self.data(),
2543 Intent::iota_app(IntentScope::SenderSignedTransaction),
2544 committee,
2545 )
2546 }
2547}
2548
2549pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2550pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2551
2552#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
2553pub enum InputObjectKind {
2554 MovePackage(ObjectId),
2556 ImmOrOwnedMoveObject(ObjectReference),
2558 SharedMoveObject {
2560 id: ObjectId,
2561 initial_shared_version: Version,
2562 mutable: bool,
2563 },
2564}
2565
2566impl InputObjectKind {
2567 pub fn object_id(&self) -> ObjectId {
2568 match self {
2569 Self::MovePackage(id) => *id,
2570 Self::ImmOrOwnedMoveObject(object_ref) => object_ref.object_id,
2571 Self::SharedMoveObject { id, .. } => *id,
2572 }
2573 }
2574
2575 pub fn version(&self) -> Option<Version> {
2576 match self {
2577 Self::MovePackage(..) => None,
2578 Self::ImmOrOwnedMoveObject(object_ref) => Some(object_ref.version),
2579 Self::SharedMoveObject { .. } => None,
2580 }
2581 }
2582
2583 pub fn object_not_found_error(&self) -> UserInputError {
2584 match *self {
2585 Self::MovePackage(package_id) => {
2586 UserInputError::DependentPackageNotFound { package_id }
2587 }
2588 Self::ImmOrOwnedMoveObject(object_ref) => UserInputError::ObjectNotFound {
2589 object_id: object_ref.object_id,
2590 version: Some(object_ref.version),
2591 },
2592 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
2593 object_id: id,
2594 version: None,
2595 },
2596 }
2597 }
2598
2599 pub fn is_shared_object(&self) -> bool {
2600 matches!(self, Self::SharedMoveObject { .. })
2601 }
2602
2603 pub fn is_mutable(&self) -> bool {
2604 match self {
2605 Self::MovePackage(..) => false,
2606 Self::ImmOrOwnedMoveObject(_) => true,
2607 Self::SharedMoveObject { mutable, .. } => *mutable,
2608 }
2609 }
2610
2611 pub fn left_union_with_checks(&mut self, other: &InputObjectKind) -> UserInputResult<()> {
2617 match self {
2618 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2619 fp_ensure!(
2620 self == other,
2621 UserInputError::InconsistentInput {
2622 object_id: other.object_id(),
2623 }
2624 );
2625 }
2626 InputObjectKind::SharedMoveObject {
2627 id,
2628 initial_shared_version,
2629 mutable,
2630 } => match other {
2631 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2632 fp_bail!(UserInputError::NotSharedObject)
2633 }
2634 InputObjectKind::SharedMoveObject {
2635 id: other_id,
2636 initial_shared_version: other_initial_shared_version,
2637 mutable: other_mutable,
2638 } => {
2639 fp_ensure!(id == other_id, UserInputError::SharedObjectIdMismatch);
2640 fp_ensure!(
2641 initial_shared_version == other_initial_shared_version,
2642 UserInputError::SharedObjectStartingVersionMismatch
2643 );
2644
2645 if !*mutable && *other_mutable {
2646 *mutable = *other_mutable;
2647 }
2648 }
2649 },
2650 }
2651
2652 Ok(())
2653 }
2654
2655 pub fn check_consistency_for_authentication(
2659 &self,
2660 other: &InputObjectKind,
2661 ) -> UserInputResult<()> {
2662 match self {
2663 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2664 fp_ensure!(
2665 self == other,
2666 UserInputError::InconsistentInput {
2667 object_id: self.object_id()
2668 }
2669 );
2670 }
2671 InputObjectKind::SharedMoveObject {
2672 id,
2673 initial_shared_version,
2674 mutable: _,
2675 } => match other {
2676 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2677 fp_bail!(UserInputError::InconsistentInput {
2678 object_id: self.object_id()
2679 })
2680 }
2681 InputObjectKind::SharedMoveObject {
2682 id: other_id,
2683 initial_shared_version: other_initial_shared_version,
2684 mutable: _,
2685 } => {
2686 fp_ensure!(
2687 id == other_id,
2688 UserInputError::InconsistentInput { object_id: *id }
2689 );
2690 fp_ensure!(
2691 initial_shared_version == other_initial_shared_version,
2692 UserInputError::InconsistentInput { object_id: *id }
2693 );
2694 }
2695 },
2696 }
2697
2698 Ok(())
2699 }
2700}
2701
2702#[derive(Clone, Debug)]
2706pub struct ObjectReadResult {
2707 pub input_object_kind: InputObjectKind,
2708 pub object: ObjectReadResultKind,
2709}
2710
2711#[derive(Clone, PartialEq)]
2712pub enum ObjectReadResultKind {
2713 Object(Object),
2714 DeletedSharedObject(Version, TransactionDigest),
2717 CancelledTransactionSharedObject(Version),
2719}
2720
2721impl std::fmt::Debug for ObjectReadResultKind {
2722 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2723 match self {
2724 ObjectReadResultKind::Object(obj) => {
2725 write!(f, "Object({:?})", obj.object_ref())
2726 }
2727 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2728 write!(f, "DeletedSharedObject({seq}, {digest})")
2729 }
2730 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2731 write!(f, "CancelledTransactionSharedObject({seq})")
2732 }
2733 }
2734 }
2735}
2736
2737impl From<Object> for ObjectReadResultKind {
2738 fn from(object: Object) -> Self {
2739 Self::Object(object)
2740 }
2741}
2742
2743impl ObjectReadResult {
2744 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
2745 if let (
2746 InputObjectKind::ImmOrOwnedMoveObject(_),
2747 ObjectReadResultKind::DeletedSharedObject(_, _),
2748 ) = (&input_object_kind, &object)
2749 {
2750 panic!("only shared objects can be DeletedSharedObject");
2751 }
2752
2753 if let (
2754 InputObjectKind::ImmOrOwnedMoveObject(_),
2755 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2756 ) = (&input_object_kind, &object)
2757 {
2758 panic!("only shared objects can be CancelledTransactionSharedObject");
2759 }
2760
2761 Self {
2762 input_object_kind,
2763 object,
2764 }
2765 }
2766
2767 pub fn id(&self) -> ObjectId {
2768 self.input_object_kind.object_id()
2769 }
2770
2771 pub fn as_object(&self) -> Option<&Object> {
2772 match &self.object {
2773 ObjectReadResultKind::Object(object) => Some(object),
2774 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
2775 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2776 }
2777 }
2778
2779 pub fn new_from_gas_object(gas: &Object) -> Self {
2780 let objref = gas.object_ref();
2781 Self {
2782 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
2783 object: ObjectReadResultKind::Object(gas.clone()),
2784 }
2785 }
2786
2787 pub fn is_mutable(&self) -> bool {
2788 match (&self.input_object_kind, &self.object) {
2789 (InputObjectKind::MovePackage(_), _) => false,
2790 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
2791 !object.is_immutable()
2792 }
2793 (
2794 InputObjectKind::ImmOrOwnedMoveObject(_),
2795 ObjectReadResultKind::DeletedSharedObject(_, _),
2796 ) => unreachable!(),
2797 (
2798 InputObjectKind::ImmOrOwnedMoveObject(_),
2799 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2800 ) => unreachable!(),
2801 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
2802 }
2803 }
2804
2805 pub fn is_shared_object(&self) -> bool {
2806 self.input_object_kind.is_shared_object()
2807 }
2808
2809 pub fn is_deleted_shared_object(&self) -> bool {
2810 self.deletion_info().is_some()
2811 }
2812
2813 pub fn deletion_info(&self) -> Option<(Version, TransactionDigest)> {
2814 match &self.object {
2815 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
2816 _ => None,
2817 }
2818 }
2819
2820 pub fn get_owned_objref(&self) -> Option<ObjectReference> {
2823 match (&self.input_object_kind, &self.object) {
2824 (InputObjectKind::MovePackage(_), _) => None,
2825 (
2826 InputObjectKind::ImmOrOwnedMoveObject(objref),
2827 ObjectReadResultKind::Object(object),
2828 ) => {
2829 if object.is_immutable() {
2830 None
2831 } else {
2832 Some(*objref)
2833 }
2834 }
2835 (
2836 InputObjectKind::ImmOrOwnedMoveObject(_),
2837 ObjectReadResultKind::DeletedSharedObject(_, _),
2838 ) => unreachable!(),
2839 (
2840 InputObjectKind::ImmOrOwnedMoveObject(_),
2841 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2842 ) => unreachable!(),
2843 (InputObjectKind::SharedMoveObject { .. }, _) => None,
2844 }
2845 }
2846
2847 pub fn is_owned(&self) -> bool {
2848 self.get_owned_objref().is_some()
2849 }
2850
2851 pub fn to_shared_input(&self) -> Option<SharedInput> {
2852 match self.input_object_kind {
2853 InputObjectKind::MovePackage(_) => None,
2854 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
2855 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
2856 ObjectReadResultKind::Object(obj) => SharedInput::Existing(obj.object_ref()),
2857 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2858 SharedInput::Deleted((id, *seq, mutable, *digest))
2859 }
2860 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2861 SharedInput::Cancelled((id, *seq))
2862 }
2863 }),
2864 }
2865 }
2866
2867 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
2868 match &self.object {
2869 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
2870 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
2871 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2872 }
2873 }
2874}
2875
2876#[derive(Clone)]
2877pub struct InputObjects {
2878 objects: Vec<ObjectReadResult>,
2879}
2880
2881impl std::fmt::Debug for InputObjects {
2882 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2883 f.debug_list().entries(self.objects.iter()).finish()
2884 }
2885}
2886
2887pub struct CheckedInputObjects(InputObjects);
2890
2891impl CheckedInputObjects {
2897 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
2899 Self(inputs)
2900 }
2901
2902 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
2904 Self(InputObjects::new(input_objects))
2905 }
2906
2907 pub fn new_for_replay(input_objects: InputObjects) -> Self {
2909 Self(input_objects)
2910 }
2911
2912 pub fn inner(&self) -> &InputObjects {
2913 &self.0
2914 }
2915
2916 pub fn into_inner(self) -> InputObjects {
2917 self.0
2918 }
2919}
2920
2921impl From<Vec<ObjectReadResult>> for InputObjects {
2922 fn from(objects: Vec<ObjectReadResult>) -> Self {
2923 Self::new(objects)
2924 }
2925}
2926
2927impl InputObjects {
2928 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
2929 Self { objects }
2930 }
2931
2932 pub fn len(&self) -> usize {
2933 self.objects.len()
2934 }
2935
2936 pub fn is_empty(&self) -> bool {
2937 self.objects.is_empty()
2938 }
2939
2940 pub fn contains_deleted_objects(&self) -> bool {
2941 self.objects
2942 .iter()
2943 .any(|obj| obj.is_deleted_shared_object())
2944 }
2945
2946 pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectId>, Version)> {
2949 let mut contains_cancelled = false;
2950 let mut cancel_reason = None;
2951 let mut cancelled_objects = Vec::new();
2952 for obj in &self.objects {
2953 if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
2954 contains_cancelled = true;
2955 if version.is_congested() || version == Version::RANDOMNESS_UNAVAILABLE {
2956 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
2958 cancel_reason = Some(version);
2959 cancelled_objects.push(obj.id());
2960 }
2961 }
2962 }
2963
2964 if !cancelled_objects.is_empty() {
2965 Some((
2966 cancelled_objects,
2967 cancel_reason
2968 .expect("there should be a cancel reason if there are cancelled objects"),
2969 ))
2970 } else {
2971 assert!(!contains_cancelled);
2972 None
2973 }
2974 }
2975
2976 pub fn filter_owned_objects(&self) -> Vec<ObjectReference> {
2977 let owned_objects: Vec<_> = self
2978 .objects
2979 .iter()
2980 .filter_map(|obj| obj.get_owned_objref())
2981 .collect();
2982
2983 trace!(
2984 num_mutable_objects = owned_objects.len(),
2985 "Checked locks and found mutable objects"
2986 );
2987
2988 owned_objects
2989 }
2990
2991 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
2992 self.objects
2993 .iter()
2994 .filter(|obj| obj.is_shared_object())
2995 .map(|obj| {
2996 obj.to_shared_input()
2997 .expect("already filtered for shared objects")
2998 })
2999 .collect()
3000 }
3001
3002 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3003 self.objects
3004 .iter()
3005 .filter_map(|obj| obj.get_previous_transaction())
3006 .collect()
3007 }
3008
3009 pub fn mutable_inputs(&self) -> BTreeMap<ObjectId, (VersionDigest, Owner)> {
3010 self.objects
3011 .iter()
3012 .filter_map(
3013 |ObjectReadResult {
3014 input_object_kind,
3015 object,
3016 }| match (input_object_kind, object) {
3017 (InputObjectKind::MovePackage(_), _) => None,
3018 (
3019 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3020 ObjectReadResultKind::Object(object),
3021 ) => {
3022 if object.is_immutable() {
3023 None
3024 } else {
3025 Some((
3026 object_ref.object_id,
3027 ((object_ref.version, object_ref.digest), object.owner),
3028 ))
3029 }
3030 }
3031 (
3032 InputObjectKind::ImmOrOwnedMoveObject(_),
3033 ObjectReadResultKind::DeletedSharedObject(_, _),
3034 ) => {
3035 unreachable!()
3036 }
3037 (
3038 InputObjectKind::SharedMoveObject { .. },
3039 ObjectReadResultKind::DeletedSharedObject(_, _),
3040 ) => None,
3041 (
3042 InputObjectKind::SharedMoveObject { mutable, .. },
3043 ObjectReadResultKind::Object(object),
3044 ) => {
3045 if *mutable {
3046 let oref = object.object_ref();
3047 Some((oref.object_id, ((oref.version, oref.digest), object.owner)))
3048 } else {
3049 None
3050 }
3051 }
3052 (
3053 InputObjectKind::ImmOrOwnedMoveObject(_),
3054 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3055 ) => {
3056 unreachable!()
3057 }
3058 (
3059 InputObjectKind::SharedMoveObject { .. },
3060 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3061 ) => None,
3062 },
3063 )
3064 .collect()
3065 }
3066
3067 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectReference]) -> Version {
3071 let input_versions = self
3072 .objects
3073 .iter()
3074 .filter_map(|object| match &object.object {
3075 ObjectReadResultKind::Object(object) => {
3076 object.data.as_opt_struct().map(MoveStruct::version)
3077 }
3078 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3079 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3080 })
3081 .chain(
3082 receiving_objects
3083 .iter()
3084 .map(|object_ref| object_ref.version),
3085 );
3086
3087 Version::lamport_increment(input_versions).unwrap()
3088 }
3089
3090 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3091 self.objects.iter().map(
3092 |ObjectReadResult {
3093 input_object_kind, ..
3094 }| input_object_kind,
3095 )
3096 }
3097
3098 pub fn into_object_map(self) -> BTreeMap<ObjectId, Object> {
3099 self.objects
3100 .into_iter()
3101 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3102 .collect()
3103 }
3104
3105 pub fn push(&mut self, object: ObjectReadResult) {
3106 self.objects.push(object);
3107 }
3108
3109 pub fn find_object_id_mut(&mut self, object_id: ObjectId) -> Option<&mut ObjectReadResult> {
3111 self.objects.iter_mut().find(|o| o.id() == object_id)
3112 }
3113
3114 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3115 self.objects.iter()
3116 }
3117
3118 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3119 self.objects.iter().filter_map(|o| o.as_object())
3120 }
3121}
3122
3123#[derive(Clone, Debug)]
3127pub enum ReceivingObjectReadResultKind {
3128 Object(Object),
3129 PreviouslyReceivedObject,
3131}
3132
3133impl ReceivingObjectReadResultKind {
3134 pub fn as_object(&self) -> Option<&Object> {
3135 match &self {
3136 Self::Object(object) => Some(object),
3137 Self::PreviouslyReceivedObject => None,
3138 }
3139 }
3140}
3141
3142pub struct ReceivingObjectReadResult {
3143 pub object_ref: ObjectReference,
3144 pub object: ReceivingObjectReadResultKind,
3145}
3146
3147impl ReceivingObjectReadResult {
3148 pub fn new(object_ref: ObjectReference, object: ReceivingObjectReadResultKind) -> Self {
3149 Self { object_ref, object }
3150 }
3151
3152 pub fn is_previously_received(&self) -> bool {
3153 matches!(
3154 self.object,
3155 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3156 )
3157 }
3158}
3159
3160impl From<Object> for ReceivingObjectReadResultKind {
3161 fn from(object: Object) -> Self {
3162 Self::Object(object)
3163 }
3164}
3165
3166pub struct ReceivingObjects {
3167 pub objects: Vec<ReceivingObjectReadResult>,
3168}
3169
3170impl ReceivingObjects {
3171 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3172 self.objects.iter()
3173 }
3174
3175 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3176 self.objects.iter().filter_map(|o| o.object.as_object())
3177 }
3178}
3179
3180impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3181 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3182 Self { objects }
3183 }
3184}
3185
3186impl Display for CertifiedTransaction {
3187 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3188 let mut writer = String::new();
3189 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3190 writeln!(
3191 writer,
3192 "Signed Authorities Bitmap : {:?}",
3193 self.auth_sig().signers_map
3194 )?;
3195 write!(writer, "{}", self.data().transaction().kind())?;
3196 write!(f, "{writer}")
3197 }
3198}
3199
3200#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3205pub enum TransactionKey {
3206 Digest(TransactionDigest),
3207 RandomnessRound(EpochId, RandomnessRound),
3208}
3209
3210impl TransactionKey {
3211 pub fn unwrap_digest(&self) -> &TransactionDigest {
3212 match self {
3213 TransactionKey::Digest(d) => d,
3214 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3215 }
3216 }
3217}