drive_abci/abci/app/
execution_result.rs1use crate::abci::handler::error::consensus::AbciResponseInfoGetter;
2use crate::abci::handler::error::HandlerError;
3use crate::error::Error;
4use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult;
5use dpp::fee::SignedCredits;
6use dpp::version::PlatformVersion;
7use dpp::version::TryIntoPlatformVersioned;
8use tenderdash_abci::proto::abci::ExecTxResult;
9
10impl TryIntoPlatformVersioned<Option<ExecTxResult>> for StateTransitionExecutionResult {
11 type Error = Error;
12
13 fn try_into_platform_versioned(
14 self,
15 platform_version: &PlatformVersion,
16 ) -> Result<Option<ExecTxResult>, Self::Error> {
17 let response = match self {
18 StateTransitionExecutionResult::SuccessfulExecution {
19 fee_result: actual_fees,
20 ..
21 } => Some(ExecTxResult {
22 code: 0,
23 gas_used: actual_fees.total_base_fee() as SignedCredits,
24 ..Default::default()
25 }),
26 StateTransitionExecutionResult::UnpaidConsensusError(error) => Some(ExecTxResult {
27 code: HandlerError::from(&error).code(),
28 info: error.response_info_for_version(platform_version)?,
29 gas_used: 0,
30 ..Default::default()
31 }),
32 StateTransitionExecutionResult::PaidConsensusError { error, actual_fees } => {
33 Some(ExecTxResult {
34 code: HandlerError::from(&error).code(),
35 info: error.response_info_for_version(platform_version)?,
36 gas_used: actual_fees.total_base_fee() as SignedCredits,
37 ..Default::default()
38 })
39 }
40 StateTransitionExecutionResult::InternalError(message) => Some(ExecTxResult {
41 code: HandlerError::Internal(message).code(),
42 info: String::default(),
44 ..Default::default()
45 }),
46 StateTransitionExecutionResult::NotExecuted(_) => None,
47 };
48
49 Ok(response)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::platform_types::state_transitions_processing_result::NotExecutedReason;
57 use dpp::consensus::ConsensusError;
58 use dpp::fee::fee_result::FeeResult;
59 use std::collections::BTreeMap;
60
61 #[test]
62 fn successful_execution_produces_result_with_zero_code() {
63 let platform_version = PlatformVersion::latest();
64
65 let fee_result = FeeResult {
66 storage_fee: 100,
67 processing_fee: 50,
68 ..Default::default()
69 };
70
71 let execution_result = StateTransitionExecutionResult::SuccessfulExecution {
72 estimated_fees: None,
73 fee_result: fee_result.clone(),
74 address_balance_changes: BTreeMap::new(),
75 };
76
77 let result: Option<ExecTxResult> = execution_result
78 .try_into_platform_versioned(platform_version)
79 .expect("conversion should succeed");
80
81 let tx_result = result.expect("should produce Some result");
82 assert_eq!(tx_result.code, 0);
83 assert_eq!(
84 tx_result.gas_used,
85 fee_result.total_base_fee() as SignedCredits
86 );
87 }
88
89 #[test]
90 fn unpaid_consensus_error_produces_result_with_error_code() {
91 let platform_version = PlatformVersion::latest();
92
93 let consensus_error = ConsensusError::DefaultError;
94 let execution_result =
95 StateTransitionExecutionResult::UnpaidConsensusError(consensus_error);
96
97 let result: Option<ExecTxResult> = execution_result
98 .try_into_platform_versioned(platform_version)
99 .expect("conversion should succeed");
100
101 let tx_result = result.expect("should produce Some result");
102 assert_ne!(tx_result.code, 0);
103 assert_eq!(tx_result.gas_used, 0);
104 assert!(!tx_result.info.is_empty());
105 }
106
107 #[test]
108 fn paid_consensus_error_produces_result_with_fees_and_error_code() {
109 let platform_version = PlatformVersion::latest();
110
111 let consensus_error = ConsensusError::DefaultError;
112 let fee_result = FeeResult {
113 storage_fee: 0,
114 processing_fee: 200,
115 ..Default::default()
116 };
117
118 let execution_result = StateTransitionExecutionResult::PaidConsensusError {
119 error: consensus_error,
120 actual_fees: fee_result.clone(),
121 };
122
123 let result: Option<ExecTxResult> = execution_result
124 .try_into_platform_versioned(platform_version)
125 .expect("conversion should succeed");
126
127 let tx_result = result.expect("should produce Some result");
128 assert_ne!(tx_result.code, 0);
129 assert_eq!(
130 tx_result.gas_used,
131 fee_result.total_base_fee() as SignedCredits
132 );
133 assert!(!tx_result.info.is_empty());
134 }
135
136 #[test]
137 fn internal_error_produces_result_with_internal_code() {
138 let platform_version = PlatformVersion::latest();
139
140 let execution_result =
141 StateTransitionExecutionResult::InternalError("something broke".to_string());
142
143 let result: Option<ExecTxResult> = execution_result
144 .try_into_platform_versioned(platform_version)
145 .expect("conversion should succeed");
146
147 let tx_result = result.expect("should produce Some result");
148 assert_eq!(tx_result.code, 13);
150 assert!(tx_result.info.is_empty());
151 }
152
153 #[test]
154 fn not_executed_produces_none() {
155 let platform_version = PlatformVersion::latest();
156
157 let execution_result =
158 StateTransitionExecutionResult::NotExecuted(NotExecutedReason::ProposerRanOutOfTime);
159
160 let result: Option<ExecTxResult> = execution_result
161 .try_into_platform_versioned(platform_version)
162 .expect("conversion should succeed");
163
164 assert!(result.is_none());
165 }
166
167 #[test]
168 fn successful_execution_with_zero_fees() {
169 let platform_version = PlatformVersion::latest();
170
171 let fee_result = FeeResult::default();
172
173 let execution_result = StateTransitionExecutionResult::SuccessfulExecution {
174 estimated_fees: None,
175 fee_result,
176 address_balance_changes: BTreeMap::new(),
177 };
178
179 let result: Option<ExecTxResult> = execution_result
180 .try_into_platform_versioned(platform_version)
181 .expect("conversion should succeed");
182
183 let tx_result = result.expect("should produce Some result");
184 assert_eq!(tx_result.code, 0);
185 assert_eq!(tx_result.gas_used, 0);
186 }
187}