1use std::{
7 collections::{BTreeMap, BTreeSet, HashSet},
8 fmt::{Debug, Display, Formatter, Write},
9 hash::Hash,
10 iter,
11 iter::once,
12 sync::Arc,
13};
14
15use anyhow::bail;
16use enum_dispatch::enum_dispatch;
17use fastcrypto::{encoding::Base64, hash::HashFunction};
18use iota_protocol_config::ProtocolConfig;
19use itertools::Either;
20use move_core_types::{
21 ident_str,
22 identifier::{IdentStr, Identifier},
23 language_storage::TypeTag,
24};
25use nonempty::{NonEmpty, nonempty};
26use serde::{Deserialize, Serialize};
27use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
28use strum::IntoStaticStr;
29use tap::Pipe;
30use tracing::trace;
31
32use super::{base_types::*, error::*};
33use crate::{
34 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION,
35 IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION, IOTA_FRAMEWORK_PACKAGE_ID,
36 IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID,
37 IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
38 authenticator_state::ActiveJwk,
39 committee::{Committee, EpochId, ProtocolVersion},
40 crypto::{
41 AuthoritySignInfo, AuthoritySignInfoTrait, AuthoritySignature,
42 AuthorityStrongQuorumSignInfo, DefaultHash, Ed25519IotaSignature, EmptySignInfo,
43 IotaSignatureInner, RandomnessRound, Signature, Signer, ToFromBytes, default_hash,
44 },
45 digests::{
46 CertificateDigest, ConsensusCommitDigest, SenderSignedDataDigest, ZKLoginInputsDigest,
47 },
48 event::Event,
49 execution::SharedInput,
50 message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
51 messages_checkpoint::CheckpointTimestamp,
52 messages_consensus::{ConsensusCommitPrologueV1, ConsensusDeterminedVersionAssignments},
53 object::{MoveObject, Object, Owner},
54 programmable_transaction_builder::ProgrammableTransactionBuilder,
55 signature::{GenericSignature, VerifyParams},
56 signature_verification::{VerifiedDigestCache, verify_sender_signed_data_message_signatures},
57};
58
59pub const TEST_ONLY_GAS_UNIT_FOR_TRANSFER: u64 = 10_000;
60pub const TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS: u64 = 50_000;
61pub const TEST_ONLY_GAS_UNIT_FOR_PUBLISH: u64 = 50_000;
62pub const TEST_ONLY_GAS_UNIT_FOR_STAKING: u64 = 50_000;
63pub const TEST_ONLY_GAS_UNIT_FOR_GENERIC: u64 = 50_000;
64pub const TEST_ONLY_GAS_UNIT_FOR_SPLIT_COIN: u64 = 10_000;
65pub const TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE: u64 = 5_000_000;
70
71pub const GAS_PRICE_FOR_SYSTEM_TX: u64 = 1;
72
73pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = 1000;
74
75const BLOCKED_MOVE_FUNCTIONS: [(ObjectID, &str, &str); 0] = [];
76
77#[cfg(test)]
78#[path = "unit_tests/messages_tests.rs"]
79mod messages_tests;
80
81#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
82pub enum CallArg {
83 Pure(Vec<u8>),
85 Object(ObjectArg),
87}
88
89impl CallArg {
90 pub const IOTA_SYSTEM_MUT: Self = Self::Object(ObjectArg::IOTA_SYSTEM_MUT);
91 pub const CLOCK_IMM: Self = Self::Object(ObjectArg::SharedObject {
92 id: IOTA_CLOCK_OBJECT_ID,
93 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
94 mutable: false,
95 });
96 pub const CLOCK_MUT: Self = Self::Object(ObjectArg::SharedObject {
97 id: IOTA_CLOCK_OBJECT_ID,
98 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
99 mutable: true,
100 });
101 pub const AUTHENTICATOR_MUT: Self = Self::Object(ObjectArg::SharedObject {
102 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
103 initial_shared_version: IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION,
104 mutable: true,
105 });
106}
107
108#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
109pub enum ObjectArg {
110 ImmOrOwnedObject(ObjectRef),
112 SharedObject {
116 id: ObjectID,
117 initial_shared_version: SequenceNumber,
118 mutable: bool,
119 },
120 Receiving(ObjectRef),
122}
123
124fn type_tag_validity_check(
125 tag: &TypeTag,
126 config: &ProtocolConfig,
127 starting_count: &mut usize,
128) -> UserInputResult<()> {
129 let mut stack = vec![(tag, 1)];
130 while let Some((tag, depth)) = stack.pop() {
131 *starting_count += 1;
132 fp_ensure!(
133 *starting_count < config.max_type_arguments() as usize,
134 UserInputError::SizeLimitExceeded {
135 limit: "maximum type arguments in a call transaction".to_string(),
136 value: config.max_type_arguments().to_string()
137 }
138 );
139 fp_ensure!(
140 depth < config.max_type_argument_depth(),
141 UserInputError::SizeLimitExceeded {
142 limit: "maximum type argument depth in a call transaction".to_string(),
143 value: config.max_type_argument_depth().to_string()
144 }
145 );
146 match tag {
147 TypeTag::Bool
148 | TypeTag::U8
149 | TypeTag::U64
150 | TypeTag::U128
151 | TypeTag::Address
152 | TypeTag::Signer
153 | TypeTag::U16
154 | TypeTag::U32
155 | TypeTag::U256 => (),
156 TypeTag::Vector(t) => {
157 stack.push((t, depth + 1));
158 }
159 TypeTag::Struct(s) => {
160 let next_depth = depth + 1;
161 stack.extend(s.type_params.iter().map(|t| (t, next_depth)));
162 }
163 }
164 }
165 Ok(())
166}
167
168#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
170pub struct ChangeEpoch {
171 pub epoch: EpochId,
173 pub protocol_version: ProtocolVersion,
175 pub storage_charge: u64,
177 pub computation_charge: u64,
179 pub storage_rebate: u64,
181 pub non_refundable_storage_fee: u64,
183 pub epoch_start_timestamp_ms: u64,
185 pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
192}
193
194#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
198pub struct ChangeEpochV2 {
199 pub epoch: EpochId,
201 pub protocol_version: ProtocolVersion,
203 pub storage_charge: u64,
205 pub computation_charge: u64,
207 pub computation_charge_burned: u64,
209 pub storage_rebate: u64,
211 pub non_refundable_storage_fee: u64,
213 pub epoch_start_timestamp_ms: u64,
215 pub system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
222}
223
224#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
225pub struct GenesisTransaction {
226 pub objects: Vec<GenesisObject>,
227 pub events: Vec<Event>,
228}
229
230#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
231pub enum GenesisObject {
232 RawObject {
233 data: crate::object::Data,
234 owner: crate::object::Owner,
235 },
236}
237
238impl GenesisObject {
239 pub fn id(&self) -> ObjectID {
240 match self {
241 GenesisObject::RawObject { data, .. } => data.id(),
242 }
243 }
244}
245
246#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
247pub struct AuthenticatorStateExpire {
248 pub min_epoch: u64,
250 pub authenticator_obj_initial_shared_version: SequenceNumber,
252}
253
254impl AuthenticatorStateExpire {
255 pub fn authenticator_obj_initial_shared_version(&self) -> SequenceNumber {
256 self.authenticator_obj_initial_shared_version
257 }
258}
259
260#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
261pub struct AuthenticatorStateUpdateV1 {
262 pub epoch: u64,
264 pub round: u64,
266 pub new_active_jwks: Vec<ActiveJwk>,
268 pub authenticator_obj_initial_shared_version: SequenceNumber,
270 }
273
274impl AuthenticatorStateUpdateV1 {
275 pub fn authenticator_obj_initial_shared_version(&self) -> SequenceNumber {
276 self.authenticator_obj_initial_shared_version
277 }
278}
279
280#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)]
281pub struct RandomnessStateUpdate {
282 pub epoch: u64,
284 pub randomness_round: RandomnessRound,
286 pub random_bytes: Vec<u8>,
288 pub randomness_obj_initial_shared_version: SequenceNumber,
290 }
293
294impl RandomnessStateUpdate {
295 pub fn randomness_obj_initial_shared_version(&self) -> SequenceNumber {
296 self.randomness_obj_initial_shared_version
297 }
298}
299
300#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)]
301pub enum TransactionKind {
302 ProgrammableTransaction(ProgrammableTransaction),
305 Genesis(GenesisTransaction),
314 ConsensusCommitPrologueV1(ConsensusCommitPrologueV1),
315 AuthenticatorStateUpdateV1(AuthenticatorStateUpdateV1),
316
317 EndOfEpochTransaction(Vec<EndOfEpochTransactionKind>),
320
321 RandomnessStateUpdate(RandomnessStateUpdate),
322 }
324
325#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)]
327pub enum EndOfEpochTransactionKind {
328 ChangeEpoch(ChangeEpoch),
329 ChangeEpochV2(ChangeEpochV2),
330 AuthenticatorStateCreate,
331 AuthenticatorStateExpire(AuthenticatorStateExpire),
332}
333
334impl EndOfEpochTransactionKind {
335 pub fn new_change_epoch(
336 next_epoch: EpochId,
337 protocol_version: ProtocolVersion,
338 storage_charge: u64,
339 computation_charge: u64,
340 storage_rebate: u64,
341 non_refundable_storage_fee: u64,
342 epoch_start_timestamp_ms: u64,
343 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
344 ) -> Self {
345 Self::ChangeEpoch(ChangeEpoch {
346 epoch: next_epoch,
347 protocol_version,
348 storage_charge,
349 computation_charge,
350 storage_rebate,
351 non_refundable_storage_fee,
352 epoch_start_timestamp_ms,
353 system_packages,
354 })
355 }
356
357 pub fn new_change_epoch_v2(
358 next_epoch: EpochId,
359 protocol_version: ProtocolVersion,
360 storage_charge: u64,
361 computation_charge: u64,
362 computation_charge_burned: u64,
363 storage_rebate: u64,
364 non_refundable_storage_fee: u64,
365 epoch_start_timestamp_ms: u64,
366 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
367 ) -> Self {
368 Self::ChangeEpochV2(ChangeEpochV2 {
369 epoch: next_epoch,
370 protocol_version,
371 storage_charge,
372 computation_charge,
373 computation_charge_burned,
374 storage_rebate,
375 non_refundable_storage_fee,
376 epoch_start_timestamp_ms,
377 system_packages,
378 })
379 }
380
381 pub fn new_authenticator_state_expire(
382 min_epoch: u64,
383 authenticator_obj_initial_shared_version: SequenceNumber,
384 ) -> Self {
385 Self::AuthenticatorStateExpire(AuthenticatorStateExpire {
386 min_epoch,
387 authenticator_obj_initial_shared_version,
388 })
389 }
390
391 pub fn new_authenticator_state_create() -> Self {
392 Self::AuthenticatorStateCreate
393 }
394
395 fn input_objects(&self) -> Vec<InputObjectKind> {
396 match self {
397 Self::ChangeEpoch(_) => {
398 vec![InputObjectKind::SharedMoveObject {
399 id: IOTA_SYSTEM_STATE_OBJECT_ID,
400 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
401 mutable: true,
402 }]
403 }
404 Self::ChangeEpochV2(_) => {
405 vec![InputObjectKind::SharedMoveObject {
406 id: IOTA_SYSTEM_STATE_OBJECT_ID,
407 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
408 mutable: true,
409 }]
410 }
411 Self::AuthenticatorStateCreate => vec![],
412 Self::AuthenticatorStateExpire(expire) => {
413 vec![InputObjectKind::SharedMoveObject {
414 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
415 initial_shared_version: expire.authenticator_obj_initial_shared_version(),
416 mutable: true,
417 }]
418 }
419 }
420 }
421
422 fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
423 match self {
424 Self::ChangeEpoch(_) => {
425 Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
426 }
427 Self::ChangeEpochV2(_) => {
428 Either::Left(vec![SharedInputObject::IOTA_SYSTEM_OBJ].into_iter())
429 }
430 Self::AuthenticatorStateExpire(expire) => Either::Left(
431 vec![SharedInputObject {
432 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
433 initial_shared_version: expire.authenticator_obj_initial_shared_version(),
434 mutable: true,
435 }]
436 .into_iter(),
437 ),
438 Self::AuthenticatorStateCreate => Either::Right(iter::empty()),
439 }
440 }
441
442 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
443 match self {
444 Self::ChangeEpoch(_) => {
445 if config.protocol_defined_base_fee() {
446 return Err(UserInputError::Unsupported(
447 "protocol defined base fee not supported".to_string(),
448 ));
449 }
450 }
451 Self::ChangeEpochV2(_) => {
452 if !config.protocol_defined_base_fee() {
453 return Err(UserInputError::Unsupported(
454 "protocol defined base fee required".to_string(),
455 ));
456 }
457 }
458 Self::AuthenticatorStateCreate | Self::AuthenticatorStateExpire(_) => {
459 if !config.enable_jwk_consensus_updates() {
460 return Err(UserInputError::Unsupported(
461 "authenticator state updates not enabled".to_string(),
462 ));
463 }
464 }
465 }
466 Ok(())
467 }
468}
469
470impl CallArg {
471 fn input_objects(&self) -> Vec<InputObjectKind> {
472 match self {
473 CallArg::Pure(_) => vec![],
474 CallArg::Object(ObjectArg::ImmOrOwnedObject(object_ref)) => {
475 vec![InputObjectKind::ImmOrOwnedMoveObject(*object_ref)]
476 }
477 CallArg::Object(ObjectArg::SharedObject {
478 id,
479 initial_shared_version,
480 mutable,
481 }) => {
482 let id = *id;
483 let initial_shared_version = *initial_shared_version;
484 let mutable = *mutable;
485 vec![InputObjectKind::SharedMoveObject {
486 id,
487 initial_shared_version,
488 mutable,
489 }]
490 }
491 CallArg::Object(ObjectArg::Receiving(_)) => vec![],
493 }
494 }
495
496 fn receiving_objects(&self) -> Vec<ObjectRef> {
497 match self {
498 CallArg::Pure(_) => vec![],
499 CallArg::Object(o) => match o {
500 ObjectArg::ImmOrOwnedObject(_) => vec![],
501 ObjectArg::SharedObject { .. } => vec![],
502 ObjectArg::Receiving(obj_ref) => vec![*obj_ref],
503 },
504 }
505 }
506
507 pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
508 match self {
509 CallArg::Pure(p) => {
510 fp_ensure!(
511 p.len() < config.max_pure_argument_size() as usize,
512 UserInputError::SizeLimitExceeded {
513 limit: "maximum pure argument size".to_string(),
514 value: config.max_pure_argument_size().to_string()
515 }
516 );
517 }
518 CallArg::Object(o) => match o {
519 ObjectArg::Receiving(_)
520 | ObjectArg::ImmOrOwnedObject(_)
521 | ObjectArg::SharedObject { .. } => (),
522 },
523 }
524 Ok(())
525 }
526}
527
528impl From<bool> for CallArg {
529 fn from(b: bool) -> Self {
530 CallArg::Pure(bcs::to_bytes(&b).unwrap())
532 }
533}
534
535impl From<u8> for CallArg {
536 fn from(n: u8) -> Self {
537 CallArg::Pure(bcs::to_bytes(&n).unwrap())
539 }
540}
541
542impl From<u16> for CallArg {
543 fn from(n: u16) -> Self {
544 CallArg::Pure(bcs::to_bytes(&n).unwrap())
546 }
547}
548
549impl From<u32> for CallArg {
550 fn from(n: u32) -> Self {
551 CallArg::Pure(bcs::to_bytes(&n).unwrap())
553 }
554}
555
556impl From<u64> for CallArg {
557 fn from(n: u64) -> Self {
558 CallArg::Pure(bcs::to_bytes(&n).unwrap())
560 }
561}
562
563impl From<u128> for CallArg {
564 fn from(n: u128) -> Self {
565 CallArg::Pure(bcs::to_bytes(&n).unwrap())
567 }
568}
569
570impl From<&Vec<u8>> for CallArg {
571 fn from(v: &Vec<u8>) -> Self {
572 CallArg::Pure(bcs::to_bytes(v).unwrap())
574 }
575}
576
577impl From<ObjectRef> for CallArg {
578 fn from(obj: ObjectRef) -> Self {
579 CallArg::Object(ObjectArg::ImmOrOwnedObject(obj))
580 }
581}
582
583impl ObjectArg {
584 pub const IOTA_SYSTEM_MUT: Self = Self::SharedObject {
585 id: IOTA_SYSTEM_STATE_OBJECT_ID,
586 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
587 mutable: true,
588 };
589
590 pub fn id(&self) -> ObjectID {
591 match self {
592 ObjectArg::Receiving((id, _, _))
593 | ObjectArg::ImmOrOwnedObject((id, _, _))
594 | ObjectArg::SharedObject { id, .. } => *id,
595 }
596 }
597}
598
599fn add_type_tag_packages(packages: &mut BTreeSet<ObjectID>, type_argument: &TypeTag) {
601 let mut stack = vec![type_argument];
602 while let Some(cur) = stack.pop() {
603 match cur {
604 TypeTag::Bool
605 | TypeTag::U8
606 | TypeTag::U64
607 | TypeTag::U128
608 | TypeTag::Address
609 | TypeTag::Signer
610 | TypeTag::U16
611 | TypeTag::U32
612 | TypeTag::U256 => (),
613 TypeTag::Vector(inner) => stack.push(inner),
614 TypeTag::Struct(struct_tag) => {
615 packages.insert(struct_tag.address.into());
616 stack.extend(struct_tag.type_params.iter())
617 }
618 }
619 }
620}
621
622#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
625pub struct ProgrammableTransaction {
626 pub inputs: Vec<CallArg>,
628 pub commands: Vec<Command>,
631}
632
633#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
635pub enum Command {
636 MoveCall(Box<ProgrammableMoveCall>),
638 TransferObjects(Vec<Argument>, Argument),
643 SplitCoins(Argument, Vec<Argument>),
646 MergeCoins(Argument, Vec<Argument>),
649 Publish(Vec<Vec<u8>>, Vec<ObjectID>),
652 MakeMoveVec(Option<TypeTag>, Vec<Argument>),
656 Upgrade(Vec<Vec<u8>>, Vec<ObjectID>, ObjectID, Argument),
665}
666
667#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
669pub enum Argument {
670 GasCoin,
673 Input(u16),
676 Result(u16),
678 NestedResult(u16, u16),
682}
683
684#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
687pub struct ProgrammableMoveCall {
688 pub package: ObjectID,
690 pub module: Identifier,
692 pub function: Identifier,
694 pub type_arguments: Vec<TypeTag>,
696 pub arguments: Vec<Argument>,
698}
699
700impl ProgrammableMoveCall {
701 fn input_objects(&self) -> Vec<InputObjectKind> {
702 let ProgrammableMoveCall {
703 package,
704 type_arguments,
705 ..
706 } = self;
707 let mut packages = BTreeSet::from([*package]);
708 for type_argument in type_arguments {
709 add_type_tag_packages(&mut packages, type_argument)
710 }
711 packages
712 .into_iter()
713 .map(InputObjectKind::MovePackage)
714 .collect()
715 }
716
717 pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
718 let is_blocked = BLOCKED_MOVE_FUNCTIONS.contains(&(
719 self.package,
720 self.module.as_str(),
721 self.function.as_str(),
722 ));
723 fp_ensure!(!is_blocked, UserInputError::BlockedMoveFunction);
724 let mut type_arguments_count = 0;
725 for tag in &self.type_arguments {
726 type_tag_validity_check(tag, config, &mut type_arguments_count)?;
727 }
728 fp_ensure!(
729 self.arguments.len() < config.max_arguments() as usize,
730 UserInputError::SizeLimitExceeded {
731 limit: "maximum arguments in a move call".to_string(),
732 value: config.max_arguments().to_string()
733 }
734 );
735 Ok(())
736 }
737
738 fn is_input_arg_used(&self, arg: u16) -> bool {
739 self.arguments
740 .iter()
741 .any(|a| matches!(a, Argument::Input(inp) if *inp == arg))
742 }
743}
744
745impl Command {
746 pub fn move_call(
747 package: ObjectID,
748 module: Identifier,
749 function: Identifier,
750 type_arguments: Vec<TypeTag>,
751 arguments: Vec<Argument>,
752 ) -> Self {
753 Command::MoveCall(Box::new(ProgrammableMoveCall {
754 package,
755 module,
756 function,
757 type_arguments,
758 arguments,
759 }))
760 }
761
762 fn input_objects(&self) -> Vec<InputObjectKind> {
763 match self {
764 Command::Upgrade(_, deps, package_id, _) => deps
765 .iter()
766 .map(|id| InputObjectKind::MovePackage(*id))
767 .chain(Some(InputObjectKind::MovePackage(*package_id)))
768 .collect(),
769 Command::Publish(_, deps) => deps
770 .iter()
771 .map(|id| InputObjectKind::MovePackage(*id))
772 .collect(),
773 Command::MoveCall(c) => c.input_objects(),
774 Command::MakeMoveVec(Some(t), _) => {
775 let mut packages = BTreeSet::new();
776 add_type_tag_packages(&mut packages, t);
777 packages
778 .into_iter()
779 .map(InputObjectKind::MovePackage)
780 .collect()
781 }
782 Command::MakeMoveVec(None, _)
783 | Command::TransferObjects(_, _)
784 | Command::SplitCoins(_, _)
785 | Command::MergeCoins(_, _) => vec![],
786 }
787 }
788
789 fn non_system_packages_to_be_published(&self) -> Option<&Vec<Vec<u8>>> {
790 match self {
791 Command::Upgrade(v, _, _, _) => Some(v),
792 Command::Publish(v, _) => Some(v),
793 Command::MoveCall(_)
794 | Command::TransferObjects(_, _)
795 | Command::SplitCoins(_, _)
796 | Command::MergeCoins(_, _)
797 | Command::MakeMoveVec(_, _) => None,
798 }
799 }
800
801 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
802 match self {
803 Command::MoveCall(call) => call.validity_check(config)?,
804 Command::TransferObjects(args, _)
805 | Command::MergeCoins(_, args)
806 | Command::SplitCoins(_, args) => {
807 fp_ensure!(!args.is_empty(), UserInputError::EmptyCommandInput);
808 fp_ensure!(
809 args.len() < config.max_arguments() as usize,
810 UserInputError::SizeLimitExceeded {
811 limit: "maximum arguments in a programmable transaction command"
812 .to_string(),
813 value: config.max_arguments().to_string()
814 }
815 );
816 }
817 Command::MakeMoveVec(ty_opt, args) => {
818 fp_ensure!(
820 ty_opt.is_some() || !args.is_empty(),
821 UserInputError::EmptyCommandInput
822 );
823 if let Some(ty) = ty_opt {
824 let mut type_arguments_count = 0;
825 type_tag_validity_check(ty, config, &mut type_arguments_count)?;
826 }
827 fp_ensure!(
828 args.len() < config.max_arguments() as usize,
829 UserInputError::SizeLimitExceeded {
830 limit: "maximum arguments in a programmable transaction command"
831 .to_string(),
832 value: config.max_arguments().to_string()
833 }
834 );
835 }
836 Command::Publish(modules, deps) | Command::Upgrade(modules, deps, _, _) => {
837 fp_ensure!(!modules.is_empty(), UserInputError::EmptyCommandInput);
838 fp_ensure!(
839 modules.len() < config.max_modules_in_publish() as usize,
840 UserInputError::SizeLimitExceeded {
841 limit: "maximum modules in a programmable transaction upgrade command"
842 .to_string(),
843 value: config.max_modules_in_publish().to_string()
844 }
845 );
846 if let Some(max_package_dependencies) = config.max_package_dependencies_as_option()
847 {
848 fp_ensure!(
849 deps.len() < max_package_dependencies as usize,
850 UserInputError::SizeLimitExceeded {
851 limit: "maximum package dependencies".to_string(),
852 value: max_package_dependencies.to_string()
853 }
854 );
855 };
856 }
857 };
858 Ok(())
859 }
860
861 fn is_input_arg_used(&self, input_arg: u16) -> bool {
862 match self {
863 Command::MoveCall(c) => c.is_input_arg_used(input_arg),
864 Command::TransferObjects(args, arg)
865 | Command::MergeCoins(arg, args)
866 | Command::SplitCoins(arg, args) => args
867 .iter()
868 .chain(once(arg))
869 .any(|a| matches!(a, Argument::Input(inp) if *inp == input_arg)),
870 Command::MakeMoveVec(_, args) => args
871 .iter()
872 .any(|a| matches!(a, Argument::Input(inp) if *inp == input_arg)),
873 Command::Upgrade(_, _, _, arg) => {
874 matches!(arg, Argument::Input(inp) if *inp == input_arg)
875 }
876 Command::Publish(_, _) => false,
877 }
878 }
879}
880
881pub fn write_sep<T: Display>(
882 f: &mut Formatter<'_>,
883 items: impl IntoIterator<Item = T>,
884 sep: &str,
885) -> std::fmt::Result {
886 let mut xs = items.into_iter();
887 let Some(x) = xs.next() else {
888 return Ok(());
889 };
890 write!(f, "{x}")?;
891 for x in xs {
892 write!(f, "{sep}{x}")?;
893 }
894 Ok(())
895}
896
897impl ProgrammableTransaction {
898 pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
899 let ProgrammableTransaction { inputs, commands } = self;
900 let input_arg_objects = inputs
901 .iter()
902 .flat_map(|arg| arg.input_objects())
903 .collect::<Vec<_>>();
904 let mut used = HashSet::new();
906 if !input_arg_objects.iter().all(|o| used.insert(o.object_id())) {
907 return Err(UserInputError::DuplicateObjectRefInput);
908 }
909 let command_input_objects: BTreeSet<InputObjectKind> = commands
911 .iter()
912 .flat_map(|command| command.input_objects())
913 .collect();
914 Ok(input_arg_objects
915 .into_iter()
916 .chain(command_input_objects)
917 .collect())
918 }
919
920 fn receiving_objects(&self) -> Vec<ObjectRef> {
921 let ProgrammableTransaction { inputs, .. } = self;
922 inputs
923 .iter()
924 .flat_map(|arg| arg.receiving_objects())
925 .collect()
926 }
927
928 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
929 let ProgrammableTransaction { inputs, commands } = self;
930 fp_ensure!(
931 commands.len() < config.max_programmable_tx_commands() as usize,
932 UserInputError::SizeLimitExceeded {
933 limit: "maximum commands in a programmable transaction".to_string(),
934 value: config.max_programmable_tx_commands().to_string()
935 }
936 );
937 let total_inputs = self.input_objects()?.len() + self.receiving_objects().len();
938 fp_ensure!(
939 total_inputs <= config.max_input_objects() as usize,
940 UserInputError::SizeLimitExceeded {
941 limit: "maximum input + receiving objects in a transaction".to_string(),
942 value: config.max_input_objects().to_string()
943 }
944 );
945 for input in inputs {
946 input.validity_check(config)?
947 }
948 if let Some(max_publish_commands) = config.max_publish_or_upgrade_per_ptb_as_option() {
949 let publish_count = commands
950 .iter()
951 .filter(|c| matches!(c, Command::Publish(_, _) | Command::Upgrade(_, _, _, _)))
952 .count() as u64;
953 fp_ensure!(
954 publish_count <= max_publish_commands,
955 UserInputError::MaxPublishCountExceeded {
956 max_publish_commands,
957 publish_count,
958 }
959 );
960 }
961 for command in commands {
962 command.validity_check(config)?;
963 }
964
965 if let Some(random_index) = inputs.iter().position(|obj| {
969 matches!(obj, CallArg::Object(ObjectArg::SharedObject { id, .. }) if *id == IOTA_RANDOMNESS_STATE_OBJECT_ID)
970 }) {
971 let mut used_random_object = false;
972 let random_index = random_index.try_into().unwrap();
973 for command in commands {
974 if !used_random_object {
975 used_random_object = command.is_input_arg_used(random_index);
976 } else {
977 fp_ensure!(
978 matches!(
979 command,
980 Command::TransferObjects(_, _) | Command::MergeCoins(_, _)
981 ),
982 UserInputError::PostRandomCommandRestrictions
983 );
984 }
985 }
986 }
987
988 Ok(())
989 }
990
991 fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
992 self.inputs
993 .iter()
994 .filter_map(|arg| match arg {
995 CallArg::Pure(_)
996 | CallArg::Object(ObjectArg::Receiving(_))
997 | CallArg::Object(ObjectArg::ImmOrOwnedObject(_)) => None,
998 CallArg::Object(ObjectArg::SharedObject {
999 id,
1000 initial_shared_version,
1001 mutable,
1002 }) => Some(vec![SharedInputObject {
1003 id: *id,
1004 initial_shared_version: *initial_shared_version,
1005 mutable: *mutable,
1006 }]),
1007 })
1008 .flatten()
1009 }
1010
1011 fn move_calls(&self) -> Vec<(&ObjectID, &IdentStr, &IdentStr)> {
1012 self.commands
1013 .iter()
1014 .filter_map(|command| match command {
1015 Command::MoveCall(m) => Some((
1016 &m.package,
1017 m.module.as_ident_str(),
1018 m.function.as_ident_str(),
1019 )),
1020 _ => None,
1021 })
1022 .collect()
1023 }
1024
1025 pub fn non_system_packages_to_be_published(&self) -> impl Iterator<Item = &Vec<Vec<u8>>> + '_ {
1026 self.commands
1027 .iter()
1028 .filter_map(|q| q.non_system_packages_to_be_published())
1029 }
1030}
1031
1032impl Display for Argument {
1033 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1034 match self {
1035 Argument::GasCoin => write!(f, "GasCoin"),
1036 Argument::Input(i) => write!(f, "Input({i})"),
1037 Argument::Result(i) => write!(f, "Result({i})"),
1038 Argument::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
1039 }
1040 }
1041}
1042
1043impl Display for ProgrammableMoveCall {
1044 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1045 let ProgrammableMoveCall {
1046 package,
1047 module,
1048 function,
1049 type_arguments,
1050 arguments,
1051 } = self;
1052 write!(f, "{package}::{module}::{function}")?;
1053 if !type_arguments.is_empty() {
1054 write!(f, "<")?;
1055 write_sep(f, type_arguments, ",")?;
1056 write!(f, ">")?;
1057 }
1058 write!(f, "(")?;
1059 write_sep(f, arguments, ",")?;
1060 write!(f, ")")
1061 }
1062}
1063
1064impl Display for Command {
1065 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1066 match self {
1067 Command::MoveCall(p) => {
1068 write!(f, "MoveCall({p})")
1069 }
1070 Command::MakeMoveVec(ty_opt, elems) => {
1071 write!(f, "MakeMoveVec(")?;
1072 if let Some(ty) = ty_opt {
1073 write!(f, "Some{ty}")?;
1074 } else {
1075 write!(f, "None")?;
1076 }
1077 write!(f, ",[")?;
1078 write_sep(f, elems, ",")?;
1079 write!(f, "])")
1080 }
1081 Command::TransferObjects(objs, addr) => {
1082 write!(f, "TransferObjects([")?;
1083 write_sep(f, objs, ",")?;
1084 write!(f, "],{addr})")
1085 }
1086 Command::SplitCoins(coin, amounts) => {
1087 write!(f, "SplitCoins({coin}")?;
1088 write_sep(f, amounts, ",")?;
1089 write!(f, ")")
1090 }
1091 Command::MergeCoins(target, coins) => {
1092 write!(f, "MergeCoins({target},")?;
1093 write_sep(f, coins, ",")?;
1094 write!(f, ")")
1095 }
1096 Command::Publish(_bytes, deps) => {
1097 write!(f, "Publish(_,")?;
1098 write_sep(f, deps, ",")?;
1099 write!(f, ")")
1100 }
1101 Command::Upgrade(_bytes, deps, current_package_id, ticket) => {
1102 write!(f, "Upgrade(_,")?;
1103 write_sep(f, deps, ",")?;
1104 write!(f, ", {current_package_id}")?;
1105 write!(f, ", {ticket}")?;
1106 write!(f, ")")
1107 }
1108 }
1109 }
1110}
1111
1112impl Display for ProgrammableTransaction {
1113 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1114 let ProgrammableTransaction { inputs, commands } = self;
1115 writeln!(f, "Inputs: {inputs:?}")?;
1116 writeln!(f, "Commands: [")?;
1117 for c in commands {
1118 writeln!(f, " {c},")?;
1119 }
1120 writeln!(f, "]")
1121 }
1122}
1123
1124#[derive(Debug, PartialEq, Eq)]
1125pub struct SharedInputObject {
1126 pub id: ObjectID,
1127 pub initial_shared_version: SequenceNumber,
1128 pub mutable: bool,
1129}
1130
1131impl SharedInputObject {
1132 pub const IOTA_SYSTEM_OBJ: Self = Self {
1133 id: IOTA_SYSTEM_STATE_OBJECT_ID,
1134 initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION,
1135 mutable: true,
1136 };
1137
1138 pub fn id(&self) -> ObjectID {
1139 self.id
1140 }
1141
1142 pub fn into_id_and_version(self) -> (ObjectID, SequenceNumber) {
1143 (self.id, self.initial_shared_version)
1144 }
1145}
1146
1147impl TransactionKind {
1148 pub fn programmable(pt: ProgrammableTransaction) -> Self {
1151 TransactionKind::ProgrammableTransaction(pt)
1152 }
1153
1154 pub fn is_system_tx(&self) -> bool {
1155 match self {
1157 TransactionKind::Genesis(_)
1158 | TransactionKind::ConsensusCommitPrologueV1(_)
1159 | TransactionKind::AuthenticatorStateUpdateV1(_)
1160 | TransactionKind::RandomnessStateUpdate(_)
1161 | TransactionKind::EndOfEpochTransaction(_) => true,
1162 TransactionKind::ProgrammableTransaction(_) => false,
1163 }
1164 }
1165
1166 pub fn is_end_of_epoch_tx(&self) -> bool {
1167 matches!(self, TransactionKind::EndOfEpochTransaction(_))
1168 }
1169
1170 pub fn get_advance_epoch_tx_gas_summary(&self) -> Option<(u64, u64)> {
1174 match self {
1175 Self::EndOfEpochTransaction(txns) => {
1176 match txns.last().expect("at least one end-of-epoch txn required") {
1177 EndOfEpochTransactionKind::ChangeEpoch(e) => {
1178 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1179 }
1180 EndOfEpochTransactionKind::ChangeEpochV2(e) => {
1181 Some((e.computation_charge + e.storage_charge, e.storage_rebate))
1182 }
1183 _ => panic!("final end-of-epoch txn must be ChangeEpoch"),
1184 }
1185 }
1186 _ => None,
1187 }
1188 }
1189
1190 pub fn contains_shared_object(&self) -> bool {
1191 self.shared_input_objects().next().is_some()
1192 }
1193
1194 pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
1197 match &self {
1198 Self::ConsensusCommitPrologueV1(_) => {
1199 Either::Left(Either::Left(iter::once(SharedInputObject {
1200 id: IOTA_CLOCK_OBJECT_ID,
1201 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
1202 mutable: true,
1203 })))
1204 }
1205 Self::AuthenticatorStateUpdateV1(update) => {
1206 Either::Left(Either::Left(iter::once(SharedInputObject {
1207 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1208 initial_shared_version: update.authenticator_obj_initial_shared_version,
1209 mutable: true,
1210 })))
1211 }
1212 Self::RandomnessStateUpdate(update) => {
1213 Either::Left(Either::Left(iter::once(SharedInputObject {
1214 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1215 initial_shared_version: update.randomness_obj_initial_shared_version,
1216 mutable: true,
1217 })))
1218 }
1219 Self::EndOfEpochTransaction(txns) => Either::Left(Either::Right(
1220 txns.iter().flat_map(|txn| txn.shared_input_objects()),
1221 )),
1222 Self::ProgrammableTransaction(pt) => {
1223 Either::Right(Either::Left(pt.shared_input_objects()))
1224 }
1225 _ => Either::Right(Either::Right(iter::empty())),
1226 }
1227 }
1228
1229 fn move_calls(&self) -> Vec<(&ObjectID, &IdentStr, &IdentStr)> {
1230 match &self {
1231 Self::ProgrammableTransaction(pt) => pt.move_calls(),
1232 _ => vec![],
1233 }
1234 }
1235
1236 pub fn receiving_objects(&self) -> Vec<ObjectRef> {
1237 match &self {
1238 TransactionKind::Genesis(_)
1239 | TransactionKind::ConsensusCommitPrologueV1(_)
1240 | TransactionKind::AuthenticatorStateUpdateV1(_)
1241 | TransactionKind::RandomnessStateUpdate(_)
1242 | TransactionKind::EndOfEpochTransaction(_) => vec![],
1243 TransactionKind::ProgrammableTransaction(pt) => pt.receiving_objects(),
1244 }
1245 }
1246
1247 pub fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1253 let input_objects = match &self {
1254 Self::Genesis(_) => {
1255 vec![]
1256 }
1257 Self::ConsensusCommitPrologueV1(_) => {
1258 vec![InputObjectKind::SharedMoveObject {
1259 id: IOTA_CLOCK_OBJECT_ID,
1260 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
1261 mutable: true,
1262 }]
1263 }
1264 Self::AuthenticatorStateUpdateV1(update) => {
1265 vec![InputObjectKind::SharedMoveObject {
1266 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1267 initial_shared_version: update.authenticator_obj_initial_shared_version(),
1268 mutable: true,
1269 }]
1270 }
1271 Self::RandomnessStateUpdate(update) => {
1272 vec![InputObjectKind::SharedMoveObject {
1273 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1274 initial_shared_version: update.randomness_obj_initial_shared_version(),
1275 mutable: true,
1276 }]
1277 }
1278 Self::EndOfEpochTransaction(txns) => {
1279 let before_dedup: Vec<_> =
1282 txns.iter().flat_map(|txn| txn.input_objects()).collect();
1283 let mut has_seen = HashSet::new();
1284 let mut after_dedup = vec![];
1285 for obj in before_dedup {
1286 if has_seen.insert(obj) {
1287 after_dedup.push(obj);
1288 }
1289 }
1290 after_dedup
1291 }
1292 Self::ProgrammableTransaction(p) => return p.input_objects(),
1293 };
1294 let mut used = HashSet::new();
1302 if !input_objects.iter().all(|o| used.insert(o.object_id())) {
1303 return Err(UserInputError::DuplicateObjectRefInput);
1304 }
1305 Ok(input_objects)
1306 }
1307
1308 pub fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1309 match self {
1310 TransactionKind::ProgrammableTransaction(p) => p.validity_check(config)?,
1311 TransactionKind::Genesis(_) | TransactionKind::ConsensusCommitPrologueV1(_) => (),
1314 TransactionKind::EndOfEpochTransaction(txns) => {
1315 for tx in txns {
1316 tx.validity_check(config)?;
1317 }
1318 }
1319
1320 TransactionKind::AuthenticatorStateUpdateV1(_) => {
1321 if !config.enable_jwk_consensus_updates() {
1322 return Err(UserInputError::Unsupported(
1323 "authenticator state updates not enabled".to_string(),
1324 ));
1325 }
1326 }
1327 TransactionKind::RandomnessStateUpdate(_) => (),
1328 };
1329 Ok(())
1330 }
1331
1332 pub fn num_commands(&self) -> usize {
1334 match self {
1335 TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1336 _ => 0,
1337 }
1338 }
1339
1340 pub fn iter_commands(&self) -> impl Iterator<Item = &Command> {
1341 match self {
1342 TransactionKind::ProgrammableTransaction(pt) => pt.commands.iter(),
1343 _ => [].iter(),
1344 }
1345 }
1346
1347 pub fn tx_count(&self) -> usize {
1349 match self {
1350 TransactionKind::ProgrammableTransaction(pt) => pt.commands.len(),
1351 _ => 1,
1352 }
1353 }
1354
1355 pub fn name(&self) -> &'static str {
1356 match self {
1357 Self::Genesis(_) => "Genesis",
1358 Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
1359 Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
1360 Self::AuthenticatorStateUpdateV1(_) => "AuthenticatorStateUpdateV1",
1361 Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
1362 Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
1363 }
1364 }
1365}
1366
1367impl Display for TransactionKind {
1368 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1369 let mut writer = String::new();
1370 match &self {
1371 Self::Genesis(_) => {
1372 writeln!(writer, "Transaction Kind : Genesis")?;
1373 }
1374 Self::ConsensusCommitPrologueV1(p) => {
1375 writeln!(writer, "Transaction Kind : Consensus Commit Prologue V1")?;
1376 writeln!(writer, "Timestamp : {}", p.commit_timestamp_ms)?;
1377 writeln!(writer, "Consensus Digest: {}", p.consensus_commit_digest)?;
1378 writeln!(
1379 writer,
1380 "Consensus determined version assignment: {:?}",
1381 p.consensus_determined_version_assignments
1382 )?;
1383 }
1384 Self::ProgrammableTransaction(p) => {
1385 writeln!(writer, "Transaction Kind : Programmable")?;
1386 write!(writer, "{p}")?;
1387 }
1388 Self::AuthenticatorStateUpdateV1(_) => {
1389 writeln!(writer, "Transaction Kind : Authenticator State Update")?;
1390 }
1391 Self::RandomnessStateUpdate(_) => {
1392 writeln!(writer, "Transaction Kind : Randomness State Update")?;
1393 }
1394 Self::EndOfEpochTransaction(_) => {
1395 writeln!(writer, "Transaction Kind : End of Epoch Transaction")?;
1396 }
1397 }
1398 write!(f, "{}", writer)
1399 }
1400}
1401
1402#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1403pub struct GasData {
1404 pub payment: Vec<ObjectRef>,
1405 pub owner: IotaAddress,
1406 pub price: u64,
1407 pub budget: u64,
1408}
1409
1410#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
1411pub enum TransactionExpiration {
1412 None,
1414 Epoch(EpochId),
1417}
1418
1419#[enum_dispatch(TransactionDataAPI)]
1420#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1421pub enum TransactionData {
1422 V1(TransactionDataV1),
1423 }
1426
1427#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1428pub struct TransactionDataV1 {
1429 pub kind: TransactionKind,
1430 pub sender: IotaAddress,
1431 pub gas_data: GasData,
1432 pub expiration: TransactionExpiration,
1433}
1434
1435impl TransactionData {
1436 fn new_system_transaction(kind: TransactionKind) -> Self {
1437 assert!(kind.is_system_tx());
1439 let sender = IotaAddress::default();
1440 TransactionData::V1(TransactionDataV1 {
1441 kind,
1442 sender,
1443 gas_data: GasData {
1444 price: GAS_PRICE_FOR_SYSTEM_TX,
1445 owner: sender,
1446 payment: vec![(ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN)],
1447 budget: 0,
1448 },
1449 expiration: TransactionExpiration::None,
1450 })
1451 }
1452
1453 pub fn new(
1454 kind: TransactionKind,
1455 sender: IotaAddress,
1456 gas_payment: ObjectRef,
1457 gas_budget: u64,
1458 gas_price: u64,
1459 ) -> Self {
1460 TransactionData::V1(TransactionDataV1 {
1461 kind,
1462 sender,
1463 gas_data: GasData {
1464 price: gas_price,
1465 owner: sender,
1466 payment: vec![gas_payment],
1467 budget: gas_budget,
1468 },
1469 expiration: TransactionExpiration::None,
1470 })
1471 }
1472
1473 pub fn new_with_gas_coins(
1474 kind: TransactionKind,
1475 sender: IotaAddress,
1476 gas_payment: Vec<ObjectRef>,
1477 gas_budget: u64,
1478 gas_price: u64,
1479 ) -> Self {
1480 Self::new_with_gas_coins_allow_sponsor(
1481 kind,
1482 sender,
1483 gas_payment,
1484 gas_budget,
1485 gas_price,
1486 sender,
1487 )
1488 }
1489
1490 pub fn new_with_gas_coins_allow_sponsor(
1491 kind: TransactionKind,
1492 sender: IotaAddress,
1493 gas_payment: Vec<ObjectRef>,
1494 gas_budget: u64,
1495 gas_price: u64,
1496 gas_sponsor: IotaAddress,
1497 ) -> Self {
1498 TransactionData::V1(TransactionDataV1 {
1499 kind,
1500 sender,
1501 gas_data: GasData {
1502 price: gas_price,
1503 owner: gas_sponsor,
1504 payment: gas_payment,
1505 budget: gas_budget,
1506 },
1507 expiration: TransactionExpiration::None,
1508 })
1509 }
1510
1511 pub fn new_with_gas_data(
1512 kind: TransactionKind,
1513 sender: IotaAddress,
1514 gas_data: GasData,
1515 ) -> Self {
1516 TransactionData::V1(TransactionDataV1 {
1517 kind,
1518 sender,
1519 gas_data,
1520 expiration: TransactionExpiration::None,
1521 })
1522 }
1523
1524 pub fn new_move_call(
1525 sender: IotaAddress,
1526 package: ObjectID,
1527 module: Identifier,
1528 function: Identifier,
1529 type_arguments: Vec<TypeTag>,
1530 gas_payment: ObjectRef,
1531 arguments: Vec<CallArg>,
1532 gas_budget: u64,
1533 gas_price: u64,
1534 ) -> anyhow::Result<Self> {
1535 Self::new_move_call_with_gas_coins(
1536 sender,
1537 package,
1538 module,
1539 function,
1540 type_arguments,
1541 vec![gas_payment],
1542 arguments,
1543 gas_budget,
1544 gas_price,
1545 )
1546 }
1547
1548 pub fn new_move_call_with_gas_coins(
1549 sender: IotaAddress,
1550 package: ObjectID,
1551 module: Identifier,
1552 function: Identifier,
1553 type_arguments: Vec<TypeTag>,
1554 gas_payment: Vec<ObjectRef>,
1555 arguments: Vec<CallArg>,
1556 gas_budget: u64,
1557 gas_price: u64,
1558 ) -> anyhow::Result<Self> {
1559 let pt = {
1560 let mut builder = ProgrammableTransactionBuilder::new();
1561 builder.move_call(package, module, function, type_arguments, arguments)?;
1562 builder.finish()
1563 };
1564 Ok(Self::new_programmable(
1565 sender,
1566 gas_payment,
1567 pt,
1568 gas_budget,
1569 gas_price,
1570 ))
1571 }
1572
1573 pub fn new_transfer(
1574 recipient: IotaAddress,
1575 object_ref: ObjectRef,
1576 sender: IotaAddress,
1577 gas_payment: ObjectRef,
1578 gas_budget: u64,
1579 gas_price: u64,
1580 ) -> Self {
1581 let pt = {
1582 let mut builder = ProgrammableTransactionBuilder::new();
1583 builder.transfer_object(recipient, object_ref).unwrap();
1584 builder.finish()
1585 };
1586 Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1587 }
1588
1589 pub fn new_transfer_iota(
1590 recipient: IotaAddress,
1591 sender: IotaAddress,
1592 amount: Option<u64>,
1593 gas_payment: ObjectRef,
1594 gas_budget: u64,
1595 gas_price: u64,
1596 ) -> Self {
1597 Self::new_transfer_iota_allow_sponsor(
1598 recipient,
1599 sender,
1600 amount,
1601 gas_payment,
1602 gas_budget,
1603 gas_price,
1604 sender,
1605 )
1606 }
1607
1608 pub fn new_transfer_iota_allow_sponsor(
1609 recipient: IotaAddress,
1610 sender: IotaAddress,
1611 amount: Option<u64>,
1612 gas_payment: ObjectRef,
1613 gas_budget: u64,
1614 gas_price: u64,
1615 gas_sponsor: IotaAddress,
1616 ) -> Self {
1617 let pt = {
1618 let mut builder = ProgrammableTransactionBuilder::new();
1619 builder.transfer_iota(recipient, amount);
1620 builder.finish()
1621 };
1622 Self::new_programmable_allow_sponsor(
1623 sender,
1624 vec![gas_payment],
1625 pt,
1626 gas_budget,
1627 gas_price,
1628 gas_sponsor,
1629 )
1630 }
1631
1632 pub fn new_pay(
1633 sender: IotaAddress,
1634 coins: Vec<ObjectRef>,
1635 recipients: Vec<IotaAddress>,
1636 amounts: Vec<u64>,
1637 gas_payment: ObjectRef,
1638 gas_budget: u64,
1639 gas_price: u64,
1640 ) -> anyhow::Result<Self> {
1641 let pt = {
1642 let mut builder = ProgrammableTransactionBuilder::new();
1643 builder.pay(coins, recipients, amounts)?;
1644 builder.finish()
1645 };
1646 Ok(Self::new_programmable(
1647 sender,
1648 vec![gas_payment],
1649 pt,
1650 gas_budget,
1651 gas_price,
1652 ))
1653 }
1654
1655 pub fn new_pay_iota(
1656 sender: IotaAddress,
1657 mut coins: Vec<ObjectRef>,
1658 recipients: Vec<IotaAddress>,
1659 amounts: Vec<u64>,
1660 gas_payment: ObjectRef,
1661 gas_budget: u64,
1662 gas_price: u64,
1663 ) -> anyhow::Result<Self> {
1664 coins.insert(0, gas_payment);
1665 let pt = {
1666 let mut builder = ProgrammableTransactionBuilder::new();
1667 builder.pay_iota(recipients, amounts)?;
1668 builder.finish()
1669 };
1670 Ok(Self::new_programmable(
1671 sender, coins, pt, gas_budget, gas_price,
1672 ))
1673 }
1674
1675 pub fn new_pay_all_iota(
1676 sender: IotaAddress,
1677 mut coins: Vec<ObjectRef>,
1678 recipient: IotaAddress,
1679 gas_payment: ObjectRef,
1680 gas_budget: u64,
1681 gas_price: u64,
1682 ) -> Self {
1683 coins.insert(0, gas_payment);
1684 let pt = {
1685 let mut builder = ProgrammableTransactionBuilder::new();
1686 builder.pay_all_iota(recipient);
1687 builder.finish()
1688 };
1689 Self::new_programmable(sender, coins, pt, gas_budget, gas_price)
1690 }
1691
1692 pub fn new_module(
1693 sender: IotaAddress,
1694 gas_payment: ObjectRef,
1695 modules: Vec<Vec<u8>>,
1696 dep_ids: Vec<ObjectID>,
1697 gas_budget: u64,
1698 gas_price: u64,
1699 ) -> Self {
1700 let pt = {
1701 let mut builder = ProgrammableTransactionBuilder::new();
1702 let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
1703 builder.transfer_arg(sender, upgrade_cap);
1704 builder.finish()
1705 };
1706 Self::new_programmable(sender, vec![gas_payment], pt, gas_budget, gas_price)
1707 }
1708
1709 pub fn new_upgrade(
1710 sender: IotaAddress,
1711 gas_payment: ObjectRef,
1712 package_id: ObjectID,
1713 modules: Vec<Vec<u8>>,
1714 dep_ids: Vec<ObjectID>,
1715 (upgrade_capability, capability_owner): (ObjectRef, Owner),
1716 upgrade_policy: u8,
1717 digest: Vec<u8>,
1718 gas_budget: u64,
1719 gas_price: u64,
1720 ) -> anyhow::Result<Self> {
1721 let pt = {
1722 let mut builder = ProgrammableTransactionBuilder::new();
1723 let capability_arg = match capability_owner {
1724 Owner::AddressOwner(_) => ObjectArg::ImmOrOwnedObject(upgrade_capability),
1725 Owner::Shared {
1726 initial_shared_version,
1727 } => ObjectArg::SharedObject {
1728 id: upgrade_capability.0,
1729 initial_shared_version,
1730 mutable: true,
1731 },
1732 Owner::Immutable => {
1733 bail!("Upgrade capability is stored immutably and cannot be used for upgrades");
1734 }
1735 Owner::ObjectOwner(_) => {
1738 bail!("Upgrade capability controlled by object");
1739 }
1740 };
1741 builder.obj(capability_arg).unwrap();
1742 let upgrade_arg = builder.pure(upgrade_policy).unwrap();
1743 let digest_arg = builder.pure(digest).unwrap();
1744 let upgrade_ticket = builder.programmable_move_call(
1745 IOTA_FRAMEWORK_PACKAGE_ID,
1746 ident_str!("package").to_owned(),
1747 ident_str!("authorize_upgrade").to_owned(),
1748 vec![],
1749 vec![Argument::Input(0), upgrade_arg, digest_arg],
1750 );
1751 let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
1752
1753 builder.programmable_move_call(
1754 IOTA_FRAMEWORK_PACKAGE_ID,
1755 ident_str!("package").to_owned(),
1756 ident_str!("commit_upgrade").to_owned(),
1757 vec![],
1758 vec![Argument::Input(0), upgrade_receipt],
1759 );
1760
1761 builder.finish()
1762 };
1763 Ok(Self::new_programmable(
1764 sender,
1765 vec![gas_payment],
1766 pt,
1767 gas_budget,
1768 gas_price,
1769 ))
1770 }
1771
1772 pub fn new_programmable(
1773 sender: IotaAddress,
1774 gas_payment: Vec<ObjectRef>,
1775 pt: ProgrammableTransaction,
1776 gas_budget: u64,
1777 gas_price: u64,
1778 ) -> Self {
1779 Self::new_programmable_allow_sponsor(sender, gas_payment, pt, gas_budget, gas_price, sender)
1780 }
1781
1782 pub fn new_programmable_allow_sponsor(
1783 sender: IotaAddress,
1784 gas_payment: Vec<ObjectRef>,
1785 pt: ProgrammableTransaction,
1786 gas_budget: u64,
1787 gas_price: u64,
1788 sponsor: IotaAddress,
1789 ) -> Self {
1790 let kind = TransactionKind::ProgrammableTransaction(pt);
1791 Self::new_with_gas_coins_allow_sponsor(
1792 kind,
1793 sender,
1794 gas_payment,
1795 gas_budget,
1796 gas_price,
1797 sponsor,
1798 )
1799 }
1800
1801 pub fn message_version(&self) -> u64 {
1802 match self {
1803 TransactionData::V1(_) => 1,
1804 }
1805 }
1806
1807 pub fn execution_parts(&self) -> (TransactionKind, IotaAddress, Vec<ObjectRef>) {
1808 (
1809 self.kind().clone(),
1810 self.sender(),
1811 self.gas_data().payment.clone(),
1812 )
1813 }
1814
1815 pub fn uses_randomness(&self) -> bool {
1816 self.shared_input_objects()
1817 .iter()
1818 .any(|obj| obj.id() == IOTA_RANDOMNESS_STATE_OBJECT_ID)
1819 }
1820
1821 pub fn digest(&self) -> TransactionDigest {
1822 TransactionDigest::new(default_hash(self))
1823 }
1824}
1825
1826#[enum_dispatch]
1827pub trait TransactionDataAPI {
1828 fn sender(&self) -> IotaAddress;
1829
1830 fn kind(&self) -> &TransactionKind;
1834
1835 fn kind_mut(&mut self) -> &mut TransactionKind;
1837
1838 fn into_kind(self) -> TransactionKind;
1840
1841 fn signers(&self) -> NonEmpty<IotaAddress>;
1843
1844 fn gas_data(&self) -> &GasData;
1845
1846 fn gas_owner(&self) -> IotaAddress;
1847
1848 fn gas(&self) -> &[ObjectRef];
1849
1850 fn gas_price(&self) -> u64;
1851
1852 fn gas_budget(&self) -> u64;
1853
1854 fn expiration(&self) -> &TransactionExpiration;
1855
1856 fn contains_shared_object(&self) -> bool;
1857
1858 fn shared_input_objects(&self) -> Vec<SharedInputObject>;
1859
1860 fn move_calls(&self) -> Vec<(&ObjectID, &IdentStr, &IdentStr)>;
1861
1862 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>>;
1863
1864 fn receiving_objects(&self) -> Vec<ObjectRef>;
1865
1866 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult;
1867
1868 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult;
1869
1870 fn check_sponsorship(&self) -> UserInputResult;
1872
1873 fn is_system_tx(&self) -> bool;
1874 fn is_genesis_tx(&self) -> bool;
1875
1876 fn is_end_of_epoch_tx(&self) -> bool;
1879
1880 fn is_sponsored_tx(&self) -> bool;
1882
1883 fn sender_mut_for_testing(&mut self) -> &mut IotaAddress;
1884
1885 fn gas_data_mut(&mut self) -> &mut GasData;
1886
1887 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration;
1889}
1890
1891impl TransactionDataAPI for TransactionDataV1 {
1892 fn sender(&self) -> IotaAddress {
1893 self.sender
1894 }
1895
1896 fn kind(&self) -> &TransactionKind {
1897 &self.kind
1898 }
1899
1900 fn kind_mut(&mut self) -> &mut TransactionKind {
1901 &mut self.kind
1902 }
1903
1904 fn into_kind(self) -> TransactionKind {
1905 self.kind
1906 }
1907
1908 fn signers(&self) -> NonEmpty<IotaAddress> {
1910 let mut signers = nonempty![self.sender];
1911 if self.gas_owner() != self.sender {
1912 signers.push(self.gas_owner());
1913 }
1914 signers
1915 }
1916
1917 fn gas_data(&self) -> &GasData {
1918 &self.gas_data
1919 }
1920
1921 fn gas_owner(&self) -> IotaAddress {
1922 self.gas_data.owner
1923 }
1924
1925 fn gas(&self) -> &[ObjectRef] {
1926 &self.gas_data.payment
1927 }
1928
1929 fn gas_price(&self) -> u64 {
1930 self.gas_data.price
1931 }
1932
1933 fn gas_budget(&self) -> u64 {
1934 self.gas_data.budget
1935 }
1936
1937 fn expiration(&self) -> &TransactionExpiration {
1938 &self.expiration
1939 }
1940
1941 fn contains_shared_object(&self) -> bool {
1942 self.kind.shared_input_objects().next().is_some()
1943 }
1944
1945 fn shared_input_objects(&self) -> Vec<SharedInputObject> {
1946 self.kind.shared_input_objects().collect()
1947 }
1948
1949 fn move_calls(&self) -> Vec<(&ObjectID, &IdentStr, &IdentStr)> {
1950 self.kind.move_calls()
1951 }
1952
1953 fn input_objects(&self) -> UserInputResult<Vec<InputObjectKind>> {
1954 let mut inputs = self.kind.input_objects()?;
1955
1956 if !self.kind.is_system_tx() {
1957 inputs.extend(
1958 self.gas()
1959 .iter()
1960 .map(|obj_ref| InputObjectKind::ImmOrOwnedMoveObject(*obj_ref)),
1961 );
1962 }
1963 Ok(inputs)
1964 }
1965
1966 fn receiving_objects(&self) -> Vec<ObjectRef> {
1967 self.kind.receiving_objects()
1968 }
1969
1970 fn validity_check(&self, config: &ProtocolConfig) -> UserInputResult {
1971 fp_ensure!(!self.gas().is_empty(), UserInputError::MissingGasPayment);
1972 fp_ensure!(
1973 self.gas().len() < config.max_gas_payment_objects() as usize,
1974 UserInputError::SizeLimitExceeded {
1975 limit: "maximum number of gas payment objects".to_string(),
1976 value: config.max_gas_payment_objects().to_string()
1977 }
1978 );
1979 self.validity_check_no_gas_check(config)
1980 }
1981
1982 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
1985 self.kind().validity_check(config)?;
1986 self.check_sponsorship()
1987 }
1988
1989 fn is_sponsored_tx(&self) -> bool {
1991 self.gas_owner() != self.sender
1992 }
1993
1994 fn check_sponsorship(&self) -> UserInputResult {
1996 if self.gas_owner() == self.sender() {
1998 return Ok(());
1999 }
2000 if matches!(&self.kind, TransactionKind::ProgrammableTransaction(_)) {
2001 return Ok(());
2002 }
2003 Err(UserInputError::UnsupportedSponsoredTransactionKind)
2004 }
2005
2006 fn is_end_of_epoch_tx(&self) -> bool {
2007 matches!(self.kind, TransactionKind::EndOfEpochTransaction(_))
2008 }
2009
2010 fn is_system_tx(&self) -> bool {
2011 self.kind.is_system_tx()
2012 }
2013
2014 fn is_genesis_tx(&self) -> bool {
2015 matches!(self.kind, TransactionKind::Genesis(_))
2016 }
2017
2018 fn sender_mut_for_testing(&mut self) -> &mut IotaAddress {
2019 &mut self.sender
2020 }
2021
2022 fn gas_data_mut(&mut self) -> &mut GasData {
2023 &mut self.gas_data
2024 }
2025
2026 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
2027 &mut self.expiration
2028 }
2029}
2030
2031impl TransactionDataV1 {}
2032
2033#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
2034pub struct SenderSignedData(SizeOneVec<SenderSignedTransaction>);
2035
2036#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2037pub struct SenderSignedTransaction {
2038 pub intent_message: IntentMessage<TransactionData>,
2039 pub tx_signatures: Vec<GenericSignature>,
2043}
2044
2045impl Serialize for SenderSignedTransaction {
2046 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2047 where
2048 S: serde::Serializer,
2049 {
2050 #[derive(Serialize)]
2051 #[serde(rename = "SenderSignedTransaction")]
2052 struct SignedTxn<'a> {
2053 intent_message: &'a IntentMessage<TransactionData>,
2054 tx_signatures: &'a Vec<GenericSignature>,
2055 }
2056
2057 if self.intent_message().intent != Intent::iota_transaction() {
2058 return Err(serde::ser::Error::custom("invalid Intent for Transaction"));
2059 }
2060
2061 let txn = SignedTxn {
2062 intent_message: self.intent_message(),
2063 tx_signatures: &self.tx_signatures,
2064 };
2065 txn.serialize(serializer)
2066 }
2067}
2068
2069impl<'de> Deserialize<'de> for SenderSignedTransaction {
2070 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2071 where
2072 D: serde::Deserializer<'de>,
2073 {
2074 #[derive(Deserialize)]
2075 #[serde(rename = "SenderSignedTransaction")]
2076 struct SignedTxn {
2077 intent_message: IntentMessage<TransactionData>,
2078 tx_signatures: Vec<GenericSignature>,
2079 }
2080
2081 let SignedTxn {
2082 intent_message,
2083 tx_signatures,
2084 } = Deserialize::deserialize(deserializer)?;
2085
2086 if intent_message.intent != Intent::iota_transaction() {
2087 return Err(serde::de::Error::custom("invalid Intent for Transaction"));
2088 }
2089
2090 Ok(Self {
2091 intent_message,
2092 tx_signatures,
2093 })
2094 }
2095}
2096
2097impl SenderSignedTransaction {
2098 pub(crate) fn get_signer_sig_mapping(
2099 &self,
2100 ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2101 let mut mapping = BTreeMap::new();
2102 for sig in &self.tx_signatures {
2103 let address = sig.try_into()?;
2104 mapping.insert(address, sig);
2105 }
2106 Ok(mapping)
2107 }
2108
2109 pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2110 &self.intent_message
2111 }
2112}
2113
2114impl SenderSignedData {
2115 pub fn new(tx_data: TransactionData, tx_signatures: Vec<GenericSignature>) -> Self {
2116 Self(SizeOneVec::new(SenderSignedTransaction {
2117 intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2118 tx_signatures,
2119 }))
2120 }
2121
2122 pub fn new_from_sender_signature(tx_data: TransactionData, tx_signature: Signature) -> Self {
2123 Self(SizeOneVec::new(SenderSignedTransaction {
2124 intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2125 tx_signatures: vec![tx_signature.into()],
2126 }))
2127 }
2128
2129 pub fn inner(&self) -> &SenderSignedTransaction {
2130 self.0.element()
2131 }
2132
2133 pub fn into_inner(self) -> SenderSignedTransaction {
2134 self.0.into_inner()
2135 }
2136
2137 pub fn inner_mut(&mut self) -> &mut SenderSignedTransaction {
2138 self.0.element_mut()
2139 }
2140
2141 pub fn add_signature(&mut self, new_signature: Signature) {
2144 self.inner_mut().tx_signatures.push(new_signature.into());
2145 }
2146
2147 pub(crate) fn get_signer_sig_mapping(
2148 &self,
2149 ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2150 self.inner().get_signer_sig_mapping()
2151 }
2152
2153 pub fn transaction_data(&self) -> &TransactionData {
2154 &self.intent_message().value
2155 }
2156
2157 pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2158 self.inner().intent_message()
2159 }
2160
2161 pub fn tx_signatures(&self) -> &[GenericSignature] {
2162 &self.inner().tx_signatures
2163 }
2164
2165 pub fn has_zklogin_sig(&self) -> bool {
2166 self.tx_signatures().iter().any(|sig| sig.is_zklogin())
2167 }
2168
2169 pub fn has_upgraded_multisig(&self) -> bool {
2170 self.tx_signatures()
2171 .iter()
2172 .any(|sig| sig.is_upgraded_multisig())
2173 }
2174
2175 #[cfg(test)]
2176 pub fn intent_message_mut_for_testing(&mut self) -> &mut IntentMessage<TransactionData> {
2177 &mut self.inner_mut().intent_message
2178 }
2179
2180 pub fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<GenericSignature> {
2182 &mut self.inner_mut().tx_signatures
2183 }
2184
2185 pub fn full_message_digest(&self) -> SenderSignedDataDigest {
2186 let mut digest = DefaultHash::default();
2187 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2188 let hash = digest.finalize();
2189 SenderSignedDataDigest::new(hash.into())
2190 }
2191
2192 pub fn serialized_size(&self) -> IotaResult<usize> {
2193 bcs::serialized_size(self).map_err(|e| IotaError::TransactionSerialization {
2194 error: e.to_string(),
2195 })
2196 }
2197
2198 fn check_user_signature_protocol_compatibility(&self, config: &ProtocolConfig) -> IotaResult {
2199 for sig in &self.inner().tx_signatures {
2200 match sig {
2201 GenericSignature::ZkLoginAuthenticator(_) => {
2202 if !config.zklogin_auth() {
2203 return Err(IotaError::UserInput {
2204 error: UserInputError::Unsupported(
2205 "zklogin is not enabled on this network".to_string(),
2206 ),
2207 });
2208 }
2209 }
2210 GenericSignature::PasskeyAuthenticator(_) => {
2211 if !config.passkey_auth() {
2212 return Err(IotaError::UserInput {
2213 error: UserInputError::Unsupported(
2214 "passkey is not enabled on this network".to_string(),
2215 ),
2216 });
2217 }
2218 }
2219 GenericSignature::Signature(_) | GenericSignature::MultiSig(_) => (),
2220 }
2221 }
2222
2223 Ok(())
2224 }
2225
2226 pub fn validity_check(
2230 &self,
2231 config: &ProtocolConfig,
2232 epoch: EpochId,
2233 ) -> Result<usize, IotaError> {
2234 self.check_user_signature_protocol_compatibility(config)?;
2237
2238 let tx_data = &self.transaction_data();
2241 fp_ensure!(
2242 !tx_data.is_system_tx(),
2243 IotaError::UserInput {
2244 error: UserInputError::Unsupported(
2245 "SenderSignedData must not contain system transaction".to_string()
2246 )
2247 }
2248 );
2249
2250 if match &tx_data.expiration() {
2252 TransactionExpiration::None => false,
2253 TransactionExpiration::Epoch(exp_poch) => *exp_poch < epoch,
2254 } {
2255 return Err(IotaError::TransactionExpired);
2256 }
2257
2258 let tx_size = self.serialized_size()?;
2260 let max_tx_size_bytes = config.max_tx_size_bytes();
2261 fp_ensure!(
2262 tx_size as u64 <= max_tx_size_bytes,
2263 IotaError::UserInput {
2264 error: UserInputError::SizeLimitExceeded {
2265 limit: format!(
2266 "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
2267 ),
2268 value: tx_size.to_string(),
2269 }
2270 }
2271 );
2272
2273 tx_data
2274 .validity_check(config)
2275 .map_err(Into::<IotaError>::into)?;
2276
2277 Ok(tx_size)
2278 }
2279}
2280
2281impl Message for SenderSignedData {
2282 type DigestType = TransactionDigest;
2283 const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2284
2285 fn digest(&self) -> Self::DigestType {
2288 self.intent_message().value.digest()
2289 }
2290}
2291
2292impl<S> Envelope<SenderSignedData, S> {
2293 pub fn sender_address(&self) -> IotaAddress {
2294 self.data().intent_message().value.sender()
2295 }
2296
2297 pub fn gas(&self) -> &[ObjectRef] {
2298 self.data().intent_message().value.gas()
2299 }
2300
2301 pub fn contains_shared_object(&self) -> bool {
2302 self.shared_input_objects().next().is_some()
2303 }
2304
2305 pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
2306 self.data()
2307 .inner()
2308 .intent_message
2309 .value
2310 .shared_input_objects()
2311 .into_iter()
2312 }
2313
2314 pub fn key(&self) -> TransactionKey {
2316 match &self.data().intent_message().value.kind() {
2317 TransactionKind::RandomnessStateUpdate(rsu) => {
2318 TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2319 }
2320 _ => TransactionKey::Digest(*self.digest()),
2321 }
2322 }
2323
2324 pub fn non_digest_key(&self) -> Option<TransactionKey> {
2329 match &self.data().intent_message().value.kind() {
2330 TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2331 rsu.epoch,
2332 rsu.randomness_round,
2333 )),
2334 _ => None,
2335 }
2336 }
2337
2338 pub fn is_system_tx(&self) -> bool {
2339 self.data().intent_message().value.is_system_tx()
2340 }
2341
2342 pub fn is_sponsored_tx(&self) -> bool {
2343 self.data().intent_message().value.is_sponsored_tx()
2344 }
2345}
2346
2347impl Transaction {
2348 pub fn from_data_and_signer(
2349 data: TransactionData,
2350 signers: Vec<&dyn Signer<Signature>>,
2351 ) -> Self {
2352 let signatures = {
2353 let intent_msg = IntentMessage::new(Intent::iota_transaction(), &data);
2354 signers
2355 .into_iter()
2356 .map(|s| Signature::new_secure(&intent_msg, s))
2357 .collect()
2358 };
2359 Self::from_data(data, signatures)
2360 }
2361
2362 pub fn from_data(data: TransactionData, signatures: Vec<Signature>) -> Self {
2364 Self::from_generic_sig_data(data, signatures.into_iter().map(|s| s.into()).collect())
2365 }
2366
2367 pub fn signature_from_signer(
2368 data: TransactionData,
2369 intent: Intent,
2370 signer: &dyn Signer<Signature>,
2371 ) -> Signature {
2372 let intent_msg = IntentMessage::new(intent, data);
2373 Signature::new_secure(&intent_msg, signer)
2374 }
2375
2376 pub fn from_generic_sig_data(data: TransactionData, signatures: Vec<GenericSignature>) -> Self {
2377 Self::new(SenderSignedData::new(data, signatures))
2378 }
2379
2380 pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2383 (
2384 Base64::from_bytes(&bcs::to_bytes(&self.data().intent_message().value).unwrap()),
2385 self.data()
2386 .inner()
2387 .tx_signatures
2388 .iter()
2389 .map(|s| Base64::from_bytes(s.as_ref()))
2390 .collect(),
2391 )
2392 }
2393}
2394
2395impl VerifiedTransaction {
2396 pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2397 GenesisTransaction { objects, events }
2398 .pipe(TransactionKind::Genesis)
2399 .pipe(Self::new_system_transaction)
2400 }
2401
2402 pub fn new_consensus_commit_prologue_v1(
2403 epoch: u64,
2404 round: u64,
2405 commit_timestamp_ms: CheckpointTimestamp,
2406 consensus_commit_digest: ConsensusCommitDigest,
2407 cancelled_txn_version_assignment: Vec<(TransactionDigest, Vec<(ObjectID, SequenceNumber)>)>,
2408 ) -> Self {
2409 ConsensusCommitPrologueV1 {
2410 epoch,
2411 round,
2412 sub_dag_index: None,
2414 commit_timestamp_ms,
2415 consensus_commit_digest,
2416 consensus_determined_version_assignments:
2417 ConsensusDeterminedVersionAssignments::CancelledTransactions(
2418 cancelled_txn_version_assignment,
2419 ),
2420 }
2421 .pipe(TransactionKind::ConsensusCommitPrologueV1)
2422 .pipe(Self::new_system_transaction)
2423 }
2424
2425 pub fn new_authenticator_state_update(
2426 epoch: u64,
2427 round: u64,
2428 new_active_jwks: Vec<ActiveJwk>,
2429 authenticator_obj_initial_shared_version: SequenceNumber,
2430 ) -> Self {
2431 AuthenticatorStateUpdateV1 {
2432 epoch,
2433 round,
2434 new_active_jwks,
2435 authenticator_obj_initial_shared_version,
2436 }
2437 .pipe(TransactionKind::AuthenticatorStateUpdateV1)
2438 .pipe(Self::new_system_transaction)
2439 }
2440
2441 pub fn new_randomness_state_update(
2442 epoch: u64,
2443 randomness_round: RandomnessRound,
2444 random_bytes: Vec<u8>,
2445 randomness_obj_initial_shared_version: SequenceNumber,
2446 ) -> Self {
2447 RandomnessStateUpdate {
2448 epoch,
2449 randomness_round,
2450 random_bytes,
2451 randomness_obj_initial_shared_version,
2452 }
2453 .pipe(TransactionKind::RandomnessStateUpdate)
2454 .pipe(Self::new_system_transaction)
2455 }
2456
2457 pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
2458 TransactionKind::EndOfEpochTransaction(txns).pipe(Self::new_system_transaction)
2459 }
2460
2461 fn new_system_transaction(system_transaction: TransactionKind) -> Self {
2462 system_transaction
2463 .pipe(TransactionData::new_system_transaction)
2464 .pipe(|data| {
2465 SenderSignedData::new_from_sender_signature(
2466 data,
2467 Ed25519IotaSignature::from_bytes(&[0; Ed25519IotaSignature::LENGTH])
2468 .unwrap()
2469 .into(),
2470 )
2471 })
2472 .pipe(Transaction::new)
2473 .pipe(Self::new_from_verified)
2474 }
2475}
2476
2477impl VerifiedSignedTransaction {
2478 pub fn new(
2480 epoch: EpochId,
2481 transaction: VerifiedTransaction,
2482 authority: AuthorityName,
2483 secret: &dyn Signer<AuthoritySignature>,
2484 ) -> Self {
2485 Self::new_from_verified(SignedTransaction::new(
2486 epoch,
2487 transaction.into_inner().into_data(),
2488 secret,
2489 authority,
2490 ))
2491 }
2492}
2493
2494pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
2496pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
2497pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
2498
2499pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
2501pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
2502
2503impl Transaction {
2504 pub fn verify_signature_for_testing(
2505 &self,
2506 current_epoch: EpochId,
2507 verify_params: &VerifyParams,
2508 ) -> IotaResult {
2509 verify_sender_signed_data_message_signatures(
2510 self.data(),
2511 current_epoch,
2512 verify_params,
2513 Arc::new(VerifiedDigestCache::new_empty()),
2514 )
2515 }
2516
2517 pub fn try_into_verified_for_testing(
2518 self,
2519 current_epoch: EpochId,
2520 verify_params: &VerifyParams,
2521 ) -> IotaResult<VerifiedTransaction> {
2522 self.verify_signature_for_testing(current_epoch, verify_params)?;
2523 Ok(VerifiedTransaction::new_from_verified(self))
2524 }
2525}
2526
2527impl SignedTransaction {
2528 pub fn verify_signatures_authenticated_for_testing(
2529 &self,
2530 committee: &Committee,
2531 verify_params: &VerifyParams,
2532 ) -> IotaResult {
2533 verify_sender_signed_data_message_signatures(
2534 self.data(),
2535 committee.epoch(),
2536 verify_params,
2537 Arc::new(VerifiedDigestCache::new_empty()),
2538 )?;
2539
2540 self.auth_sig().verify_secure(
2541 self.data(),
2542 Intent::iota_app(IntentScope::SenderSignedTransaction),
2543 committee,
2544 )
2545 }
2546
2547 pub fn try_into_verified_for_testing(
2548 self,
2549 committee: &Committee,
2550 verify_params: &VerifyParams,
2551 ) -> IotaResult<VerifiedSignedTransaction> {
2552 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
2553 Ok(VerifiedSignedTransaction::new_from_verified(self))
2554 }
2555}
2556
2557pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2558
2559impl CertifiedTransaction {
2560 pub fn certificate_digest(&self) -> CertificateDigest {
2561 let mut digest = DefaultHash::default();
2562 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2563 let hash = digest.finalize();
2564 CertificateDigest::new(hash.into())
2565 }
2566
2567 pub fn gas_price(&self) -> u64 {
2568 self.data().transaction_data().gas_price()
2569 }
2570
2571 pub fn verify_signatures_authenticated(
2574 &self,
2575 committee: &Committee,
2576 verify_params: &VerifyParams,
2577 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
2578 ) -> IotaResult {
2579 verify_sender_signed_data_message_signatures(
2580 self.data(),
2581 committee.epoch(),
2582 verify_params,
2583 zklogin_inputs_cache,
2584 )?;
2585 self.auth_sig().verify_secure(
2586 self.data(),
2587 Intent::iota_app(IntentScope::SenderSignedTransaction),
2588 committee,
2589 )
2590 }
2591
2592 pub fn try_into_verified_for_testing(
2593 self,
2594 committee: &Committee,
2595 verify_params: &VerifyParams,
2596 ) -> IotaResult<VerifiedCertificate> {
2597 self.verify_signatures_authenticated(
2598 committee,
2599 verify_params,
2600 Arc::new(VerifiedDigestCache::new_empty()),
2601 )?;
2602 Ok(VerifiedCertificate::new_from_verified(self))
2603 }
2604
2605 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
2606 self.auth_sig().verify_secure(
2607 self.data(),
2608 Intent::iota_app(IntentScope::SenderSignedTransaction),
2609 committee,
2610 )
2611 }
2612}
2613
2614pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2615pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2616
2617#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
2618pub enum InputObjectKind {
2619 MovePackage(ObjectID),
2621 ImmOrOwnedMoveObject(ObjectRef),
2623 SharedMoveObject {
2625 id: ObjectID,
2626 initial_shared_version: SequenceNumber,
2627 mutable: bool,
2628 },
2629}
2630
2631impl InputObjectKind {
2632 pub fn object_id(&self) -> ObjectID {
2633 match self {
2634 Self::MovePackage(id) => *id,
2635 Self::ImmOrOwnedMoveObject((id, _, _)) => *id,
2636 Self::SharedMoveObject { id, .. } => *id,
2637 }
2638 }
2639
2640 pub fn version(&self) -> Option<SequenceNumber> {
2641 match self {
2642 Self::MovePackage(..) => None,
2643 Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version),
2644 Self::SharedMoveObject { .. } => None,
2645 }
2646 }
2647
2648 pub fn object_not_found_error(&self) -> UserInputError {
2649 match *self {
2650 Self::MovePackage(package_id) => {
2651 UserInputError::DependentPackageNotFound { package_id }
2652 }
2653 Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound {
2654 object_id,
2655 version: Some(version),
2656 },
2657 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
2658 object_id: id,
2659 version: None,
2660 },
2661 }
2662 }
2663
2664 pub fn is_shared_object(&self) -> bool {
2665 matches!(self, Self::SharedMoveObject { .. })
2666 }
2667
2668 pub fn is_mutable(&self) -> bool {
2669 match self {
2670 Self::MovePackage(..) => false,
2671 Self::ImmOrOwnedMoveObject((_, _, _)) => true,
2672 Self::SharedMoveObject { mutable, .. } => *mutable,
2673 }
2674 }
2675}
2676
2677#[derive(Clone, Debug)]
2681pub struct ObjectReadResult {
2682 pub input_object_kind: InputObjectKind,
2683 pub object: ObjectReadResultKind,
2684}
2685
2686#[derive(Clone)]
2687pub enum ObjectReadResultKind {
2688 Object(Object),
2689 DeletedSharedObject(SequenceNumber, TransactionDigest),
2692 CancelledTransactionSharedObject(SequenceNumber),
2694}
2695
2696impl std::fmt::Debug for ObjectReadResultKind {
2697 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2698 match self {
2699 ObjectReadResultKind::Object(obj) => {
2700 write!(f, "Object({:?})", obj.compute_object_reference())
2701 }
2702 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2703 write!(f, "DeletedSharedObject({}, {:?})", seq, digest)
2704 }
2705 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2706 write!(f, "CancelledTransactionSharedObject({})", seq)
2707 }
2708 }
2709 }
2710}
2711
2712impl From<Object> for ObjectReadResultKind {
2713 fn from(object: Object) -> Self {
2714 Self::Object(object)
2715 }
2716}
2717
2718impl ObjectReadResult {
2719 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
2720 if let (
2721 InputObjectKind::ImmOrOwnedMoveObject(_),
2722 ObjectReadResultKind::DeletedSharedObject(_, _),
2723 ) = (&input_object_kind, &object)
2724 {
2725 panic!("only shared objects can be DeletedSharedObject");
2726 }
2727
2728 if let (
2729 InputObjectKind::ImmOrOwnedMoveObject(_),
2730 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2731 ) = (&input_object_kind, &object)
2732 {
2733 panic!("only shared objects can be CancelledTransactionSharedObject");
2734 }
2735
2736 Self {
2737 input_object_kind,
2738 object,
2739 }
2740 }
2741
2742 pub fn id(&self) -> ObjectID {
2743 self.input_object_kind.object_id()
2744 }
2745
2746 pub fn as_object(&self) -> Option<&Object> {
2747 match &self.object {
2748 ObjectReadResultKind::Object(object) => Some(object),
2749 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
2750 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2751 }
2752 }
2753
2754 pub fn new_from_gas_object(gas: &Object) -> Self {
2755 let objref = gas.compute_object_reference();
2756 Self {
2757 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
2758 object: ObjectReadResultKind::Object(gas.clone()),
2759 }
2760 }
2761
2762 pub fn is_mutable(&self) -> bool {
2763 match (&self.input_object_kind, &self.object) {
2764 (InputObjectKind::MovePackage(_), _) => false,
2765 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
2766 !object.is_immutable()
2767 }
2768 (
2769 InputObjectKind::ImmOrOwnedMoveObject(_),
2770 ObjectReadResultKind::DeletedSharedObject(_, _),
2771 ) => unreachable!(),
2772 (
2773 InputObjectKind::ImmOrOwnedMoveObject(_),
2774 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2775 ) => unreachable!(),
2776 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
2777 }
2778 }
2779
2780 pub fn is_shared_object(&self) -> bool {
2781 self.input_object_kind.is_shared_object()
2782 }
2783
2784 pub fn is_deleted_shared_object(&self) -> bool {
2785 self.deletion_info().is_some()
2786 }
2787
2788 pub fn deletion_info(&self) -> Option<(SequenceNumber, TransactionDigest)> {
2789 match &self.object {
2790 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
2791 _ => None,
2792 }
2793 }
2794
2795 pub fn get_owned_objref(&self) -> Option<ObjectRef> {
2798 match (&self.input_object_kind, &self.object) {
2799 (InputObjectKind::MovePackage(_), _) => None,
2800 (
2801 InputObjectKind::ImmOrOwnedMoveObject(objref),
2802 ObjectReadResultKind::Object(object),
2803 ) => {
2804 if object.is_immutable() {
2805 None
2806 } else {
2807 Some(*objref)
2808 }
2809 }
2810 (
2811 InputObjectKind::ImmOrOwnedMoveObject(_),
2812 ObjectReadResultKind::DeletedSharedObject(_, _),
2813 ) => unreachable!(),
2814 (
2815 InputObjectKind::ImmOrOwnedMoveObject(_),
2816 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2817 ) => unreachable!(),
2818 (InputObjectKind::SharedMoveObject { .. }, _) => None,
2819 }
2820 }
2821
2822 pub fn is_owned(&self) -> bool {
2823 self.get_owned_objref().is_some()
2824 }
2825
2826 pub fn to_shared_input(&self) -> Option<SharedInput> {
2827 match self.input_object_kind {
2828 InputObjectKind::MovePackage(_) => None,
2829 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
2830 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
2831 ObjectReadResultKind::Object(obj) => {
2832 SharedInput::Existing(obj.compute_object_reference())
2833 }
2834 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2835 SharedInput::Deleted((id, *seq, mutable, *digest))
2836 }
2837 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2838 SharedInput::Cancelled((id, *seq))
2839 }
2840 }),
2841 }
2842 }
2843
2844 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
2845 match &self.object {
2846 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
2847 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
2848 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2849 }
2850 }
2851}
2852
2853#[derive(Clone)]
2854pub struct InputObjects {
2855 objects: Vec<ObjectReadResult>,
2856}
2857
2858impl std::fmt::Debug for InputObjects {
2859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2860 f.debug_list().entries(self.objects.iter()).finish()
2861 }
2862}
2863
2864pub struct CheckedInputObjects(InputObjects);
2867
2868impl CheckedInputObjects {
2874 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
2876 Self(inputs)
2877 }
2878
2879 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
2881 Self(InputObjects::new(input_objects))
2882 }
2883
2884 pub fn new_for_replay(input_objects: InputObjects) -> Self {
2886 Self(input_objects)
2887 }
2888
2889 pub fn inner(&self) -> &InputObjects {
2890 &self.0
2891 }
2892
2893 pub fn into_inner(self) -> InputObjects {
2894 self.0
2895 }
2896}
2897
2898impl From<Vec<ObjectReadResult>> for InputObjects {
2899 fn from(objects: Vec<ObjectReadResult>) -> Self {
2900 Self::new(objects)
2901 }
2902}
2903
2904impl InputObjects {
2905 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
2906 Self { objects }
2907 }
2908
2909 pub fn len(&self) -> usize {
2910 self.objects.len()
2911 }
2912
2913 pub fn is_empty(&self) -> bool {
2914 self.objects.is_empty()
2915 }
2916
2917 pub fn contains_deleted_objects(&self) -> bool {
2918 self.objects
2919 .iter()
2920 .any(|obj| obj.is_deleted_shared_object())
2921 }
2922
2923 pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectID>, SequenceNumber)> {
2926 let mut contains_cancelled = false;
2927 let mut cancel_reason = None;
2928 let mut cancelled_objects = Vec::new();
2929 for obj in &self.objects {
2930 if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
2931 contains_cancelled = true;
2932 if version == SequenceNumber::CONGESTED
2933 || version == SequenceNumber::RANDOMNESS_UNAVAILABLE
2934 {
2935 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
2937 cancel_reason = Some(version);
2938 cancelled_objects.push(obj.id());
2939 }
2940 }
2941 }
2942
2943 if !cancelled_objects.is_empty() {
2944 Some((
2945 cancelled_objects,
2946 cancel_reason
2947 .expect("there should be a cancel reason if there are cancelled objects"),
2948 ))
2949 } else {
2950 assert!(!contains_cancelled);
2951 None
2952 }
2953 }
2954
2955 pub fn filter_owned_objects(&self) -> Vec<ObjectRef> {
2956 let owned_objects: Vec<_> = self
2957 .objects
2958 .iter()
2959 .filter_map(|obj| obj.get_owned_objref())
2960 .collect();
2961
2962 trace!(
2963 num_mutable_objects = owned_objects.len(),
2964 "Checked locks and found mutable objects"
2965 );
2966
2967 owned_objects
2968 }
2969
2970 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
2971 self.objects
2972 .iter()
2973 .filter(|obj| obj.is_shared_object())
2974 .map(|obj| {
2975 obj.to_shared_input()
2976 .expect("already filtered for shared objects")
2977 })
2978 .collect()
2979 }
2980
2981 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
2982 self.objects
2983 .iter()
2984 .filter_map(|obj| obj.get_previous_transaction())
2985 .collect()
2986 }
2987
2988 pub fn mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
2989 self.objects
2990 .iter()
2991 .filter_map(
2992 |ObjectReadResult {
2993 input_object_kind,
2994 object,
2995 }| match (input_object_kind, object) {
2996 (InputObjectKind::MovePackage(_), _) => None,
2997 (
2998 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
2999 ObjectReadResultKind::Object(object),
3000 ) => {
3001 if object.is_immutable() {
3002 None
3003 } else {
3004 Some((object_ref.0, ((object_ref.1, object_ref.2), object.owner)))
3005 }
3006 }
3007 (
3008 InputObjectKind::ImmOrOwnedMoveObject(_),
3009 ObjectReadResultKind::DeletedSharedObject(_, _),
3010 ) => {
3011 unreachable!()
3012 }
3013 (
3014 InputObjectKind::SharedMoveObject { .. },
3015 ObjectReadResultKind::DeletedSharedObject(_, _),
3016 ) => None,
3017 (
3018 InputObjectKind::SharedMoveObject { mutable, .. },
3019 ObjectReadResultKind::Object(object),
3020 ) => {
3021 if *mutable {
3022 let oref = object.compute_object_reference();
3023 Some((oref.0, ((oref.1, oref.2), object.owner)))
3024 } else {
3025 None
3026 }
3027 }
3028 (
3029 InputObjectKind::ImmOrOwnedMoveObject(_),
3030 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3031 ) => {
3032 unreachable!()
3033 }
3034 (
3035 InputObjectKind::SharedMoveObject { .. },
3036 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3037 ) => None,
3038 },
3039 )
3040 .collect()
3041 }
3042
3043 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectRef]) -> SequenceNumber {
3047 let input_versions = self
3048 .objects
3049 .iter()
3050 .filter_map(|object| match &object.object {
3051 ObjectReadResultKind::Object(object) => {
3052 object.data.try_as_move().map(MoveObject::version)
3053 }
3054 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3055 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3056 })
3057 .chain(receiving_objects.iter().map(|object_ref| object_ref.1));
3058
3059 SequenceNumber::lamport_increment(input_versions)
3060 }
3061
3062 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3063 self.objects.iter().map(
3064 |ObjectReadResult {
3065 input_object_kind, ..
3066 }| input_object_kind,
3067 )
3068 }
3069
3070 pub fn into_object_map(self) -> BTreeMap<ObjectID, Object> {
3071 self.objects
3072 .into_iter()
3073 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3074 .collect()
3075 }
3076
3077 pub fn push(&mut self, object: ObjectReadResult) {
3078 self.objects.push(object);
3079 }
3080
3081 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3082 self.objects.iter()
3083 }
3084
3085 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3086 self.objects.iter().filter_map(|o| o.as_object())
3087 }
3088}
3089
3090#[derive(Clone, Debug)]
3094pub enum ReceivingObjectReadResultKind {
3095 Object(Object),
3096 PreviouslyReceivedObject,
3098}
3099
3100impl ReceivingObjectReadResultKind {
3101 pub fn as_object(&self) -> Option<&Object> {
3102 match &self {
3103 Self::Object(object) => Some(object),
3104 Self::PreviouslyReceivedObject => None,
3105 }
3106 }
3107}
3108
3109pub struct ReceivingObjectReadResult {
3110 pub object_ref: ObjectRef,
3111 pub object: ReceivingObjectReadResultKind,
3112}
3113
3114impl ReceivingObjectReadResult {
3115 pub fn new(object_ref: ObjectRef, object: ReceivingObjectReadResultKind) -> Self {
3116 Self { object_ref, object }
3117 }
3118
3119 pub fn is_previously_received(&self) -> bool {
3120 matches!(
3121 self.object,
3122 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3123 )
3124 }
3125}
3126
3127impl From<Object> for ReceivingObjectReadResultKind {
3128 fn from(object: Object) -> Self {
3129 Self::Object(object)
3130 }
3131}
3132
3133pub struct ReceivingObjects {
3134 pub objects: Vec<ReceivingObjectReadResult>,
3135}
3136
3137impl ReceivingObjects {
3138 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3139 self.objects.iter()
3140 }
3141
3142 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3143 self.objects.iter().filter_map(|o| o.object.as_object())
3144 }
3145}
3146
3147impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3148 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3149 Self { objects }
3150 }
3151}
3152
3153impl Display for CertifiedTransaction {
3154 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3155 let mut writer = String::new();
3156 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3157 writeln!(
3158 writer,
3159 "Signed Authorities Bitmap : {:?}",
3160 self.auth_sig().signers_map
3161 )?;
3162 write!(writer, "{}", &self.data().intent_message().value.kind())?;
3163 write!(f, "{}", writer)
3164 }
3165}
3166
3167#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3172pub enum TransactionKey {
3173 Digest(TransactionDigest),
3174 RandomnessRound(EpochId, RandomnessRound),
3175}
3176
3177impl TransactionKey {
3178 pub fn unwrap_digest(&self) -> &TransactionDigest {
3179 match self {
3180 TransactionKey::Digest(d) => d,
3181 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3182 }
3183 }
3184}