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