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