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