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::{instrument, 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 #[instrument(level = "trace", skip_all)]
2133 fn validity_check_no_gas_check(&self, config: &ProtocolConfig) -> UserInputResult {
2134 self.kind().validity_check(config)?;
2135 self.check_sponsorship()
2136 }
2137
2138 fn is_sponsored_tx(&self) -> bool {
2140 self.gas_owner() != self.sender
2141 }
2142
2143 fn check_sponsorship(&self) -> UserInputResult {
2145 if self.gas_owner() == self.sender() {
2147 return Ok(());
2148 }
2149 if matches!(&self.kind, TransactionKind::ProgrammableTransaction(_)) {
2150 return Ok(());
2151 }
2152 Err(UserInputError::UnsupportedSponsoredTransactionKind)
2153 }
2154
2155 fn is_end_of_epoch_tx(&self) -> bool {
2156 matches!(self.kind, TransactionKind::EndOfEpochTransaction(_))
2157 }
2158
2159 fn is_system_tx(&self) -> bool {
2160 self.kind.is_system_tx()
2161 }
2162
2163 fn is_genesis_tx(&self) -> bool {
2164 matches!(self.kind, TransactionKind::Genesis(_))
2165 }
2166
2167 fn sender_mut_for_testing(&mut self) -> &mut IotaAddress {
2168 &mut self.sender
2169 }
2170
2171 fn gas_data_mut(&mut self) -> &mut GasData {
2172 &mut self.gas_data
2173 }
2174
2175 fn expiration_mut_for_testing(&mut self) -> &mut TransactionExpiration {
2176 &mut self.expiration
2177 }
2178}
2179
2180impl TransactionDataV1 {}
2181
2182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
2183pub struct SenderSignedData(SizeOneVec<SenderSignedTransaction>);
2184
2185#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2186pub struct SenderSignedTransaction {
2187 pub intent_message: IntentMessage<TransactionData>,
2188 pub tx_signatures: Vec<GenericSignature>,
2192}
2193
2194impl Serialize for SenderSignedTransaction {
2195 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2196 where
2197 S: serde::Serializer,
2198 {
2199 #[derive(Serialize)]
2200 #[serde(rename = "SenderSignedTransaction")]
2201 struct SignedTxn<'a> {
2202 intent_message: &'a IntentMessage<TransactionData>,
2203 tx_signatures: &'a Vec<GenericSignature>,
2204 }
2205
2206 if self.intent_message().intent != Intent::iota_transaction() {
2207 return Err(serde::ser::Error::custom("invalid Intent for Transaction"));
2208 }
2209
2210 let txn = SignedTxn {
2211 intent_message: self.intent_message(),
2212 tx_signatures: &self.tx_signatures,
2213 };
2214 txn.serialize(serializer)
2215 }
2216}
2217
2218impl<'de> Deserialize<'de> for SenderSignedTransaction {
2219 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2220 where
2221 D: serde::Deserializer<'de>,
2222 {
2223 #[derive(Deserialize)]
2224 #[serde(rename = "SenderSignedTransaction")]
2225 struct SignedTxn {
2226 intent_message: IntentMessage<TransactionData>,
2227 tx_signatures: Vec<GenericSignature>,
2228 }
2229
2230 let SignedTxn {
2231 intent_message,
2232 tx_signatures,
2233 } = Deserialize::deserialize(deserializer)?;
2234
2235 if intent_message.intent != Intent::iota_transaction() {
2236 return Err(serde::de::Error::custom("invalid Intent for Transaction"));
2237 }
2238
2239 Ok(Self {
2240 intent_message,
2241 tx_signatures,
2242 })
2243 }
2244}
2245
2246impl SenderSignedTransaction {
2247 pub(crate) fn get_signer_sig_mapping(
2248 &self,
2249 ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2250 let mut mapping = BTreeMap::new();
2251 for sig in &self.tx_signatures {
2252 let address = sig.try_into()?;
2253 mapping.insert(address, sig);
2254 }
2255 Ok(mapping)
2256 }
2257
2258 pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2259 &self.intent_message
2260 }
2261}
2262
2263impl SenderSignedData {
2264 pub fn new(tx_data: TransactionData, tx_signatures: Vec<GenericSignature>) -> Self {
2265 Self(SizeOneVec::new(SenderSignedTransaction {
2266 intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2267 tx_signatures,
2268 }))
2269 }
2270
2271 pub fn new_from_sender_signature(tx_data: TransactionData, tx_signature: Signature) -> Self {
2272 Self(SizeOneVec::new(SenderSignedTransaction {
2273 intent_message: IntentMessage::new(Intent::iota_transaction(), tx_data),
2274 tx_signatures: vec![tx_signature.into()],
2275 }))
2276 }
2277
2278 pub fn inner(&self) -> &SenderSignedTransaction {
2279 self.0.element()
2280 }
2281
2282 pub fn into_inner(self) -> SenderSignedTransaction {
2283 self.0.into_inner()
2284 }
2285
2286 pub fn inner_mut(&mut self) -> &mut SenderSignedTransaction {
2287 self.0.element_mut()
2288 }
2289
2290 pub fn add_signature(&mut self, new_signature: Signature) {
2293 self.inner_mut().tx_signatures.push(new_signature.into());
2294 }
2295
2296 pub(crate) fn get_signer_sig_mapping(
2297 &self,
2298 ) -> IotaResult<BTreeMap<IotaAddress, &GenericSignature>> {
2299 self.inner().get_signer_sig_mapping()
2300 }
2301
2302 pub fn transaction_data(&self) -> &TransactionData {
2303 &self.intent_message().value
2304 }
2305
2306 pub fn intent_message(&self) -> &IntentMessage<TransactionData> {
2307 self.inner().intent_message()
2308 }
2309
2310 pub fn tx_signatures(&self) -> &[GenericSignature] {
2311 &self.inner().tx_signatures
2312 }
2313
2314 pub fn has_zklogin_sig(&self) -> bool {
2315 self.tx_signatures().iter().any(|sig| sig.is_zklogin())
2316 }
2317
2318 pub fn has_upgraded_multisig(&self) -> bool {
2319 self.tx_signatures()
2320 .iter()
2321 .any(|sig| sig.is_upgraded_multisig())
2322 }
2323
2324 #[cfg(test)]
2325 pub fn intent_message_mut_for_testing(&mut self) -> &mut IntentMessage<TransactionData> {
2326 &mut self.inner_mut().intent_message
2327 }
2328
2329 pub fn tx_signatures_mut_for_testing(&mut self) -> &mut Vec<GenericSignature> {
2331 &mut self.inner_mut().tx_signatures
2332 }
2333
2334 pub fn full_message_digest(&self) -> SenderSignedDataDigest {
2335 let mut digest = DefaultHash::default();
2336 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2337 let hash = digest.finalize();
2338 SenderSignedDataDigest::new(hash.into())
2339 }
2340
2341 pub fn serialized_size(&self) -> IotaResult<usize> {
2342 bcs::serialized_size(self).map_err(|e| IotaError::TransactionSerialization {
2343 error: e.to_string(),
2344 })
2345 }
2346
2347 fn check_user_signature_protocol_compatibility(&self, config: &ProtocolConfig) -> IotaResult {
2348 for sig in &self.inner().tx_signatures {
2349 match sig {
2350 GenericSignature::ZkLoginAuthenticator(_) => {
2351 if !config.zklogin_auth() {
2352 return Err(IotaError::UserInput {
2353 error: UserInputError::Unsupported(
2354 "zklogin is not enabled on this network".to_string(),
2355 ),
2356 });
2357 }
2358 }
2359 GenericSignature::PasskeyAuthenticator(_) => {
2360 if !config.passkey_auth() {
2361 return Err(IotaError::UserInput {
2362 error: UserInputError::Unsupported(
2363 "passkey is not enabled on this network".to_string(),
2364 ),
2365 });
2366 }
2367 }
2368 GenericSignature::Signature(_) | GenericSignature::MultiSig(_) => (),
2369 }
2370 }
2371
2372 Ok(())
2373 }
2374
2375 pub fn validity_check(
2379 &self,
2380 config: &ProtocolConfig,
2381 epoch: EpochId,
2382 ) -> Result<usize, IotaError> {
2383 self.check_user_signature_protocol_compatibility(config)?;
2386
2387 let tx_data = &self.transaction_data();
2390 fp_ensure!(
2391 !tx_data.is_system_tx(),
2392 IotaError::UserInput {
2393 error: UserInputError::Unsupported(
2394 "SenderSignedData must not contain system transaction".to_string()
2395 )
2396 }
2397 );
2398
2399 if match &tx_data.expiration() {
2401 TransactionExpiration::None => false,
2402 TransactionExpiration::Epoch(exp_poch) => *exp_poch < epoch,
2403 } {
2404 return Err(IotaError::TransactionExpired);
2405 }
2406
2407 let tx_size = self.serialized_size()?;
2409 let max_tx_size_bytes = config.max_tx_size_bytes();
2410 fp_ensure!(
2411 tx_size as u64 <= max_tx_size_bytes,
2412 IotaError::UserInput {
2413 error: UserInputError::SizeLimitExceeded {
2414 limit: format!(
2415 "serialized transaction size exceeded maximum of {max_tx_size_bytes}"
2416 ),
2417 value: tx_size.to_string(),
2418 }
2419 }
2420 );
2421
2422 tx_data
2423 .validity_check(config)
2424 .map_err(Into::<IotaError>::into)?;
2425
2426 Ok(tx_size)
2427 }
2428}
2429
2430impl Message for SenderSignedData {
2431 type DigestType = TransactionDigest;
2432 const SCOPE: IntentScope = IntentScope::SenderSignedTransaction;
2433
2434 fn digest(&self) -> Self::DigestType {
2437 self.intent_message().value.digest()
2438 }
2439}
2440
2441impl<S> Envelope<SenderSignedData, S> {
2442 pub fn sender_address(&self) -> IotaAddress {
2443 self.data().intent_message().value.sender()
2444 }
2445
2446 pub fn gas(&self) -> &[ObjectRef] {
2447 self.data().intent_message().value.gas()
2448 }
2449
2450 pub fn contains_shared_object(&self) -> bool {
2451 self.shared_input_objects().next().is_some()
2452 }
2453
2454 pub fn shared_input_objects(&self) -> impl Iterator<Item = SharedInputObject> + '_ {
2455 self.data()
2456 .inner()
2457 .intent_message
2458 .value
2459 .shared_input_objects()
2460 .into_iter()
2461 }
2462
2463 pub fn key(&self) -> TransactionKey {
2465 match &self.data().intent_message().value.kind() {
2466 TransactionKind::RandomnessStateUpdate(rsu) => {
2467 TransactionKey::RandomnessRound(rsu.epoch, rsu.randomness_round)
2468 }
2469 _ => TransactionKey::Digest(*self.digest()),
2470 }
2471 }
2472
2473 pub fn non_digest_key(&self) -> Option<TransactionKey> {
2478 match &self.data().intent_message().value.kind() {
2479 TransactionKind::RandomnessStateUpdate(rsu) => Some(TransactionKey::RandomnessRound(
2480 rsu.epoch,
2481 rsu.randomness_round,
2482 )),
2483 _ => None,
2484 }
2485 }
2486
2487 pub fn is_system_tx(&self) -> bool {
2488 self.data().intent_message().value.is_system_tx()
2489 }
2490
2491 pub fn is_sponsored_tx(&self) -> bool {
2492 self.data().intent_message().value.is_sponsored_tx()
2493 }
2494}
2495
2496impl Transaction {
2497 pub fn from_data_and_signer(
2498 data: TransactionData,
2499 signers: Vec<&dyn Signer<Signature>>,
2500 ) -> Self {
2501 let signatures = {
2502 let intent_msg = IntentMessage::new(Intent::iota_transaction(), &data);
2503 signers
2504 .into_iter()
2505 .map(|s| Signature::new_secure(&intent_msg, s))
2506 .collect()
2507 };
2508 Self::from_data(data, signatures)
2509 }
2510
2511 pub fn from_data(data: TransactionData, signatures: Vec<Signature>) -> Self {
2513 Self::from_generic_sig_data(data, signatures.into_iter().map(|s| s.into()).collect())
2514 }
2515
2516 pub fn signature_from_signer(
2517 data: TransactionData,
2518 intent: Intent,
2519 signer: &dyn Signer<Signature>,
2520 ) -> Signature {
2521 let intent_msg = IntentMessage::new(intent, data);
2522 Signature::new_secure(&intent_msg, signer)
2523 }
2524
2525 pub fn from_generic_sig_data(data: TransactionData, signatures: Vec<GenericSignature>) -> Self {
2526 Self::new(SenderSignedData::new(data, signatures))
2527 }
2528
2529 pub fn to_tx_bytes_and_signatures(&self) -> (Base64, Vec<Base64>) {
2532 (
2533 Base64::from_bytes(&bcs::to_bytes(&self.data().intent_message().value).unwrap()),
2534 self.data()
2535 .inner()
2536 .tx_signatures
2537 .iter()
2538 .map(|s| Base64::from_bytes(s.as_ref()))
2539 .collect(),
2540 )
2541 }
2542}
2543
2544impl VerifiedTransaction {
2545 pub fn new_genesis_transaction(objects: Vec<GenesisObject>, events: Vec<Event>) -> Self {
2546 GenesisTransaction { objects, events }
2547 .pipe(TransactionKind::Genesis)
2548 .pipe(Self::new_system_transaction)
2549 }
2550
2551 pub fn new_consensus_commit_prologue_v1(
2552 epoch: u64,
2553 round: u64,
2554 commit_timestamp_ms: CheckpointTimestamp,
2555 consensus_commit_digest: ConsensusCommitDigest,
2556 cancelled_txn_version_assignment: Vec<(TransactionDigest, Vec<(ObjectID, SequenceNumber)>)>,
2557 ) -> Self {
2558 ConsensusCommitPrologueV1 {
2559 epoch,
2560 round,
2561 sub_dag_index: None,
2563 commit_timestamp_ms,
2564 consensus_commit_digest,
2565 consensus_determined_version_assignments:
2566 ConsensusDeterminedVersionAssignments::CancelledTransactions(
2567 cancelled_txn_version_assignment,
2568 ),
2569 }
2570 .pipe(TransactionKind::ConsensusCommitPrologueV1)
2571 .pipe(Self::new_system_transaction)
2572 }
2573
2574 pub fn new_authenticator_state_update(
2575 epoch: u64,
2576 round: u64,
2577 new_active_jwks: Vec<ActiveJwk>,
2578 authenticator_obj_initial_shared_version: SequenceNumber,
2579 ) -> Self {
2580 AuthenticatorStateUpdateV1 {
2581 epoch,
2582 round,
2583 new_active_jwks,
2584 authenticator_obj_initial_shared_version,
2585 }
2586 .pipe(TransactionKind::AuthenticatorStateUpdateV1)
2587 .pipe(Self::new_system_transaction)
2588 }
2589
2590 pub fn new_randomness_state_update(
2591 epoch: u64,
2592 randomness_round: RandomnessRound,
2593 random_bytes: Vec<u8>,
2594 randomness_obj_initial_shared_version: SequenceNumber,
2595 ) -> Self {
2596 RandomnessStateUpdate {
2597 epoch,
2598 randomness_round,
2599 random_bytes,
2600 randomness_obj_initial_shared_version,
2601 }
2602 .pipe(TransactionKind::RandomnessStateUpdate)
2603 .pipe(Self::new_system_transaction)
2604 }
2605
2606 pub fn new_end_of_epoch_transaction(txns: Vec<EndOfEpochTransactionKind>) -> Self {
2607 TransactionKind::EndOfEpochTransaction(txns).pipe(Self::new_system_transaction)
2608 }
2609
2610 fn new_system_transaction(system_transaction: TransactionKind) -> Self {
2611 system_transaction
2612 .pipe(TransactionData::new_system_transaction)
2613 .pipe(|data| {
2614 SenderSignedData::new_from_sender_signature(
2615 data,
2616 Ed25519IotaSignature::from_bytes(&[0; Ed25519IotaSignature::LENGTH])
2617 .unwrap()
2618 .into(),
2619 )
2620 })
2621 .pipe(Transaction::new)
2622 .pipe(Self::new_from_verified)
2623 }
2624}
2625
2626impl VerifiedSignedTransaction {
2627 pub fn new(
2629 epoch: EpochId,
2630 transaction: VerifiedTransaction,
2631 authority: AuthorityName,
2632 secret: &dyn Signer<AuthoritySignature>,
2633 ) -> Self {
2634 Self::new_from_verified(SignedTransaction::new(
2635 epoch,
2636 transaction.into_inner().into_data(),
2637 secret,
2638 authority,
2639 ))
2640 }
2641}
2642
2643pub type Transaction = Envelope<SenderSignedData, EmptySignInfo>;
2645pub type VerifiedTransaction = VerifiedEnvelope<SenderSignedData, EmptySignInfo>;
2646pub type TrustedTransaction = TrustedEnvelope<SenderSignedData, EmptySignInfo>;
2647
2648pub type SignedTransaction = Envelope<SenderSignedData, AuthoritySignInfo>;
2650pub type VerifiedSignedTransaction = VerifiedEnvelope<SenderSignedData, AuthoritySignInfo>;
2651
2652impl Transaction {
2653 pub fn verify_signature_for_testing(
2654 &self,
2655 current_epoch: EpochId,
2656 verify_params: &VerifyParams,
2657 ) -> IotaResult {
2658 verify_sender_signed_data_message_signatures(
2659 self.data(),
2660 current_epoch,
2661 verify_params,
2662 Arc::new(VerifiedDigestCache::new_empty()),
2663 )
2664 }
2665
2666 pub fn try_into_verified_for_testing(
2667 self,
2668 current_epoch: EpochId,
2669 verify_params: &VerifyParams,
2670 ) -> IotaResult<VerifiedTransaction> {
2671 self.verify_signature_for_testing(current_epoch, verify_params)?;
2672 Ok(VerifiedTransaction::new_from_verified(self))
2673 }
2674}
2675
2676impl SignedTransaction {
2677 pub fn verify_signatures_authenticated_for_testing(
2678 &self,
2679 committee: &Committee,
2680 verify_params: &VerifyParams,
2681 ) -> IotaResult {
2682 verify_sender_signed_data_message_signatures(
2683 self.data(),
2684 committee.epoch(),
2685 verify_params,
2686 Arc::new(VerifiedDigestCache::new_empty()),
2687 )?;
2688
2689 self.auth_sig().verify_secure(
2690 self.data(),
2691 Intent::iota_app(IntentScope::SenderSignedTransaction),
2692 committee,
2693 )
2694 }
2695
2696 pub fn try_into_verified_for_testing(
2697 self,
2698 committee: &Committee,
2699 verify_params: &VerifyParams,
2700 ) -> IotaResult<VerifiedSignedTransaction> {
2701 self.verify_signatures_authenticated_for_testing(committee, verify_params)?;
2702 Ok(VerifiedSignedTransaction::new_from_verified(self))
2703 }
2704}
2705
2706pub type CertifiedTransaction = Envelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2707
2708impl CertifiedTransaction {
2709 pub fn certificate_digest(&self) -> CertificateDigest {
2710 let mut digest = DefaultHash::default();
2711 bcs::serialize_into(&mut digest, self).expect("serialization should not fail");
2712 let hash = digest.finalize();
2713 CertificateDigest::new(hash.into())
2714 }
2715
2716 pub fn gas_price(&self) -> u64 {
2717 self.data().transaction_data().gas_price()
2718 }
2719
2720 pub fn verify_signatures_authenticated(
2723 &self,
2724 committee: &Committee,
2725 verify_params: &VerifyParams,
2726 zklogin_inputs_cache: Arc<VerifiedDigestCache<ZKLoginInputsDigest>>,
2727 ) -> IotaResult {
2728 verify_sender_signed_data_message_signatures(
2729 self.data(),
2730 committee.epoch(),
2731 verify_params,
2732 zklogin_inputs_cache,
2733 )?;
2734 self.auth_sig().verify_secure(
2735 self.data(),
2736 Intent::iota_app(IntentScope::SenderSignedTransaction),
2737 committee,
2738 )
2739 }
2740
2741 pub fn try_into_verified_for_testing(
2742 self,
2743 committee: &Committee,
2744 verify_params: &VerifyParams,
2745 ) -> IotaResult<VerifiedCertificate> {
2746 self.verify_signatures_authenticated(
2747 committee,
2748 verify_params,
2749 Arc::new(VerifiedDigestCache::new_empty()),
2750 )?;
2751 Ok(VerifiedCertificate::new_from_verified(self))
2752 }
2753
2754 pub fn verify_committee_sigs_only(&self, committee: &Committee) -> IotaResult {
2755 self.auth_sig().verify_secure(
2756 self.data(),
2757 Intent::iota_app(IntentScope::SenderSignedTransaction),
2758 committee,
2759 )
2760 }
2761}
2762
2763pub type VerifiedCertificate = VerifiedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2764pub type TrustedCertificate = TrustedEnvelope<SenderSignedData, AuthorityStrongQuorumSignInfo>;
2765
2766#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
2767pub enum InputObjectKind {
2768 MovePackage(ObjectID),
2770 ImmOrOwnedMoveObject(ObjectRef),
2772 SharedMoveObject {
2774 id: ObjectID,
2775 initial_shared_version: SequenceNumber,
2776 mutable: bool,
2777 },
2778}
2779
2780impl InputObjectKind {
2781 pub fn object_id(&self) -> ObjectID {
2782 match self {
2783 Self::MovePackage(id) => *id,
2784 Self::ImmOrOwnedMoveObject((id, _, _)) => *id,
2785 Self::SharedMoveObject { id, .. } => *id,
2786 }
2787 }
2788
2789 pub fn version(&self) -> Option<SequenceNumber> {
2790 match self {
2791 Self::MovePackage(..) => None,
2792 Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version),
2793 Self::SharedMoveObject { .. } => None,
2794 }
2795 }
2796
2797 pub fn object_not_found_error(&self) -> UserInputError {
2798 match *self {
2799 Self::MovePackage(package_id) => {
2800 UserInputError::DependentPackageNotFound { package_id }
2801 }
2802 Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound {
2803 object_id,
2804 version: Some(version),
2805 },
2806 Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound {
2807 object_id: id,
2808 version: None,
2809 },
2810 }
2811 }
2812
2813 pub fn is_shared_object(&self) -> bool {
2814 matches!(self, Self::SharedMoveObject { .. })
2815 }
2816
2817 pub fn is_mutable(&self) -> bool {
2818 match self {
2819 Self::MovePackage(..) => false,
2820 Self::ImmOrOwnedMoveObject((_, _, _)) => true,
2821 Self::SharedMoveObject { mutable, .. } => *mutable,
2822 }
2823 }
2824}
2825
2826#[derive(Clone, Debug)]
2830pub struct ObjectReadResult {
2831 pub input_object_kind: InputObjectKind,
2832 pub object: ObjectReadResultKind,
2833}
2834
2835#[derive(Clone)]
2836pub enum ObjectReadResultKind {
2837 Object(Object),
2838 DeletedSharedObject(SequenceNumber, TransactionDigest),
2841 CancelledTransactionSharedObject(SequenceNumber),
2843}
2844
2845impl std::fmt::Debug for ObjectReadResultKind {
2846 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2847 match self {
2848 ObjectReadResultKind::Object(obj) => {
2849 write!(f, "Object({:?})", obj.compute_object_reference())
2850 }
2851 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2852 write!(f, "DeletedSharedObject({seq}, {digest:?})")
2853 }
2854 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2855 write!(f, "CancelledTransactionSharedObject({seq})")
2856 }
2857 }
2858 }
2859}
2860
2861impl From<Object> for ObjectReadResultKind {
2862 fn from(object: Object) -> Self {
2863 Self::Object(object)
2864 }
2865}
2866
2867impl ObjectReadResult {
2868 pub fn new(input_object_kind: InputObjectKind, object: ObjectReadResultKind) -> Self {
2869 if let (
2870 InputObjectKind::ImmOrOwnedMoveObject(_),
2871 ObjectReadResultKind::DeletedSharedObject(_, _),
2872 ) = (&input_object_kind, &object)
2873 {
2874 panic!("only shared objects can be DeletedSharedObject");
2875 }
2876
2877 if let (
2878 InputObjectKind::ImmOrOwnedMoveObject(_),
2879 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2880 ) = (&input_object_kind, &object)
2881 {
2882 panic!("only shared objects can be CancelledTransactionSharedObject");
2883 }
2884
2885 Self {
2886 input_object_kind,
2887 object,
2888 }
2889 }
2890
2891 pub fn id(&self) -> ObjectID {
2892 self.input_object_kind.object_id()
2893 }
2894
2895 pub fn as_object(&self) -> Option<&Object> {
2896 match &self.object {
2897 ObjectReadResultKind::Object(object) => Some(object),
2898 ObjectReadResultKind::DeletedSharedObject(_, _) => None,
2899 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2900 }
2901 }
2902
2903 pub fn new_from_gas_object(gas: &Object) -> Self {
2904 let objref = gas.compute_object_reference();
2905 Self {
2906 input_object_kind: InputObjectKind::ImmOrOwnedMoveObject(objref),
2907 object: ObjectReadResultKind::Object(gas.clone()),
2908 }
2909 }
2910
2911 pub fn is_mutable(&self) -> bool {
2912 match (&self.input_object_kind, &self.object) {
2913 (InputObjectKind::MovePackage(_), _) => false,
2914 (InputObjectKind::ImmOrOwnedMoveObject(_), ObjectReadResultKind::Object(object)) => {
2915 !object.is_immutable()
2916 }
2917 (
2918 InputObjectKind::ImmOrOwnedMoveObject(_),
2919 ObjectReadResultKind::DeletedSharedObject(_, _),
2920 ) => unreachable!(),
2921 (
2922 InputObjectKind::ImmOrOwnedMoveObject(_),
2923 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2924 ) => unreachable!(),
2925 (InputObjectKind::SharedMoveObject { mutable, .. }, _) => *mutable,
2926 }
2927 }
2928
2929 pub fn is_shared_object(&self) -> bool {
2930 self.input_object_kind.is_shared_object()
2931 }
2932
2933 pub fn is_deleted_shared_object(&self) -> bool {
2934 self.deletion_info().is_some()
2935 }
2936
2937 pub fn deletion_info(&self) -> Option<(SequenceNumber, TransactionDigest)> {
2938 match &self.object {
2939 ObjectReadResultKind::DeletedSharedObject(v, tx) => Some((*v, *tx)),
2940 _ => None,
2941 }
2942 }
2943
2944 pub fn get_owned_objref(&self) -> Option<ObjectRef> {
2947 match (&self.input_object_kind, &self.object) {
2948 (InputObjectKind::MovePackage(_), _) => None,
2949 (
2950 InputObjectKind::ImmOrOwnedMoveObject(objref),
2951 ObjectReadResultKind::Object(object),
2952 ) => {
2953 if object.is_immutable() {
2954 None
2955 } else {
2956 Some(*objref)
2957 }
2958 }
2959 (
2960 InputObjectKind::ImmOrOwnedMoveObject(_),
2961 ObjectReadResultKind::DeletedSharedObject(_, _),
2962 ) => unreachable!(),
2963 (
2964 InputObjectKind::ImmOrOwnedMoveObject(_),
2965 ObjectReadResultKind::CancelledTransactionSharedObject(_),
2966 ) => unreachable!(),
2967 (InputObjectKind::SharedMoveObject { .. }, _) => None,
2968 }
2969 }
2970
2971 pub fn is_owned(&self) -> bool {
2972 self.get_owned_objref().is_some()
2973 }
2974
2975 pub fn to_shared_input(&self) -> Option<SharedInput> {
2976 match self.input_object_kind {
2977 InputObjectKind::MovePackage(_) => None,
2978 InputObjectKind::ImmOrOwnedMoveObject(_) => None,
2979 InputObjectKind::SharedMoveObject { id, mutable, .. } => Some(match &self.object {
2980 ObjectReadResultKind::Object(obj) => {
2981 SharedInput::Existing(obj.compute_object_reference())
2982 }
2983 ObjectReadResultKind::DeletedSharedObject(seq, digest) => {
2984 SharedInput::Deleted((id, *seq, mutable, *digest))
2985 }
2986 ObjectReadResultKind::CancelledTransactionSharedObject(seq) => {
2987 SharedInput::Cancelled((id, *seq))
2988 }
2989 }),
2990 }
2991 }
2992
2993 pub fn get_previous_transaction(&self) -> Option<TransactionDigest> {
2994 match &self.object {
2995 ObjectReadResultKind::Object(obj) => Some(obj.previous_transaction),
2996 ObjectReadResultKind::DeletedSharedObject(_, digest) => Some(*digest),
2997 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
2998 }
2999 }
3000}
3001
3002#[derive(Clone)]
3003pub struct InputObjects {
3004 objects: Vec<ObjectReadResult>,
3005}
3006
3007impl std::fmt::Debug for InputObjects {
3008 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3009 f.debug_list().entries(self.objects.iter()).finish()
3010 }
3011}
3012
3013pub struct CheckedInputObjects(InputObjects);
3016
3017impl CheckedInputObjects {
3023 pub fn new_with_checked_transaction_inputs(inputs: InputObjects) -> Self {
3025 Self(inputs)
3026 }
3027
3028 pub fn new_for_genesis(input_objects: Vec<ObjectReadResult>) -> Self {
3030 Self(InputObjects::new(input_objects))
3031 }
3032
3033 pub fn new_for_replay(input_objects: InputObjects) -> Self {
3035 Self(input_objects)
3036 }
3037
3038 pub fn inner(&self) -> &InputObjects {
3039 &self.0
3040 }
3041
3042 pub fn into_inner(self) -> InputObjects {
3043 self.0
3044 }
3045}
3046
3047impl From<Vec<ObjectReadResult>> for InputObjects {
3048 fn from(objects: Vec<ObjectReadResult>) -> Self {
3049 Self::new(objects)
3050 }
3051}
3052
3053impl InputObjects {
3054 pub fn new(objects: Vec<ObjectReadResult>) -> Self {
3055 Self { objects }
3056 }
3057
3058 pub fn len(&self) -> usize {
3059 self.objects.len()
3060 }
3061
3062 pub fn is_empty(&self) -> bool {
3063 self.objects.is_empty()
3064 }
3065
3066 pub fn contains_deleted_objects(&self) -> bool {
3067 self.objects
3068 .iter()
3069 .any(|obj| obj.is_deleted_shared_object())
3070 }
3071
3072 pub fn get_cancelled_objects(&self) -> Option<(Vec<ObjectID>, SequenceNumber)> {
3075 let mut contains_cancelled = false;
3076 let mut cancel_reason = None;
3077 let mut cancelled_objects = Vec::new();
3078 for obj in &self.objects {
3079 if let ObjectReadResultKind::CancelledTransactionSharedObject(version) = obj.object {
3080 contains_cancelled = true;
3081 if version.is_congested() || version == SequenceNumber::RANDOMNESS_UNAVAILABLE {
3082 assert!(cancel_reason.is_none() || cancel_reason == Some(version));
3084 cancel_reason = Some(version);
3085 cancelled_objects.push(obj.id());
3086 }
3087 }
3088 }
3089
3090 if !cancelled_objects.is_empty() {
3091 Some((
3092 cancelled_objects,
3093 cancel_reason
3094 .expect("there should be a cancel reason if there are cancelled objects"),
3095 ))
3096 } else {
3097 assert!(!contains_cancelled);
3098 None
3099 }
3100 }
3101
3102 pub fn filter_owned_objects(&self) -> Vec<ObjectRef> {
3103 let owned_objects: Vec<_> = self
3104 .objects
3105 .iter()
3106 .filter_map(|obj| obj.get_owned_objref())
3107 .collect();
3108
3109 trace!(
3110 num_mutable_objects = owned_objects.len(),
3111 "Checked locks and found mutable objects"
3112 );
3113
3114 owned_objects
3115 }
3116
3117 pub fn filter_shared_objects(&self) -> Vec<SharedInput> {
3118 self.objects
3119 .iter()
3120 .filter(|obj| obj.is_shared_object())
3121 .map(|obj| {
3122 obj.to_shared_input()
3123 .expect("already filtered for shared objects")
3124 })
3125 .collect()
3126 }
3127
3128 pub fn transaction_dependencies(&self) -> BTreeSet<TransactionDigest> {
3129 self.objects
3130 .iter()
3131 .filter_map(|obj| obj.get_previous_transaction())
3132 .collect()
3133 }
3134
3135 pub fn mutable_inputs(&self) -> BTreeMap<ObjectID, (VersionDigest, Owner)> {
3136 self.objects
3137 .iter()
3138 .filter_map(
3139 |ObjectReadResult {
3140 input_object_kind,
3141 object,
3142 }| match (input_object_kind, object) {
3143 (InputObjectKind::MovePackage(_), _) => None,
3144 (
3145 InputObjectKind::ImmOrOwnedMoveObject(object_ref),
3146 ObjectReadResultKind::Object(object),
3147 ) => {
3148 if object.is_immutable() {
3149 None
3150 } else {
3151 Some((object_ref.0, ((object_ref.1, object_ref.2), object.owner)))
3152 }
3153 }
3154 (
3155 InputObjectKind::ImmOrOwnedMoveObject(_),
3156 ObjectReadResultKind::DeletedSharedObject(_, _),
3157 ) => {
3158 unreachable!()
3159 }
3160 (
3161 InputObjectKind::SharedMoveObject { .. },
3162 ObjectReadResultKind::DeletedSharedObject(_, _),
3163 ) => None,
3164 (
3165 InputObjectKind::SharedMoveObject { mutable, .. },
3166 ObjectReadResultKind::Object(object),
3167 ) => {
3168 if *mutable {
3169 let oref = object.compute_object_reference();
3170 Some((oref.0, ((oref.1, oref.2), object.owner)))
3171 } else {
3172 None
3173 }
3174 }
3175 (
3176 InputObjectKind::ImmOrOwnedMoveObject(_),
3177 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3178 ) => {
3179 unreachable!()
3180 }
3181 (
3182 InputObjectKind::SharedMoveObject { .. },
3183 ObjectReadResultKind::CancelledTransactionSharedObject(_),
3184 ) => None,
3185 },
3186 )
3187 .collect()
3188 }
3189
3190 pub fn lamport_timestamp(&self, receiving_objects: &[ObjectRef]) -> SequenceNumber {
3194 let input_versions = self
3195 .objects
3196 .iter()
3197 .filter_map(|object| match &object.object {
3198 ObjectReadResultKind::Object(object) => {
3199 object.data.try_as_move().map(MoveObject::version)
3200 }
3201 ObjectReadResultKind::DeletedSharedObject(v, _) => Some(*v),
3202 ObjectReadResultKind::CancelledTransactionSharedObject(_) => None,
3203 })
3204 .chain(receiving_objects.iter().map(|object_ref| object_ref.1));
3205
3206 SequenceNumber::lamport_increment(input_versions)
3207 }
3208
3209 pub fn object_kinds(&self) -> impl Iterator<Item = &InputObjectKind> {
3210 self.objects.iter().map(
3211 |ObjectReadResult {
3212 input_object_kind, ..
3213 }| input_object_kind,
3214 )
3215 }
3216
3217 pub fn into_object_map(self) -> BTreeMap<ObjectID, Object> {
3218 self.objects
3219 .into_iter()
3220 .filter_map(|o| o.as_object().map(|object| (o.id(), object.clone())))
3221 .collect()
3222 }
3223
3224 pub fn push(&mut self, object: ObjectReadResult) {
3225 self.objects.push(object);
3226 }
3227
3228 pub fn iter(&self) -> impl Iterator<Item = &ObjectReadResult> {
3229 self.objects.iter()
3230 }
3231
3232 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3233 self.objects.iter().filter_map(|o| o.as_object())
3234 }
3235}
3236
3237#[derive(Clone, Debug)]
3241pub enum ReceivingObjectReadResultKind {
3242 Object(Object),
3243 PreviouslyReceivedObject,
3245}
3246
3247impl ReceivingObjectReadResultKind {
3248 pub fn as_object(&self) -> Option<&Object> {
3249 match &self {
3250 Self::Object(object) => Some(object),
3251 Self::PreviouslyReceivedObject => None,
3252 }
3253 }
3254}
3255
3256pub struct ReceivingObjectReadResult {
3257 pub object_ref: ObjectRef,
3258 pub object: ReceivingObjectReadResultKind,
3259}
3260
3261impl ReceivingObjectReadResult {
3262 pub fn new(object_ref: ObjectRef, object: ReceivingObjectReadResultKind) -> Self {
3263 Self { object_ref, object }
3264 }
3265
3266 pub fn is_previously_received(&self) -> bool {
3267 matches!(
3268 self.object,
3269 ReceivingObjectReadResultKind::PreviouslyReceivedObject
3270 )
3271 }
3272}
3273
3274impl From<Object> for ReceivingObjectReadResultKind {
3275 fn from(object: Object) -> Self {
3276 Self::Object(object)
3277 }
3278}
3279
3280pub struct ReceivingObjects {
3281 pub objects: Vec<ReceivingObjectReadResult>,
3282}
3283
3284impl ReceivingObjects {
3285 pub fn iter(&self) -> impl Iterator<Item = &ReceivingObjectReadResult> {
3286 self.objects.iter()
3287 }
3288
3289 pub fn iter_objects(&self) -> impl Iterator<Item = &Object> {
3290 self.objects.iter().filter_map(|o| o.object.as_object())
3291 }
3292}
3293
3294impl From<Vec<ReceivingObjectReadResult>> for ReceivingObjects {
3295 fn from(objects: Vec<ReceivingObjectReadResult>) -> Self {
3296 Self { objects }
3297 }
3298}
3299
3300impl Display for CertifiedTransaction {
3301 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3302 let mut writer = String::new();
3303 writeln!(writer, "Transaction Hash: {:?}", self.digest())?;
3304 writeln!(
3305 writer,
3306 "Signed Authorities Bitmap : {:?}",
3307 self.auth_sig().signers_map
3308 )?;
3309 write!(writer, "{}", &self.data().intent_message().value.kind())?;
3310 write!(f, "{writer}")
3311 }
3312}
3313
3314#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3319pub enum TransactionKey {
3320 Digest(TransactionDigest),
3321 RandomnessRound(EpochId, RandomnessRound),
3322}
3323
3324impl TransactionKey {
3325 pub fn unwrap_digest(&self) -> &TransactionDigest {
3326 match self {
3327 TransactionKey::Digest(d) => d,
3328 _ => panic!("called expect_digest on a non-Digest TransactionKey: {self:?}"),
3329 }
3330 }
3331}