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