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 {
33 error, actual_fees, ..
34 } => Some(ExecTxResult {
35 code: HandlerError::from(&error).code(),
36 info: error.response_info_for_version(platform_version)?,
37 gas_used: actual_fees.total_base_fee() as SignedCredits,
38 ..Default::default()
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 address_balance_changes: BTreeMap::new(),
122 };
123
124 let result: Option<ExecTxResult> = execution_result
125 .try_into_platform_versioned(platform_version)
126 .expect("conversion should succeed");
127
128 let tx_result = result.expect("should produce Some result");
129 assert_ne!(tx_result.code, 0);
130 assert_eq!(
131 tx_result.gas_used,
132 fee_result.total_base_fee() as SignedCredits
133 );
134 assert!(!tx_result.info.is_empty());
135 }
136
137 #[test]
138 fn internal_error_produces_result_with_internal_code() {
139 let platform_version = PlatformVersion::latest();
140
141 let execution_result =
142 StateTransitionExecutionResult::InternalError("something broke".to_string());
143
144 let result: Option<ExecTxResult> = execution_result
145 .try_into_platform_versioned(platform_version)
146 .expect("conversion should succeed");
147
148 let tx_result = result.expect("should produce Some result");
149 assert_eq!(tx_result.code, 13);
151 assert!(tx_result.info.is_empty());
152 }
153
154 #[test]
155 fn not_executed_produces_none() {
156 let platform_version = PlatformVersion::latest();
157
158 let execution_result =
159 StateTransitionExecutionResult::NotExecuted(NotExecutedReason::ProposerRanOutOfTime);
160
161 let result: Option<ExecTxResult> = execution_result
162 .try_into_platform_versioned(platform_version)
163 .expect("conversion should succeed");
164
165 assert!(result.is_none());
166 }
167
168 #[test]
169 fn successful_execution_with_zero_fees() {
170 let platform_version = PlatformVersion::latest();
171
172 let fee_result = FeeResult::default();
173
174 let execution_result = StateTransitionExecutionResult::SuccessfulExecution {
175 estimated_fees: None,
176 fee_result,
177 address_balance_changes: BTreeMap::new(),
178 };
179
180 let result: Option<ExecTxResult> = execution_result
181 .try_into_platform_versioned(platform_version)
182 .expect("conversion should succeed");
183
184 let tx_result = result.expect("should produce Some result");
185 assert_eq!(tx_result.code, 0);
186 assert_eq!(tx_result.gas_used, 0);
187 }
188}