sui_types/
execution_params.rsuse std::collections::HashSet;
use once_cell::sync::Lazy;
use crate::{
base_types::SequenceNumber, digests::TransactionDigest, error::ExecutionErrorKind,
execution_status::CongestedObjects, transaction::CheckedInputObjects,
};
pub type ExecutionOrEarlyError = Result<(), ExecutionErrorKind>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BalanceWithdrawStatus {
NoWithdraw,
SufficientBalance,
InsufficientBalance,
}
pub fn get_early_execution_error(
transaction_digest: &TransactionDigest,
input_objects: &CheckedInputObjects,
config_certificate_deny_set: &HashSet<TransactionDigest>,
balance_withdraw_status: &BalanceWithdrawStatus,
) -> Option<ExecutionErrorKind> {
if is_certificate_denied(transaction_digest, config_certificate_deny_set) {
return Some(ExecutionErrorKind::CertificateDenied);
}
if input_objects
.inner()
.contains_consensus_stream_ended_objects()
{
return Some(ExecutionErrorKind::InputObjectDeleted);
}
let cancelled_objects = input_objects.inner().get_cancelled_objects();
if let Some((cancelled_objects, reason)) = cancelled_objects {
match reason {
SequenceNumber::CONGESTED => {
return Some(
ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
congested_objects: CongestedObjects(cancelled_objects),
},
);
}
SequenceNumber::RANDOMNESS_UNAVAILABLE => {
return Some(ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable);
}
_ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
}
}
if matches!(
balance_withdraw_status,
BalanceWithdrawStatus::InsufficientBalance
) {
return Some(ExecutionErrorKind::InsufficientBalanceForWithdraw);
}
None
}
fn get_denied_certificates() -> &'static HashSet<TransactionDigest> {
static DENIED_CERTIFICATES: Lazy<HashSet<TransactionDigest>> = Lazy::new(|| HashSet::from([]));
Lazy::force(&DENIED_CERTIFICATES)
}
#[cfg(msim)]
pub fn get_denied_certificates_for_sim_test() -> &'static HashSet<TransactionDigest> {
get_denied_certificates()
}
fn is_certificate_denied(
transaction_digest: &TransactionDigest,
certificate_deny_set: &HashSet<TransactionDigest>,
) -> bool {
certificate_deny_set.contains(transaction_digest)
|| get_denied_certificates().contains(transaction_digest)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
base_types::ObjectID,
transaction::{
CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
ObjectReadResultKind,
},
};
fn create_test_input_objects() -> CheckedInputObjects {
let input_objects = InputObjects::new(vec![]);
CheckedInputObjects::new_for_replay(input_objects)
}
#[test]
fn test_early_execution_error_insufficient_balance() {
let tx_digest = crate::digests::TransactionDigest::random();
let input_objects = create_test_input_objects();
let deny_set = HashSet::new();
let result = get_early_execution_error(
&tx_digest,
&input_objects,
&deny_set,
&BalanceWithdrawStatus::InsufficientBalance,
);
assert_eq!(
result,
Some(ExecutionErrorKind::InsufficientBalanceForWithdraw)
);
let result = get_early_execution_error(
&tx_digest,
&input_objects,
&deny_set,
&BalanceWithdrawStatus::SufficientBalance,
);
assert_eq!(result, None);
}
#[test]
fn test_early_execution_error_precedence() {
let tx_digest = crate::digests::TransactionDigest::random();
let input_objects = create_test_input_objects();
let mut deny_set = HashSet::new();
deny_set.insert(tx_digest);
let result = get_early_execution_error(
&tx_digest,
&input_objects,
&deny_set,
&BalanceWithdrawStatus::InsufficientBalance,
);
assert_eq!(result, Some(ExecutionErrorKind::CertificateDenied));
let input_objects = InputObjects::new(vec![
ObjectReadResult {
input_object_kind: InputObjectKind::SharedMoveObject {
id: ObjectID::random(),
initial_shared_version: SequenceNumber::MIN,
mutable: false,
},
object: ObjectReadResultKind::ObjectConsensusStreamEnded(
SequenceNumber::MIN, tx_digest,
),
},
]);
deny_set.clear();
let result = get_early_execution_error(
&tx_digest,
&CheckedInputObjects::new_for_replay(input_objects),
&deny_set,
&BalanceWithdrawStatus::InsufficientBalance,
);
assert_eq!(result, Some(ExecutionErrorKind::InputObjectDeleted));
let input_objects = InputObjects::new(vec![
ObjectReadResult {
input_object_kind: InputObjectKind::SharedMoveObject {
id: ObjectID::random(),
initial_shared_version: SequenceNumber::MIN,
mutable: false,
},
object: ObjectReadResultKind::CancelledTransactionSharedObject(
SequenceNumber::CONGESTED,
),
},
]);
let result = get_early_execution_error(
&tx_digest,
&CheckedInputObjects::new_for_replay(input_objects),
&deny_set,
&BalanceWithdrawStatus::InsufficientBalance,
);
assert!(matches!(
result,
Some(ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion { .. })
));
}
}