drive_abci/abci/app/
consensus.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 ConsensusAbciApplication<'a, C> {
20    /// Platform
21    platform: &'a Platform<C>,
22    /// The current GroveDb transaction
23    transaction: RwLock<Option<Transaction<'a>>>,
24    /// The current block execution context
25    block_execution_context: RwLock<Option<BlockExecutionContext>>,
26}
27
28impl<'a, C> ConsensusAbciApplication<'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 ConsensusAbciApplication<'_, C> {
40    fn platform(&self) -> &Platform<C> {
41        self.platform
42    }
43}
44
45impl<C> BlockExecutionApplication for ConsensusAbciApplication<'_, C> {
46    fn block_execution_context(&self) -> &RwLock<Option<BlockExecutionContext>> {
47        &self.block_execution_context
48    }
49}
50
51impl<'a, C> TransactionalApplication<'a> for ConsensusAbciApplication<'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 ConsensusAbciApplication<'_, C> {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        write!(f, "<ConsensusAbciApplication>")
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::rpc::core::MockCoreRPCLike;
90
91    #[test]
92    fn consensus_abci_application_debug_format() {
93        let platform =
94            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
95
96        let app = ConsensusAbciApplication::new(&platform.platform);
97
98        let debug_str = format!("{:?}", app);
99        assert_eq!(debug_str, "<ConsensusAbciApplication>");
100    }
101
102    #[test]
103    fn consensus_abci_application_platform_returns_reference() {
104        let platform =
105            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
106
107        let app = ConsensusAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
108        let _platform_ref = app.platform();
109    }
110
111    #[test]
112    fn consensus_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 = ConsensusAbciApplication::<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 consensus_abci_application_transaction_is_initially_none() {
124        let platform =
125            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
126
127        let app = ConsensusAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
128
129        let tx = app.transaction().read().unwrap();
130        assert!(tx.is_none());
131    }
132
133    #[test]
134    fn consensus_abci_application_start_transaction_creates_transaction() {
135        let platform =
136            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
137
138        let app = ConsensusAbciApplication::<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 consensus_abci_application_commit_without_transaction_fails() {
148        let platform =
149            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
150
151        let app = ConsensusAbciApplication::<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
159impl<C> tenderdash_abci::Application for ConsensusAbciApplication<'_, C>
160where
161    C: CoreRPCLike,
162{
163    fn info(
164        &self,
165        request: proto::RequestInfo,
166    ) -> Result<proto::ResponseInfo, proto::ResponseException> {
167        handler::info(self, request).map_err(error_into_exception)
168    }
169
170    fn init_chain(
171        &self,
172        request: proto::RequestInitChain,
173    ) -> Result<proto::ResponseInitChain, proto::ResponseException> {
174        handler::init_chain(self, request).map_err(error_into_exception)
175    }
176
177    fn query(
178        &self,
179        _request: proto::RequestQuery,
180    ) -> Result<proto::ResponseQuery, proto::ResponseException> {
181        unreachable!("query is not implemented for consensus ABCI application")
182    }
183
184    fn check_tx(
185        &self,
186        _request: proto::RequestCheckTx,
187    ) -> Result<proto::ResponseCheckTx, proto::ResponseException> {
188        unreachable!("check_tx is not implemented for consensus ABCI application")
189    }
190
191    fn extend_vote(
192        &self,
193        request: proto::RequestExtendVote,
194    ) -> Result<proto::ResponseExtendVote, proto::ResponseException> {
195        handler::extend_vote(self, request).map_err(error_into_exception)
196    }
197
198    fn finalize_block(
199        &self,
200        request: proto::RequestFinalizeBlock,
201    ) -> Result<proto::ResponseFinalizeBlock, proto::ResponseException> {
202        handler::finalize_block(self, request).map_err(error_into_exception)
203    }
204
205    fn prepare_proposal(
206        &self,
207        request: proto::RequestPrepareProposal,
208    ) -> Result<proto::ResponsePrepareProposal, proto::ResponseException> {
209        handler::prepare_proposal(self, request).map_err(error_into_exception)
210    }
211
212    fn process_proposal(
213        &self,
214        request: proto::RequestProcessProposal,
215    ) -> Result<proto::ResponseProcessProposal, proto::ResponseException> {
216        handler::process_proposal(self, request).map_err(error_into_exception)
217    }
218
219    fn verify_vote_extension(
220        &self,
221        request: proto::RequestVerifyVoteExtension,
222    ) -> Result<proto::ResponseVerifyVoteExtension, proto::ResponseException> {
223        handler::verify_vote_extension(self, request).map_err(error_into_exception)
224    }
225}