drive_abci/abci/app/
full.rs

1use crate::abci::app::{BlockExecutionApplication, PlatformApplication, TransactionalApplication};
2use crate::abci::handler;
3use crate::abci::handler::error::error_into_exception;
4use crate::error::execution::ExecutionError;
5use crate::error::Error;
6use crate::execution::types::block_execution_context::BlockExecutionContext;
7use crate::platform_types::platform::Platform;
8use crate::rpc::core::CoreRPCLike;
9use dpp::version::PlatformVersion;
10use drive::grovedb::Transaction;
11use std::fmt::Debug;
12use std::sync::RwLock;
13use tenderdash_abci::proto::abci as proto;
14
15/// AbciApp is an implementation of ABCI Application, as defined by Tenderdash.
16///
17/// AbciApp implements logic that should be triggered when Tenderdash performs various operations, like
18/// creating new proposal or finalizing new block.
19pub struct FullAbciApplication<'a, C> {
20    /// Platform
21    pub platform: &'a Platform<C>,
22    /// The current GroveDB transaction
23    pub transaction: RwLock<Option<Transaction<'a>>>,
24    /// The current block execution context
25    pub block_execution_context: RwLock<Option<BlockExecutionContext>>,
26}
27
28impl<'a, C> FullAbciApplication<'a, C> {
29    /// Create new ABCI app
30    pub fn new(platform: &'a Platform<C>) -> Self {
31        Self {
32            platform,
33            transaction: Default::default(),
34            block_execution_context: Default::default(),
35        }
36    }
37}
38
39impl<C> PlatformApplication<C> for FullAbciApplication<'_, C> {
40    fn platform(&self) -> &Platform<C> {
41        self.platform
42    }
43}
44
45impl<C> BlockExecutionApplication for FullAbciApplication<'_, C> {
46    fn block_execution_context(&self) -> &RwLock<Option<BlockExecutionContext>> {
47        &self.block_execution_context
48    }
49}
50
51impl<'a, C> TransactionalApplication<'a> for FullAbciApplication<'a, C> {
52    /// create and store a new transaction
53    fn start_transaction(&self) {
54        let transaction = self.platform.drive.grove.start_transaction();
55        self.transaction.write().unwrap().replace(transaction);
56    }
57
58    fn transaction(&self) -> &RwLock<Option<Transaction<'a>>> {
59        &self.transaction
60    }
61
62    /// Commit a transaction
63    fn commit_transaction(&self, platform_version: &PlatformVersion) -> Result<(), Error> {
64        let transaction = self
65            .transaction
66            .write()
67            .unwrap()
68            .take()
69            .ok_or(Error::Execution(ExecutionError::NotInTransaction(
70                "trying to commit a transaction, but we are not in one",
71            )))?;
72
73        self.platform
74            .drive
75            .commit_transaction(transaction, &platform_version.drive)
76            .map_err(Error::Drive)
77    }
78}
79
80impl<C> Debug for FullAbciApplication<'_, C> {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        write!(f, "<FullAbciApplication>")
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::rpc::core::MockCoreRPCLike;
90
91    #[test]
92    fn full_abci_application_debug_format() {
93        let platform =
94            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
95
96        let app = FullAbciApplication::new(&platform.platform);
97
98        let debug_str = format!("{:?}", app);
99        assert_eq!(debug_str, "<FullAbciApplication>");
100    }
101
102    #[test]
103    fn full_abci_application_platform_returns_reference() {
104        let platform =
105            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
106
107        let app = FullAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
108        let _platform_ref = app.platform();
109    }
110
111    #[test]
112    fn full_abci_application_block_execution_context_is_initially_none() {
113        let platform =
114            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
115
116        let app = FullAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
117
118        let ctx = app.block_execution_context().read().unwrap();
119        assert!(ctx.is_none());
120    }
121
122    #[test]
123    fn full_abci_application_transaction_is_initially_none() {
124        let platform =
125            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
126
127        let app = FullAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
128
129        let tx = app.transaction().read().unwrap();
130        assert!(tx.is_none());
131    }
132
133    #[test]
134    fn full_abci_application_start_transaction_creates_transaction() {
135        let platform =
136            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
137
138        let app = FullAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
139
140        app.start_transaction();
141
142        let tx = app.transaction().read().unwrap();
143        assert!(tx.is_some());
144    }
145
146    #[test]
147    fn full_abci_application_commit_without_transaction_fails() {
148        let platform =
149            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
150
151        let app = FullAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
152        let platform_version = PlatformVersion::latest();
153
154        let result = app.commit_transaction(platform_version);
155        assert!(result.is_err());
156    }
157
158    #[test]
159    fn full_abci_application_start_and_commit_transaction_succeeds() {
160        let platform =
161            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
162
163        let app = FullAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
164        let platform_version = PlatformVersion::latest();
165
166        app.start_transaction();
167
168        let result = app.commit_transaction(platform_version);
169        assert!(result.is_ok());
170
171        // After commit, transaction should be consumed
172        let tx = app.transaction().read().unwrap();
173        assert!(tx.is_none());
174    }
175}
176
177impl<C> tenderdash_abci::Application for FullAbciApplication<'_, C>
178where
179    C: CoreRPCLike,
180{
181    fn info(
182        &self,
183        request: proto::RequestInfo,
184    ) -> Result<proto::ResponseInfo, proto::ResponseException> {
185        handler::info(self, request).map_err(error_into_exception)
186    }
187
188    fn init_chain(
189        &self,
190        request: proto::RequestInitChain,
191    ) -> Result<proto::ResponseInitChain, proto::ResponseException> {
192        handler::init_chain(self, request).map_err(error_into_exception)
193    }
194
195    fn query(
196        &self,
197        _request: proto::RequestQuery,
198    ) -> Result<proto::ResponseQuery, proto::ResponseException> {
199        unreachable!("query is not supported in full ABCI application")
200    }
201
202    fn check_tx(
203        &self,
204        request: proto::RequestCheckTx,
205    ) -> Result<proto::ResponseCheckTx, proto::ResponseException> {
206        handler::check_tx(self.platform, &self.platform.core_rpc, request)
207            .map_err(error_into_exception)
208    }
209
210    fn extend_vote(
211        &self,
212        request: proto::RequestExtendVote,
213    ) -> Result<proto::ResponseExtendVote, proto::ResponseException> {
214        handler::extend_vote(self, request).map_err(error_into_exception)
215    }
216
217    fn finalize_block(
218        &self,
219        request: proto::RequestFinalizeBlock,
220    ) -> Result<proto::ResponseFinalizeBlock, proto::ResponseException> {
221        handler::finalize_block(self, request).map_err(error_into_exception)
222    }
223
224    fn prepare_proposal(
225        &self,
226        request: proto::RequestPrepareProposal,
227    ) -> Result<proto::ResponsePrepareProposal, proto::ResponseException> {
228        handler::prepare_proposal(self, request).map_err(error_into_exception)
229    }
230
231    fn process_proposal(
232        &self,
233        request: proto::RequestProcessProposal,
234    ) -> Result<proto::ResponseProcessProposal, proto::ResponseException> {
235        handler::process_proposal(self, request).map_err(error_into_exception)
236    }
237
238    fn verify_vote_extension(
239        &self,
240        request: proto::RequestVerifyVoteExtension,
241    ) -> Result<proto::ResponseVerifyVoteExtension, proto::ResponseException> {
242        handler::verify_vote_extension(self, request).map_err(error_into_exception)
243    }
244}