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