Skip to main content

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::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 ConsensusAbciApplication<'a, C> {
21    /// Platform
22    platform: &'a Platform<C>,
23    /// The current GroveDb transaction
24    transaction: RwLock<Option<Transaction<'a>>>,
25    /// The current block execution context
26    block_execution_context: RwLock<Option<BlockExecutionContext>>,
27    /// The unsigned withdrawal transactions of every proposal accepted at the current height
28    unsigned_withdrawal_txs_by_round: RwLock<UnsignedWithdrawalTxsByRound>,
29}
30
31impl<'a, C> ConsensusAbciApplication<'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 ConsensusAbciApplication<'_, C> {
44    fn platform(&self) -> &Platform<C> {
45        self.platform
46    }
47}
48
49impl<C> BlockExecutionApplication for ConsensusAbciApplication<'_, 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 ConsensusAbciApplication<'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 ConsensusAbciApplication<'_, C> {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        write!(f, "<ConsensusAbciApplication>")
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use crate::rpc::core::MockCoreRPCLike;
98
99    #[test]
100    fn consensus_abci_application_debug_format() {
101        let platform =
102            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
103
104        let app = ConsensusAbciApplication::new(&platform.platform);
105
106        let debug_str = format!("{:?}", app);
107        assert_eq!(debug_str, "<ConsensusAbciApplication>");
108    }
109
110    #[test]
111    fn consensus_abci_application_platform_returns_reference() {
112        let platform =
113            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
114
115        let app = ConsensusAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
116        let _platform_ref = app.platform();
117    }
118
119    #[test]
120    fn consensus_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 = ConsensusAbciApplication::<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 consensus_abci_application_transaction_is_initially_none() {
132        let platform =
133            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
134
135        let app = ConsensusAbciApplication::<MockCoreRPCLike>::new(&platform.platform);
136
137        let tx = app.transaction().read().unwrap();
138        assert!(tx.is_none());
139    }
140
141    #[test]
142    fn consensus_abci_application_start_transaction_creates_transaction() {
143        let platform =
144            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
145
146        let app = ConsensusAbciApplication::<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 consensus_abci_application_commit_without_transaction_fails() {
156        let platform =
157            crate::test::helpers::setup::TestPlatformBuilder::new().build_with_mock_rpc();
158
159        let app = ConsensusAbciApplication::<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
167impl<C> tenderdash_abci::Application for ConsensusAbciApplication<'_, C>
168where
169    C: CoreRPCLike,
170{
171    fn info(
172        &self,
173        request: proto::RequestInfo,
174    ) -> Result<proto::ResponseInfo, proto::ResponseException> {
175        handler::info(self, request).map_err(error_into_exception)
176    }
177
178    fn init_chain(
179        &self,
180        request: proto::RequestInitChain,
181    ) -> Result<proto::ResponseInitChain, proto::ResponseException> {
182        handler::init_chain(self, request).map_err(error_into_exception)
183    }
184
185    fn query(
186        &self,
187        _request: proto::RequestQuery,
188    ) -> Result<proto::ResponseQuery, proto::ResponseException> {
189        unreachable!("query is not implemented for consensus ABCI application")
190    }
191
192    fn check_tx(
193        &self,
194        _request: proto::RequestCheckTx,
195    ) -> Result<proto::ResponseCheckTx, proto::ResponseException> {
196        unreachable!("check_tx is not implemented for consensus ABCI application")
197    }
198
199    fn extend_vote(
200        &self,
201        request: proto::RequestExtendVote,
202    ) -> Result<proto::ResponseExtendVote, proto::ResponseException> {
203        handler::extend_vote(self, request).map_err(error_into_exception)
204    }
205
206    fn finalize_block(
207        &self,
208        request: proto::RequestFinalizeBlock,
209    ) -> Result<proto::ResponseFinalizeBlock, proto::ResponseException> {
210        handler::finalize_block(self, request).map_err(error_into_exception)
211    }
212
213    fn prepare_proposal(
214        &self,
215        request: proto::RequestPrepareProposal,
216    ) -> Result<proto::ResponsePrepareProposal, proto::ResponseException> {
217        handler::prepare_proposal(self, request).map_err(error_into_exception)
218    }
219
220    fn process_proposal(
221        &self,
222        request: proto::RequestProcessProposal,
223    ) -> Result<proto::ResponseProcessProposal, proto::ResponseException> {
224        handler::process_proposal(self, request).map_err(error_into_exception)
225    }
226
227    fn verify_vote_extension(
228        &self,
229        request: proto::RequestVerifyVoteExtension,
230    ) -> Result<proto::ResponseVerifyVoteExtension, proto::ResponseException> {
231        handler::verify_vote_extension(self, request).map_err(error_into_exception)
232    }
233}