penumbra_sdk_mock_consensus/
abci.rs

1//! [`TestNode`] interfaces for sending consensus requests to an ABCI application.
2
3use {
4    super::TestNode,
5    anyhow::{anyhow, Context},
6    bytes::Bytes,
7    tap::{Tap, TapFallible},
8    tendermint::{
9        abci::types::CommitInfo,
10        block::Header,
11        v0_37::abci::{request, response, ConsensusRequest, ConsensusResponse},
12    },
13    tower::{BoxError, Service},
14    tracing::{error, instrument, trace},
15};
16
17/// ABCI-related interfaces.
18impl<C> TestNode<C>
19where
20    C: Service<ConsensusRequest, Response = ConsensusResponse, Error = BoxError>
21        + Send
22        + Clone
23        + 'static,
24    C::Future: Send + 'static,
25    C::Error: Sized,
26{
27    /// Yields a mutable reference to the consensus service when it is ready to accept a request.
28    async fn service(&mut self) -> Result<&mut C, anyhow::Error> {
29        use tower::ServiceExt;
30        self.consensus
31            .ready()
32            .tap(|_| trace!("waiting for consensus service"))
33            .await
34            .tap_err(|error| error!(?error, "failed waiting for consensus service"))
35            .map_err(|_| anyhow!("failed waiting for consensus service"))
36            .tap_ok(|_| trace!("consensus service is now ready"))
37    }
38
39    /// Sends a [`ConsensusRequest::BeginBlock`] request to the ABCI application.
40    #[instrument(level = "debug", skip_all)]
41    pub async fn begin_block(
42        &mut self,
43        header: Header,
44        last_commit_info: CommitInfo,
45    ) -> Result<response::BeginBlock, anyhow::Error> {
46        let request = ConsensusRequest::BeginBlock(request::BeginBlock {
47            hash: tendermint::Hash::None,
48            header,
49            last_commit_info,
50            byzantine_validators: Default::default(),
51        });
52        let service = self.service().await?;
53        match service
54            .tap(|_| trace!("sending BeginBlock request"))
55            .call(request)
56            .await
57            .tap_err(|error| error!(?error, "consensus service returned error"))
58            .map_err(|_| anyhow!("consensus service returned error"))?
59        {
60            ConsensusResponse::BeginBlock(response) => {
61                let response::BeginBlock { events } = &response;
62                trace!(?events, "received BeginBlock events");
63                Ok(response)
64            }
65            response => {
66                error!(?response, "unexpected InitChain response");
67                Err(anyhow!("unexpected InitChain response"))
68            }
69        }
70    }
71
72    /// Sends a [`ConsensusRequest::DeliverTx`] request to the ABCI application.
73    #[instrument(level = "debug", skip_all)]
74    pub async fn deliver_tx(&mut self, tx: Bytes) -> Result<response::DeliverTx, anyhow::Error> {
75        let request = ConsensusRequest::DeliverTx(request::DeliverTx { tx });
76        let service = self.service().await?;
77        match service
78            .tap(|_| trace!("sending DeliverTx request"))
79            .call(request)
80            .await
81            .tap_err(|error| error!(?error, "consensus service returned error"))
82            .map_err(|_| anyhow!("consensus service returned error"))?
83        {
84            ConsensusResponse::DeliverTx(response) => {
85                let response::DeliverTx {
86                    code,
87                    gas_used,
88                    gas_wanted,
89                    events,
90                    ..
91                } = &response;
92                trace!(
93                    ?code,
94                    ?gas_used,
95                    ?gas_wanted,
96                    ?events,
97                    "received DeliverTx response"
98                );
99                Ok(response)
100            }
101            response => {
102                error!(?response, "unexpected DeliverTx response");
103                Err(anyhow!("unexpected DeliverTx response"))
104            }
105        }
106    }
107
108    /// Sends a [`ConsensusRequest::EndBlock`] request to the ABCI application.
109    #[instrument(level = "debug", skip_all)]
110    pub async fn end_block(&mut self) -> Result<response::EndBlock, anyhow::Error> {
111        let height = self
112            .height
113            .value()
114            .try_into()
115            .context("converting height into `i64`")?;
116        let request = ConsensusRequest::EndBlock(request::EndBlock { height });
117
118        let service = self.service().await?;
119        match service
120            .call(request)
121            .await
122            .tap_err(|error| error!(?error, "consensus service returned error"))
123            .map_err(|_| anyhow!("consensus service returned error"))?
124        {
125            ConsensusResponse::EndBlock(response) => {
126                let response::EndBlock {
127                    validator_updates,
128                    consensus_param_updates,
129                    events,
130                } = &response;
131                trace!(
132                    ?validator_updates,
133                    ?consensus_param_updates,
134                    ?events,
135                    "received EndBlock response"
136                );
137                Ok(response)
138            }
139            response => {
140                error!(?response, "unexpected EndBlock response");
141                Err(anyhow!("unexpected EndBlock response"))
142            }
143        }
144    }
145
146    /// Sends a [`ConsensusRequest::Commit`] request to the ABCI application.
147    #[instrument(level = "debug", skip_all)]
148    pub async fn commit(&mut self) -> Result<response::Commit, anyhow::Error> {
149        let request = ConsensusRequest::Commit;
150        let service = self.service().await?;
151        match service
152            .call(request)
153            .await
154            .tap_err(|error| error!(?error, "consensus service returned error"))
155            .map_err(|_| anyhow!("consensus service returned error"))?
156        {
157            ConsensusResponse::Commit(response) => {
158                let response::Commit {
159                    data,
160                    retain_height,
161                } = &response;
162                trace!(?data, ?retain_height, "received Commit response");
163
164                Ok(response)
165            }
166            response => {
167                error!(?response, "unexpected Commit response");
168                Err(anyhow!("unexpected Commit response"))
169            }
170        }
171    }
172}