Skip to main content

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