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 BalanceWithdrawStatus {
17 NoWithdraw,
18 SufficientBalance,
19 InsufficientBalance,
21}
22
23pub fn get_early_execution_error(
28 transaction_digest: &TransactionDigest,
29 input_objects: &CheckedInputObjects,
30 config_certificate_deny_set: &HashSet<TransactionDigest>,
31 balance_withdraw_status: &BalanceWithdrawStatus,
32) -> Option<ExecutionErrorKind> {
33 if is_certificate_denied(transaction_digest, config_certificate_deny_set) {
34 return Some(ExecutionErrorKind::CertificateDenied);
35 }
36
37 if input_objects
38 .inner()
39 .contains_consensus_stream_ended_objects()
40 {
41 return Some(ExecutionErrorKind::InputObjectDeleted);
42 }
43
44 let cancelled_objects = input_objects.inner().get_cancelled_objects();
45 if let Some((cancelled_objects, reason)) = cancelled_objects {
46 match reason {
47 SequenceNumber::CONGESTED => {
48 return Some(
49 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
50 congested_objects: CongestedObjects(cancelled_objects),
51 },
52 );
53 }
54 SequenceNumber::RANDOMNESS_UNAVAILABLE => {
55 return Some(ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable);
56 }
57 _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
58 }
59 }
60
61 if matches!(
62 balance_withdraw_status,
63 BalanceWithdrawStatus::InsufficientBalance
64 ) {
65 return Some(ExecutionErrorKind::InsufficientBalanceForWithdraw);
66 }
67
68 None
69}
70
71fn get_denied_certificates() -> &'static HashSet<TransactionDigest> {
92 static DENIED_CERTIFICATES: Lazy<HashSet<TransactionDigest>> = Lazy::new(|| HashSet::from([]));
93 Lazy::force(&DENIED_CERTIFICATES)
94}
95
96#[cfg(msim)]
98pub fn get_denied_certificates_for_sim_test() -> &'static HashSet<TransactionDigest> {
99 get_denied_certificates()
100}
101
102fn is_certificate_denied(
103 transaction_digest: &TransactionDigest,
104 certificate_deny_set: &HashSet<TransactionDigest>,
105) -> bool {
106 certificate_deny_set.contains(transaction_digest)
107 || get_denied_certificates().contains(transaction_digest)
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::{
114 base_types::ObjectID,
115 transaction::{
116 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
117 ObjectReadResultKind, SharedObjectMutability,
118 },
119 };
120
121 fn create_test_input_objects() -> CheckedInputObjects {
122 let input_objects = InputObjects::new(vec![]);
123 CheckedInputObjects::new_for_replay(input_objects)
124 }
125
126 #[test]
127 fn test_early_execution_error_insufficient_balance() {
128 let tx_digest = crate::digests::TransactionDigest::random();
129 let input_objects = create_test_input_objects();
130 let deny_set = HashSet::new();
131
132 let result = get_early_execution_error(
134 &tx_digest,
135 &input_objects,
136 &deny_set,
137 &BalanceWithdrawStatus::InsufficientBalance,
138 );
139 assert_eq!(
140 result,
141 Some(ExecutionErrorKind::InsufficientBalanceForWithdraw)
142 );
143
144 let result = get_early_execution_error(
146 &tx_digest,
147 &input_objects,
148 &deny_set,
149 &BalanceWithdrawStatus::SufficientBalance,
150 );
151 assert_eq!(result, None);
152 }
153
154 #[test]
155 fn test_early_execution_error_precedence() {
156 let tx_digest = crate::digests::TransactionDigest::random();
157 let input_objects = create_test_input_objects();
158
159 let mut deny_set = HashSet::new();
161 deny_set.insert(tx_digest);
162 let result = get_early_execution_error(
163 &tx_digest,
164 &input_objects,
165 &deny_set,
166 &BalanceWithdrawStatus::InsufficientBalance,
167 );
168 assert_eq!(result, Some(ExecutionErrorKind::CertificateDenied));
169
170 let input_objects = InputObjects::new(vec![
172 ObjectReadResult {
174 input_object_kind: InputObjectKind::SharedMoveObject {
175 id: ObjectID::random(),
176 initial_shared_version: SequenceNumber::MIN,
177 mutability: SharedObjectMutability::Immutable,
178 },
179 object: ObjectReadResultKind::ObjectConsensusStreamEnded(
180 SequenceNumber::MIN, tx_digest,
182 ),
183 },
184 ]);
185 deny_set.clear();
186 let result = get_early_execution_error(
187 &tx_digest,
188 &CheckedInputObjects::new_for_replay(input_objects),
189 &deny_set,
190 &BalanceWithdrawStatus::InsufficientBalance,
191 );
192 assert_eq!(result, Some(ExecutionErrorKind::InputObjectDeleted));
193
194 let input_objects = InputObjects::new(vec![
196 ObjectReadResult {
198 input_object_kind: InputObjectKind::SharedMoveObject {
199 id: ObjectID::random(),
200 initial_shared_version: SequenceNumber::MIN,
201 mutability: SharedObjectMutability::Immutable,
202 },
203 object: ObjectReadResultKind::CancelledTransactionSharedObject(
204 SequenceNumber::CONGESTED,
205 ),
206 },
207 ]);
208 let result = get_early_execution_error(
209 &tx_digest,
210 &CheckedInputObjects::new_for_replay(input_objects),
211 &deny_set,
212 &BalanceWithdrawStatus::InsufficientBalance,
213 );
214 assert!(matches!(
215 result,
216 Some(ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion { .. })
217 ));
218 }
219}