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