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 {
1026 fn sender(&self) -> Address;
1028
1029 fn kind(&self) -> &TransactionKind;
1031
1032 fn kind_mut(&mut self) -> &mut TransactionKind;
1034
1035 fn into_kind(self) -> TransactionKind;
1037
1038 fn signers(&self) -> NonEmpty<Address>;
1041
1042 fn gas_data(&self) -> &GasPayment;
1045
1046 fn gas_owner(&self) -> Address;
1048
1049 fn gas(&self) -> &[ObjectReference];
1051
1052 fn gas_price(&self) -> u64;
1054
1055 fn gas_budget(&self) -> u64;
1057
1058 fn expiration(&self) -> &TransactionExpiration;
1060
1061 fn shared_input_objects(&self) -> Vec<SharedObjectReference>;
1067
1068 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)>;
1071
1072 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
1080
1081 fn receiving_objects(&self) -> Vec<ObjectReference>;
1084
1085 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
1088
1089 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult;
1092
1093 fn check_serialized_size(&self, config: &ProtocolConfig) -> IotaResult;
1096
1097 fn check_gas_payment_size(&self, config: &ProtocolConfig) -> UserInputResult;
1100
1101 fn check_sponsorship(&self) -> UserInputResult;
1103
1104 fn is_system_tx(&self) -> bool;
1106 fn is_genesis_tx(&self) -> bool;
1108
1109 fn is_end_of_epoch_tx(&self) -> bool;
1112
1113 fn is_sponsored_tx(&self) -> bool;
1115
1116 fn sender_mut_for_testing(&mut self) -> &mut Address;
1118
1119 fn gas_data_mut(&mut self) -> &mut GasPayment;
1121
1122 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration;
1124
1125 fn new_system_transaction(kind: TransactionKind) -> Transaction;
1128
1129 #[allow(clippy::new_ret_no_self)]
1132 fn new(
1133 kind: TransactionKind,
1134 sender: Address,
1135 gas_payment: ObjectReference,
1136 gas_budget: u64,
1137 gas_price: u64,
1138 ) -> Transaction;
1139
1140 fn new_with_gas_coins(
1143 kind: TransactionKind,
1144 sender: Address,
1145 gas_payment: Vec<ObjectReference>,
1146 gas_budget: u64,
1147 gas_price: u64,
1148 ) -> Transaction;
1149
1150 fn new_with_gas_coins_allow_sponsor(
1154 kind: TransactionKind,
1155 sender: Address,
1156 gas_payment: Vec<ObjectReference>,
1157 gas_budget: u64,
1158 gas_price: u64,
1159 gas_sponsor: Address,
1160 ) -> Transaction;
1161
1162 fn new_with_gas_data(
1164 kind: TransactionKind,
1165 sender: Address,
1166 gas_data: GasPayment,
1167 ) -> Transaction;
1168
1169 fn new_move_call(
1172 sender: Address,
1173 package: ObjectId,
1174 module: Identifier,
1175 function: Identifier,
1176 type_arguments: Vec<TypeTag>,
1177 gas_payment: ObjectReference,
1178 arguments: Vec<CallArg>,
1179 gas_budget: u64,
1180 gas_price: u64,
1181 ) -> anyhow::Result<Transaction>;
1182
1183 fn new_move_call_with_gas_coins(
1186 sender: Address,
1187 package: ObjectId,
1188 module: Identifier,
1189 function: Identifier,
1190 type_arguments: Vec<TypeTag>,
1191 gas_payment: Vec<ObjectReference>,
1192 arguments: Vec<CallArg>,
1193 gas_budget: u64,
1194 gas_price: u64,
1195 ) -> anyhow::Result<Transaction>;
1196
1197 fn new_transfer(
1199 recipient: Address,
1200 object_ref: ObjectReference,
1201 sender: Address,
1202 gas_payment: ObjectReference,
1203 gas_budget: u64,
1204 gas_price: u64,
1205 ) -> Transaction;
1206
1207 fn new_transfer_iota(
1211 recipient: Address,
1212 sender: Address,
1213 amount: Option<u64>,
1214 gas_payment: ObjectReference,
1215 gas_budget: u64,
1216 gas_price: u64,
1217 ) -> Transaction;
1218
1219 fn new_transfer_iota_allow_sponsor(
1223 recipient: Address,
1224 sender: Address,
1225 amount: Option<u64>,
1226 gas_payment: ObjectReference,
1227 gas_budget: u64,
1228 gas_price: u64,
1229 gas_sponsor: Address,
1230 ) -> Transaction;
1231
1232 fn new_pay(
1236 sender: Address,
1237 coins: Vec<ObjectReference>,
1238 recipients: Vec<Address>,
1239 amounts: Vec<u64>,
1240 gas_payment: ObjectReference,
1241 gas_budget: u64,
1242 gas_price: u64,
1243 ) -> anyhow::Result<Transaction>;
1244
1245 fn new_pay_iota(
1249 sender: Address,
1250 coins: Vec<ObjectReference>,
1251 recipients: Vec<Address>,
1252 amounts: Vec<u64>,
1253 gas_payment: ObjectReference,
1254 gas_budget: u64,
1255 gas_price: u64,
1256 ) -> anyhow::Result<Transaction>;
1257
1258 fn new_pay_all_iota(
1261 sender: Address,
1262 coins: Vec<ObjectReference>,
1263 recipient: Address,
1264 gas_payment: ObjectReference,
1265 gas_budget: u64,
1266 gas_price: u64,
1267 ) -> Transaction;
1268
1269 fn new_split_coin(
1272 sender: Address,
1273 coin: ObjectReference,
1274 amounts: Vec<u64>,
1275 gas_payment: ObjectReference,
1276 gas_budget: u64,
1277 gas_price: u64,
1278 ) -> Transaction;
1279
1280 fn new_module(
1282 sender: Address,
1283 gas_payment: ObjectReference,
1284 modules: Vec<Vec<u8>>,
1285 dep_ids: Vec<ObjectId>,
1286 gas_budget: u64,
1287 gas_price: u64,
1288 ) -> Transaction;
1289
1290 fn new_upgrade(
1293 sender: Address,
1294 gas_payment: ObjectReference,
1295 package_id: ObjectId,
1296 modules: Vec<Vec<u8>>,
1297 dep_ids: Vec<ObjectId>,
1298 upgrade_capability_and_owner: (ObjectReference, Owner),
1299 upgrade_policy: u8,
1300 digest: Vec<u8>,
1301 gas_budget: u64,
1302 gas_price: u64,
1303 ) -> anyhow::Result<Transaction>;
1304
1305 fn new_programmable(
1308 sender: Address,
1309 gas_payment: Vec<ObjectReference>,
1310 pt: ProgrammableTransaction,
1311 gas_budget: u64,
1312 gas_price: u64,
1313 ) -> Transaction;
1314
1315 fn new_programmable_allow_sponsor(
1318 sender: Address,
1319 gas_payment: Vec<ObjectReference>,
1320 pt: ProgrammableTransaction,
1321 gas_budget: u64,
1322 gas_price: u64,
1323 sponsor: Address,
1324 ) -> Transaction;
1325
1326 fn message_version(&self) -> u64;
1328
1329 fn execution_parts(&self) -> (TransactionKind, Address, GasPayment);
1332}
1333
1334fn tx_bcs_size<T: Serialize>(tx: &T) -> IotaResult<usize> {
1335 bcs::serialized_size(tx).map_err(|e| IotaError::TransactionSerialization {
1336 error: e.to_string(),
1337 })
1338}
1339
1340fn check_transaction_size<T: Serialize>(
1343 transaction: &T,
1344 config: &ProtocolConfig,
1345) -> IotaResult<usize> {
1346 let tx_size = tx_bcs_size(transaction)?;
1347 let max_tx_size_bytes = config.max_tx_size_bytes();
1348 fp_ensure!(
1349 tx_size as u64 <= max_tx_size_bytes,
1350 IotaError::UserInput {
1351 error: UserInputError::SizeLimitExceeded {
1352 limit: format!(
1353 "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
1354 ),
1355 value: tx_size.to_string(),
1356 }
1357 }
1358 );
1359 Ok(tx_size)
1360}
1361
1362impl TransactionAPI for Transaction {
1363 fn check_serialized_size(&self, config: &ProtocolConfig) -> IotaResult {
1364 check_transaction_size(self, config).map(|_| ())
1365 }
1366
1367 fn sender(&self) -> Address {
1368 match self {
1369 Self::V1(v1) => v1.sender,
1370 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1371 }
1372 }
1373
1374 fn kind(&self) -> &TransactionKind {
1375 match self {
1376 Self::V1(v1) => &v1.kind,
1377 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1378 }
1379 }
1380
1381 fn kind_mut(&mut self) -> &mut TransactionKind {
1382 match self {
1383 Self::V1(v1) => &mut v1.kind,
1384 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1385 }
1386 }
1387
1388 fn into_kind(self) -> TransactionKind {
1389 match self {
1390 Self::V1(v1) => v1.kind,
1391 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1392 }
1393 }
1394
1395 fn signers(&self) -> NonEmpty<Address> {
1396 let mut signers = nonempty![self.sender()];
1397 if self.gas_owner() != self.sender() {
1398 signers.push(self.gas_owner());
1399 }
1400 signers
1401 }
1402
1403 fn gas_data(&self) -> &GasPayment {
1404 match self {
1405 Self::V1(v1) => &v1.gas_payment,
1406 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1407 }
1408 }
1409
1410 fn gas_owner(&self) -> Address {
1411 self.gas_data().owner
1412 }
1413
1414 fn gas(&self) -> &[ObjectReference] {
1415 &self.gas_data().objects
1416 }
1417
1418 fn gas_price(&self) -> u64 {
1419 self.gas_data().price
1420 }
1421
1422 fn gas_budget(&self) -> u64 {
1423 self.gas_data().budget
1424 }
1425
1426 fn expiration(&self) -> &TransactionExpiration {
1427 match self {
1428 Self::V1(v1) => &v1.expiration,
1429 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1430 }
1431 }
1432
1433 fn shared_input_objects(&self) -> Vec<SharedObjectReference> {
1434 self.kind().shared_input_objects().collect()
1435 }
1436
1437 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
1438 self.kind().move_calls()
1439 }
1440
1441 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1442 let mut inputs = self.kind().input_objects()?;
1443
1444 if !self.kind().is_system() {
1445 inputs.extend(
1446 self.gas()
1447 .iter()
1448 .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
1449 );
1450 }
1451 Ok(inputs)
1452 }
1453
1454 fn receiving_objects(&self) -> Vec<ObjectReference> {
1455 self.kind().receiving_objects()
1456 }
1457
1458 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1459 fp_ensure!(!self.gas().is_empty(), UserInputError::MissingGasPayment);
1460 self.check_gas_payment_size(config)?;
1461 if config.validate_input_object_versions() {
1462 for gas_object in self.gas() {
1463 input_object_version_validity_check(gas_object.version)?;
1464 }
1465 }
1466 self.validity_check_no_gas_check(config)
1467 }
1468
1469 #[instrument(level = "trace", skip_all)]
1470 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
1471 self.kind().validity_check(config)?;
1472 self.check_sponsorship()
1473 }
1474
1475 fn check_gas_payment_size(&self, config: &ProtocolConfig) -> UserInputResult {
1476 fp_ensure!(
1477 self.gas().len() < config.max_gas_payment_objects() as usize,
1478 UserInputError::SizeLimitExceeded {
1479 limit: "maximum number of gas payment objects".to_string(),
1480 value: config.max_gas_payment_objects().to_string()
1481 }
1482 );
1483 Ok(())
1484 }
1485
1486 fn is_sponsored_tx(&self) -> bool {
1487 self.gas_owner() != self.sender()
1488 }
1489
1490 fn check_sponsorship(&self) -> UserInputResult {
1491 if self.gas_owner() == self.sender() {
1492 return Ok(());
1493 }
1494 if matches!(self.kind(), TransactionKind::Programmable(_)) {
1495 return Ok(());
1496 }
1497 Err(UserInputError::UnsupportedSponsoredTransactionKind)
1498 }
1499
1500 fn is_end_of_epoch_tx(&self) -> bool {
1501 matches!(self.kind(), TransactionKind::EndOfEpoch(_))
1502 }
1503
1504 fn is_system_tx(&self) -> bool {
1505 self.kind().is_system()
1506 }
1507
1508 fn is_genesis_tx(&self) -> bool {
1509 matches!(self.kind(), TransactionKind::Genesis(_))
1510 }
1511
1512 fn sender_mut_for_testing(&mut self) -> &mut Address {
1513 match self {
1514 Self::V1(v1) => &mut v1.sender,
1515 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1516 }
1517 }
1518
1519 fn gas_data_mut(&mut self) -> &mut GasPayment {
1520 match self {
1521 Self::V1(v1) => &mut v1.gas_payment,
1522 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1523 }
1524 }
1525
1526 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
1527 match self {
1528 Self::V1(v1) => &mut v1.expiration,
1529 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1530 }
1531 }
1532
1533 fn new_system_transaction(kind: TransactionKind) -> Transaction {
1534 assert!(kind.is_system());
1535 let sender = Address::ZERO;
1536 Transaction::V1(TransactionV1 {
1537 kind,
1538 sender,
1539 gas_payment: GasPayment {
1540 price: GAS_PRICE_FOR_SYSTEM_TX,
1541 owner: sender,
1542 objects: vec![ObjectReference::new(
1543 ObjectId::ZERO,
1544 Version::default(),
1545 ObjectDigest::MIN,
1546 )],
1547 budget: 0,
1548 },
1549 expiration: TransactionExpiration::None,
1550 })
1551 }
1552
1553 fn new(
1554 kind: TransactionKind,
1555 sender: Address,
1556 gas_payment: ObjectReference,
1557 gas_budget: u64,
1558 gas_price: u64,
1559 ) -> Transaction {
1560 Transaction::V1(TransactionV1 {
1561 kind,
1562 sender,
1563 gas_payment: GasPayment {
1564 price: gas_price,
1565 owner: sender,
1566 objects: vec![gas_payment],
1567 budget: gas_budget,
1568 },
1569 expiration: TransactionExpiration::None,
1570 })
1571 }
1572
1573 fn new_with_gas_coins(
1574 kind: TransactionKind,
1575 sender: Address,
1576 gas_payment: Vec<ObjectReference>,
1577 gas_budget: u64,
1578 gas_price: u64,
1579 ) -> Transaction {
1580 Transaction::new_with_gas_coins_allow_sponsor(
1581 kind,
1582 sender,
1583 gas_payment,
1584 gas_budget,
1585 gas_price,
1586 sender,
1587 )
1588 }
1589
1590 fn new_with_gas_coins_allow_sponsor(
1591 kind: TransactionKind,
1592 sender: Address,
1593 gas_payment: Vec<ObjectReference>,
1594 gas_budget: u64,
1595 gas_price: u64,
1596 gas_sponsor: Address,
1597 ) -> Transaction {
1598 Transaction::V1(TransactionV1 {
1599 kind,
1600 sender,
1601 gas_payment: GasPayment {
1602 price: gas_price,
1603 owner: gas_sponsor,
1604 objects: gas_payment,
1605 budget: gas_budget,
1606 },
1607 expiration: TransactionExpiration::None,
1608 })
1609 }
1610
1611 fn new_with_gas_data(
1612 kind: TransactionKind,
1613 sender: Address,
1614 gas_data: GasPayment,
1615 ) -> Transaction {
1616 Transaction::V1(TransactionV1 {
1617 kind,
1618 sender,
1619 gas_payment: gas_data,
1620 expiration: TransactionExpiration::None,
1621 })
1622 }
1623
1624 fn new_move_call(
1625 sender: Address,
1626 package: ObjectId,
1627 module: Identifier,
1628 function: Identifier,
1629 type_arguments: Vec<TypeTag>,
1630 gas_payment: ObjectReference,
1631 arguments: Vec<CallArg>,
1632 gas_budget: u64,
1633 gas_price: u64,
1634 ) -> anyhow::Result<Transaction> {
1635 Transaction::new_move_call_with_gas_coins(
1636 sender,
1637 package,
1638 module,
1639 function,
1640 type_arguments,
1641 vec![gas_payment],
1642 arguments,
1643 gas_budget,
1644 gas_price,
1645 )
1646 }
1647
1648 fn new_move_call_with_gas_coins(
1649 sender: Address,
1650 package: ObjectId,
1651 module: Identifier,
1652 function: Identifier,
1653 type_arguments: Vec<TypeTag>,
1654 gas_payment: Vec<ObjectReference>,
1655 arguments: Vec<CallArg>,
1656 gas_budget: u64,
1657 gas_price: u64,
1658 ) -> anyhow::Result<Transaction> {
1659 let pt = {
1660 let mut builder = ProgrammableTransactionBuilder::new();
1661 builder.move_call(package, module, function, type_arguments, arguments)?;
1662 builder.finish()
1663 };
1664 Ok(Transaction::new_programmable(
1665 sender,
1666 gas_payment,
1667 pt,
1668 gas_budget,
1669 gas_price,
1670 ))
1671 }
1672
1673 fn new_transfer(
1674 recipient: Address,
1675 object_ref: ObjectReference,
1676 sender: Address,
1677 gas_payment: ObjectReference,
1678 gas_budget: u64,
1679 gas_price: u64,
1680 ) -> Transaction {
1681 let pt = {
1682 let mut builder = ProgrammableTransactionBuilder::new();
1683 builder.transfer_object(recipient, object_ref).unwrap();
1684 builder.finish()
1685 };
1686 Transaction::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1687 }
1688
1689 fn new_transfer_iota(
1690 recipient: Address,
1691 sender: Address,
1692 amount: Option<u64>,
1693 gas_payment: ObjectReference,
1694 gas_budget: u64,
1695 gas_price: u64,
1696 ) -> Transaction {
1697 Transaction::new_transfer_iota_allow_sponsor(
1698 recipient,
1699 sender,
1700 amount,
1701 gas_payment,
1702 gas_budget,
1703 gas_price,
1704 sender,
1705 )
1706 }
1707
1708 fn new_transfer_iota_allow_sponsor(
1709 recipient: Address,
1710 sender: Address,
1711 amount: Option<u64>,
1712 gas_payment: ObjectReference,
1713 gas_budget: u64,
1714 gas_price: u64,
1715 gas_sponsor: Address,
1716 ) -> Transaction {
1717 let pt = {
1718 let mut builder = ProgrammableTransactionBuilder::new();
1719 builder.transfer_iota(recipient, amount);
1720 builder.finish()
1721 };
1722 Transaction::new_programmable_allow_sponsor(
1723 sender,
1724 vec![gas_payment],
1725 pt,
1726 gas_budget,
1727 gas_price,
1728 gas_sponsor,
1729 )
1730 }
1731
1732 fn new_pay(
1733 sender: Address,
1734 coins: Vec<ObjectReference>,
1735 recipients: Vec<Address>,
1736 amounts: Vec<u64>,
1737 gas_payment: ObjectReference,
1738 gas_budget: u64,
1739 gas_price: u64,
1740 ) -> anyhow::Result<Transaction> {
1741 let pt = {
1742 let mut builder = ProgrammableTransactionBuilder::new();
1743 builder.pay(coins, recipients, amounts)?;
1744 builder.finish()
1745 };
1746 Ok(Transaction::new_programmable(
1747 sender,
1748 vec![gas_payment],
1749 pt,
1750 gas_budget,
1751 gas_price,
1752 ))
1753 }
1754
1755 fn new_pay_iota(
1756 sender: Address,
1757 mut coins: Vec<ObjectReference>,
1758 recipients: Vec<Address>,
1759 amounts: Vec<u64>,
1760 gas_payment: ObjectReference,
1761 gas_budget: u64,
1762 gas_price: u64,
1763 ) -> anyhow::Result<Transaction> {
1764 coins.insert(0, gas_payment);
1765 let pt = {
1766 let mut builder = ProgrammableTransactionBuilder::new();
1767 builder.pay_iota(recipients, amounts)?;
1768 builder.finish()
1769 };
1770 Ok(Transaction::new_programmable(
1771 sender, coins, pt, gas_budget, gas_price,
1772 ))
1773 }
1774
1775 fn new_pay_all_iota(
1776 sender: Address,
1777 mut coins: Vec<ObjectReference>,
1778 recipient: Address,
1779 gas_payment: ObjectReference,
1780 gas_budget: u64,
1781 gas_price: u64,
1782 ) -> Transaction {
1783 coins.insert(0, gas_payment);
1784 let pt = {
1785 let mut builder = ProgrammableTransactionBuilder::new();
1786 builder.pay_all_iota(recipient);
1787 builder.finish()
1788 };
1789 Transaction::new_programmable(sender, coins, pt, gas_budget, gas_price)
1790 }
1791
1792 fn new_split_coin(
1793 sender: Address,
1794 coin: ObjectReference,
1795 amounts: Vec<u64>,
1796 gas_payment: ObjectReference,
1797 gas_budget: u64,
1798 gas_price: u64,
1799 ) -> Transaction {
1800 let pt = {
1801 let mut builder = ProgrammableTransactionBuilder::new();
1802 builder.split_coin(sender, coin, amounts);
1803 builder.finish()
1804 };
1805 Transaction::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1806 }
1807
1808 fn new_module(
1809 sender: Address,
1810 gas_payment: ObjectReference,
1811 modules: Vec<Vec<u8>>,
1812 dep_ids: Vec<ObjectId>,
1813 gas_budget: u64,
1814 gas_price: u64,
1815 ) -> Transaction {
1816 let pt = {
1817 let mut builder = ProgrammableTransactionBuilder::new();
1818 let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
1819 builder.transfer_arg(sender, upgrade_cap);
1820 builder.finish()
1821 };
1822 Transaction::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1823 }
1824
1825 fn new_upgrade(
1826 sender: Address,
1827 gas_payment: ObjectReference,
1828 package_id: ObjectId,
1829 modules: Vec<Vec<u8>>,
1830 dep_ids: Vec<ObjectId>,
1831 (upgrade_capability, capability_owner): (ObjectReference, Owner),
1832 upgrade_policy: u8,
1833 digest: Vec<u8>,
1834 gas_budget: u64,
1835 gas_price: u64,
1836 ) -> anyhow::Result<Transaction> {
1837 let pt = {
1838 let mut builder = ProgrammableTransactionBuilder::new();
1839 let capability_arg = match capability_owner {
1840 Owner::Address(_) => CallArg::ImmutableOrOwned(upgrade_capability),
1841 Owner::Shared(initial_shared_version) => {
1842 CallArg::Shared(SharedObjectReference::new(
1843 upgrade_capability.object_id,
1844 initial_shared_version,
1845 true,
1846 ))
1847 }
1848 Owner::Immutable => {
1849 bail!("Upgrade capability is stored immutably and cannot be used for upgrades");
1850 }
1851 Owner::Object(_) => {
1852 bail!("Upgrade capability controlled by object");
1853 }
1854 _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
1855 };
1856 builder.obj(capability_arg).unwrap();
1857 let upgrade_arg = builder.pure(upgrade_policy).unwrap();
1858 let digest_arg = builder.pure(digest).unwrap();
1859 let upgrade_ticket = builder.programmable_move_call(
1860 ObjectId::FRAMEWORK,
1861 Identifier::PACKAGE_MODULE,
1862 Identifier::from_static("authorize_upgrade"),
1863 vec![],
1864 vec![Argument::Input(0), upgrade_arg, digest_arg],
1865 );
1866 let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
1867
1868 builder.programmable_move_call(
1869 ObjectId::FRAMEWORK,
1870 Identifier::PACKAGE_MODULE,
1871 Identifier::from_static("commit_upgrade"),
1872 vec![],
1873 vec![Argument::Input(0), upgrade_receipt],
1874 );
1875
1876 builder.finish()
1877 };
1878 Ok(Transaction::new_programmable(
1879 sender,
1880 vec![gas_payment],
1881 pt,
1882 gas_budget,
1883 gas_price,
1884 ))
1885 }
1886
1887 fn new_programmable(
1888 sender: Address,
1889 gas_payment: Vec<ObjectReference>,
1890 pt: ProgrammableTransaction,
1891 gas_budget: u64,
1892 gas_price: u64,
1893 ) -> Transaction {
1894 Transaction::new_programmable_allow_sponsor(
1895 sender,
1896 gas_payment,
1897 pt,
1898 gas_budget,
1899 gas_price,
1900 sender,
1901 )
1902 }
1903
1904 fn new_programmable_allow_sponsor(
1905 sender: Address,
1906 gas_payment: Vec<ObjectReference>,
1907 pt: ProgrammableTransaction,
1908 gas_budget: u64,
1909 gas_price: u64,
1910 sponsor: Address,
1911 ) -> Transaction {
1912 let kind = TransactionKind::Programmable(pt);
1913 Transaction::new_with_gas_coins_allow_sponsor(
1914 kind,
1915 sender,
1916 gas_payment,
1917 gas_budget,
1918 gas_price,
1919 sponsor,
1920 )
1921 }
1922
1923 fn message_version(&self) -> u64 {
1924 match self {
1925 Transaction::V1(_) => 1,
1926 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1927 }
1928 }
1929
1930 fn execution_parts(&self) -> (TransactionKind, Address, GasPayment) {
1931 (self.kind().clone(), self.sender(), self.gas_data().clone())
1932 }
1933}
1934
1935pub struct TxValidityCheckContext<'a> {
1936 pub config: &'a ProtocolConfig,
1937 pub epoch: EpochId,
1938}
1939
1940pub fn merge_authenticator_input_objects<'a>(
1947 move_authenticators: impl IntoIterator<Item = &'a MoveAuthenticator>,
1948 input_objects: &mut Vec<InputObjectKind>,
1949) -> UserInputResult<()> {
1950 for move_authenticator in move_authenticators {
1951 for auth_object in move_authenticator.input_objects() {
1952 let entry = input_objects
1953 .iter_mut()
1954 .find(|o| o.object_id() == auth_object.object_id());
1955
1956 match entry {
1957 None => input_objects.push(auth_object),
1958 Some(existing) => existing.left_union_with_checks(&auth_object)?,
1959 }
1960 }
1961 }
1962 Ok(())
1963}
1964
1965pub trait SenderSignedTransactionAPI {
1972 fn new_from_sender_signature(
1975 tx: Transaction,
1976 tx_signature: SimpleSignature,
1977 ) -> SenderSignedTransaction;
1978
1979 fn add_signature(&mut self, new_signature: SimpleSignature);
1982
1983 fn get_signer_sig_mapping(&self) -> IotaResult<BTreeMap<Address, &UserSignature>>;
1986
1987 fn has_multisig(&self) -> bool;
1989
1990 fn transaction_mut_for_testing(&mut self) -> &mut Transaction;
1992
1993 fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<UserSignature>;
1995
1996 fn serialized_size(&self) -> IotaResult<usize>;
1998
1999 fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, IotaError>;
2003
2004 fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>>;
2012
2013 fn split_input_objects_into_groups_for_reading(
2019 &self,
2020 input_objects: InputObjects,
2021 ) -> IotaResult<(InputObjects, Vec<(InputObjects, ObjectReadResult)>)>;
2022
2023 fn contains_shared_object(&self) -> bool;
2027
2028 fn shared_input_objects(&self) -> Vec<SharedObjectReference>;
2037
2038 fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>>;
2050
2051 fn uses_randomness(&self) -> bool;
2056}
2057
2058impl SenderSignedTransactionAPI for SenderSignedTransaction {
2059 fn new_from_sender_signature(
2060 tx: Transaction,
2061 tx_signature: SimpleSignature,
2062 ) -> SenderSignedTransaction {
2063 Self::new(tx, vec![tx_signature.into()])
2064 }
2065
2066 fn add_signature(&mut self, new_signature: SimpleSignature) {
2067 self.signed_transaction_mut()
2068 .signatures
2069 .push(new_signature.into());
2070 }
2071
2072 fn get_signer_sig_mapping(&self) -> IotaResult<BTreeMap<Address, &UserSignature>> {
2073 let mut mapping = BTreeMap::new();
2074 for sig in &self.signed_transaction().signatures {
2075 let address = sig.derive_address();
2076 mapping.insert(address, sig);
2077 }
2078 Ok(mapping)
2079 }
2080
2081 fn has_multisig(&self) -> bool {
2082 self.signatures().iter().any(|sig| sig.is_multisig())
2083 }
2084
2085 fn transaction_mut_for_testing(&mut self) -> &mut Transaction {
2086 &mut self.signed_transaction_mut().transaction
2087 }
2088
2089 fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<UserSignature> {
2090 &mut self.signed_transaction_mut().signatures
2091 }
2092
2093 fn serialized_size(&self) -> IotaResult<usize> {
2094 tx_bcs_size(self)
2095 }
2096
2097 fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, IotaError> {
2098 check_user_signature_protocol_compatibility(self, context.config)?;
2101
2102 let tx = self.transaction();
2105 fp_ensure!(
2106 !tx.is_system_tx(),
2107 IotaError::UserInput {
2108 error: UserInputError::Unsupported(
2109 "SenderSignedTransaction must not contain system transaction".to_string()
2110 )
2111 }
2112 );
2113
2114 if match &tx.expiration() {
2116 TransactionExpiration::None => false,
2117 TransactionExpiration::Epoch(exp_poch) => *exp_poch < context.epoch,
2118 _ => unimplemented!(
2119 "a new TransactionExpiration enum variant was added and needs to be handled"
2120 ),
2121 } {
2122 return Err(IotaError::TransactionExpired);
2123 }
2124
2125 let tx_size = check_transaction_size(self, context.config)?;
2127
2128 tx.validity_check(context.config)
2129 .map_err(Into::<IotaError>::into)?;
2130
2131 move_authenticators_validity_check(self, context.config)?;
2132
2133 Ok(tx_size)
2134 }
2135
2136 fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>> {
2137 let mut input_objects_set = self
2138 .transaction()
2139 .input_objects()?
2140 .into_iter()
2141 .collect::<HashSet<_>>();
2142
2143 self.move_authenticators()
2144 .into_iter()
2145 .for_each(|authenticator| {
2146 input_objects_set.extend(authenticator.input_objects());
2147 });
2148
2149 Ok(input_objects_set.into_iter().collect::<Vec<_>>())
2150 }
2151
2152 fn split_input_objects_into_groups_for_reading(
2153 &self,
2154 input_objects: InputObjects,
2155 ) -> IotaResult<(InputObjects, Vec<(InputObjects, ObjectReadResult)>)> {
2156 let input_objects_map = input_objects
2157 .iter()
2158 .map(|o| (&o.input_object_kind, o))
2159 .collect::<HashMap<_, _>>();
2160
2161 let mut tx_input_objects: InputObjects = self
2162 .transaction()
2163 .input_objects()?
2164 .iter()
2165 .map(|k| {
2166 input_objects_map
2167 .get(k)
2168 .map(|&r| r.clone())
2169 .expect("All transaction input objects are expected to be present")
2170 })
2171 .collect::<Vec<_>>()
2172 .into();
2173 if let Some((gas_object_id, version)) = input_objects.gas_object_cancellation() {
2174 tx_input_objects.set_gas_object_cancellation(gas_object_id, version);
2175 }
2176
2177 let per_authenticator_inputs =
2178 self.move_authenticators()
2179 .into_iter()
2180 .map(|move_authenticator| {
2181 let authenticator_input_objects = move_authenticator
2182 .input_objects()
2183 .iter()
2184 .map(|k| {
2185 input_objects_map.get(k).map(|&r| r.clone()).expect(
2186 "All authenticator input objects are expected to be present",
2187 )
2188 })
2189 .collect::<Vec<_>>()
2190 .into();
2191
2192 let account_objects = move_authenticator
2193 .object_to_authenticate()
2194 .input_object_kind()
2195 .iter()
2196 .map(|k| {
2197 input_objects_map
2198 .get(k)
2199 .map(|&r| r.clone())
2200 .expect("Account object is expected to be present")
2201 })
2202 .collect::<Vec<_>>();
2203
2204 debug_assert!(
2205 account_objects.len() == 1,
2206 "Only one account object must be loaded"
2207 );
2208
2209 (
2210 authenticator_input_objects,
2211 account_objects
2212 .into_iter()
2213 .next()
2214 .expect("Account object is expected to be present"),
2215 )
2216 })
2217 .collect();
2218
2219 Ok((tx_input_objects, per_authenticator_inputs))
2220 }
2221
2222 fn contains_shared_object(&self) -> bool {
2223 !self.shared_input_objects().is_empty()
2224 }
2225
2226 fn shared_input_objects(&self) -> Vec<SharedObjectReference> {
2227 let mut input_objects = self.transaction().shared_input_objects();
2229
2230 self.move_authenticators()
2232 .into_iter()
2233 .for_each(|move_authenticator| {
2234 for auth_shared_object in move_authenticator.shared_objects() {
2235 let entry = input_objects
2236 .iter_mut()
2237 .find(|o| o.object_id == auth_shared_object.object_id);
2238
2239 match entry {
2240 None => input_objects.push(auth_shared_object),
2241 Some(existing) => {
2242 left_union_shared_input_objects(existing, &auth_shared_object)
2243 .expect("union of shared objects should not fail")
2244 }
2245 }
2246 }
2247 });
2248
2249 input_objects
2250 }
2251
2252 fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>> {
2253 let mut input_objects = self.transaction().input_objects()?;
2256
2257 merge_authenticator_input_objects(self.move_authenticators(), &mut input_objects)?;
2259
2260 Ok(input_objects)
2261 }
2262
2263 fn uses_randomness(&self) -> bool {
2264 self.shared_input_objects()
2265 .iter()
2266 .any(|obj| obj.object_id == ObjectId::RANDOMNESS_STATE)
2267 }
2268}
2269
2270fn check_user_signature_protocol_compatibility(
2271 signed_tx: &SenderSignedTransaction,
2272 config: &ProtocolConfig,
2273) -> IotaResult {
2274 for sig in signed_tx.signatures() {
2275 match sig {
2276 UserSignature::PasskeyAuthenticator(_) => {
2277 if !config.passkey_auth() {
2278 return Err(IotaError::UserInput {
2279 error: UserInputError::Unsupported(
2280 "passkey is not enabled on this network".to_string(),
2281 ),
2282 });
2283 }
2284 }
2285 UserSignature::MoveAuthenticator(_) => {
2286 if !config.enable_move_authentication() {
2287 return Err(IotaError::UserInput {
2288 error: UserInputError::Unsupported(
2289 "`Move authentication` is not enabled on this network".to_string(),
2290 ),
2291 });
2292 }
2293 }
2294 UserSignature::Simple(_) | UserSignature::Multisig(_) => (),
2295 _ => {
2296 unimplemented!("a new UserSignature variant was added and needs to be handled")
2297 }
2298 }
2299 }
2300
2301 Ok(())
2302}
2303
2304fn move_authenticators_validity_check(
2305 signed_tx: &SenderSignedTransaction,
2306 config: &ProtocolConfig,
2307) -> IotaResult {
2308 let authenticators = signed_tx.move_authenticators();
2309
2310 authenticators
2312 .iter()
2313 .try_for_each(|authenticator| authenticator.validity_check(config))?;
2314
2315 let authenticators_num = authenticators.len();
2317 if authenticators_num > 0 {
2318 let tx = signed_tx.transaction();
2319
2320 fp_ensure!(
2321 tx.kind().is_programmable(),
2322 UserInputError::Unsupported(
2323 "SenderSignedTransaction with MoveAuthenticator must be a programmable transaction"
2324 .to_string(),
2325 )
2326 .into()
2327 );
2328
2329 if !config.enable_move_authentication_for_sponsor() {
2330 fp_ensure!(
2331 authenticators_num == 1,
2332 UserInputError::Unsupported(
2333 "SenderSignedTransaction with more than one MoveAuthenticator is not supported"
2334 .to_string(),
2335 )
2336 .into()
2337 );
2338
2339 fp_ensure!(
2340 signed_tx.sender_move_authenticator().is_some(),
2341 UserInputError::Unsupported(
2342 "SenderSignedTransaction can have MoveAuthenticator only for the sender"
2343 .to_string(),
2344 )
2345 .into()
2346 );
2347 }
2348
2349 check_move_authenticators_input_consistency(tx, &authenticators)?;
2350 }
2351
2352 Ok(())
2353}
2354
2355fn check_move_authenticators_input_consistency(
2356 tx: &Transaction,
2357 authenticators: &[&MoveAuthenticator],
2358) -> IotaResult {
2359 let mut checked_inputs = tx
2361 .kind()
2362 .input_objects()?
2363 .into_iter()
2364 .map(|o| (o.object_id(), o))
2365 .collect::<HashMap<_, _>>();
2366
2367 authenticators.iter().try_for_each(|authenticator| {
2368 authenticator
2369 .input_objects()
2370 .iter()
2371 .try_for_each(|auth_input_object| {
2372 match checked_inputs.get(&auth_input_object.object_id()) {
2373 Some(existing) => {
2374 auth_input_object.check_consistency_for_authentication(existing)?
2375 }
2376 None => {
2377 checked_inputs.insert(auth_input_object.object_id(), *auth_input_object);
2378 }
2379 };
2380
2381 Ok(())
2382 })
2383 })
2384}
2385
2386impl Message for SenderSignedTransaction {
2387 type DigestType = TransactionDigest;
2388 const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2389
2390 fn digest(&self) -> Self::DigestType {
2393 self.transaction().digest()
2394 }
2395}
2396
2397impl<S> Envelope<SenderSignedTransaction, S> {
2398 pub fn sender_address(&self) -> Address {
2399 self.data().transaction().sender()
2400 }
2401
2402 pub fn gas(&self) -> &[ObjectReference] {
2403 self.data().transaction().gas()
2404 }
2405
2406 pub fn key(&self) -> TransactionKey {
2408 match &self.data().transaction().kind() {
2409 TransactionKind::RandomnessStateUpdate(rsu) => {
2410 TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2411 }
2412 _ => TransactionKey::Digest(*self.digest()),
2413 }
2414 }
2415
2416 pub fn non_digest_key(&self) -> Option<TransactionKey> {
2421 match &self.data().transaction().kind() {
2422 TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2423 rsu.epoch,
2424 rsu.randomness_round,
2425 )),
2426 _ => None,
2427 }
2428 }
2429
2430 pub fn is_system_tx(&self) -> bool {
2431 self.data().transaction().is_system_tx()
2432 }
2433
2434 pub fn is_sponsored_tx(&self) -> bool {
2435 self.data().transaction().is_sponsored_tx()
2436 }
2437}
2438
2439impl TransactionEnvelope {
2440 pub fn from_data_and_signer(
2441 tx: Transaction,
2442 signers: Vec<&impl iota_sdk_crypto::Signer<SimpleSignature>>,
2443 ) -> Self {
2444 let signatures = {
2445 let digest = tx.signing_digest();
2446 signers.into_iter().map(|s| s.sign(&digest)).collect()
2447 };
2448 Self::from_data(tx, signatures)
2449 }
2450
2451 pub fn from_data(tx: Transaction, signatures: Vec<SimpleSignature>) -> Self {
2453 Self::from_user_sig_data(tx, signatures.into_iter().map(|s| s.into()).collect())
2454 }
2455
2456 pub fn signature_from_signer(
2457 tx: Transaction,
2458 intent: Intent,
2459 signer: &impl iota_sdk_crypto::Signer<SimpleSignature>,
2460 ) -> SimpleSignature {
2461 let digest = IntentMessage::new(intent, tx).signing_digest();
2462 signer.sign(&digest)
2463 }
2464
2465 pub fn from_user_sig_data(tx: Transaction, signatures: Vec<UserSignature>) -> Self {
2466 Self::new(SenderSignedTransaction::new(tx, signatures))
2467 }
2468
2469 pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2472 (
2473 Base64::from_bytes(&self.data().transaction().to_bcs()),
2474 self.data()
2475 .signatures()
2476 .iter()
2477 .map(|s| Base64::from_bytes(&s.to_bytes()))
2478 .collect(),
2479 )
2480 }
2481}
2482
2483impl VerifiedTransaction {
2484 pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2485 GenesisTransaction { objects, events }
2486 .pipe(TransactionKind::Genesis)
2487 .pipe(Self::new_system_transaction)
2488 }
2489
2490 pub fn new_consensus_commit_prologue_v1(
2491 epoch: u64,
2492 round: u64,
2493 commit_timestamp_ms: CheckpointTimestamp,
2494 consensus_commit_digest: ConsensusCommitDigest,
2495 canceled_transactions: Vec<CanceledTransaction>,
2496 ) -> Self {
2497 ConsensusCommitPrologueV1 {
2498 epoch,
2499 round,
2500 sub_dag_index: None,
2502 commit_timestamp_ms,
2503 consensus_commit_digest,
2504 consensus_determined_version_assignments:
2505 ConsensusDeterminedVersionAssignments::CanceledTransactions {
2506 canceled_transactions,
2507 },
2508 }
2509 .pipe(TransactionKind::ConsensusCommitPrologueV1)
2510 .pipe(Self::new_system_transaction)
2511 }
2512
2513 pub fn new_randomness_state_update(
2514 epoch: u64,
2515 randomness_round: RandomnessRound,
2516 random_bytes: Vec<u8>,
2517 randomness_obj_initial_shared_version: Version,
2518 ) -> Self {
2519 RandomnessStateUpdate {
2520 epoch,
2521 randomness_round,
2522 random_bytes,
2523 randomness_obj_initial_shared_version,
2524 }
2525 .pipe(TransactionKind::RandomnessStateUpdate)
2526 .pipe(Self::new_system_transaction)
2527 }
2528
2529 pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
2530 TransactionKind::EndOfEpoch(txns).pipe(Self::new_system_transaction)
2531 }
2532
2533 pub fn new_transaction_deny_rules_update(update: TransactionDenyRulesUpdate) -> Self {
2534 TransactionKind::TransactionDenyRulesUpdate(update).pipe(Self::new_system_transaction)
2535 }
2536
2537 fn new_system_transaction(system_transaction: TransactionKind) -> Self {
2538 system_transaction
2539 .pipe(Transaction::new_system_transaction)
2540 .pipe(|data| {
2541 SenderSignedTransaction::new_from_sender_signature(data, zero_ed25519_signature())
2542 })
2543 .pipe(TransactionEnvelope::new)
2544 .pipe(Self::new_from_verified)
2545 }
2546}
2547
2548impl VerifiedSignedTransaction {
2549 #[instrument(level = "trace", skip_all)]
2551 pub fn new(
2552 epoch: EpochId,
2553 transaction: VerifiedTransaction,
2554 authority: AuthorityName,
2555 secret: &dyn Signer<AuthoritySignature>,
2556 ) -> Self {
2557 Self::new_from_verified(SignedTransaction::new(
2558 epoch,
2559 transaction.into_inner().into_data(),
2560 secret,
2561 authority,
2562 ))
2563 }
2564}
2565
2566pub type TransactionEnvelope = Envelope<SenderSignedTransaction, EmptySignInfo>;
2568pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedTransaction, EmptySignInfo>;
2569pub type TrustedTransaction = TrustedEnvelope<SenderSignedTransaction, EmptySignInfo>;
2570
2571pub type SignedTransaction = Envelope<SenderSignedTransaction, AuthoritySignInfo>;
2573pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedTransaction, AuthoritySignInfo>;
2574
2575impl TransactionEnvelope {
2576 pub fn verify_signature_for_testing(&self, verify_params: &VerifyParams) -> IotaResult {
2577 verify_sender_signed_data_message_signatures(self.data(), verify_params)
2578 }
2579
2580 pub fn try_into_verified_for_testing(
2581 self,
2582 verify_params: &VerifyParams,
2583 ) -> IotaResult<VerifiedTransaction> {
2584 self.verify_signature_for_testing(verify_params)?;
2585 Ok(VerifiedTransaction::new_from_verified(self))
2586 }
2587
2588 pub fn gas_price(&self) -> u64 {
2589 self.data().transaction().gas_price()
2590 }
2591}
2592
2593impl SignedTransaction {
2594 pub fn verify_signatures_authenticated_for_testing(
2595 &self,
2596 committee: &Committee,
2597 verify_params: &VerifyParams,
2598 ) -> IotaResult {
2599 verify_sender_signed_data_message_signatures(self.data(), verify_params)?;
2600
2601 self.auth_sig().verify_secure(
2602 self.data(),
2603 Intent::iota_app(IntentScope::SenderSignedTransaction),
2604 committee,
2605 )
2606 }
2607
2608 pub fn try_into_verified_for_testing(
2609 self,
2610 committee: &Committee,
2611 verify_params: &VerifyParams,
2612 ) -> IotaResult<VerifiedSignedTransaction> {
2613 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
2614 Ok(VerifiedSignedTransaction::new_from_verified(self))
2615 }
2616}
2617
2618pub type CertifiedTransaction = Envelope<SenderSignedTransaction, AuthorityStrongQuorumSignInfo>;
2619
2620impl CertifiedTransaction {
2621 pub fn certificate_digest(&self) -> CertificateDigest {
2622 let mut digest = DefaultHash::default();
2623 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2624 let hash = digest.finalize();
2625 CertificateDigest::new(hash.into())
2626 }
2627
2628 pub fn gas_price(&self) -> u64 {
2629 self.data().transaction().gas_price()
2630 }
2631
2632 #[instrument(level = "trace", skip_all)]
2635 pub fn verify_signatures_authenticated(
2636 &self,
2637 committee: &Committee,
2638 verify_params: &VerifyParams,
2639 ) -> IotaResult {
2640 verify_sender_signed_data_message_signatures(self.data(), verify_params)?;
2641 self.verify_committee_sigs_only(committee)
2642 }
2643
2644 pub fn try_into_verified_for_testing(
2645 self,
2646 committee: &Committee,
2647 verify_params: &VerifyParams,
2648 ) -> IotaResult<VerifiedCertificate> {
2649 self.verify_signatures_authenticated(committee, verify_params)?;
2650 Ok(VerifiedCertificate::new_from_verified(self))
2651 }
2652
2653 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
2654 self.auth_sig().verify_secure(
2655 self.data(),
2656 Intent::iota_app(IntentScope::SenderSignedTransaction),
2657 committee,
2658 )
2659 }
2660}
2661
2662pub type VerifiedCertificate =
2663 VerifiedEnvelope<SenderSignedTransaction, AuthorityStrongQuorumSignInfo>;
2664pub type TrustedCertificate =
2665 TrustedEnvelope<SenderSignedTransaction, AuthorityStrongQuorumSignInfo>;
2666
2667#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
2668pub enum InputObjectKind {
2669 MovePackage(ObjectId),
2671 ImmOrOwnedMoveObject(ObjectReference),
2673 SharedMoveObject {
2675 id: ObjectId,
2676 initial_shared_version: Version,
2677 mutable: bool,
2678 },
2679}
2680
2681impl InputObjectKind {
2682 pub fn object_id(&self) -> ObjectId {
2683 match self {
2684 Self::MovePackage(id) => *id,
2685 Self::ImmOrOwnedMoveObject(object_ref) => object_ref.object_id,
2686 Self::SharedMoveObject { id, .. } => *id,
2687 }
2688 }
2689
2690 pub fn version(&self) -> Option<Version> {
2691 match self {
2692 Self::MovePackage(..) => None,
2693 Self::ImmOrOwnedMoveObject(object_ref) => Some(object_ref.version),
2694 Self::SharedMoveObject { .. } => None,
2695 }
2696 }
2697
2698 pub fn object_not_found_error(&self) -> UserInputError {
2699 match *self {
2700 Self::MovePackage(package_id) => {
2701 UserInputError::DependentPackageNotFound { package_id }
2702 }
2703 Self::ImmOrOwnedMoveObject(object_ref) => UserInputError::ObjectNotFound {
2704 object_id: object_ref.object_id,
2705 version: Some(object_ref.version),
2706 },
2707 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
2708 object_id: id,
2709 version: None,
2710 },
2711 }
2712 }
2713
2714 pub fn is_shared_object(&self) -> bool {
2715 matches!(self, Self::SharedMoveObject { .. })
2716 }
2717
2718 pub fn is_mutable(&self) -> bool {
2719 match self {
2720 Self::MovePackage(..) => false,
2721 Self::ImmOrOwnedMoveObject(_) => true,
2722 Self::SharedMoveObject { mutable, .. } => *mutable,
2723 }
2724 }
2725
2726 pub fn left_union_with_checks(&mut self, other: &InputObjectKind) -> UserInputResult<()> {
2732 match self {
2733 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2734 fp_ensure!(
2735 self == other,
2736 UserInputError::InconsistentInput {
2737 object_id: other.object_id(),
2738 }
2739 );
2740 }
2741 InputObjectKind::SharedMoveObject {
2742 id,
2743 initial_shared_version,
2744 mutable,
2745 } => match other {
2746 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2747 fp_bail!(UserInputError::NotSharedObject)
2748 }
2749 InputObjectKind::SharedMoveObject {
2750 id: other_id,
2751 initial_shared_version: other_initial_shared_version,
2752 mutable: other_mutable,
2753 } => {
2754 fp_ensure!(id == other_id, UserInputError::SharedObjectIdMismatch);
2755 fp_ensure!(
2756 initial_shared_version == other_initial_shared_version,
2757 UserInputError::SharedObjectStartingVersionMismatch
2758 );
2759
2760 if !*mutable && *other_mutable {
2761 *mutable = *other_mutable;
2762 }
2763 }
2764 },
2765 }
2766
2767 Ok(())
2768 }
2769
2770 pub fn check_consistency_for_authentication(
2774 &self,
2775 other: &InputObjectKind,
2776 ) -> UserInputResult<()> {
2777 match self {
2778 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2779 fp_ensure!(
2780 self == other,
2781 UserInputError::InconsistentInput {
2782 object_id: self.object_id()
2783 }
2784 );
2785 }
2786 InputObjectKind::SharedMoveObject {
2787 id,
2788 initial_shared_version,
2789 mutable: _,
2790 } => match other {
2791 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2792 fp_bail!(UserInputError::InconsistentInput {
2793 object_id: self.object_id()
2794 })
2795 }
2796 InputObjectKind::SharedMoveObject {
2797 id: other_id,
2798 initial_shared_version: other_initial_shared_version,
2799 mutable: _,
2800 } => {
2801 fp_ensure!(
2802 id == other_id,
2803 UserInputError::InconsistentInput { object_id: *id }
2804 );
2805 fp_ensure!(
2806 initial_shared_version == other_initial_shared_version,
2807 UserInputError::InconsistentInput { object_id: *id }
2808 );
2809 }
2810 },
2811 }
2812
2813 Ok(())
2814 }
2815}
2816
2817#[derive(Clone, Debug)]
2821pub struct ObjectReadResult {
2822 pub input_object_kind: InputObjectKind,
2823 pub object: ObjectReadResultKind,
2824}
2825
2826#[derive(Clone, PartialEq)]
2827pub enum ObjectReadResultKind {
2828 Object(Object),
2829 DeletedSharedObject(Version, TransactionDigest),
2832 CancelledTransactionObject(Version),
2834}
2835
2836impl std::fmt::Debug for ObjectReadResultKind {
2837 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2838 match self {
2839 ObjectReadResultKind::Object(obj) => {
2840 write!(f, "Object({:?})", obj.object_ref())
2841 }
2842 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2843 write!(f, "DeletedSharedObject({seq}, {digest})")
2844 }
2845 ObjectReadResultKind::CancelledTransactionObject(seq) => {
2846 write!(f, "CancelledTransactionObject({seq})")
2847 }
2848 }
2849 }
2850}
2851
2852impl From<Object> for ObjectReadResultKind {
2853 fn from(object: Object) -> Self {
2854 Self::Object(object)
2855 }
2856}
2857
2858impl ObjectReadResult {
2859 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
2860 if let (
2861 InputObjectKind::ImmOrOwnedMoveObject(_),
2862 ObjectReadResultKind::DeletedSharedObject(_, _),
2863 ) = (&input_object_kind, &object)
2864 {
2865 panic!("only shared objects can be DeletedSharedObject");
2866 }
2867
2868 if let (
2869 InputObjectKind::ImmOrOwnedMoveObject(_),
2870 ObjectReadResultKind::CancelledTransactionObject(_),
2871 ) = (&input_object_kind, &object)
2872 {
2873 panic!("only shared objects can be CancelledTransactionObject");
2874 }
2875
2876 Self {
2877 input_object_kind,
2878 object,
2879 }
2880 }
2881
2882 pub fn id(&self) -> ObjectId {
2883 self.input_object_kind.object_id()
2884 }
2885
2886 pub fn as_object(&self) -> Option<&Object> {
2887 match &self.object {
2888 ObjectReadResultKind::Object(object) => Some(object),
2889 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
2890 ObjectReadResultKind::CancelledTransactionObject(_) => None,
2891 }
2892 }
2893
2894 pub fn new_from_gas_object(gas: &Object) -> Self {
2895 let objref = gas.object_ref();
2896 Self {
2897 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
2898 object: ObjectReadResultKind::Object(gas.clone()),
2899 }
2900 }
2901
2902 pub fn is_mutable(&self) -> bool {
2903 match (&self.input_object_kind, &self.object) {
2904 (InputObjectKind::MovePackage(_), _) => false,
2905 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
2906 !object.is_immutable()
2907 }
2908 (
2909 InputObjectKind::ImmOrOwnedMoveObject(_),
2910 ObjectReadResultKind::DeletedSharedObject(_, _),
2911 ) => unreachable!(),
2912 (
2913 InputObjectKind::ImmOrOwnedMoveObject(_),
2914 ObjectReadResultKind::CancelledTransactionObject(_),
2915 ) => unreachable!(),
2916 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
2917 }
2918 }
2919
2920 pub fn is_shared_object(&self) -> bool {
2921 self.input_object_kind.is_shared_object()
2922 }
2923
2924 pub fn is_deleted_shared_object(&self) -> bool {
2925 self.deletion_info().is_some()
2926 }
2927
2928 pub fn deletion_info(&self) -> Option<(Version, TransactionDigest)> {
2929 match &self.object {
2930 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
2931 _ => None,
2932 }
2933 }
2934
2935 pub fn get_owned_objref(&self) -> Option<ObjectReference> {
2938 match (&self.input_object_kind, &self.object) {
2939 (InputObjectKind::MovePackage(_), _) => None,
2940 (
2941 InputObjectKind::ImmOrOwnedMoveObject(objref),
2942 ObjectReadResultKind::Object(object),
2943 ) => {
2944 if object.is_immutable() {
2945 None
2946 } else {
2947 Some(*objref)
2948 }
2949 }
2950 (
2951 InputObjectKind::ImmOrOwnedMoveObject(_),
2952 ObjectReadResultKind::DeletedSharedObject(_, _),
2953 ) => unreachable!(),
2954 (
2955 InputObjectKind::ImmOrOwnedMoveObject(_),
2956 ObjectReadResultKind::CancelledTransactionObject(_),
2957 ) => unreachable!(),
2958 (InputObjectKind::SharedMoveObject { .. }, _) => None,
2959 }
2960 }
2961
2962 pub fn is_owned(&self) -> bool {
2963 self.get_owned_objref().is_some()
2964 }
2965
2966 pub fn to_shared_input(&self) -> Option<SharedInput> {
2967 match self.input_object_kind {
2968 InputObjectKind::MovePackage(_) => None,
2969 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
2970 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
2971 ObjectReadResultKind::Object(obj) => SharedInput::Existing(obj.object_ref()),
2972 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2973 SharedInput::Deleted((id, *seq, mutable, *digest))
2974 }
2975 ObjectReadResultKind::CancelledTransactionObject(seq) => {
2976 SharedInput::Cancelled((id, *seq))
2977 }
2978 }),
2979 }
2980 }
2981
2982 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
2983 match &self.object {
2984 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
2985 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
2986 ObjectReadResultKind::CancelledTransactionObject(_) => None,
2987 }
2988 }
2989}
2990
2991#[derive(Clone, Debug, PartialEq, Eq)]
2994pub enum CancelledObjects {
2995 SharedObjects(Vec<ObjectId>),
2997 GasObject,
3002}
3003
3004#[derive(Clone)]
3005pub struct InputObjects {
3006 objects: Vec<ObjectReadResult>,
3007 gas_object_cancellation: Option<(ObjectId, Version)>,
3014}
3015
3016impl std::fmt::Debug for InputObjects {
3017 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3018 match &self.gas_object_cancellation {
3019 None => f.debug_list().entries(self.objects.iter()).finish(),
3020 Some(gas_object_cancellation) => f
3021 .debug_struct("InputObjects")
3022 .field("objects", &self.objects)
3023 .field("gas_object_cancellation", gas_object_cancellation)
3024 .finish(),
3025 }
3026 }
3027}
3028
3029pub struct CheckedInputObjects(InputObjects);
3032
3033impl CheckedInputObjects {
3039 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3041 Self(inputs)
3042 }
3043
3044 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3046 Self(InputObjects::new(input_objects))
3047 }
3048
3049 pub fn new_for_replay(input_objects: InputObjects) -> Self {
3051 Self(input_objects)
3052 }
3053
3054 pub fn inner(&self) -> &InputObjects {
3055 &self.0
3056 }
3057
3058 pub fn into_inner(self) -> InputObjects {
3059 self.0
3060 }
3061}
3062
3063impl From<Vec<ObjectReadResult>> for InputObjects {
3064 fn from(objects: Vec<ObjectReadResult>) -> Self {
3065 Self::new(objects)
3066 }
3067}
3068
3069impl InputObjects {
3070 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
3071 Self {
3072 objects,
3073 gas_object_cancellation: None,
3074 }
3075 }
3076
3077 pub fn set_gas_object_cancellation(&mut self, gas_object_id: ObjectId, version: Version) {
3082 assert!(
3083 version.is_canceled(),
3084 "gas object cancellation must use a cancellation version, got {version:?}"
3085 );
3086 assert!(
3087 !self.objects.iter().any(|obj| obj.is_shared_object()),
3088 "transactions with shared inputs carry cancellation on their shared objects"
3089 );
3090 self.gas_object_cancellation = Some((gas_object_id, version));
3091 }
3092
3093 pub fn gas_object_cancellation(&self) -> Option<(ObjectId, Version)> {
3094 self.gas_object_cancellation
3095 }
3096
3097 pub fn len(&self) -> usize {
3098 self.objects.len()
3099 }
3100
3101 pub fn is_empty(&self) -> bool {
3102 self.objects.is_empty()
3103 }
3104
3105 pub fn contains_deleted_objects(&self) -> bool {
3106 self.objects
3107 .iter()
3108 .any(|obj| obj.is_deleted_shared_object())
3109 }
3110
3111 pub fn get_cancelled_objects(&self) -> Option<(CancelledObjects, Version)> {
3114 if let Some((_, version)) = &self.gas_object_cancellation {
3115 return Some((CancelledObjects::GasObject, *version));
3116 }
3117
3118 let mut contains_cancelled = false;
3119 let mut cancel_reason = None;
3120 let mut cancelled_objects = Vec::new();
3121 for obj in &self.objects {
3122 if let ObjectReadResultKind::CancelledTransactionObject(version) = obj.object {
3123 contains_cancelled = true;
3124 if version.is_congested() || version == Version::RANDOMNESS_UNAVAILABLE {
3125 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
3127 cancel_reason = Some(version);
3128 cancelled_objects.push(obj.id());
3129 }
3130 }
3131 }
3132
3133 if !cancelled_objects.is_empty() {
3134 Some((
3135 CancelledObjects::SharedObjects(cancelled_objects),
3136 cancel_reason
3137 .expect("there should be a cancel reason if there are cancelled objects"),
3138 ))
3139 } else {
3140 assert!(!contains_cancelled);
3141 None
3142 }
3143 }
3144
3145 pub fn filter_owned_objects(&self) -> Vec<ObjectReference> {
3146 let owned_objects: Vec<_> = self
3147 .objects
3148 .iter()
3149 .filter_map(|obj| obj.get_owned_objref())
3150 .collect();
3151
3152 trace!(
3153 num_mutable_objects = owned_objects.len(),
3154 "Checked locks and found mutable objects"
3155 );
3156
3157 owned_objects
3158 }
3159
3160 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
3161 self.objects
3162 .iter()
3163 .filter(|obj| obj.is_shared_object())
3164 .map(|obj| {
3165 obj.to_shared_input()
3166 .expect("already filtered for shared objects")
3167 })
3168 .collect()
3169 }
3170
3171 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3172 self.objects
3173 .iter()
3174 .filter_map(|obj| obj.get_previous_transaction())
3175 .collect()
3176 }
3177
3178 pub fn mutable_inputs(&self) -> BTreeMap<ObjectId, (VersionDigest, Owner)> {
3179 self.objects
3180 .iter()
3181 .filter_map(
3182 |ObjectReadResult {
3183 input_object_kind,
3184 object,
3185 }| match (input_object_kind, object) {
3186 (InputObjectKind::MovePackage(_), _) => None,
3187 (
3188 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3189 ObjectReadResultKind::Object(object),
3190 ) => {
3191 if object.is_immutable() {
3192 None
3193 } else {
3194 Some((
3195 object_ref.object_id,
3196 ((object_ref.version, object_ref.digest), object.owner),
3197 ))
3198 }
3199 }
3200 (
3201 InputObjectKind::ImmOrOwnedMoveObject(_),
3202 ObjectReadResultKind::DeletedSharedObject(_, _),
3203 ) => {
3204 unreachable!()
3205 }
3206 (
3207 InputObjectKind::SharedMoveObject { .. },
3208 ObjectReadResultKind::DeletedSharedObject(_, _),
3209 ) => None,
3210 (
3211 InputObjectKind::SharedMoveObject { mutable, .. },
3212 ObjectReadResultKind::Object(object),
3213 ) => {
3214 if *mutable {
3215 let oref = object.object_ref();
3216 Some((oref.object_id, ((oref.version, oref.digest), object.owner)))
3217 } else {
3218 None
3219 }
3220 }
3221 (
3222 InputObjectKind::ImmOrOwnedMoveObject(_),
3223 ObjectReadResultKind::CancelledTransactionObject(_),
3224 ) => {
3225 unreachable!()
3226 }
3227 (
3228 InputObjectKind::SharedMoveObject { .. },
3229 ObjectReadResultKind::CancelledTransactionObject(_),
3230 ) => None,
3231 },
3232 )
3233 .collect()
3234 }
3235
3236 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectReference]) -> Version {
3240 let input_versions = self
3241 .objects
3242 .iter()
3243 .filter_map(|object| match &object.object {
3244 ObjectReadResultKind::Object(object) => {
3245 object.data.as_opt_struct().map(MoveStruct::version)
3246 }
3247 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3248 ObjectReadResultKind::CancelledTransactionObject(_) => None,
3249 })
3250 .chain(
3251 receiving_objects
3252 .iter()
3253 .map(|object_ref| object_ref.version),
3254 );
3255
3256 Version::lamport_increment(input_versions).unwrap()
3257 }
3258
3259 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3260 self.objects.iter().map(
3261 |ObjectReadResult {
3262 input_object_kind, ..
3263 }| input_object_kind,
3264 )
3265 }
3266
3267 pub fn into_object_map(self) -> BTreeMap<ObjectId, Object> {
3268 self.objects
3269 .into_iter()
3270 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3271 .collect()
3272 }
3273
3274 pub fn push(&mut self, object: ObjectReadResult) {
3275 self.objects.push(object);
3276 }
3277
3278 pub fn find_object_id_mut(&mut self, object_id: ObjectId) -> Option<&mut ObjectReadResult> {
3280 self.objects.iter_mut().find(|o| o.id() == object_id)
3281 }
3282
3283 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3284 self.objects.iter()
3285 }
3286
3287 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3288 self.objects.iter().filter_map(|o| o.as_object())
3289 }
3290}
3291
3292#[derive(Clone, Debug)]
3296pub enum ReceivingObjectReadResultKind {
3297 Object(Object),
3298 PreviouslyReceivedObject,
3300}
3301
3302impl ReceivingObjectReadResultKind {
3303 pub fn as_object(&self) -> Option<&Object> {
3304 match &self {
3305 Self::Object(object) => Some(object),
3306 Self::PreviouslyReceivedObject => None,
3307 }
3308 }
3309}
3310
3311pub struct ReceivingObjectReadResult {
3312 pub object_ref: ObjectReference,
3313 pub object: ReceivingObjectReadResultKind,
3314}
3315
3316impl ReceivingObjectReadResult {
3317 pub fn new(object_ref: ObjectReference, object: ReceivingObjectReadResultKind) -> Self {
3318 Self { object_ref, object }
3319 }
3320
3321 pub fn is_previously_received(&self) -> bool {
3322 matches!(
3323 self.object,
3324 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3325 )
3326 }
3327}
3328
3329impl From<Object> for ReceivingObjectReadResultKind {
3330 fn from(object: Object) -> Self {
3331 Self::Object(object)
3332 }
3333}
3334
3335pub struct ReceivingObjects {
3336 pub objects: Vec<ReceivingObjectReadResult>,
3337}
3338
3339impl ReceivingObjects {
3340 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3341 self.objects.iter()
3342 }
3343
3344 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3345 self.objects.iter().filter_map(|o| o.object.as_object())
3346 }
3347}
3348
3349impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3350 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3351 Self { objects }
3352 }
3353}
3354
3355impl Display for CertifiedTransaction {
3356 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3357 let mut writer = String::new();
3358 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3359 writeln!(
3360 writer,
3361 "Signed Authorities Bitmap : {:?}",
3362 self.auth_sig().signers_map
3363 )?;
3364 write!(writer, "{}", self.data().transaction().kind())?;
3365 write!(f, "{writer}")
3366 }
3367}
3368
3369#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3374pub enum TransactionKey {
3375 Digest(TransactionDigest),
3376 RandomnessRound(EpochId, RandomnessRound),
3377}
3378
3379impl TransactionKey {
3380 pub fn unwrap_digest(&self) -> &TransactionDigest {
3381 match self {
3382 TransactionKey::Digest(d) => d,
3383 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3384 }
3385 }
3386
3387 pub fn as_digest(&self) -> Option<&TransactionDigest> {
3388 match self {
3389 TransactionKey::Digest(d) => Some(d),
3390 _ => None,
3391 }
3392 }
3393}