Skip to main content

drive_abci/abci/app/
execution_result.rs

1use 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                // TODO: That would be nice to provide more information about the error for debugging
43                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        // Internal error code is 13
150        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}