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