sui_types/
execution_params.rs1use std::collections::HashSet;
5
6use mysten_common::assert_reachable;
7use once_cell::sync::Lazy;
8
9use crate::{
10 base_types::SequenceNumber, digests::TransactionDigest, error::ExecutionErrorKind,
11 execution_status::CongestedObjects, transaction::CheckedInputObjects,
12};
13
14pub type ExecutionOrEarlyError = Result<(), ExecutionErrorKind>;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum FundsWithdrawStatus {
18 MaybeSufficient,
23 Insufficient,
27}
28
29pub fn get_early_execution_error(
34 transaction_digest: &TransactionDigest,
35 input_objects: &CheckedInputObjects,
36 config_certificate_deny_set: &HashSet<TransactionDigest>,
37 funds_withdraw_status: &FundsWithdrawStatus,
38) -> Option<ExecutionErrorKind> {
39 if is_certificate_denied(transaction_digest, config_certificate_deny_set) {
40 return Some(ExecutionErrorKind::CertificateDenied);
41 }
42
43 if input_objects
44 .inner()
45 .contains_consensus_stream_ended_objects()
46 {
47 return Some(ExecutionErrorKind::InputObjectDeleted);
48 }
49
50 let cancelled_objects = input_objects.inner().get_cancelled_objects();
51 if let Some((cancelled_objects, reason)) = cancelled_objects {
52 match reason {
53 SequenceNumber::CONGESTED => {
54 return Some(
55 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
56 congested_objects: CongestedObjects(cancelled_objects),
57 },
58 );
59 }
60 SequenceNumber::RANDOMNESS_UNAVAILABLE => {
61 return Some(ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable);
62 }
63 _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
64 }
65 }
66
67 if matches!(funds_withdraw_status, FundsWithdrawStatus::Insufficient) {
68 assert_reachable!("insufficient funds for withdraw");
69 return Some(ExecutionErrorKind::InsufficientFundsForWithdraw);
70 }
71
72 None
73}
74
75fn get_denied_certificates() -> &'static HashSet<TransactionDigest> {
96 static DENIED_CERTIFICATES: Lazy<HashSet<TransactionDigest>> = Lazy::new(|| HashSet::from([]));
97 Lazy::force(&DENIED_CERTIFICATES)
98}
99
100#[cfg(msim)]
102pub fn get_denied_certificates_for_sim_test() -> &'static HashSet<TransactionDigest> {
103 get_denied_certificates()
104}
105
106fn is_certificate_denied(
107 transaction_digest: &TransactionDigest,
108 certificate_deny_set: &HashSet<TransactionDigest>,
109) -> bool {
110 certificate_deny_set.contains(transaction_digest)
111 || get_denied_certificates().contains(transaction_digest)
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::{
118 base_types::ObjectID,
119 transaction::{
120 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
121 ObjectReadResultKind, SharedObjectMutability,
122 },
123 };
124
125 fn create_test_input_objects() -> CheckedInputObjects {
126 let input_objects = InputObjects::new(vec![]);
127 CheckedInputObjects::new_for_replay(input_objects)
128 }
129
130 #[test]
131 fn test_early_execution_error_insufficient_balance() {
132 let tx_digest = crate::digests::TransactionDigest::random();
133 let input_objects = create_test_input_objects();
134 let deny_set = HashSet::new();
135
136 let result = get_early_execution_error(
138 &tx_digest,
139 &input_objects,
140 &deny_set,
141 &FundsWithdrawStatus::Insufficient,
142 );
143 assert_eq!(
144 result,
145 Some(ExecutionErrorKind::InsufficientFundsForWithdraw)
146 );
147
148 let result = get_early_execution_error(
150 &tx_digest,
151 &input_objects,
152 &deny_set,
153 &FundsWithdrawStatus::MaybeSufficient,
154 );
155 assert_eq!(result, None);
156 }
157
158 #[test]
159 fn test_early_execution_error_precedence() {
160 let tx_digest = crate::digests::TransactionDigest::random();
161 let input_objects = create_test_input_objects();
162
163 let mut deny_set = HashSet::new();
165 deny_set.insert(tx_digest);
166 let result = get_early_execution_error(
167 &tx_digest,
168 &input_objects,
169 &deny_set,
170 &FundsWithdrawStatus::Insufficient,
171 );
172 assert_eq!(result, Some(ExecutionErrorKind::CertificateDenied));
173
174 let input_objects = InputObjects::new(vec![
176 ObjectReadResult {
178 input_object_kind: InputObjectKind::SharedMoveObject {
179 id: ObjectID::random(),
180 initial_shared_version: SequenceNumber::MIN,
181 mutability: SharedObjectMutability::Immutable,
182 },
183 object: ObjectReadResultKind::ObjectConsensusStreamEnded(
184 SequenceNumber::MIN, tx_digest,
186 ),
187 },
188 ]);
189 deny_set.clear();
190 let result = get_early_execution_error(
191 &tx_digest,
192 &CheckedInputObjects::new_for_replay(input_objects),
193 &deny_set,
194 &FundsWithdrawStatus::Insufficient,
195 );
196 assert_eq!(result, Some(ExecutionErrorKind::InputObjectDeleted));
197
198 let input_objects = InputObjects::new(vec![
200 ObjectReadResult {
202 input_object_kind: InputObjectKind::SharedMoveObject {
203 id: ObjectID::random(),
204 initial_shared_version: SequenceNumber::MIN,
205 mutability: SharedObjectMutability::Immutable,
206 },
207 object: ObjectReadResultKind::CancelledTransactionSharedObject(
208 SequenceNumber::CONGESTED,
209 ),
210 },
211 ]);
212 let result = get_early_execution_error(
213 &tx_digest,
214 &CheckedInputObjects::new_for_replay(input_objects),
215 &deny_set,
216 &FundsWithdrawStatus::Insufficient,
217 );
218 assert!(matches!(
219 result,
220 Some(ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion { .. })
221 ));
222 }
223}