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.enable_pcool_flow() {
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
599fn command_arguments(command: &Command) -> Box<dyn Iterator<Item = &Argument> + '_> {
601 match command {
602 Command::MoveCall(c) => Box::new(c.arguments.iter()),
603 Command::TransferObjects(TransferObjects { objects, address }) => {
604 Box::new(objects.iter().chain(iter::once(address)))
605 }
606 Command::SplitCoins(SplitCoins { coin, amounts }) => {
607 Box::new(iter::once(coin).chain(amounts))
608 }
609 Command::MergeCoins(MergeCoins {
610 coin,
611 coins_to_merge,
612 }) => Box::new(iter::once(coin).chain(coins_to_merge)),
613 Command::MakeMoveVector(MakeMoveVector { elements, .. }) => Box::new(elements.iter()),
614 Command::Upgrade(Upgrade { ticket, .. }) => Box::new(iter::once(ticket)),
615 Command::Publish(_) => Box::new(iter::empty()),
616 _ => unimplemented!("a new Command enum variant was added and needs to be handled"),
617 }
618}
619
620mod programmable_transaction_ext {
621 pub trait Sealed {}
622 impl Sealed for super::ProgrammableTransaction {}
623}
624
625pub trait ProgrammableTransactionExt: Sized + programmable_transaction_ext::Sealed {
626 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
627 fn receiving_objects(&self) -> Vec<ObjectReference>;
628 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
629 fn validate_argument_indices(&self) -> UserInputResult;
630 fn shared_input_objects(&self) -> impl Iterator<Item = SharedObjectReference>;
631 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)>;
632 fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>>;
633}
634
635impl ProgrammableTransactionExt for ProgrammableTransaction {
636 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
637 let ProgrammableTransaction { inputs, commands } = self;
638 let input_arg_objects = inputs
639 .iter()
640 .filter_map(|arg| arg.input_object_kind())
641 .collect::<Vec<_>>();
642 let mut used = HashSet::new();
644 if !input_arg_objects.iter().all(|o| used.insert(o.object_id())) {
645 return Err(UserInputError::DuplicateObjectRefInput);
646 }
647 let command_input_objects: BTreeSet<InputObjectKind> = commands
649 .iter()
650 .flat_map(|command| command.input_objects())
651 .collect();
652 Ok(input_arg_objects
653 .into_iter()
654 .chain(command_input_objects)
655 .collect())
656 }
657
658 fn receiving_objects(&self) -> Vec<ObjectReference> {
659 let ProgrammableTransaction { inputs, .. } = self;
660 inputs
661 .iter()
662 .filter_map(|arg| arg.as_opt_receiving().copied())
663 .collect()
664 }
665
666 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
667 let ProgrammableTransaction { inputs, commands } = self;
668 fp_ensure!(
669 commands.len() < config.max_programmable_tx_commands() as usize,
670 UserInputError::SizeLimitExceeded {
671 limit: "maximum commands in a programmable transaction".to_string(),
672 value: config.max_programmable_tx_commands().to_string()
673 }
674 );
675 fp_ensure!(
678 inputs.len() <= MAX_PROGRAMMABLE_TX_INPUTS,
679 UserInputError::SizeLimitExceeded {
680 limit: "maximum inputs in a programmable transaction".to_string(),
681 value: MAX_PROGRAMMABLE_TX_INPUTS.to_string(),
682 }
683 );
684 let total_inputs = self.input_objects()?.len() + self.receiving_objects().len();
685 fp_ensure!(
686 total_inputs <= config.max_input_objects() as usize,
687 UserInputError::SizeLimitExceeded {
688 limit: "maximum input + receiving objects in a transaction".to_string(),
689 value: config.max_input_objects().to_string()
690 }
691 );
692 for input in inputs {
693 input.validity_check(config)?
694 }
695 if let Some(max_publish_commands) = config.max_publish_or_upgrade_per_ptb_as_option() {
696 let publish_count = commands
697 .iter()
698 .filter(|c| c.is_publish() || c.is_upgrade())
699 .count() as u64;
700 fp_ensure!(
701 publish_count <= max_publish_commands,
702 UserInputError::MaxPublishCountExceeded {
703 max_publish_commands,
704 publish_count,
705 }
706 );
707 }
708 for command in commands {
709 command.validity_check(config)?;
710 }
711 if config.validate_ptb_argument_indices() {
712 self.validate_argument_indices()?;
713 }
714
715 if let Some(random_index) = inputs.iter().position(|obj| {
719 matches!(obj, CallArg::Shared(SharedObjectReference { object_id, .. }) if *object_id == ObjectId::RANDOMNESS_STATE)
720 }) {
721 let mut used_random_object = false;
722 for command in commands {
723 if !used_random_object {
724 used_random_object = command.is_input_arg_used(random_index);
725 } else {
726 fp_ensure!(
727 command.is_transfer_objects() || command.is_merge_coins(),
728 UserInputError::PostRandomCommandRestrictions
729 );
730 }
731 }
732 }
733
734 Ok(())
735 }
736
737 fn validate_argument_indices(&self) -> UserInputResult {
738 for (command_idx, command) in self.commands.iter().enumerate() {
739 for (argument_idx, argument) in command_arguments(command).enumerate() {
740 let index = match argument {
741 Argument::Input(index) if *index as usize >= self.inputs.len() => *index,
742 Argument::Result(index) | Argument::NestedResult(index, _)
743 if *index as usize >= command_idx =>
744 {
745 *index
746 }
747 Argument::Gas
748 | Argument::Input(_)
749 | Argument::Result(_)
750 | Argument::NestedResult(_, _) => continue,
751 _ => unimplemented!(
752 "a new Argument enum variant was added and needs to be handled"
753 ),
754 };
755 return Err(UserInputError::InvalidArgumentIndex {
756 command_idx,
757 argument_idx,
758 index,
759 });
760 }
761 }
762 Ok(())
763 }
764
765 fn shared_input_objects(&self) -> impl Iterator<Item = SharedObjectReference> {
766 self.inputs.iter().filter_map(|arg| match arg {
767 CallArg::Shared(shared) => Some(*shared),
768 CallArg::Pure(_) | CallArg::Receiving(_) | CallArg::ImmutableOrOwned(_) => None,
769 _ => unimplemented!("a new CallArg enum variant was added and needs to be handled"),
770 })
771 }
772
773 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
774 self.commands
775 .iter()
776 .filter_map(|command| match command {
777 Command::MoveCall(m) => Some((&m.package, m.module.as_str(), m.function.as_str())),
778 _ => None,
779 })
780 .collect()
781 }
782
783 fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>> {
784 self.commands
785 .iter()
786 .filter_map(|q| q.non_system_packages_to_be_published())
787 }
788}
789
790fn left_union_shared_input_objects(
794 this: &mut SharedObjectReference,
795 other: &SharedObjectReference,
796) -> UserInputResult<()> {
797 fp_ensure!(
798 this.object_id == other.object_id,
799 UserInputError::SharedObjectIdMismatch
800 );
801 fp_ensure!(
802 this.initial_shared_version == other.initial_shared_version,
803 UserInputError::SharedObjectStartingVersionMismatch
804 );
805
806 if !this.mutable && other.mutable {
807 this.mutable = other.mutable;
808 }
809
810 Ok(())
811}
812
813mod transaction_kind_ext {
814 pub trait Sealed {}
815 impl Sealed for super::TransactionKind {}
816}
817
818pub trait TransactionKindExt: Sized + transaction_kind_ext::Sealed {
819 fn get_advance_epoch_tx_gas_summary(&self) -> Option<(u64, u64)>;
823 fn contains_shared_object(&self) -> bool;
825 fn shared_input_objects(&self) -> impl Iterator<Item = SharedObjectReference> + '_;
828 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)>;
831 fn receiving_objects(&self) -> Vec<ObjectReference>;
833 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
839 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
841 fn iter_commands(&self) -> impl Iterator<Item = &Command>;
843 fn name(&self) -> &'static str;
845}
846
847impl TransactionKindExt for TransactionKind {
848 fn get_advance_epoch_tx_gas_summary(&self) -> Option<(u64, u64)> {
849 match self {
850 Self::EndOfEpoch(txns) => {
851 match txns.last().expect("at least one end-of-epoch txn required") {
852 EndOfEpochTransactionKind::ChangeEpoch(e) => {
853 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
854 }
855 EndOfEpochTransactionKind::ChangeEpochV2(e) => {
856 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
857 }
858 EndOfEpochTransactionKind::ChangeEpochV3(e) => {
859 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
860 }
861 EndOfEpochTransactionKind::ChangeEpochV4(e) => {
862 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
863 }
864 EndOfEpochTransactionKind::TransactionDenyRulesCreate => None,
867 _ => unimplemented!(
868 "a new EndOfEpochTransactionKind enum variant was added and needs to be handled"
869 ),
870 }
871 }
872 _ => None,
873 }
874 }
875
876 fn contains_shared_object(&self) -> bool {
877 self.shared_input_objects().next().is_some()
878 }
879
880 fn shared_input_objects(&self) -> impl Iterator<Item = SharedObjectReference> + '_ {
881 match &self {
882 Self::ConsensusCommitPrologueV1(_) => Either::Left(Either::Left(iter::once(
883 SharedObjectReference::new(ObjectId::CLOCK, IOTA_CLOCK_OBJECT_SHARED_VERSION, true),
884 ))),
885 #[allow(deprecated)]
886 Self::AuthenticatorStateUpdateV1Deprecated => {
887 Either::Right(Either::Right(iter::empty()))
891 }
892 Self::RandomnessStateUpdate(update) => {
893 Either::Left(Either::Left(iter::once(SharedObjectReference::new(
894 ObjectId::RANDOMNESS_STATE,
895 update.randomness_obj_initial_shared_version,
896 true,
897 ))))
898 }
899 Self::TransactionDenyRulesUpdate(update) => {
900 Either::Left(Either::Left(iter::once(SharedObjectReference::new(
901 ObjectId::TRANSACTION_DENY_RULES,
902 update.deny_rules_obj_initial_shared_version,
903 true,
904 ))))
905 }
906 Self::EndOfEpoch(txns) => Either::Left(Either::Right(
907 txns.iter().flat_map(|txn| txn.shared_input_objects()),
908 )),
909 Self::Programmable(pt) => Either::Right(Either::Left(pt.shared_input_objects())),
910 _ => Either::Right(Either::Right(iter::empty())),
911 }
912 }
913
914 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
915 match &self {
916 Self::Programmable(pt) => pt.move_calls(),
917 _ => vec![],
918 }
919 }
920
921 fn receiving_objects(&self) -> Vec<ObjectReference> {
922 match &self {
923 #[allow(deprecated)]
924 TransactionKind::Genesis(_)
925 | TransactionKind::ConsensusCommitPrologueV1(_)
926 | TransactionKind::AuthenticatorStateUpdateV1Deprecated
927 | TransactionKind::RandomnessStateUpdate(_)
928 | TransactionKind::TransactionDenyRulesUpdate(_)
929 | TransactionKind::EndOfEpoch(_) => vec![],
930 TransactionKind::Programmable(pt) => pt.receiving_objects(),
931 _ => unimplemented!(
932 "a new TransactionKind enum variant was added and needs to be handled"
933 ),
934 }
935 }
936
937 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
938 let input_objects = match &self {
939 Self::Genesis(_) => {
940 vec![]
941 }
942 Self::ConsensusCommitPrologueV1(_) => {
943 vec![InputObjectKind::SharedMoveObject {
944 id: ObjectId::CLOCK,
945 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
946 mutable: true,
947 }]
948 }
949 #[allow(deprecated)]
950 Self::AuthenticatorStateUpdateV1Deprecated => {
951 vec![]
955 }
956 Self::RandomnessStateUpdate(update) => {
957 vec![InputObjectKind::SharedMoveObject {
958 id: ObjectId::RANDOMNESS_STATE,
959 initial_shared_version: update.randomness_obj_initial_shared_version,
960 mutable: true,
961 }]
962 }
963 Self::TransactionDenyRulesUpdate(update) => {
964 vec![InputObjectKind::SharedMoveObject {
965 id: ObjectId::TRANSACTION_DENY_RULES,
966 initial_shared_version: update.deny_rules_obj_initial_shared_version,
967 mutable: true,
968 }]
969 }
970 Self::EndOfEpoch(txns) => {
971 let before_dedup: Vec<_> =
974 txns.iter().flat_map(|txn| txn.input_objects()).collect();
975 let mut has_seen = HashSet::new();
976 let mut after_dedup = vec![];
977 for obj in before_dedup {
978 if has_seen.insert(obj) {
979 after_dedup.push(obj);
980 }
981 }
982 after_dedup
983 }
984 Self::Programmable(p) => return p.input_objects(),
985 _ => unimplemented!(
986 "a new TransactionKind enum variant was added and needs to be handled"
987 ),
988 };
989 let mut used = HashSet::new();
997 if !input_objects.iter().all(|o| used.insert(o.object_id())) {
998 return Err(UserInputError::DuplicateObjectRefInput);
999 }
1000 Ok(input_objects)
1001 }
1002
1003 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1004 match self {
1005 TransactionKind::Programmable(p) => p.validity_check(config)?,
1006 TransactionKind::Genesis(_) | TransactionKind::ConsensusCommitPrologueV1(_) => (),
1009 TransactionKind::EndOfEpoch(txns) => {
1010 for tx in txns {
1011 tx.validity_check(config)?;
1012 }
1013 }
1014
1015 #[allow(deprecated)]
1016 TransactionKind::AuthenticatorStateUpdateV1Deprecated => {
1017 return Err(UserInputError::Unsupported(
1021 "authenticator state transactions are deprecated and were never created on IOTA"
1022 .to_string(),
1023 ));
1024 }
1025 TransactionKind::RandomnessStateUpdate(_) => (),
1026 TransactionKind::TransactionDenyRulesUpdate(_) => {
1027 if !config.enable_pcool_flow() {
1028 return Err(UserInputError::Unsupported(
1029 "on-chain deny rule governance not supported at current protocol version"
1030 .to_string(),
1031 ));
1032 }
1033 }
1034 _ => unimplemented!(
1035 "a new TransactionKind enum variant was added and needs to be handled"
1036 ),
1037 };
1038 Ok(())
1039 }
1040
1041 fn iter_commands(&self) -> impl Iterator<Item = &Command> {
1042 match self {
1043 TransactionKind::Programmable(pt) => pt.commands.iter(),
1044 _ => [].iter(),
1045 }
1046 }
1047
1048 fn name(&self) -> &'static str {
1049 match self {
1050 Self::Genesis(_) => "Genesis",
1051 Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
1052 Self::Programmable(_) => "Programmable",
1053 #[allow(deprecated)]
1054 Self::AuthenticatorStateUpdateV1Deprecated => "AuthenticatorStateUpdateV1Deprecated",
1055 Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
1056 Self::TransactionDenyRulesUpdate(_) => "TransactionDenyRulesUpdate",
1057 Self::EndOfEpoch(_) => "EndOfEpoch",
1058 _ => unimplemented!(
1059 "a new TransactionKind enum variant was added and needs to be handled"
1060 ),
1061 }
1062 }
1063}
1064
1065pub trait TransactionAPI {
1076 fn sender(&self) -> Address;
1078
1079 fn kind(&self) -> &TransactionKind;
1081
1082 fn kind_mut(&mut self) -> &mut TransactionKind;
1084
1085 fn into_kind(self) -> TransactionKind;
1087
1088 fn signers(&self) -> NonEmpty<Address>;
1091
1092 fn gas_data(&self) -> &GasPayment;
1095
1096 fn gas_owner(&self) -> Address;
1098
1099 fn gas(&self) -> &[ObjectReference];
1101
1102 fn gas_price(&self) -> u64;
1104
1105 fn gas_budget(&self) -> u64;
1107
1108 fn expiration(&self) -> &TransactionExpiration;
1110
1111 fn shared_input_objects(&self) -> Vec<SharedObjectReference>;
1117
1118 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)>;
1121
1122 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
1130
1131 fn receiving_objects(&self) -> Vec<ObjectReference>;
1134
1135 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
1138
1139 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult;
1142
1143 fn check_serialized_size(&self, config: &ProtocolConfig) -> IotaResult;
1146
1147 fn check_gas_payment_size(&self, config: &ProtocolConfig) -> UserInputResult;
1150
1151 fn check_sponsorship(&self) -> UserInputResult;
1153
1154 fn is_system_tx(&self) -> bool;
1156 fn is_genesis_tx(&self) -> bool;
1158
1159 fn is_end_of_epoch_tx(&self) -> bool;
1162
1163 fn is_sponsored_tx(&self) -> bool;
1165
1166 fn sender_mut_for_testing(&mut self) -> &mut Address;
1168
1169 fn gas_data_mut(&mut self) -> &mut GasPayment;
1171
1172 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration;
1174
1175 fn new_system_transaction(kind: TransactionKind) -> Transaction;
1178
1179 #[allow(clippy::new_ret_no_self)]
1182 fn new(
1183 kind: TransactionKind,
1184 sender: Address,
1185 gas_payment: ObjectReference,
1186 gas_budget: u64,
1187 gas_price: u64,
1188 ) -> Transaction;
1189
1190 fn new_with_gas_coins(
1193 kind: TransactionKind,
1194 sender: Address,
1195 gas_payment: Vec<ObjectReference>,
1196 gas_budget: u64,
1197 gas_price: u64,
1198 ) -> Transaction;
1199
1200 fn new_with_gas_coins_allow_sponsor(
1204 kind: TransactionKind,
1205 sender: Address,
1206 gas_payment: Vec<ObjectReference>,
1207 gas_budget: u64,
1208 gas_price: u64,
1209 gas_sponsor: Address,
1210 ) -> Transaction;
1211
1212 fn new_with_gas_data(
1214 kind: TransactionKind,
1215 sender: Address,
1216 gas_data: GasPayment,
1217 ) -> Transaction;
1218
1219 fn new_move_call(
1222 sender: Address,
1223 package: ObjectId,
1224 module: Identifier,
1225 function: Identifier,
1226 type_arguments: Vec<TypeTag>,
1227 gas_payment: ObjectReference,
1228 arguments: Vec<CallArg>,
1229 gas_budget: u64,
1230 gas_price: u64,
1231 ) -> anyhow::Result<Transaction>;
1232
1233 fn new_move_call_with_gas_coins(
1236 sender: Address,
1237 package: ObjectId,
1238 module: Identifier,
1239 function: Identifier,
1240 type_arguments: Vec<TypeTag>,
1241 gas_payment: Vec<ObjectReference>,
1242 arguments: Vec<CallArg>,
1243 gas_budget: u64,
1244 gas_price: u64,
1245 ) -> anyhow::Result<Transaction>;
1246
1247 fn new_transfer(
1249 recipient: Address,
1250 object_ref: ObjectReference,
1251 sender: Address,
1252 gas_payment: ObjectReference,
1253 gas_budget: u64,
1254 gas_price: u64,
1255 ) -> Transaction;
1256
1257 fn new_transfer_iota(
1261 recipient: Address,
1262 sender: Address,
1263 amount: Option<u64>,
1264 gas_payment: ObjectReference,
1265 gas_budget: u64,
1266 gas_price: u64,
1267 ) -> Transaction;
1268
1269 fn new_transfer_iota_allow_sponsor(
1273 recipient: Address,
1274 sender: Address,
1275 amount: Option<u64>,
1276 gas_payment: ObjectReference,
1277 gas_budget: u64,
1278 gas_price: u64,
1279 gas_sponsor: Address,
1280 ) -> Transaction;
1281
1282 fn new_pay(
1286 sender: Address,
1287 coins: Vec<ObjectReference>,
1288 recipients: Vec<Address>,
1289 amounts: Vec<u64>,
1290 gas_payment: ObjectReference,
1291 gas_budget: u64,
1292 gas_price: u64,
1293 ) -> anyhow::Result<Transaction>;
1294
1295 fn new_pay_iota(
1299 sender: Address,
1300 coins: Vec<ObjectReference>,
1301 recipients: Vec<Address>,
1302 amounts: Vec<u64>,
1303 gas_payment: ObjectReference,
1304 gas_budget: u64,
1305 gas_price: u64,
1306 ) -> anyhow::Result<Transaction>;
1307
1308 fn new_pay_all_iota(
1311 sender: Address,
1312 coins: Vec<ObjectReference>,
1313 recipient: Address,
1314 gas_payment: ObjectReference,
1315 gas_budget: u64,
1316 gas_price: u64,
1317 ) -> Transaction;
1318
1319 fn new_split_coin(
1322 sender: Address,
1323 coin: ObjectReference,
1324 amounts: Vec<u64>,
1325 gas_payment: ObjectReference,
1326 gas_budget: u64,
1327 gas_price: u64,
1328 ) -> Transaction;
1329
1330 fn new_module(
1332 sender: Address,
1333 gas_payment: ObjectReference,
1334 modules: Vec<Vec<u8>>,
1335 dep_ids: Vec<ObjectId>,
1336 gas_budget: u64,
1337 gas_price: u64,
1338 ) -> Transaction;
1339
1340 fn new_upgrade(
1343 sender: Address,
1344 gas_payment: ObjectReference,
1345 package_id: ObjectId,
1346 modules: Vec<Vec<u8>>,
1347 dep_ids: Vec<ObjectId>,
1348 upgrade_capability_and_owner: (ObjectReference, Owner),
1349 upgrade_policy: u8,
1350 digest: Vec<u8>,
1351 gas_budget: u64,
1352 gas_price: u64,
1353 ) -> anyhow::Result<Transaction>;
1354
1355 fn new_programmable(
1358 sender: Address,
1359 gas_payment: Vec<ObjectReference>,
1360 pt: ProgrammableTransaction,
1361 gas_budget: u64,
1362 gas_price: u64,
1363 ) -> Transaction;
1364
1365 fn new_programmable_allow_sponsor(
1368 sender: Address,
1369 gas_payment: Vec<ObjectReference>,
1370 pt: ProgrammableTransaction,
1371 gas_budget: u64,
1372 gas_price: u64,
1373 sponsor: Address,
1374 ) -> Transaction;
1375
1376 fn message_version(&self) -> u64;
1378
1379 fn execution_parts(&self) -> (TransactionKind, Address, GasPayment);
1382}
1383
1384fn tx_bcs_size<T: Serialize>(tx: &T) -> IotaResult<usize> {
1385 bcs::serialized_size(tx).map_err(|e| IotaError::TransactionSerialization {
1386 error: e.to_string(),
1387 })
1388}
1389
1390fn check_transaction_size<T: Serialize>(
1393 transaction: &T,
1394 config: &ProtocolConfig,
1395) -> IotaResult<usize> {
1396 let tx_size = tx_bcs_size(transaction)?;
1397 let max_tx_size_bytes = config.max_tx_size_bytes();
1398 fp_ensure!(
1399 tx_size as u64 <= max_tx_size_bytes,
1400 IotaError::UserInput {
1401 error: UserInputError::SizeLimitExceeded {
1402 limit: format!(
1403 "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
1404 ),
1405 value: tx_size.to_string(),
1406 }
1407 }
1408 );
1409 Ok(tx_size)
1410}
1411
1412impl TransactionAPI for Transaction {
1413 fn check_serialized_size(&self, config: &ProtocolConfig) -> IotaResult {
1414 check_transaction_size(self, config).map(|_| ())
1415 }
1416
1417 fn sender(&self) -> Address {
1418 match self {
1419 Self::V1(v1) => v1.sender,
1420 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1421 }
1422 }
1423
1424 fn kind(&self) -> &TransactionKind {
1425 match self {
1426 Self::V1(v1) => &v1.kind,
1427 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1428 }
1429 }
1430
1431 fn kind_mut(&mut self) -> &mut TransactionKind {
1432 match self {
1433 Self::V1(v1) => &mut v1.kind,
1434 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1435 }
1436 }
1437
1438 fn into_kind(self) -> TransactionKind {
1439 match self {
1440 Self::V1(v1) => v1.kind,
1441 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1442 }
1443 }
1444
1445 fn signers(&self) -> NonEmpty<Address> {
1446 let mut signers = nonempty![self.sender()];
1447 if self.gas_owner() != self.sender() {
1448 signers.push(self.gas_owner());
1449 }
1450 signers
1451 }
1452
1453 fn gas_data(&self) -> &GasPayment {
1454 match self {
1455 Self::V1(v1) => &v1.gas_payment,
1456 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1457 }
1458 }
1459
1460 fn gas_owner(&self) -> Address {
1461 self.gas_data().owner
1462 }
1463
1464 fn gas(&self) -> &[ObjectReference] {
1465 &self.gas_data().objects
1466 }
1467
1468 fn gas_price(&self) -> u64 {
1469 self.gas_data().price
1470 }
1471
1472 fn gas_budget(&self) -> u64 {
1473 self.gas_data().budget
1474 }
1475
1476 fn expiration(&self) -> &TransactionExpiration {
1477 match self {
1478 Self::V1(v1) => &v1.expiration,
1479 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1480 }
1481 }
1482
1483 fn shared_input_objects(&self) -> Vec<SharedObjectReference> {
1484 self.kind().shared_input_objects().collect()
1485 }
1486
1487 fn move_calls(&self) -> Vec<(&ObjectId, &str, &str)> {
1488 self.kind().move_calls()
1489 }
1490
1491 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1492 let mut inputs = self.kind().input_objects()?;
1493
1494 if !self.kind().is_system() {
1495 inputs.extend(
1496 self.gas()
1497 .iter()
1498 .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
1499 );
1500 }
1501 Ok(inputs)
1502 }
1503
1504 fn receiving_objects(&self) -> Vec<ObjectReference> {
1505 self.kind().receiving_objects()
1506 }
1507
1508 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1509 fp_ensure!(!self.gas().is_empty(), UserInputError::MissingGasPayment);
1510 self.check_gas_payment_size(config)?;
1511 if config.validate_input_object_versions() {
1512 for gas_object in self.gas() {
1513 input_object_version_validity_check(gas_object.version)?;
1514 }
1515 }
1516 self.validity_check_no_gas_check(config)
1517 }
1518
1519 #[instrument(level = "trace", skip_all)]
1520 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
1521 self.kind().validity_check(config)?;
1522 self.check_sponsorship()
1523 }
1524
1525 fn check_gas_payment_size(&self, config: &ProtocolConfig) -> UserInputResult {
1526 fp_ensure!(
1527 self.gas().len() < config.max_gas_payment_objects() as usize,
1528 UserInputError::SizeLimitExceeded {
1529 limit: "maximum number of gas payment objects".to_string(),
1530 value: config.max_gas_payment_objects().to_string()
1531 }
1532 );
1533 Ok(())
1534 }
1535
1536 fn is_sponsored_tx(&self) -> bool {
1537 self.gas_owner() != self.sender()
1538 }
1539
1540 fn check_sponsorship(&self) -> UserInputResult {
1541 if self.gas_owner() == self.sender() {
1542 return Ok(());
1543 }
1544 if matches!(self.kind(), TransactionKind::Programmable(_)) {
1545 return Ok(());
1546 }
1547 Err(UserInputError::UnsupportedSponsoredTransactionKind)
1548 }
1549
1550 fn is_end_of_epoch_tx(&self) -> bool {
1551 matches!(self.kind(), TransactionKind::EndOfEpoch(_))
1552 }
1553
1554 fn is_system_tx(&self) -> bool {
1555 self.kind().is_system()
1556 }
1557
1558 fn is_genesis_tx(&self) -> bool {
1559 matches!(self.kind(), TransactionKind::Genesis(_))
1560 }
1561
1562 fn sender_mut_for_testing(&mut self) -> &mut Address {
1563 match self {
1564 Self::V1(v1) => &mut v1.sender,
1565 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1566 }
1567 }
1568
1569 fn gas_data_mut(&mut self) -> &mut GasPayment {
1570 match self {
1571 Self::V1(v1) => &mut v1.gas_payment,
1572 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1573 }
1574 }
1575
1576 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
1577 match self {
1578 Self::V1(v1) => &mut v1.expiration,
1579 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1580 }
1581 }
1582
1583 fn new_system_transaction(kind: TransactionKind) -> Transaction {
1584 assert!(kind.is_system());
1585 let sender = Address::ZERO;
1586 Transaction::V1(TransactionV1 {
1587 kind,
1588 sender,
1589 gas_payment: GasPayment {
1590 price: GAS_PRICE_FOR_SYSTEM_TX,
1591 owner: sender,
1592 objects: vec![ObjectReference::new(
1593 ObjectId::ZERO,
1594 Version::default(),
1595 ObjectDigest::MIN,
1596 )],
1597 budget: 0,
1598 },
1599 expiration: TransactionExpiration::None,
1600 })
1601 }
1602
1603 fn new(
1604 kind: TransactionKind,
1605 sender: Address,
1606 gas_payment: ObjectReference,
1607 gas_budget: u64,
1608 gas_price: u64,
1609 ) -> Transaction {
1610 Transaction::V1(TransactionV1 {
1611 kind,
1612 sender,
1613 gas_payment: GasPayment {
1614 price: gas_price,
1615 owner: sender,
1616 objects: vec![gas_payment],
1617 budget: gas_budget,
1618 },
1619 expiration: TransactionExpiration::None,
1620 })
1621 }
1622
1623 fn new_with_gas_coins(
1624 kind: TransactionKind,
1625 sender: Address,
1626 gas_payment: Vec<ObjectReference>,
1627 gas_budget: u64,
1628 gas_price: u64,
1629 ) -> Transaction {
1630 Transaction::new_with_gas_coins_allow_sponsor(
1631 kind,
1632 sender,
1633 gas_payment,
1634 gas_budget,
1635 gas_price,
1636 sender,
1637 )
1638 }
1639
1640 fn new_with_gas_coins_allow_sponsor(
1641 kind: TransactionKind,
1642 sender: Address,
1643 gas_payment: Vec<ObjectReference>,
1644 gas_budget: u64,
1645 gas_price: u64,
1646 gas_sponsor: Address,
1647 ) -> Transaction {
1648 Transaction::V1(TransactionV1 {
1649 kind,
1650 sender,
1651 gas_payment: GasPayment {
1652 price: gas_price,
1653 owner: gas_sponsor,
1654 objects: gas_payment,
1655 budget: gas_budget,
1656 },
1657 expiration: TransactionExpiration::None,
1658 })
1659 }
1660
1661 fn new_with_gas_data(
1662 kind: TransactionKind,
1663 sender: Address,
1664 gas_data: GasPayment,
1665 ) -> Transaction {
1666 Transaction::V1(TransactionV1 {
1667 kind,
1668 sender,
1669 gas_payment: gas_data,
1670 expiration: TransactionExpiration::None,
1671 })
1672 }
1673
1674 fn new_move_call(
1675 sender: Address,
1676 package: ObjectId,
1677 module: Identifier,
1678 function: Identifier,
1679 type_arguments: Vec<TypeTag>,
1680 gas_payment: ObjectReference,
1681 arguments: Vec<CallArg>,
1682 gas_budget: u64,
1683 gas_price: u64,
1684 ) -> anyhow::Result<Transaction> {
1685 Transaction::new_move_call_with_gas_coins(
1686 sender,
1687 package,
1688 module,
1689 function,
1690 type_arguments,
1691 vec![gas_payment],
1692 arguments,
1693 gas_budget,
1694 gas_price,
1695 )
1696 }
1697
1698 fn new_move_call_with_gas_coins(
1699 sender: Address,
1700 package: ObjectId,
1701 module: Identifier,
1702 function: Identifier,
1703 type_arguments: Vec<TypeTag>,
1704 gas_payment: Vec<ObjectReference>,
1705 arguments: Vec<CallArg>,
1706 gas_budget: u64,
1707 gas_price: u64,
1708 ) -> anyhow::Result<Transaction> {
1709 let pt = {
1710 let mut builder = ProgrammableTransactionBuilder::new();
1711 builder.move_call(package, module, function, type_arguments, arguments)?;
1712 builder.finish()
1713 };
1714 Ok(Transaction::new_programmable(
1715 sender,
1716 gas_payment,
1717 pt,
1718 gas_budget,
1719 gas_price,
1720 ))
1721 }
1722
1723 fn new_transfer(
1724 recipient: Address,
1725 object_ref: ObjectReference,
1726 sender: Address,
1727 gas_payment: ObjectReference,
1728 gas_budget: u64,
1729 gas_price: u64,
1730 ) -> Transaction {
1731 let pt = {
1732 let mut builder = ProgrammableTransactionBuilder::new();
1733 builder.transfer_object(recipient, object_ref).unwrap();
1734 builder.finish()
1735 };
1736 Transaction::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1737 }
1738
1739 fn new_transfer_iota(
1740 recipient: Address,
1741 sender: Address,
1742 amount: Option<u64>,
1743 gas_payment: ObjectReference,
1744 gas_budget: u64,
1745 gas_price: u64,
1746 ) -> Transaction {
1747 Transaction::new_transfer_iota_allow_sponsor(
1748 recipient,
1749 sender,
1750 amount,
1751 gas_payment,
1752 gas_budget,
1753 gas_price,
1754 sender,
1755 )
1756 }
1757
1758 fn new_transfer_iota_allow_sponsor(
1759 recipient: Address,
1760 sender: Address,
1761 amount: Option<u64>,
1762 gas_payment: ObjectReference,
1763 gas_budget: u64,
1764 gas_price: u64,
1765 gas_sponsor: Address,
1766 ) -> Transaction {
1767 let pt = {
1768 let mut builder = ProgrammableTransactionBuilder::new();
1769 builder.transfer_iota(recipient, amount);
1770 builder.finish()
1771 };
1772 Transaction::new_programmable_allow_sponsor(
1773 sender,
1774 vec![gas_payment],
1775 pt,
1776 gas_budget,
1777 gas_price,
1778 gas_sponsor,
1779 )
1780 }
1781
1782 fn new_pay(
1783 sender: Address,
1784 coins: Vec<ObjectReference>,
1785 recipients: Vec<Address>,
1786 amounts: Vec<u64>,
1787 gas_payment: ObjectReference,
1788 gas_budget: u64,
1789 gas_price: u64,
1790 ) -> anyhow::Result<Transaction> {
1791 let pt = {
1792 let mut builder = ProgrammableTransactionBuilder::new();
1793 builder.pay(coins, recipients, amounts)?;
1794 builder.finish()
1795 };
1796 Ok(Transaction::new_programmable(
1797 sender,
1798 vec![gas_payment],
1799 pt,
1800 gas_budget,
1801 gas_price,
1802 ))
1803 }
1804
1805 fn new_pay_iota(
1806 sender: Address,
1807 mut coins: Vec<ObjectReference>,
1808 recipients: Vec<Address>,
1809 amounts: Vec<u64>,
1810 gas_payment: ObjectReference,
1811 gas_budget: u64,
1812 gas_price: u64,
1813 ) -> anyhow::Result<Transaction> {
1814 coins.insert(0, gas_payment);
1815 let pt = {
1816 let mut builder = ProgrammableTransactionBuilder::new();
1817 builder.pay_iota(recipients, amounts)?;
1818 builder.finish()
1819 };
1820 Ok(Transaction::new_programmable(
1821 sender, coins, pt, gas_budget, gas_price,
1822 ))
1823 }
1824
1825 fn new_pay_all_iota(
1826 sender: Address,
1827 mut coins: Vec<ObjectReference>,
1828 recipient: Address,
1829 gas_payment: ObjectReference,
1830 gas_budget: u64,
1831 gas_price: u64,
1832 ) -> Transaction {
1833 coins.insert(0, gas_payment);
1834 let pt = {
1835 let mut builder = ProgrammableTransactionBuilder::new();
1836 builder.pay_all_iota(recipient);
1837 builder.finish()
1838 };
1839 Transaction::new_programmable(sender, coins, pt, gas_budget, gas_price)
1840 }
1841
1842 fn new_split_coin(
1843 sender: Address,
1844 coin: ObjectReference,
1845 amounts: Vec<u64>,
1846 gas_payment: ObjectReference,
1847 gas_budget: u64,
1848 gas_price: u64,
1849 ) -> Transaction {
1850 let pt = {
1851 let mut builder = ProgrammableTransactionBuilder::new();
1852 builder.split_coin(sender, coin, amounts);
1853 builder.finish()
1854 };
1855 Transaction::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1856 }
1857
1858 fn new_module(
1859 sender: Address,
1860 gas_payment: ObjectReference,
1861 modules: Vec<Vec<u8>>,
1862 dep_ids: Vec<ObjectId>,
1863 gas_budget: u64,
1864 gas_price: u64,
1865 ) -> Transaction {
1866 let pt = {
1867 let mut builder = ProgrammableTransactionBuilder::new();
1868 let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
1869 builder.transfer_arg(sender, upgrade_cap);
1870 builder.finish()
1871 };
1872 Transaction::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1873 }
1874
1875 fn new_upgrade(
1876 sender: Address,
1877 gas_payment: ObjectReference,
1878 package_id: ObjectId,
1879 modules: Vec<Vec<u8>>,
1880 dep_ids: Vec<ObjectId>,
1881 (upgrade_capability, capability_owner): (ObjectReference, Owner),
1882 upgrade_policy: u8,
1883 digest: Vec<u8>,
1884 gas_budget: u64,
1885 gas_price: u64,
1886 ) -> anyhow::Result<Transaction> {
1887 let pt = {
1888 let mut builder = ProgrammableTransactionBuilder::new();
1889 let capability_arg = match capability_owner {
1890 Owner::Address(_) => CallArg::ImmutableOrOwned(upgrade_capability),
1891 Owner::Shared(initial_shared_version) => {
1892 CallArg::Shared(SharedObjectReference::new(
1893 upgrade_capability.object_id,
1894 initial_shared_version,
1895 true,
1896 ))
1897 }
1898 Owner::Immutable => {
1899 bail!("Upgrade capability is stored immutably and cannot be used for upgrades");
1900 }
1901 Owner::Object(_) => {
1902 bail!("Upgrade capability controlled by object");
1903 }
1904 _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
1905 };
1906 builder.obj(capability_arg).unwrap();
1907 let upgrade_arg = builder.pure(upgrade_policy).unwrap();
1908 let digest_arg = builder.pure(digest).unwrap();
1909 let upgrade_ticket = builder.programmable_move_call(
1910 ObjectId::FRAMEWORK,
1911 Identifier::PACKAGE_MODULE,
1912 Identifier::from_static("authorize_upgrade"),
1913 vec![],
1914 vec![Argument::Input(0), upgrade_arg, digest_arg],
1915 );
1916 let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
1917
1918 builder.programmable_move_call(
1919 ObjectId::FRAMEWORK,
1920 Identifier::PACKAGE_MODULE,
1921 Identifier::from_static("commit_upgrade"),
1922 vec![],
1923 vec![Argument::Input(0), upgrade_receipt],
1924 );
1925
1926 builder.finish()
1927 };
1928 Ok(Transaction::new_programmable(
1929 sender,
1930 vec![gas_payment],
1931 pt,
1932 gas_budget,
1933 gas_price,
1934 ))
1935 }
1936
1937 fn new_programmable(
1938 sender: Address,
1939 gas_payment: Vec<ObjectReference>,
1940 pt: ProgrammableTransaction,
1941 gas_budget: u64,
1942 gas_price: u64,
1943 ) -> Transaction {
1944 Transaction::new_programmable_allow_sponsor(
1945 sender,
1946 gas_payment,
1947 pt,
1948 gas_budget,
1949 gas_price,
1950 sender,
1951 )
1952 }
1953
1954 fn new_programmable_allow_sponsor(
1955 sender: Address,
1956 gas_payment: Vec<ObjectReference>,
1957 pt: ProgrammableTransaction,
1958 gas_budget: u64,
1959 gas_price: u64,
1960 sponsor: Address,
1961 ) -> Transaction {
1962 let kind = TransactionKind::Programmable(pt);
1963 Transaction::new_with_gas_coins_allow_sponsor(
1964 kind,
1965 sender,
1966 gas_payment,
1967 gas_budget,
1968 gas_price,
1969 sponsor,
1970 )
1971 }
1972
1973 fn message_version(&self) -> u64 {
1974 match self {
1975 Transaction::V1(_) => 1,
1976 _ => unimplemented!("a new Transaction enum variant was added and needs to be handled"),
1977 }
1978 }
1979
1980 fn execution_parts(&self) -> (TransactionKind, Address, GasPayment) {
1981 (self.kind().clone(), self.sender(), self.gas_data().clone())
1982 }
1983}
1984
1985pub struct TxValidityCheckContext<'a> {
1986 pub config: &'a ProtocolConfig,
1987 pub epoch: EpochId,
1988}
1989
1990pub fn merge_authenticator_input_objects<'a>(
1997 move_authenticators: impl IntoIterator<Item = &'a MoveAuthenticator>,
1998 input_objects: &mut Vec<InputObjectKind>,
1999) -> UserInputResult<()> {
2000 for move_authenticator in move_authenticators {
2001 for auth_object in move_authenticator.input_objects() {
2002 let entry = input_objects
2003 .iter_mut()
2004 .find(|o| o.object_id() == auth_object.object_id());
2005
2006 match entry {
2007 None => input_objects.push(auth_object),
2008 Some(existing) => existing.left_union_with_checks(&auth_object)?,
2009 }
2010 }
2011 }
2012 Ok(())
2013}
2014
2015pub trait SenderSignedTransactionAPI {
2022 fn new_from_sender_signature(
2025 tx: Transaction,
2026 tx_signature: SimpleSignature,
2027 ) -> SenderSignedTransaction;
2028
2029 fn add_signature(&mut self, new_signature: SimpleSignature);
2032
2033 fn get_signer_sig_mapping(&self) -> IotaResult<BTreeMap<Address, &UserSignature>>;
2036
2037 fn has_multisig(&self) -> bool;
2039
2040 fn transaction_mut_for_testing(&mut self) -> &mut Transaction;
2042
2043 fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<UserSignature>;
2045
2046 fn serialized_size(&self) -> IotaResult<usize>;
2048
2049 fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, IotaError>;
2053
2054 fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>>;
2063
2064 fn split_input_objects_into_groups_for_reading(
2070 &self,
2071 input_objects: InputObjects,
2072 ) -> IotaResult<(InputObjects, Vec<(InputObjects, ObjectReadResult)>)>;
2073
2074 fn contains_shared_object(&self) -> bool;
2082
2083 fn shared_input_objects(&self) -> Vec<SharedObjectReference>;
2092
2093 fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>>;
2105
2106 fn uses_randomness(&self) -> bool;
2111}
2112
2113impl SenderSignedTransactionAPI for SenderSignedTransaction {
2114 fn new_from_sender_signature(
2115 tx: Transaction,
2116 tx_signature: SimpleSignature,
2117 ) -> SenderSignedTransaction {
2118 Self::new(tx, vec![tx_signature.into()])
2119 }
2120
2121 fn add_signature(&mut self, new_signature: SimpleSignature) {
2122 self.signed_transaction_mut()
2123 .signatures
2124 .push(new_signature.into());
2125 }
2126
2127 fn get_signer_sig_mapping(&self) -> IotaResult<BTreeMap<Address, &UserSignature>> {
2128 let mut mapping = BTreeMap::new();
2129 for sig in &self.signed_transaction().signatures {
2130 let address = sig.derive_address();
2131 mapping.insert(address, sig);
2132 }
2133 Ok(mapping)
2134 }
2135
2136 fn has_multisig(&self) -> bool {
2137 self.signatures().iter().any(|sig| sig.is_multisig())
2138 }
2139
2140 fn transaction_mut_for_testing(&mut self) -> &mut Transaction {
2141 &mut self.signed_transaction_mut().transaction
2142 }
2143
2144 fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<UserSignature> {
2145 &mut self.signed_transaction_mut().signatures
2146 }
2147
2148 fn serialized_size(&self) -> IotaResult<usize> {
2149 tx_bcs_size(self)
2150 }
2151
2152 fn validity_check(&self, context: &TxValidityCheckContext<'_>) -> Result<usize, IotaError> {
2153 check_user_signature_protocol_compatibility(self, context.config)?;
2156
2157 let tx = self.transaction();
2160 fp_ensure!(
2161 !tx.is_system_tx(),
2162 IotaError::UserInput {
2163 error: UserInputError::Unsupported(
2164 "SenderSignedTransaction must not contain system transaction".to_string()
2165 )
2166 }
2167 );
2168
2169 if match &tx.expiration() {
2171 TransactionExpiration::None => false,
2172 TransactionExpiration::Epoch(exp_poch) => *exp_poch < context.epoch,
2173 _ => unimplemented!(
2174 "a new TransactionExpiration enum variant was added and needs to be handled"
2175 ),
2176 } {
2177 return Err(IotaError::TransactionExpired);
2178 }
2179
2180 let tx_size = check_transaction_size(self, context.config)?;
2182
2183 tx.validity_check(context.config)
2184 .map_err(Into::<IotaError>::into)?;
2185
2186 move_authenticators_validity_check(self, context.config)?;
2187
2188 Ok(tx_size)
2189 }
2190
2191 fn collect_all_input_object_kind_for_reading(&self) -> IotaResult<Vec<InputObjectKind>> {
2192 let mut seen = HashSet::new();
2193 Ok(self
2194 .transaction()
2195 .input_objects()?
2196 .into_iter()
2197 .chain(
2198 self.move_authenticators()
2199 .into_iter()
2200 .flat_map(|authenticator| authenticator.input_objects()),
2201 )
2202 .filter(|kind| seen.insert(*kind))
2203 .collect())
2204 }
2205
2206 fn split_input_objects_into_groups_for_reading(
2207 &self,
2208 input_objects: InputObjects,
2209 ) -> IotaResult<(InputObjects, Vec<(InputObjects, ObjectReadResult)>)> {
2210 let input_objects_map = input_objects
2211 .iter()
2212 .map(|o| (&o.input_object_kind, o))
2213 .collect::<HashMap<_, _>>();
2214
2215 let mut tx_input_objects: InputObjects = self
2216 .transaction()
2217 .input_objects()?
2218 .iter()
2219 .map(|k| {
2220 input_objects_map
2221 .get(k)
2222 .map(|&r| r.clone())
2223 .expect("All transaction input objects are expected to be present")
2224 })
2225 .collect::<Vec<_>>()
2226 .into();
2227 if let Some((gas_object_id, version)) = input_objects.gas_object_cancellation() {
2228 tx_input_objects.set_gas_object_cancellation(gas_object_id, version);
2229 }
2230
2231 let per_authenticator_inputs =
2232 self.move_authenticators()
2233 .into_iter()
2234 .map(|move_authenticator| {
2235 let authenticator_input_objects = move_authenticator
2236 .input_objects()
2237 .iter()
2238 .map(|k| {
2239 input_objects_map.get(k).map(|&r| r.clone()).expect(
2240 "All authenticator input objects are expected to be present",
2241 )
2242 })
2243 .collect::<Vec<_>>()
2244 .into();
2245
2246 let account_objects = move_authenticator
2247 .object_to_authenticate()
2248 .input_object_kind()
2249 .iter()
2250 .map(|k| {
2251 input_objects_map
2252 .get(k)
2253 .map(|&r| r.clone())
2254 .expect("Account object is expected to be present")
2255 })
2256 .collect::<Vec<_>>();
2257
2258 debug_assert!(
2259 account_objects.len() == 1,
2260 "Only one account object must be loaded"
2261 );
2262
2263 (
2264 authenticator_input_objects,
2265 account_objects
2266 .into_iter()
2267 .next()
2268 .expect("Account object is expected to be present"),
2269 )
2270 })
2271 .collect();
2272
2273 Ok((tx_input_objects, per_authenticator_inputs))
2274 }
2275
2276 fn contains_shared_object(&self) -> bool {
2277 self.transaction().kind().contains_shared_object()
2278 || self
2279 .move_authenticators()
2280 .iter()
2281 .any(|move_authenticator| !move_authenticator.shared_objects().is_empty())
2282 }
2283
2284 fn shared_input_objects(&self) -> Vec<SharedObjectReference> {
2285 let mut input_objects = self.transaction().shared_input_objects();
2287
2288 self.move_authenticators()
2290 .into_iter()
2291 .for_each(|move_authenticator| {
2292 for auth_shared_object in move_authenticator.shared_objects() {
2293 let entry = input_objects
2294 .iter_mut()
2295 .find(|o| o.object_id == auth_shared_object.object_id);
2296
2297 match entry {
2298 None => input_objects.push(auth_shared_object),
2299 Some(existing) => {
2300 left_union_shared_input_objects(existing, &auth_shared_object)
2301 .expect("union of shared objects should not fail")
2302 }
2303 }
2304 }
2305 });
2306
2307 input_objects
2308 }
2309
2310 fn input_objects(&self) -> IotaResult<Vec<InputObjectKind>> {
2311 let mut input_objects = self.transaction().input_objects()?;
2314
2315 merge_authenticator_input_objects(self.move_authenticators(), &mut input_objects)?;
2317
2318 Ok(input_objects)
2319 }
2320
2321 fn uses_randomness(&self) -> bool {
2322 self.shared_input_objects()
2323 .iter()
2324 .any(|obj| obj.object_id == ObjectId::RANDOMNESS_STATE)
2325 }
2326}
2327
2328fn check_user_signature_protocol_compatibility(
2329 signed_tx: &SenderSignedTransaction,
2330 config: &ProtocolConfig,
2331) -> IotaResult {
2332 for sig in signed_tx.signatures() {
2333 match sig {
2334 UserSignature::PasskeyAuthenticator(_) => {
2335 if !config.passkey_auth() {
2336 return Err(IotaError::UserInput {
2337 error: UserInputError::Unsupported(
2338 "passkey is not enabled on this network".to_string(),
2339 ),
2340 });
2341 }
2342 }
2343 UserSignature::MoveAuthenticator(_) => {
2344 if !config.enable_move_authentication() {
2345 return Err(IotaError::UserInput {
2346 error: UserInputError::Unsupported(
2347 "`Move authentication` is not enabled on this network".to_string(),
2348 ),
2349 });
2350 }
2351 }
2352 UserSignature::Simple(_) | UserSignature::Multisig(_) => (),
2353 _ => {
2354 unimplemented!("a new UserSignature variant was added and needs to be handled")
2355 }
2356 }
2357 }
2358
2359 Ok(())
2360}
2361
2362fn move_authenticators_validity_check(
2363 signed_tx: &SenderSignedTransaction,
2364 config: &ProtocolConfig,
2365) -> IotaResult {
2366 let authenticators = signed_tx.move_authenticators();
2367
2368 authenticators
2370 .iter()
2371 .try_for_each(|authenticator| authenticator.validity_check(config))?;
2372
2373 let authenticators_num = authenticators.len();
2375 if authenticators_num > 0 {
2376 let tx = signed_tx.transaction();
2377
2378 fp_ensure!(
2379 tx.kind().is_programmable(),
2380 UserInputError::Unsupported(
2381 "SenderSignedTransaction with MoveAuthenticator must be a programmable transaction"
2382 .to_string(),
2383 )
2384 .into()
2385 );
2386
2387 if !config.enable_move_authentication_for_sponsor() {
2388 fp_ensure!(
2389 authenticators_num == 1,
2390 UserInputError::Unsupported(
2391 "SenderSignedTransaction with more than one MoveAuthenticator is not supported"
2392 .to_string(),
2393 )
2394 .into()
2395 );
2396
2397 fp_ensure!(
2398 signed_tx.sender_move_authenticator().is_some(),
2399 UserInputError::Unsupported(
2400 "SenderSignedTransaction can have MoveAuthenticator only for the sender"
2401 .to_string(),
2402 )
2403 .into()
2404 );
2405 }
2406
2407 check_move_authenticators_input_consistency(tx, &authenticators)?;
2408 }
2409
2410 Ok(())
2411}
2412
2413fn check_move_authenticators_input_consistency(
2414 tx: &Transaction,
2415 authenticators: &[&MoveAuthenticator],
2416) -> IotaResult {
2417 let mut checked_inputs = tx
2419 .kind()
2420 .input_objects()?
2421 .into_iter()
2422 .map(|o| (o.object_id(), o))
2423 .collect::<HashMap<_, _>>();
2424
2425 authenticators.iter().try_for_each(|authenticator| {
2426 authenticator
2427 .input_objects()
2428 .iter()
2429 .try_for_each(|auth_input_object| {
2430 match checked_inputs.get(&auth_input_object.object_id()) {
2431 Some(existing) => {
2432 auth_input_object.check_consistency_for_authentication(existing)?
2433 }
2434 None => {
2435 checked_inputs.insert(auth_input_object.object_id(), *auth_input_object);
2436 }
2437 };
2438
2439 Ok(())
2440 })
2441 })
2442}
2443
2444impl Message for SenderSignedTransaction {
2445 type DigestType = TransactionDigest;
2446 const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2447
2448 fn digest(&self) -> Self::DigestType {
2451 self.transaction().digest()
2452 }
2453}
2454
2455impl<S> Envelope<SenderSignedTransaction, S> {
2456 pub fn sender_address(&self) -> Address {
2457 self.data().transaction().sender()
2458 }
2459
2460 pub fn gas(&self) -> &[ObjectReference] {
2461 self.data().transaction().gas()
2462 }
2463
2464 pub fn key(&self) -> TransactionKey {
2466 match &self.data().transaction().kind() {
2467 TransactionKind::RandomnessStateUpdate(rsu) => {
2468 TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2469 }
2470 _ => TransactionKey::Digest(*self.digest()),
2471 }
2472 }
2473
2474 pub fn non_digest_key(&self) -> Option<TransactionKey> {
2479 match &self.data().transaction().kind() {
2480 TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2481 rsu.epoch,
2482 rsu.randomness_round,
2483 )),
2484 _ => None,
2485 }
2486 }
2487
2488 pub fn is_system_tx(&self) -> bool {
2489 self.data().transaction().is_system_tx()
2490 }
2491
2492 pub fn is_sponsored_tx(&self) -> bool {
2493 self.data().transaction().is_sponsored_tx()
2494 }
2495}
2496
2497impl TransactionEnvelope {
2498 pub fn from_data_and_signer(
2499 tx: Transaction,
2500 signers: Vec<&impl iota_sdk_crypto::Signer<SimpleSignature>>,
2501 ) -> Self {
2502 let signatures = {
2503 let digest = tx.signing_digest();
2504 signers.into_iter().map(|s| s.sign(&digest)).collect()
2505 };
2506 Self::from_data(tx, signatures)
2507 }
2508
2509 pub fn from_data(tx: Transaction, signatures: Vec<SimpleSignature>) -> Self {
2511 Self::from_user_sig_data(tx, signatures.into_iter().map(|s| s.into()).collect())
2512 }
2513
2514 pub fn signature_from_signer(
2515 tx: Transaction,
2516 intent: Intent,
2517 signer: &impl iota_sdk_crypto::Signer<SimpleSignature>,
2518 ) -> SimpleSignature {
2519 let digest = IntentMessage::new(intent, tx).signing_digest();
2520 signer.sign(&digest)
2521 }
2522
2523 pub fn from_user_sig_data(tx: Transaction, signatures: Vec<UserSignature>) -> Self {
2524 Self::new(SenderSignedTransaction::new(tx, signatures))
2525 }
2526
2527 pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2530 (
2531 Base64::from_bytes(&self.data().transaction().to_bcs()),
2532 self.data()
2533 .signatures()
2534 .iter()
2535 .map(|s| Base64::from_bytes(&s.to_bytes()))
2536 .collect(),
2537 )
2538 }
2539}
2540
2541impl VerifiedTransaction {
2542 pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2543 GenesisTransaction { objects, events }
2544 .pipe(TransactionKind::Genesis)
2545 .pipe(Self::new_system_transaction)
2546 }
2547
2548 pub fn new_consensus_commit_prologue_v1(
2549 epoch: u64,
2550 round: u64,
2551 commit_timestamp_ms: CheckpointTimestamp,
2552 consensus_commit_digest: ConsensusCommitDigest,
2553 canceled_transactions: Vec<CanceledTransaction>,
2554 ) -> Self {
2555 ConsensusCommitPrologueV1 {
2556 epoch,
2557 round,
2558 sub_dag_index: None,
2560 commit_timestamp_ms,
2561 consensus_commit_digest,
2562 consensus_determined_version_assignments:
2563 ConsensusDeterminedVersionAssignments::CanceledTransactions {
2564 canceled_transactions,
2565 },
2566 }
2567 .pipe(TransactionKind::ConsensusCommitPrologueV1)
2568 .pipe(Self::new_system_transaction)
2569 }
2570
2571 pub fn new_randomness_state_update(
2572 epoch: u64,
2573 randomness_round: RandomnessRound,
2574 random_bytes: Vec<u8>,
2575 randomness_obj_initial_shared_version: Version,
2576 ) -> Self {
2577 RandomnessStateUpdate {
2578 epoch,
2579 randomness_round,
2580 random_bytes,
2581 randomness_obj_initial_shared_version,
2582 }
2583 .pipe(TransactionKind::RandomnessStateUpdate)
2584 .pipe(Self::new_system_transaction)
2585 }
2586
2587 pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
2588 TransactionKind::EndOfEpoch(txns).pipe(Self::new_system_transaction)
2589 }
2590
2591 pub fn new_transaction_deny_rules_update(update: TransactionDenyRulesUpdate) -> Self {
2592 TransactionKind::TransactionDenyRulesUpdate(update).pipe(Self::new_system_transaction)
2593 }
2594
2595 fn new_system_transaction(system_transaction: TransactionKind) -> Self {
2596 system_transaction
2597 .pipe(Transaction::new_system_transaction)
2598 .pipe(|data| {
2599 SenderSignedTransaction::new_from_sender_signature(data, zero_ed25519_signature())
2600 })
2601 .pipe(TransactionEnvelope::new)
2602 .pipe(Self::new_from_verified)
2603 }
2604}
2605
2606impl VerifiedSignedTransaction {
2607 #[instrument(level = "trace", skip_all)]
2609 pub fn new(
2610 epoch: EpochId,
2611 transaction: VerifiedTransaction,
2612 authority: AuthorityName,
2613 secret: &dyn Signer<AuthoritySignature>,
2614 ) -> Self {
2615 Self::new_from_verified(SignedTransaction::new(
2616 epoch,
2617 transaction.into_inner().into_data(),
2618 secret,
2619 authority,
2620 ))
2621 }
2622}
2623
2624pub type TransactionEnvelope = Envelope<SenderSignedTransaction, EmptySignInfo>;
2626pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedTransaction, EmptySignInfo>;
2627pub type TrustedTransaction = TrustedEnvelope<SenderSignedTransaction, EmptySignInfo>;
2628
2629pub type SignedTransaction = Envelope<SenderSignedTransaction, AuthoritySignInfo>;
2631pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedTransaction, AuthoritySignInfo>;
2632
2633impl TransactionEnvelope {
2634 pub fn verify_signature_for_testing(&self, verify_params: &VerifyParams) -> IotaResult {
2635 verify_sender_signed_data_message_signatures(self.data(), verify_params)
2636 }
2637
2638 pub fn try_into_verified_for_testing(
2639 self,
2640 verify_params: &VerifyParams,
2641 ) -> IotaResult<VerifiedTransaction> {
2642 self.verify_signature_for_testing(verify_params)?;
2643 Ok(VerifiedTransaction::new_from_verified(self))
2644 }
2645
2646 pub fn gas_price(&self) -> u64 {
2647 self.data().transaction().gas_price()
2648 }
2649}
2650
2651impl SignedTransaction {
2652 pub fn verify_signatures_authenticated_for_testing(
2653 &self,
2654 committee: &Committee,
2655 verify_params: &VerifyParams,
2656 ) -> IotaResult {
2657 verify_sender_signed_data_message_signatures(self.data(), verify_params)?;
2658
2659 self.auth_sig().verify_secure(
2660 self.data(),
2661 Intent::iota_app(IntentScope::SenderSignedTransaction),
2662 committee,
2663 )
2664 }
2665
2666 pub fn try_into_verified_for_testing(
2667 self,
2668 committee: &Committee,
2669 verify_params: &VerifyParams,
2670 ) -> IotaResult<VerifiedSignedTransaction> {
2671 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
2672 Ok(VerifiedSignedTransaction::new_from_verified(self))
2673 }
2674}
2675
2676pub type CertifiedTransaction = Envelope<SenderSignedTransaction, AuthorityStrongQuorumSignInfo>;
2677
2678impl CertifiedTransaction {
2679 pub fn certificate_digest(&self) -> CertificateDigest {
2680 let mut digest = DefaultHash::default();
2681 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2682 let hash = digest.finalize();
2683 CertificateDigest::new(hash.into())
2684 }
2685
2686 pub fn gas_price(&self) -> u64 {
2687 self.data().transaction().gas_price()
2688 }
2689
2690 #[instrument(level = "trace", skip_all)]
2693 pub fn verify_signatures_authenticated(
2694 &self,
2695 committee: &Committee,
2696 verify_params: &VerifyParams,
2697 ) -> IotaResult {
2698 verify_sender_signed_data_message_signatures(self.data(), verify_params)?;
2699 self.verify_committee_sigs_only(committee)
2700 }
2701
2702 pub fn try_into_verified_for_testing(
2703 self,
2704 committee: &Committee,
2705 verify_params: &VerifyParams,
2706 ) -> IotaResult<VerifiedCertificate> {
2707 self.verify_signatures_authenticated(committee, verify_params)?;
2708 Ok(VerifiedCertificate::new_from_verified(self))
2709 }
2710
2711 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
2712 self.auth_sig().verify_secure(
2713 self.data(),
2714 Intent::iota_app(IntentScope::SenderSignedTransaction),
2715 committee,
2716 )
2717 }
2718}
2719
2720pub type VerifiedCertificate =
2721 VerifiedEnvelope<SenderSignedTransaction, AuthorityStrongQuorumSignInfo>;
2722pub type TrustedCertificate =
2723 TrustedEnvelope<SenderSignedTransaction, AuthorityStrongQuorumSignInfo>;
2724
2725#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
2726pub enum InputObjectKind {
2727 MovePackage(ObjectId),
2729 ImmOrOwnedMoveObject(ObjectReference),
2731 SharedMoveObject {
2733 id: ObjectId,
2734 initial_shared_version: Version,
2735 mutable: bool,
2736 },
2737}
2738
2739impl InputObjectKind {
2740 pub fn object_id(&self) -> ObjectId {
2741 match self {
2742 Self::MovePackage(id) => *id,
2743 Self::ImmOrOwnedMoveObject(object_ref) => object_ref.object_id,
2744 Self::SharedMoveObject { id, .. } => *id,
2745 }
2746 }
2747
2748 pub fn version(&self) -> Option<Version> {
2749 match self {
2750 Self::MovePackage(..) => None,
2751 Self::ImmOrOwnedMoveObject(object_ref) => Some(object_ref.version),
2752 Self::SharedMoveObject { .. } => None,
2753 }
2754 }
2755
2756 pub fn object_not_found_error(&self) -> UserInputError {
2757 match *self {
2758 Self::MovePackage(package_id) => {
2759 UserInputError::DependentPackageNotFound { package_id }
2760 }
2761 Self::ImmOrOwnedMoveObject(object_ref) => UserInputError::ObjectNotFound {
2762 object_id: object_ref.object_id,
2763 version: Some(object_ref.version),
2764 },
2765 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
2766 object_id: id,
2767 version: None,
2768 },
2769 }
2770 }
2771
2772 pub fn not_available_at_commit_error(&self) -> UserInputError {
2776 let (object_id, version) = match *self {
2777 Self::MovePackage(package_id) => (package_id, None),
2778 Self::ImmOrOwnedMoveObject(object_ref) => {
2779 (object_ref.object_id, Some(object_ref.version))
2780 }
2781 Self::SharedMoveObject { id, .. } => (id, None),
2782 };
2783 UserInputError::InputObjectNotAvailableAtCommit { object_id, version }
2784 }
2785
2786 pub fn is_shared_object(&self) -> bool {
2787 matches!(self, Self::SharedMoveObject { .. })
2788 }
2789
2790 pub fn is_mutable(&self) -> bool {
2791 match self {
2792 Self::MovePackage(..) => false,
2793 Self::ImmOrOwnedMoveObject(_) => true,
2794 Self::SharedMoveObject { mutable, .. } => *mutable,
2795 }
2796 }
2797
2798 pub fn left_union_with_checks(&mut self, other: &InputObjectKind) -> UserInputResult<()> {
2804 match self {
2805 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2806 fp_ensure!(
2807 self == other,
2808 UserInputError::InconsistentInput {
2809 object_id: other.object_id(),
2810 }
2811 );
2812 }
2813 InputObjectKind::SharedMoveObject {
2814 id,
2815 initial_shared_version,
2816 mutable,
2817 } => match other {
2818 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2819 fp_bail!(UserInputError::NotSharedObject)
2820 }
2821 InputObjectKind::SharedMoveObject {
2822 id: other_id,
2823 initial_shared_version: other_initial_shared_version,
2824 mutable: other_mutable,
2825 } => {
2826 fp_ensure!(id == other_id, UserInputError::SharedObjectIdMismatch);
2827 fp_ensure!(
2828 initial_shared_version == other_initial_shared_version,
2829 UserInputError::SharedObjectStartingVersionMismatch
2830 );
2831
2832 if !*mutable && *other_mutable {
2833 *mutable = *other_mutable;
2834 }
2835 }
2836 },
2837 }
2838
2839 Ok(())
2840 }
2841
2842 pub fn check_consistency_for_authentication(
2846 &self,
2847 other: &InputObjectKind,
2848 ) -> UserInputResult<()> {
2849 match self {
2850 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2851 fp_ensure!(
2852 self == other,
2853 UserInputError::InconsistentInput {
2854 object_id: self.object_id()
2855 }
2856 );
2857 }
2858 InputObjectKind::SharedMoveObject {
2859 id,
2860 initial_shared_version,
2861 mutable: _,
2862 } => match other {
2863 InputObjectKind::MovePackage(_) | InputObjectKind::ImmOrOwnedMoveObject(_) => {
2864 fp_bail!(UserInputError::InconsistentInput {
2865 object_id: self.object_id()
2866 })
2867 }
2868 InputObjectKind::SharedMoveObject {
2869 id: other_id,
2870 initial_shared_version: other_initial_shared_version,
2871 mutable: _,
2872 } => {
2873 fp_ensure!(
2874 id == other_id,
2875 UserInputError::InconsistentInput { object_id: *id }
2876 );
2877 fp_ensure!(
2878 initial_shared_version == other_initial_shared_version,
2879 UserInputError::InconsistentInput { object_id: *id }
2880 );
2881 }
2882 },
2883 }
2884
2885 Ok(())
2886 }
2887}
2888
2889#[derive(Clone, Debug)]
2893pub struct ObjectReadResult {
2894 pub input_object_kind: InputObjectKind,
2895 pub object: ObjectReadResultKind,
2896}
2897
2898#[derive(Clone, PartialEq)]
2899pub enum ObjectReadResultKind {
2900 Object(Object),
2901 DeletedSharedObject(Version, TransactionDigest),
2904 CancelledTransactionObject(Version),
2906 UnreadSharedObject,
2911}
2912
2913impl std::fmt::Debug for ObjectReadResultKind {
2914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2915 match self {
2916 ObjectReadResultKind::Object(obj) => {
2917 write!(f, "Object({:?})", obj.object_ref())
2918 }
2919 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2920 write!(f, "DeletedSharedObject({seq}, {digest})")
2921 }
2922 ObjectReadResultKind::CancelledTransactionObject(seq) => {
2923 write!(f, "CancelledTransactionObject({seq})")
2924 }
2925 ObjectReadResultKind::UnreadSharedObject => write!(f, "UnreadSharedObject"),
2926 }
2927 }
2928}
2929
2930impl From<Object> for ObjectReadResultKind {
2931 fn from(object: Object) -> Self {
2932 Self::Object(object)
2933 }
2934}
2935
2936impl ObjectReadResult {
2937 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
2938 if let (
2939 InputObjectKind::ImmOrOwnedMoveObject(_),
2940 ObjectReadResultKind::DeletedSharedObject(_, _),
2941 ) = (&input_object_kind, &object)
2942 {
2943 panic!("only shared objects can be DeletedSharedObject");
2944 }
2945
2946 if let (
2947 InputObjectKind::ImmOrOwnedMoveObject(_),
2948 ObjectReadResultKind::CancelledTransactionObject(_),
2949 ) = (&input_object_kind, &object)
2950 {
2951 panic!("only shared objects can be CancelledTransactionObject");
2952 }
2953
2954 if let (
2955 InputObjectKind::ImmOrOwnedMoveObject(_),
2956 ObjectReadResultKind::UnreadSharedObject,
2957 ) = (&input_object_kind, &object)
2958 {
2959 panic!("only shared objects can be UnreadSharedObject");
2960 }
2961
2962 Self {
2963 input_object_kind,
2964 object,
2965 }
2966 }
2967
2968 pub fn id(&self) -> ObjectId {
2969 self.input_object_kind.object_id()
2970 }
2971
2972 pub fn as_object(&self) -> Option<&Object> {
2973 match &self.object {
2974 ObjectReadResultKind::Object(object) => Some(object),
2975 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
2976 ObjectReadResultKind::CancelledTransactionObject(_) => None,
2977 ObjectReadResultKind::UnreadSharedObject => None,
2978 }
2979 }
2980
2981 pub fn new_from_gas_object(gas: &Object) -> Self {
2982 let objref = gas.object_ref();
2983 Self {
2984 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
2985 object: ObjectReadResultKind::Object(gas.clone()),
2986 }
2987 }
2988
2989 pub fn is_mutable(&self) -> bool {
2990 match (&self.input_object_kind, &self.object) {
2991 (InputObjectKind::MovePackage(_), _) => false,
2992 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
2993 !object.is_immutable()
2994 }
2995 (
2996 InputObjectKind::ImmOrOwnedMoveObject(_),
2997 ObjectReadResultKind::DeletedSharedObject(_, _),
2998 ) => unreachable!(),
2999 (
3000 InputObjectKind::ImmOrOwnedMoveObject(_),
3001 ObjectReadResultKind::CancelledTransactionObject(_),
3002 ) => unreachable!(),
3003 (
3004 InputObjectKind::ImmOrOwnedMoveObject(_),
3005 ObjectReadResultKind::UnreadSharedObject,
3006 ) => unreachable!(),
3007 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
3008 }
3009 }
3010
3011 pub fn is_shared_object(&self) -> bool {
3012 self.input_object_kind.is_shared_object()
3013 }
3014
3015 pub fn is_deleted_shared_object(&self) -> bool {
3016 self.deletion_info().is_some()
3017 }
3018
3019 pub fn deletion_info(&self) -> Option<(Version, TransactionDigest)> {
3020 match &self.object {
3021 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
3022 _ => None,
3023 }
3024 }
3025
3026 pub fn get_owned_objref(&self) -> Option<ObjectReference> {
3029 match (&self.input_object_kind, &self.object) {
3030 (InputObjectKind::MovePackage(_), _) => None,
3031 (
3032 InputObjectKind::ImmOrOwnedMoveObject(objref),
3033 ObjectReadResultKind::Object(object),
3034 ) => {
3035 if object.is_immutable() {
3036 None
3037 } else {
3038 Some(*objref)
3039 }
3040 }
3041 (
3042 InputObjectKind::ImmOrOwnedMoveObject(_),
3043 ObjectReadResultKind::DeletedSharedObject(_, _),
3044 ) => unreachable!(),
3045 (
3046 InputObjectKind::ImmOrOwnedMoveObject(_),
3047 ObjectReadResultKind::CancelledTransactionObject(_),
3048 ) => unreachable!(),
3049 (
3050 InputObjectKind::ImmOrOwnedMoveObject(_),
3051 ObjectReadResultKind::UnreadSharedObject,
3052 ) => unreachable!(),
3053 (InputObjectKind::SharedMoveObject { .. }, _) => None,
3054 }
3055 }
3056
3057 pub fn is_owned(&self) -> bool {
3058 self.get_owned_objref().is_some()
3059 }
3060
3061 pub fn to_shared_input(&self) -> Option<SharedInput> {
3062 match self.input_object_kind {
3063 InputObjectKind::MovePackage(_) => None,
3064 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
3065 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
3066 ObjectReadResultKind::Object(obj) => SharedInput::Existing(obj.object_ref()),
3067 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
3068 SharedInput::Deleted((id, *seq, mutable, *digest))
3069 }
3070 ObjectReadResultKind::CancelledTransactionObject(seq) => {
3071 SharedInput::Cancelled((id, *seq))
3072 }
3073 ObjectReadResultKind::UnreadSharedObject => {
3074 unreachable!("an unread shared object has no version to execute at")
3075 }
3076 }),
3077 }
3078 }
3079
3080 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
3081 match &self.object {
3082 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
3083 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
3084 ObjectReadResultKind::CancelledTransactionObject(_) => None,
3085 ObjectReadResultKind::UnreadSharedObject => None,
3086 }
3087 }
3088}
3089
3090#[derive(Clone, Debug, PartialEq, Eq)]
3093pub enum CancelledObjects {
3094 SharedObjects(Vec<ObjectId>),
3096 GasObject,
3101}
3102
3103#[derive(Clone)]
3104pub struct InputObjects {
3105 objects: Vec<ObjectReadResult>,
3106 gas_object_cancellation: Option<(ObjectId, Version)>,
3113}
3114
3115impl std::fmt::Debug for InputObjects {
3116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3117 match &self.gas_object_cancellation {
3118 None => f.debug_list().entries(self.objects.iter()).finish(),
3119 Some(gas_object_cancellation) => f
3120 .debug_struct("InputObjects")
3121 .field("objects", &self.objects)
3122 .field("gas_object_cancellation", gas_object_cancellation)
3123 .finish(),
3124 }
3125 }
3126}
3127
3128pub struct CheckedInputObjects(InputObjects);
3131
3132impl CheckedInputObjects {
3138 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3140 Self(inputs)
3141 }
3142
3143 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3145 Self(InputObjects::new(input_objects))
3146 }
3147
3148 pub fn new_for_replay(input_objects: InputObjects) -> Self {
3150 Self(input_objects)
3151 }
3152
3153 pub fn inner(&self) -> &InputObjects {
3154 &self.0
3155 }
3156
3157 pub fn into_inner(self) -> InputObjects {
3158 self.0
3159 }
3160}
3161
3162impl From<Vec<ObjectReadResult>> for InputObjects {
3163 fn from(objects: Vec<ObjectReadResult>) -> Self {
3164 Self::new(objects)
3165 }
3166}
3167
3168impl InputObjects {
3169 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
3170 Self {
3171 objects,
3172 gas_object_cancellation: None,
3173 }
3174 }
3175
3176 pub fn set_gas_object_cancellation(&mut self, gas_object_id: ObjectId, version: Version) {
3181 assert!(
3182 version.is_canceled(),
3183 "gas object cancellation must use a cancellation version, got {version:?}"
3184 );
3185 assert!(
3186 !self.objects.iter().any(|obj| obj.is_shared_object()),
3187 "transactions with shared inputs carry cancellation on their shared objects"
3188 );
3189 self.gas_object_cancellation = Some((gas_object_id, version));
3190 }
3191
3192 pub fn gas_object_cancellation(&self) -> Option<(ObjectId, Version)> {
3193 self.gas_object_cancellation
3194 }
3195
3196 pub fn len(&self) -> usize {
3197 self.objects.len()
3198 }
3199
3200 pub fn is_empty(&self) -> bool {
3201 self.objects.is_empty()
3202 }
3203
3204 pub fn contains_deleted_objects(&self) -> bool {
3205 self.objects
3206 .iter()
3207 .any(|obj| obj.is_deleted_shared_object())
3208 }
3209
3210 pub fn get_cancelled_objects(&self) -> Option<(CancelledObjects, Version)> {
3213 if let Some((_, version)) = &self.gas_object_cancellation {
3214 return Some((CancelledObjects::GasObject, *version));
3215 }
3216
3217 let mut contains_cancelled = false;
3218 let mut cancel_reason = None;
3219 let mut cancelled_objects = Vec::new();
3220 for obj in &self.objects {
3221 if let ObjectReadResultKind::CancelledTransactionObject(version) = obj.object {
3222 contains_cancelled = true;
3223 if version.is_congested() || version == Version::RANDOMNESS_UNAVAILABLE {
3224 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
3226 cancel_reason = Some(version);
3227 cancelled_objects.push(obj.id());
3228 }
3229 }
3230 }
3231
3232 if !cancelled_objects.is_empty() {
3233 Some((
3234 CancelledObjects::SharedObjects(cancelled_objects),
3235 cancel_reason
3236 .expect("there should be a cancel reason if there are cancelled objects"),
3237 ))
3238 } else {
3239 assert!(!contains_cancelled);
3240 None
3241 }
3242 }
3243
3244 pub fn filter_owned_objects(&self) -> Vec<ObjectReference> {
3245 let owned_objects: Vec<_> = self
3246 .objects
3247 .iter()
3248 .filter_map(|obj| obj.get_owned_objref())
3249 .collect();
3250
3251 trace!(
3252 num_mutable_objects = owned_objects.len(),
3253 "Checked locks and found mutable objects"
3254 );
3255
3256 owned_objects
3257 }
3258
3259 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
3260 self.objects
3261 .iter()
3262 .filter(|obj| obj.is_shared_object())
3263 .map(|obj| {
3264 obj.to_shared_input()
3265 .expect("already filtered for shared objects")
3266 })
3267 .collect()
3268 }
3269
3270 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3271 self.objects
3272 .iter()
3273 .filter_map(|obj| obj.get_previous_transaction())
3274 .collect()
3275 }
3276
3277 pub fn mutable_inputs(&self) -> BTreeMap<ObjectId, (VersionDigest, Owner)> {
3278 self.objects
3279 .iter()
3280 .filter_map(
3281 |ObjectReadResult {
3282 input_object_kind,
3283 object,
3284 }| match (input_object_kind, object) {
3285 (InputObjectKind::MovePackage(_), _) => None,
3286 (
3287 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3288 ObjectReadResultKind::Object(object),
3289 ) => {
3290 if object.is_immutable() {
3291 None
3292 } else {
3293 Some((
3294 object_ref.object_id,
3295 ((object_ref.version, object_ref.digest), object.owner),
3296 ))
3297 }
3298 }
3299 (
3300 InputObjectKind::ImmOrOwnedMoveObject(_),
3301 ObjectReadResultKind::DeletedSharedObject(_, _),
3302 ) => {
3303 unreachable!()
3304 }
3305 (
3306 InputObjectKind::SharedMoveObject { .. },
3307 ObjectReadResultKind::DeletedSharedObject(_, _),
3308 ) => None,
3309 (
3310 InputObjectKind::SharedMoveObject { mutable, .. },
3311 ObjectReadResultKind::Object(object),
3312 ) => {
3313 if *mutable {
3314 let oref = object.object_ref();
3315 Some((oref.object_id, ((oref.version, oref.digest), object.owner)))
3316 } else {
3317 None
3318 }
3319 }
3320 (
3321 InputObjectKind::ImmOrOwnedMoveObject(_),
3322 ObjectReadResultKind::CancelledTransactionObject(_),
3323 ) => {
3324 unreachable!()
3325 }
3326 (
3327 InputObjectKind::SharedMoveObject { .. },
3328 ObjectReadResultKind::CancelledTransactionObject(_),
3329 ) => None,
3330 (
3331 InputObjectKind::ImmOrOwnedMoveObject(_),
3332 ObjectReadResultKind::UnreadSharedObject,
3333 ) => {
3334 unreachable!()
3335 }
3336 (
3337 InputObjectKind::SharedMoveObject { .. },
3338 ObjectReadResultKind::UnreadSharedObject,
3339 ) => {
3340 unreachable!("an unread shared object has no version, digest or owner")
3341 }
3342 },
3343 )
3344 .collect()
3345 }
3346
3347 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectReference]) -> Version {
3351 let input_versions = self
3352 .objects
3353 .iter()
3354 .filter_map(|object| match &object.object {
3355 ObjectReadResultKind::Object(object) => {
3356 object.data.as_opt_struct().map(MoveStruct::version)
3357 }
3358 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3359 ObjectReadResultKind::CancelledTransactionObject(_) => None,
3360 ObjectReadResultKind::UnreadSharedObject => {
3361 unreachable!("an unread shared object has no version to exceed")
3362 }
3363 })
3364 .chain(
3365 receiving_objects
3366 .iter()
3367 .map(|object_ref| object_ref.version),
3368 );
3369
3370 Version::lamport_increment(input_versions).unwrap()
3371 }
3372
3373 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3374 self.objects.iter().map(
3375 |ObjectReadResult {
3376 input_object_kind, ..
3377 }| input_object_kind,
3378 )
3379 }
3380
3381 pub fn into_object_map(self) -> BTreeMap<ObjectId, Object> {
3382 self.objects
3383 .into_iter()
3384 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3385 .collect()
3386 }
3387
3388 pub fn push(&mut self, object: ObjectReadResult) {
3389 self.objects.push(object);
3390 }
3391
3392 pub fn find_object_id_mut(&mut self, object_id: ObjectId) -> Option<&mut ObjectReadResult> {
3394 self.objects.iter_mut().find(|o| o.id() == object_id)
3395 }
3396
3397 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3398 self.objects.iter()
3399 }
3400
3401 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3402 self.objects.iter().filter_map(|o| o.as_object())
3403 }
3404}
3405
3406#[derive(Clone, Debug)]
3410pub enum ReceivingObjectReadResultKind {
3411 Object(Object),
3412 PreviouslyReceivedObject,
3414}
3415
3416impl ReceivingObjectReadResultKind {
3417 pub fn as_object(&self) -> Option<&Object> {
3418 match &self {
3419 Self::Object(object) => Some(object),
3420 Self::PreviouslyReceivedObject => None,
3421 }
3422 }
3423}
3424
3425pub struct ReceivingObjectReadResult {
3426 pub object_ref: ObjectReference,
3427 pub object: ReceivingObjectReadResultKind,
3428}
3429
3430impl ReceivingObjectReadResult {
3431 pub fn new(object_ref: ObjectReference, object: ReceivingObjectReadResultKind) -> Self {
3432 Self { object_ref, object }
3433 }
3434
3435 pub fn is_previously_received(&self) -> bool {
3436 matches!(
3437 self.object,
3438 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3439 )
3440 }
3441}
3442
3443impl From<Object> for ReceivingObjectReadResultKind {
3444 fn from(object: Object) -> Self {
3445 Self::Object(object)
3446 }
3447}
3448
3449pub struct ReceivingObjects {
3450 pub objects: Vec<ReceivingObjectReadResult>,
3451}
3452
3453impl ReceivingObjects {
3454 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3455 self.objects.iter()
3456 }
3457
3458 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3459 self.objects.iter().filter_map(|o| o.object.as_object())
3460 }
3461}
3462
3463impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3464 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3465 Self { objects }
3466 }
3467}
3468
3469impl Display for CertifiedTransaction {
3470 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3471 let mut writer = String::new();
3472 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3473 writeln!(
3474 writer,
3475 "Signed Authorities Bitmap : {:?}",
3476 self.auth_sig().signers_map
3477 )?;
3478 write!(writer, "{}", self.data().transaction().kind())?;
3479 write!(f, "{writer}")
3480 }
3481}
3482
3483#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3488pub enum TransactionKey {
3489 Digest(TransactionDigest),
3490 RandomnessRound(EpochId, RandomnessRound),
3491}
3492
3493impl TransactionKey {
3494 pub fn unwrap_digest(&self) -> &TransactionDigest {
3495 match self {
3496 TransactionKey::Digest(d) => d,
3497 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3498 }
3499 }
3500
3501 pub fn as_digest(&self) -> Option<&TransactionDigest> {
3502 match self {
3503 TransactionKey::Digest(d) => Some(d),
3504 _ => None,
3505 }
3506 }
3507}