sui_types/
execution_params.rs1use std::collections::HashSet;
5
6use mysten_common::assert_reachable;
7use nonempty::NonEmpty;
8use once_cell::sync::Lazy;
9
10use crate::{
11 base_types::SequenceNumber, digests::TransactionDigest, execution_status::CongestedObjects,
12 execution_status::ExecutionErrorKind, transaction::CheckedInputObjects,
13};
14
15#[derive(Debug, Clone)]
19pub struct ExecutionOrEarlyError {
20 early_errors: Option<NonEmpty<ExecutionErrorKind>>,
21 accumulator_version: Option<SequenceNumber>,
25}
26
27impl ExecutionOrEarlyError {
28 pub fn ok(accumulator_version: Option<SequenceNumber>) -> Self {
30 Self {
31 early_errors: None,
32 accumulator_version,
33 }
34 }
35
36 pub fn failed(
38 errors: NonEmpty<ExecutionErrorKind>,
39 accumulator_version: Option<SequenceNumber>,
40 ) -> Self {
41 Self {
42 early_errors: Some(errors),
43 accumulator_version,
44 }
45 }
46
47 pub fn is_ok(&self) -> bool {
48 self.early_errors.is_none()
49 }
50
51 pub fn early_errors(&self) -> Option<&NonEmpty<ExecutionErrorKind>> {
53 self.early_errors.as_ref()
54 }
55
56 pub fn into_early_errors(self) -> Option<NonEmpty<ExecutionErrorKind>> {
58 self.early_errors
59 }
60
61 pub fn accumulator_version(&self) -> Option<SequenceNumber> {
62 self.accumulator_version
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub enum FundsWithdrawStatus {
68 MaybeSufficient,
73 Insufficient,
77}
78
79pub fn get_early_execution_error(
84 transaction_digest: &TransactionDigest,
85 input_objects: &CheckedInputObjects,
86 config_certificate_deny_set: &HashSet<TransactionDigest>,
87 funds_withdraw_status: &FundsWithdrawStatus,
88) -> Option<NonEmpty<ExecutionErrorKind>> {
89 let mut errors = vec![];
90 if is_certificate_denied(transaction_digest, config_certificate_deny_set) {
91 errors.push(ExecutionErrorKind::CertificateDenied);
92 }
93
94 if input_objects
95 .inner()
96 .contains_consensus_stream_ended_objects()
97 {
98 errors.push(ExecutionErrorKind::InputObjectDeleted);
99 }
100
101 let cancelled_objects = input_objects.inner().get_cancelled_objects();
102 if let Some((cancelled_objects, reason)) = cancelled_objects {
103 match reason {
104 SequenceNumber::CONGESTED => {
105 errors.push(
106 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
107 congested_objects: CongestedObjects(cancelled_objects),
108 },
109 );
110 }
111 SequenceNumber::RANDOMNESS_UNAVAILABLE => {
112 errors.push(ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable);
113 }
114 _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
115 }
116 }
117
118 if matches!(funds_withdraw_status, FundsWithdrawStatus::Insufficient) {
119 assert_reachable!("insufficient funds for withdraw");
120 errors.push(ExecutionErrorKind::InsufficientFundsForWithdraw);
121 }
122
123 NonEmpty::from_vec(errors)
124}
125
126fn get_denied_certificates() -> &'static HashSet<TransactionDigest> {
147 static DENIED_CERTIFICATES: Lazy<HashSet<TransactionDigest>> = Lazy::new(|| HashSet::from([]));
148 Lazy::force(&DENIED_CERTIFICATES)
149}
150
151#[cfg(msim)]
153pub fn get_denied_certificates_for_sim_test() -> &'static HashSet<TransactionDigest> {
154 get_denied_certificates()
155}
156
157fn is_certificate_denied(
158 transaction_digest: &TransactionDigest,
159 certificate_deny_set: &HashSet<TransactionDigest>,
160) -> bool {
161 certificate_deny_set.contains(transaction_digest)
162 || get_denied_certificates().contains(transaction_digest)
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::{
169 base_types::ObjectID,
170 transaction::{
171 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
172 ObjectReadResultKind, SharedObjectMutability,
173 },
174 };
175
176 fn create_test_input_objects() -> CheckedInputObjects {
177 let input_objects = InputObjects::new(vec![]);
178 CheckedInputObjects::new_for_replay(input_objects)
179 }
180
181 #[test]
182 fn test_early_execution_error_insufficient_balance() {
183 let tx_digest = crate::digests::TransactionDigest::random();
184 let input_objects = create_test_input_objects();
185 let deny_set = HashSet::new();
186
187 let result = get_early_execution_error(
189 &tx_digest,
190 &input_objects,
191 &deny_set,
192 &FundsWithdrawStatus::Insufficient,
193 );
194 assert_eq!(
195 result.unwrap().into_iter().collect::<Vec<_>>(),
196 vec![ExecutionErrorKind::InsufficientFundsForWithdraw],
197 );
198
199 let result = get_early_execution_error(
201 &tx_digest,
202 &input_objects,
203 &deny_set,
204 &FundsWithdrawStatus::MaybeSufficient,
205 );
206 assert!(result.is_none());
207 }
208
209 #[test]
210 fn test_early_execution_error_collects_all() {
211 let tx_digest = crate::digests::TransactionDigest::random();
212 let input_objects = create_test_input_objects();
213
214 let mut deny_set = HashSet::new();
216 deny_set.insert(tx_digest);
217 let result = get_early_execution_error(
218 &tx_digest,
219 &input_objects,
220 &deny_set,
221 &FundsWithdrawStatus::Insufficient,
222 );
223 assert_eq!(
224 result.unwrap().into_iter().collect::<Vec<_>>(),
225 vec![
226 ExecutionErrorKind::CertificateDenied,
227 ExecutionErrorKind::InsufficientFundsForWithdraw,
228 ],
229 );
230
231 let input_objects = InputObjects::new(vec![ObjectReadResult {
233 input_object_kind: InputObjectKind::SharedMoveObject {
234 id: ObjectID::random(),
235 initial_shared_version: SequenceNumber::MIN,
236 mutability: SharedObjectMutability::Immutable,
237 },
238 object: ObjectReadResultKind::ObjectConsensusStreamEnded(
239 SequenceNumber::MIN, tx_digest,
241 ),
242 }]);
243 deny_set.clear();
244 let result = get_early_execution_error(
245 &tx_digest,
246 &CheckedInputObjects::new_for_replay(input_objects),
247 &deny_set,
248 &FundsWithdrawStatus::Insufficient,
249 );
250 assert_eq!(
251 result.unwrap().into_iter().collect::<Vec<_>>(),
252 vec![
253 ExecutionErrorKind::InputObjectDeleted,
254 ExecutionErrorKind::InsufficientFundsForWithdraw,
255 ],
256 );
257
258 let input_objects = InputObjects::new(vec![ObjectReadResult {
260 input_object_kind: InputObjectKind::SharedMoveObject {
261 id: ObjectID::random(),
262 initial_shared_version: SequenceNumber::MIN,
263 mutability: SharedObjectMutability::Immutable,
264 },
265 object: ObjectReadResultKind::CancelledTransactionSharedObject(
266 SequenceNumber::CONGESTED,
267 ),
268 }]);
269 let result = get_early_execution_error(
270 &tx_digest,
271 &CheckedInputObjects::new_for_replay(input_objects),
272 &deny_set,
273 &FundsWithdrawStatus::Insufficient,
274 );
275 let result: Vec<_> = result.unwrap().into_iter().collect();
276 assert_eq!(result.len(), 2);
277 assert!(matches!(
278 result[0],
279 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion { .. }
280 ));
281 assert_eq!(result[1], ExecutionErrorKind::InsufficientFundsForWithdraw);
282 }
283}