drive_abci/abci/app/
check_tx.rs1use crate::abci::app::PlatformApplication;
2use crate::abci::handler;
3use crate::error::execution::ExecutionError;
4use crate::error::Error;
5use crate::platform_types::platform::Platform;
6use crate::rpc::core::CoreRPCLike;
7use crate::utils::spawn_blocking_task_with_name_if_supported;
8use async_trait::async_trait;
9use std::fmt::Debug;
10use std::sync::Arc;
11use tenderdash_abci::proto::abci as proto;
12use tenderdash_abci::proto::abci::abci_application_server as grpc_abci_server;
13use tenderdash_abci::proto::tonic;
14
15pub struct CheckTxAbciApplication<C>
20where
21 C: CoreRPCLike + Send + Sync + 'static,
22{
23 platform: Arc<Platform<C>>,
25 core_rpc: Arc<C>,
26}
27
28impl<C> PlatformApplication<C> for CheckTxAbciApplication<C>
29where
30 C: CoreRPCLike + Send + Sync + 'static,
31{
32 fn platform(&self) -> &Platform<C> {
33 self.platform.as_ref()
34 }
35}
36
37impl<C> CheckTxAbciApplication<C>
38where
39 C: CoreRPCLike + Send + Sync + 'static,
40{
41 pub fn new(platform: Arc<Platform<C>>, core_rpc: Arc<C>) -> Self {
43 Self { platform, core_rpc }
44 }
45}
46
47impl<C> Debug for CheckTxAbciApplication<C>
48where
49 C: CoreRPCLike + Send + Sync + 'static,
50{
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 write!(f, "<CheckTxAbciApplication>")
53 }
54}
55
56#[async_trait]
57impl<C> grpc_abci_server::AbciApplication for CheckTxAbciApplication<C>
58where
59 C: CoreRPCLike + Send + Sync + 'static,
60{
61 async fn echo(
62 &self,
63 request: tonic::Request<proto::RequestEcho>,
64 ) -> Result<tonic::Response<proto::ResponseEcho>, tonic::Status> {
65 let response = handler::echo(self, request.into_inner()).map_err(error_into_status)?;
66
67 Ok(tonic::Response::new(response))
68 }
69
70 async fn check_tx(
71 &self,
72 request: tonic::Request<proto::RequestCheckTx>,
73 ) -> Result<tonic::Response<proto::ResponseCheckTx>, tonic::Status> {
74 let platform = Arc::clone(&self.platform);
75 let core_rpc = Arc::clone(&self.core_rpc);
76
77 let proto_request = request.into_inner();
78
79 let check_tx_type = proto::CheckTxType::try_from(proto_request.r#type)
80 .map_err(|_| tonic::Status::invalid_argument("invalid check tx type"))?;
81
82 let thread_name = match check_tx_type {
83 proto::CheckTxType::New => "check_tx",
84 proto::CheckTxType::Recheck => "re_check_tx",
85 };
86
87 spawn_blocking_task_with_name_if_supported(thread_name, move || {
88 let response = handler::check_tx(&platform, &core_rpc, proto_request)
89 .map_err(error_into_status)?;
90
91 Ok(tonic::Response::new(response))
92 })?
93 .await
94 .map_err(|error| tonic::Status::internal(format!("check tx panics: {}", error)))?
95 }
96}
97
98pub fn error_into_status(error: Error) -> tonic::Status {
99 match error {
100 Error::Execution(ExecutionError::CheckTxProofVerificationBusy) => {
101 tonic::Status::resource_exhausted(
102 "check tx verification capacity is temporarily unavailable",
103 )
104 }
105 error => tonic::Status::internal(error.to_string()),
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use crate::error::execution::ExecutionError;
113 use crate::rpc::core::MockCoreRPCLike;
114
115 #[test]
116 fn error_into_status_produces_internal_status() {
117 let error = Error::Execution(ExecutionError::CorruptedCodeExecution("test error message"));
118 let status = error_into_status(error);
119
120 assert_eq!(status.code(), tonic::Code::Internal);
121 assert!(status.message().contains("test error message"));
122 }
123
124 #[test]
125 fn error_into_status_preserves_error_message() {
126 let error = Error::Execution(ExecutionError::NotInTransaction("no active transaction"));
127 let status = error_into_status(error);
128
129 assert!(status.message().contains("no active transaction"));
130 }
131
132 #[test]
133 fn error_into_status_marks_proof_capacity_as_retryable() {
134 let status = error_into_status(Error::Execution(
135 ExecutionError::CheckTxProofVerificationBusy,
136 ));
137
138 assert_eq!(status.code(), tonic::Code::ResourceExhausted);
139 assert!(!status.message().contains("proof"));
140 }
141
142 #[test]
143 fn check_tx_abci_application_debug_format() {
144 let platform =
146 crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
147
148 let core_rpc = MockCoreRPCLike::new();
149
150 let app = CheckTxAbciApplication::new(Arc::new(platform.platform), Arc::new(core_rpc));
151
152 let debug_str = format!("{:?}", app);
153 assert_eq!(debug_str, "<CheckTxAbciApplication>");
154 }
155
156 #[test]
157 fn check_tx_abci_application_platform_returns_platform() {
158 let platform =
159 crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
160
161 let core_rpc = MockCoreRPCLike::new();
162
163 let app = CheckTxAbciApplication::new(Arc::new(platform.platform), Arc::new(core_rpc));
164
165 let _platform_ref = app.platform();
167 }
168}