1use tenderdash_proto::abci::{ExecTxResult, ValidatorSetUpdate};
4use tracing::{debug, error};
5
6use crate::proto::{
7 abci,
8 abci::{request, response},
9};
10
11pub trait Application {
13 fn echo(
15 &self,
16 request: abci::RequestEcho,
17 ) -> Result<abci::ResponseEcho, abci::ResponseException> {
18 Ok(abci::ResponseEcho {
19 message: request.message,
20 })
21 }
22
23 fn flush(
26 &self,
27 _request: abci::RequestFlush,
28 ) -> Result<abci::ResponseFlush, abci::ResponseException> {
29 Ok(Default::default())
30 }
31
32 fn info(
34 &self,
35 request: abci::RequestInfo,
36 ) -> Result<abci::ResponseInfo, abci::ResponseException> {
37 if !check_version(&request.abci_version) {
38 return Err(abci::ResponseException {
39 error: format!(
40 "version mismatch: tenderdash {} vs our {}",
41 request.version,
42 crate::proto::ABCI_VERSION
43 ),
44 });
45 }
46
47 Ok(Default::default())
48 }
49
50 fn init_chain(
52 &self,
53 _request: abci::RequestInitChain,
54 ) -> Result<abci::ResponseInitChain, abci::ResponseException> {
55 Ok(Default::default())
56 }
57
58 fn query(
60 &self,
61 _request: abci::RequestQuery,
62 ) -> Result<abci::ResponseQuery, abci::ResponseException> {
63 Ok(Default::default())
64 }
65
66 fn check_tx(
68 &self,
69 _request: abci::RequestCheckTx,
70 ) -> Result<abci::ResponseCheckTx, abci::ResponseException> {
71 Ok(Default::default())
72 }
73
74 fn list_snapshots(
76 &self,
77 _request: abci::RequestListSnapshots,
78 ) -> Result<abci::ResponseListSnapshots, abci::ResponseException> {
79 Ok(Default::default())
80 }
81
82 fn offer_snapshot(
84 &self,
85 _request: abci::RequestOfferSnapshot,
86 ) -> Result<abci::ResponseOfferSnapshot, abci::ResponseException> {
87 Ok(Default::default())
88 }
89
90 fn load_snapshot_chunk(
92 &self,
93 _request: abci::RequestLoadSnapshotChunk,
94 ) -> Result<abci::ResponseLoadSnapshotChunk, abci::ResponseException> {
95 Ok(Default::default())
96 }
97
98 fn apply_snapshot_chunk(
100 &self,
101 _request: abci::RequestApplySnapshotChunk,
102 ) -> Result<abci::ResponseApplySnapshotChunk, abci::ResponseException> {
103 Ok(Default::default())
104 }
105
106 fn extend_vote(
107 &self,
108 _request: abci::RequestExtendVote,
109 ) -> Result<abci::ResponseExtendVote, abci::ResponseException> {
110 Ok(Default::default())
111 }
112
113 fn finalize_block(
114 &self,
115 _request: abci::RequestFinalizeBlock,
116 ) -> Result<abci::ResponseFinalizeBlock, abci::ResponseException> {
117 Ok(Default::default())
118 }
119
120 fn prepare_proposal(
121 &self,
122 _request: abci::RequestPrepareProposal,
123 ) -> Result<abci::ResponsePrepareProposal, abci::ResponseException> {
124 Ok(Default::default())
125 }
126
127 fn process_proposal(
128 &self,
129 _request: abci::RequestProcessProposal,
130 ) -> Result<abci::ResponseProcessProposal, abci::ResponseException> {
131 Ok(Default::default())
132 }
133
134 fn verify_vote_extension(
135 &self,
136 _request: abci::RequestVerifyVoteExtension,
137 ) -> Result<abci::ResponseVerifyVoteExtension, abci::ResponseException> {
138 Ok(Default::default())
139 }
140}
141
142pub trait RequestDispatcher {
143 fn handle(&self, request: abci::Request) -> Option<abci::Response>;
149}
150
151impl<A: Application> RequestDispatcher for A {
153 fn handle(&self, request: abci::Request) -> Option<abci::Response> {
154 #[cfg(feature = "tracing-span")]
155 let _span = super::tracing_span::span(request.clone().value?);
156 tracing::trace!(?request, "received ABCI request");
157
158 let response: response::Value = match request.value? {
159 request::Value::Echo(req) => self.echo(req).map(|v| v.into()),
160 request::Value::Flush(req) => self.flush(req).map(|v| v.into()),
161 request::Value::Info(req) => self.info(req).map(|v| v.into()),
162 request::Value::InitChain(req) => self.init_chain(req).map(|v| v.into()),
163 request::Value::Query(req) => self.query(req).map(|v| v.into()),
164 request::Value::CheckTx(req) => self.check_tx(req).map(|v| v.into()),
165 request::Value::OfferSnapshot(req) => self.offer_snapshot(req).map(|v| v.into()),
166 request::Value::LoadSnapshotChunk(req) => {
167 self.load_snapshot_chunk(req).map(|v| v.into())
168 },
169 request::Value::ApplySnapshotChunk(req) => {
170 self.apply_snapshot_chunk(req).map(|v| v.into())
171 },
172 request::Value::ListSnapshots(req) => self.list_snapshots(req).map(|v| v.into()),
173 request::Value::PrepareProposal(req) => self.prepare_proposal(req).map(|v| v.into()),
174 request::Value::ProcessProposal(req) => self.process_proposal(req).map(|v| v.into()),
175 request::Value::FinalizeBlock(req) => self.finalize_block(req).map(|v| v.into()),
176 request::Value::ExtendVote(req) => self.extend_vote(req).map(|v| v.into()),
177 request::Value::VerifyVoteExtension(req) => {
178 self.verify_vote_extension(req).map(|v| v.into())
179 },
180 }
181 .unwrap_or_else(|e| e.into());
182
183 if let response::Value::Exception(_) = response {
184 tracing::error!(?response, "sending ABCI exception");
185 } else {
186 let response_log = serialize_response_for_logging(&response);
187 tracing::trace!(?response_log, "sending ABCI response");
188 };
189
190 Some(abci::Response {
191 value: Some(response),
192 })
193 }
194}
195
196macro_rules! serialize {
202 ($($key:expr => $value:expr),* $(,)?) => {
203 {
204 #[cfg(feature = "serde")]
205 {
206 serde_json::json!({ $($key: $value),* }).to_string()
207 }
208
209 #[cfg(not(feature = "serde"))]
210 {
211 format!(stringify!($($key " {:?}",)*), $($value,)*)
212 }
213 }
214 };
215}
216
217fn serialize_response_for_logging(response: &response::Value) -> String {
218 match response {
219 response::Value::PrepareProposal(response) => {
220 let tx_records_hex: Vec<String> = response
221 .tx_records
222 .iter()
223 .map(|tx_record| {
224 let tx_hex = hex::encode(&tx_record.tx);
226 serialize!(
227 "action" => tx_record.action, "tx" => tx_hex,
229 )
230 .to_string()
231 })
232 .collect();
233
234 let app_hash_hex = hex::encode(&response.app_hash);
235
236 let tx_results_hex: Vec<String> = exec_tx_results_to_string(&response.tx_results);
237
238 let consensus_params = format!("{:?}", response.consensus_param_updates);
239
240 let validator_set_update =
241 validator_set_update_to_string(response.validator_set_update.as_ref());
242
243 serialize!(
244 "tx_records" => tx_records_hex,
245 "app_hash" => app_hash_hex,
246 "tx_results" => tx_results_hex,
247 "consensus_param_updates" => consensus_params,
248 "core_chain_lock_update" => response.core_chain_lock_update,
249 "validator_set_update" => validator_set_update,
250 )
251 .to_string()
252 },
253 response::Value::ProcessProposal(response) => {
254 let status_string = match response.status {
255 0 => "Unknown",
256 1 => "Accepted",
257 2 => "Rejected",
258 _ => "Unknown(too high)",
259 };
260
261 let app_hash_hex = hex::encode(&response.app_hash);
262
263 let tx_results_hex: Vec<String> = exec_tx_results_to_string(&response.tx_results);
264
265 let consensus_params = format!("{:?}", response.consensus_param_updates);
266
267 let validator_set_update =
268 validator_set_update_to_string(response.validator_set_update.as_ref());
269
270 serialize!(
271 "status" => status_string,
272 "app_hash" => app_hash_hex,
273 "tx_results" => tx_results_hex,
274 "consensus_param_updates" => consensus_params,
275 "validator_set_update" => validator_set_update,
276 )
277 .to_string()
278 },
279
280 value => format!("{:?}", value),
281 }
282}
283
284fn exec_tx_results_to_string(tx_results: &[ExecTxResult]) -> Vec<String> {
285 tx_results
286 .iter()
287 .map(|tx_result| {
288 let data_hex = hex::encode(&tx_result.data);
289
290 let events_serialized = format!("{:?}", tx_result.events);
294
295 serialize!(
296 "code" => tx_result.code,
297 "data" =>data_hex,
298 "log" => tx_result.log,
299 "info" => tx_result.info,
300 "gas_used" => tx_result.gas_used,
301 "events" => events_serialized,
302 "codespace" => tx_result.codespace,
303 )
304 .to_string()
305 })
306 .collect()
307}
308
309fn validator_set_update_to_string(validator_set_update: Option<&ValidatorSetUpdate>) -> String {
311 validator_set_update
312 .as_ref()
313 .map(|validator_set_update| {
314 let quorum_hash_hex = hex::encode(&validator_set_update.quorum_hash);
315
316 let validator_updates_string: Vec<String> = validator_set_update
317 .validator_updates
318 .iter()
319 .map(|validator_update| {
320 let pro_tx_hash_hex = hex::encode(&validator_update.pro_tx_hash);
321 serialize!(
322 "pub_key" => validator_update.pub_key,
323 "power" => validator_update.power,
324 "pro_tx_hash" => pro_tx_hash_hex,
325 "node_address" => validator_update.node_address,
326 )
327 .to_string()
328 })
329 .collect();
330 serialize!(
331 "validator_updates" => validator_updates_string,
332 "threshold_public_key" => validator_set_update.threshold_public_key,
333 "quorum_hash" => quorum_hash_hex,
334 )
335 .to_string()
336 })
337 .unwrap_or("None".to_string())
338}
339
340pub fn check_version(tenderdash_version: &str) -> bool {
374 match_versions(tenderdash_version, tenderdash_proto::ABCI_VERSION)
375}
376
377fn match_versions(tenderdash_version: &str, rs_tenderdash_abci_version: &str) -> bool {
388 let rs_tenderdash_abci_version = semver::Version::parse(rs_tenderdash_abci_version)
389 .expect("cannot parse protobuf library version");
390 let tenderdash_version =
391 semver::Version::parse(tenderdash_version).expect("cannot parse tenderdash version");
392
393 let requirement = match rs_tenderdash_abci_version.pre.as_str() {
394 "" => format!(
395 "^{}.{}",
396 rs_tenderdash_abci_version.major, rs_tenderdash_abci_version.minor
397 ),
398 pre => format!(
399 "^{}.{}.0-{}",
400 rs_tenderdash_abci_version.major, rs_tenderdash_abci_version.minor, pre
401 ),
402 };
403
404 let matcher = semver::VersionReq::parse(&requirement).expect("cannot parse tenderdash version");
405
406 match matcher.matches(&tenderdash_version) {
407 true => {
408 debug!(
409 "version match(rs-tenderdash-abci proto version: {}), tenderdash server proto version {} = {}",
410 rs_tenderdash_abci_version, tenderdash_version, requirement
411 );
412 true
413 },
414 false => {
415 error!(
416 "version mismatch(rs-tenderdash-abci proto version: {}), tenderdash server proto version {} != {}",
417 rs_tenderdash_abci_version, tenderdash_version, requirement
418 );
419 false
420 },
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::match_versions;
427
428 fn setup_logs() {
429 #[cfg(feature = "server")]
430 tracing_subscriber::fmt()
431 .with_env_filter(tracing_subscriber::EnvFilter::new("trace"))
432 .try_init()
433 .ok();
434 }
435
436 macro_rules! test_versions {
446 ($($name:ident: $value:expr,)*) => {
447 $(
448 #[test]
449 fn $name() {
450 setup_logs();
451 let (td, our, expect) = $value;
452 assert_eq!(match_versions(td, our),expect,
453 "tenderdash version: {}, rs-tenderdash-abci version: {}, expect: {}", td, our,expect);
454 }
455 )*
456 }
457 }
458
459 test_versions! {
460 test_major_0: ("0.23.1", "0.23.1", true),
470 test_major_0_old_minor: ("0.23.1", "0.22.1", false),
472 test_major_0_new_patch: ("0.23.2", "0.23.1", true),
474 test_major_0_old_patch: ("0.23.0", "0.23.1", true),
476 test_major_0_new_minor: ("0.23.1", "0.24.1", false),
478 test_major_0_new_major: ("0.23.1", "1.23.1", false),
479
480 test_major_1: ("1.23.1", "1.23.1", true),
483 test_major_1_old_minor: ("1.23.1", "1.22.1", true),
485 test_major_1_new_patch: ("1.23.2", "1.23.1", true),
487 test_major_1_old_patch: ("1.23.0", "1.23.1", true),
489 test_major_1_new_minor: ("1.23.1", "1.24.1", false),
491 test_major_1_old_major: ("1.23.1", "0.23.1", false),
492
493 test_dev_td_newer: ("0.1.2-dev.1", "0.1.0", false),
494 test_dev_equal: ("0.1.0-dev.1","0.1.0-dev.1",true),
495 test_dev_our_newer_dev: ("0.1.0-dev.1", "0.1.0-dev.2",false),
496 }
497}