drive_abci/abci/app/
check_tx.rs

1use 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
14/// AbciApp is an implementation of gRPC ABCI Application, as defined by Tenderdash.
15///
16/// AbciApp implements logic that should be triggered when Tenderdash performs various operations, like
17/// creating new proposal or finalizing new block.
18pub struct CheckTxAbciApplication<C>
19where
20    C: CoreRPCLike + Send + Sync + 'static,
21{
22    /// Platform
23    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    /// Create new ABCI app
41    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    tonic::Status::internal(error.to_string())
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use crate::error::execution::ExecutionError;
105    use crate::rpc::core::MockCoreRPCLike;
106
107    #[test]
108    fn error_into_status_produces_internal_status() {
109        let error = Error::Execution(ExecutionError::CorruptedCodeExecution("test error message"));
110        let status = error_into_status(error);
111
112        assert_eq!(status.code(), tonic::Code::Internal);
113        assert!(status.message().contains("test error message"));
114    }
115
116    #[test]
117    fn error_into_status_preserves_error_message() {
118        let error = Error::Execution(ExecutionError::NotInTransaction("no active transaction"));
119        let status = error_into_status(error);
120
121        assert!(status.message().contains("no active transaction"));
122    }
123
124    #[test]
125    fn check_tx_abci_application_debug_format() {
126        // Verify the Debug implementation for CheckTxAbciApplication produces expected output
127        let platform =
128            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
129
130        let core_rpc = MockCoreRPCLike::new();
131
132        let app = CheckTxAbciApplication::new(Arc::new(platform.platform), Arc::new(core_rpc));
133
134        let debug_str = format!("{:?}", app);
135        assert_eq!(debug_str, "<CheckTxAbciApplication>");
136    }
137
138    #[test]
139    fn check_tx_abci_application_platform_returns_platform() {
140        let platform =
141            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
142
143        let core_rpc = MockCoreRPCLike::new();
144
145        let app = CheckTxAbciApplication::new(Arc::new(platform.platform), Arc::new(core_rpc));
146
147        // Just verify we can call platform() without panicking
148        let _platform_ref = app.platform();
149    }
150}