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