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