diff --git a/.changelog/unreleased/breaking-changes/1203-bytes-for-abci-proto.md b/.changelog/unreleased/breaking-changes/1203-bytes-for-abci-proto.md new file mode 100644 index 000000000..c06a80b7f --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1203-bytes-for-abci-proto.md @@ -0,0 +1,2 @@ +- `[tendermint-proto]` Use `Bytes` for byte array fields of ABCI protobuf types. + ([#1203](https://github.com/informalsystems/tendermint-rs/pull/1203)) diff --git a/.changelog/unreleased/breaking-changes/862-abci-domain-types.md b/.changelog/unreleased/breaking-changes/862-abci-domain-types.md new file mode 100644 index 000000000..220b52dc6 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/862-abci-domain-types.md @@ -0,0 +1,2 @@ +- `[tendermint]` Added domain types for ABCI + ([#862](https://github.com/informalsystems/tendermint-rs/issues/862)) \ No newline at end of file diff --git a/abci/src/application.rs b/abci/src/application.rs index a3c025a56..193307832 100644 --- a/abci/src/application.rs +++ b/abci/src/application.rs @@ -8,11 +8,11 @@ pub mod kvstore; use tendermint_proto::abci::{ request::Value, response, Request, RequestApplySnapshotChunk, RequestBeginBlock, RequestCheckTx, RequestDeliverTx, RequestEcho, RequestEndBlock, RequestInfo, RequestInitChain, - RequestLoadSnapshotChunk, RequestOfferSnapshot, RequestQuery, RequestSetOption, Response, + RequestLoadSnapshotChunk, RequestOfferSnapshot, RequestQuery, Response, ResponseApplySnapshotChunk, ResponseBeginBlock, ResponseCheckTx, ResponseCommit, ResponseDeliverTx, ResponseEcho, ResponseEndBlock, ResponseFlush, ResponseInfo, ResponseInitChain, ResponseListSnapshots, ResponseLoadSnapshotChunk, ResponseOfferSnapshot, - ResponseQuery, ResponseSetOption, + ResponseQuery, }; /// An ABCI application. @@ -76,12 +76,6 @@ pub trait Application: Send + Clone + 'static { Default::default() } - /// Allows the Tendermint node to request that the application set an - /// option to a particular value. - fn set_option(&self, _request: RequestSetOption) -> ResponseSetOption { - Default::default() - } - /// Used during state sync to discover available snapshots on peers. fn list_snapshots(&self) -> ResponseListSnapshots { Default::default() @@ -123,7 +117,6 @@ impl RequestDispatcher for A { Value::Echo(req) => response::Value::Echo(self.echo(req)), Value::Flush(_) => response::Value::Flush(self.flush()), Value::Info(req) => response::Value::Info(self.info(req)), - Value::SetOption(req) => response::Value::SetOption(self.set_option(req)), Value::InitChain(req) => response::Value::InitChain(self.init_chain(req)), Value::Query(req) => response::Value::Query(self.query(req)), Value::BeginBlock(req) => response::Value::BeginBlock(self.begin_block(req)), @@ -141,6 +134,7 @@ impl RequestDispatcher for A { Value::ApplySnapshotChunk(req) => { response::Value::ApplySnapshotChunk(self.apply_snapshot_chunk(req)) }, + Value::SetOption(_) => response::Value::SetOption(Default::default()), }), } } diff --git a/abci/src/application/kvstore.rs b/abci/src/application/kvstore.rs index 33aa15eff..62c117a44 100644 --- a/abci/src/application/kvstore.rs +++ b/abci/src/application/kvstore.rs @@ -12,10 +12,7 @@ use tendermint_proto::abci::{ }; use tracing::{debug, info}; -use crate::{ - codec::{encode_varint, MAX_VARINT_LENGTH}, - Application, Error, -}; +use crate::{codec::MAX_VARINT_LENGTH, Application, Error}; /// In-memory, hashmap-backed key/value store ABCI application. /// @@ -50,7 +47,7 @@ use crate::{ /// // Deliver a transaction and then commit the transaction /// client /// .deliver_tx(RequestDeliverTx { -/// tx: "test-key=test-value".as_bytes().to_owned(), +/// tx: "test-key=test-value".into(), /// }) /// .unwrap(); /// client.commit().unwrap(); @@ -58,7 +55,7 @@ use crate::{ /// // We should be able to query for the data we just delivered above /// let res = client /// .query(RequestQuery { -/// data: "test-key".as_bytes().to_owned(), +/// data: "test-key".into(), /// path: "".to_string(), /// height: 0, /// prove: false, @@ -129,17 +126,17 @@ impl Application for KeyValueStoreApp { version: "0.1.0".to_string(), app_version: 1, last_block_height, - last_block_app_hash, + last_block_app_hash: last_block_app_hash.into(), } } fn query(&self, request: RequestQuery) -> ResponseQuery { - let key = match String::from_utf8(request.data.clone()) { + let key = match std::str::from_utf8(&request.data) { Ok(s) => s, Err(e) => panic!("Failed to intepret key as UTF-8: {}", e), }; debug!("Attempting to get key: {}", key); - match self.get(key.clone()) { + match self.get(key) { Ok((height, value_opt)) => match value_opt { Some(value) => ResponseQuery { code: 0, @@ -147,7 +144,7 @@ impl Application for KeyValueStoreApp { info: "".to_string(), index: 0, key: request.data, - value: value.into_bytes(), + value: value.into_bytes().into(), proof_ops: None, height, codespace: "".to_string(), @@ -158,7 +155,7 @@ impl Application for KeyValueStoreApp { info: "".to_string(), index: 0, key: request.data, - value: vec![], + value: Default::default(), proof_ops: None, height, codespace: "".to_string(), @@ -171,7 +168,7 @@ impl Application for KeyValueStoreApp { fn check_tx(&self, _request: RequestCheckTx) -> ResponseCheckTx { ResponseCheckTx { code: 0, - data: vec![], + data: Default::default(), log: "".to_string(), info: "".to_string(), gas_wanted: 1, @@ -183,17 +180,17 @@ impl Application for KeyValueStoreApp { } fn deliver_tx(&self, request: RequestDeliverTx) -> ResponseDeliverTx { - let tx = String::from_utf8(request.tx).unwrap(); + let tx = std::str::from_utf8(&request.tx).unwrap(); let tx_parts = tx.split('=').collect::>(); let (key, value) = if tx_parts.len() == 2 { (tx_parts[0], tx_parts[1]) } else { - (tx.as_ref(), tx.as_ref()) + (tx, tx) }; let _ = self.set(key, value).unwrap(); ResponseDeliverTx { code: 0, - data: vec![], + data: Default::default(), log: "".to_string(), info: "".to_string(), gas_wanted: 0, @@ -202,18 +199,18 @@ impl Application for KeyValueStoreApp { r#type: "app".to_string(), attributes: vec![ EventAttribute { - key: "key".as_bytes().to_owned(), - value: key.as_bytes().to_owned(), + key: "key".to_string().into_bytes().into(), + value: key.to_string().into_bytes().into(), index: true, }, EventAttribute { - key: "index_key".as_bytes().to_owned(), - value: "index is working".as_bytes().to_owned(), + key: "index_key".to_string().into_bytes().into(), + value: "index is working".to_string().into_bytes().into(), index: true, }, EventAttribute { - key: "noindex_key".as_bytes().to_owned(), - value: "index is working".as_bytes().to_owned(), + key: "noindex_key".to_string().into_bytes().into(), + value: "index is working".to_string().into_bytes().into(), index: false, }, ], @@ -228,7 +225,7 @@ impl Application for KeyValueStoreApp { let (height, app_hash) = channel_recv(&result_rx).unwrap(); info!("Committed height {}", height); ResponseCommit { - data: app_hash, + data: app_hash.into(), retain_height: height - 1, } } @@ -285,7 +282,7 @@ impl KeyValueStoreDriver { // As in the Go-based key/value store, simply encode the number of // items as the "app hash" let mut app_hash = BytesMut::with_capacity(MAX_VARINT_LENGTH); - encode_varint(self.store.len() as u64, &mut app_hash); + prost::encoding::encode_varint(self.store.len() as u64, &mut app_hash); self.app_hash = app_hash.to_vec(); self.height += 1; channel_send(&result_tx, (self.height, self.app_hash.clone())) diff --git a/abci/src/client.rs b/abci/src/client.rs index 3e9b1cce0..a7a82be44 100644 --- a/abci/src/client.rs +++ b/abci/src/client.rs @@ -6,10 +6,10 @@ use tendermint_proto::abci::{ request, response, Request, RequestApplySnapshotChunk, RequestBeginBlock, RequestCheckTx, RequestCommit, RequestDeliverTx, RequestEcho, RequestEndBlock, RequestFlush, RequestInfo, RequestInitChain, RequestListSnapshots, RequestLoadSnapshotChunk, RequestOfferSnapshot, - RequestQuery, RequestSetOption, ResponseApplySnapshotChunk, ResponseBeginBlock, - ResponseCheckTx, ResponseCommit, ResponseDeliverTx, ResponseEcho, ResponseEndBlock, - ResponseFlush, ResponseInfo, ResponseInitChain, ResponseListSnapshots, - ResponseLoadSnapshotChunk, ResponseOfferSnapshot, ResponseQuery, ResponseSetOption, + RequestQuery, ResponseApplySnapshotChunk, ResponseBeginBlock, ResponseCheckTx, ResponseCommit, + ResponseDeliverTx, ResponseEcho, ResponseEndBlock, ResponseFlush, ResponseInfo, + ResponseInitChain, ResponseListSnapshots, ResponseLoadSnapshotChunk, ResponseOfferSnapshot, + ResponseQuery, }; use crate::{codec::ClientCodec, Error}; @@ -113,11 +113,6 @@ impl Client { perform!(self, Commit, RequestCommit {}) } - /// Request that the application set an option to a particular value. - pub fn set_option(&mut self, req: RequestSetOption) -> Result { - perform!(self, SetOption, req) - } - /// Used during state sync to discover available snapshots on peers. pub fn list_snapshots(&mut self) -> Result { perform!(self, ListSnapshots, RequestListSnapshots {}) diff --git a/abci/src/codec.rs b/abci/src/codec.rs index ce85acd24..d9fd649c6 100644 --- a/abci/src/codec.rs +++ b/abci/src/codec.rs @@ -146,6 +146,7 @@ where Ok(len) => len, // We've potentially only received a partial length delimiter Err(_) if src_len <= MAX_VARINT_LENGTH => return Ok(None), + // Err(e) => return Err(Error::decode(e)), Err(e) => return Err(e), }; let remaining = tmp.remaining() as u64; diff --git a/abci/tests/kvstore_app.rs b/abci/tests/kvstore_app.rs index 21d48dfa1..05c821483 100644 --- a/abci/tests/kvstore_app.rs +++ b/abci/tests/kvstore_app.rs @@ -25,19 +25,19 @@ mod kvstore_app_integration { client .deliver_tx(RequestDeliverTx { - tx: "test-key=test-value".as_bytes().to_owned(), + tx: "test-key=test-value".into(), }) .unwrap(); client.commit().unwrap(); let res = client .query(RequestQuery { - data: "test-key".as_bytes().to_owned(), + data: "test-key".into(), path: "".to_string(), height: 0, prove: false, }) .unwrap(); - assert_eq!(res.value, "test-value".as_bytes().to_owned()); + assert_eq!(res.value, "test-value".as_bytes()); } } diff --git a/config/src/prelude.rs b/config/src/prelude.rs index a5b91aa13..aa9a20b65 100644 --- a/config/src/prelude.rs +++ b/config/src/prelude.rs @@ -1,7 +1,7 @@ // Re-export according to alloc::prelude::v1 because it is not yet stabilized // https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html -pub use alloc::borrow::ToOwned; pub use alloc::{ + borrow::ToOwned, boxed::Box, format, string::{String, ToString}, diff --git a/light-client-verifier/src/prelude.rs b/light-client-verifier/src/prelude.rs index 2aca81d15..57a0cedc3 100644 --- a/light-client-verifier/src/prelude.rs +++ b/light-client-verifier/src/prelude.rs @@ -1,7 +1,7 @@ // Re-export according to alloc::prelude::v1 because it is not yet stabilized // https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html -pub use alloc::borrow::ToOwned; pub use alloc::{ + borrow::ToOwned, boxed::Box, format, string::{String, ToString}, diff --git a/light-client/src/evidence.rs b/light-client/src/evidence.rs index 60f76271d..df7cddb94 100644 --- a/light-client/src/evidence.rs +++ b/light-client/src/evidence.rs @@ -1,8 +1,8 @@ //! Fork evidence data structures and interfaces. use contracts::contract_trait; -use tendermint::abci::transaction::Hash; pub use tendermint::evidence::Evidence; +use tendermint_rpc::abci::transaction::Hash; use crate::{components::io::IoError, verifier::types::PeerId}; diff --git a/light-client/src/tests.rs b/light-client/src/tests.rs index 4bae5517d..435cc3855 100644 --- a/light-client/src/tests.rs +++ b/light-client/src/tests.rs @@ -5,11 +5,11 @@ use std::{collections::HashMap, time::Duration}; use contracts::contract_trait; use serde::{Deserialize, Serialize}; use tendermint::{ - abci::transaction::Hash, block::Height as HeightStr, evidence::{Duration as DurationStr, Evidence}, }; use tendermint_rpc as rpc; +use tendermint_rpc::abci::transaction::Hash; use crate::{ components::{ diff --git a/proto/Cargo.toml b/proto/Cargo.toml index be7b5a9e2..99092b7f3 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -19,7 +19,7 @@ all-features = true [dependencies] prost = { version = "0.11", default-features = false } prost-types = { version = "0.11", default-features = false } -bytes = { version = "1.0", default-features = false } +bytes = { version = "1.0", default-features = false, features = ["serde"]} serde = { version = "1.0", default-features = false, features = ["derive"] } serde_bytes = { version = "0.11", default-features = false, features = ["alloc"] } subtle-encoding = { version = "0.5", default-features = false, features = ["hex", "base64", "alloc"] } diff --git a/proto/src/prelude.rs b/proto/src/prelude.rs index a5b91aa13..aa9a20b65 100644 --- a/proto/src/prelude.rs +++ b/proto/src/prelude.rs @@ -1,7 +1,7 @@ // Re-export according to alloc::prelude::v1 because it is not yet stabilized // https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html -pub use alloc::borrow::ToOwned; pub use alloc::{ + borrow::ToOwned, boxed::Box, format, string::{String, ToString}, diff --git a/proto/src/prost/tendermint.abci.rs b/proto/src/prost/tendermint.abci.rs index e2f493e6f..8e03b04e8 100644 --- a/proto/src/prost/tendermint.abci.rs +++ b/proto/src/prost/tendermint.abci.rs @@ -81,15 +81,15 @@ pub struct RequestInitChain { pub consensus_params: ::core::option::Option, #[prost(message, repeated, tag="4")] pub validators: ::prost::alloc::vec::Vec, - #[prost(bytes="vec", tag="5")] - pub app_state_bytes: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="5")] + pub app_state_bytes: ::prost::bytes::Bytes, #[prost(int64, tag="6")] pub initial_height: i64, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RequestQuery { - #[prost(bytes="vec", tag="1")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub data: ::prost::bytes::Bytes, #[prost(string, tag="2")] pub path: ::prost::alloc::string::String, #[prost(int64, tag="3")] @@ -99,8 +99,8 @@ pub struct RequestQuery { } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RequestBeginBlock { - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub hash: ::prost::bytes::Bytes, #[prost(message, optional, tag="2")] pub header: ::core::option::Option, #[prost(message, optional, tag="3")] @@ -110,15 +110,15 @@ pub struct RequestBeginBlock { } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RequestCheckTx { - #[prost(bytes="vec", tag="1")] - pub tx: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub tx: ::prost::bytes::Bytes, #[prost(enumeration="CheckTxType", tag="2")] pub r#type: i32, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RequestDeliverTx { - #[prost(bytes="vec", tag="1")] - pub tx: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub tx: ::prost::bytes::Bytes, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RequestEndBlock { @@ -139,8 +139,8 @@ pub struct RequestOfferSnapshot { #[prost(message, optional, tag="1")] pub snapshot: ::core::option::Option, /// light client-verified app hash for snapshot height - #[prost(bytes="vec", tag="2")] - pub app_hash: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="2")] + pub app_hash: ::prost::bytes::Bytes, } /// loads a snapshot chunk #[derive(Clone, PartialEq, ::prost::Message)] @@ -157,8 +157,8 @@ pub struct RequestLoadSnapshotChunk { pub struct RequestApplySnapshotChunk { #[prost(uint32, tag="1")] pub index: u32, - #[prost(bytes="vec", tag="2")] - pub chunk: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="2")] + pub chunk: ::prost::bytes::Bytes, #[prost(string, tag="3")] pub sender: ::prost::alloc::string::String, } @@ -237,10 +237,10 @@ pub struct ResponseInfo { #[prost(int64, tag="4")] #[serde(with = "crate::serializers::from_str", default)] pub last_block_height: i64, - #[prost(bytes="vec", tag="5")] + #[prost(bytes="bytes", tag="5")] #[serde(default)] - #[serde(skip_serializing_if = "::prost::alloc::vec::Vec::is_empty", with = "serde_bytes")] - pub last_block_app_hash: ::prost::alloc::vec::Vec, + #[serde(skip_serializing_if = "bytes::Bytes::is_empty")] + pub last_block_app_hash: ::prost::bytes::Bytes, } /// nondeterministic #[derive(Clone, PartialEq, ::prost::Message)] @@ -259,8 +259,8 @@ pub struct ResponseInitChain { pub consensus_params: ::core::option::Option, #[prost(message, repeated, tag="2")] pub validators: ::prost::alloc::vec::Vec, - #[prost(bytes="vec", tag="3")] - pub app_hash: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="3")] + pub app_hash: ::prost::bytes::Bytes, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResponseQuery { @@ -276,10 +276,10 @@ pub struct ResponseQuery { pub info: ::prost::alloc::string::String, #[prost(int64, tag="5")] pub index: i64, - #[prost(bytes="vec", tag="6")] - pub key: ::prost::alloc::vec::Vec, - #[prost(bytes="vec", tag="7")] - pub value: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="6")] + pub key: ::prost::bytes::Bytes, + #[prost(bytes="bytes", tag="7")] + pub value: ::prost::bytes::Bytes, #[prost(message, optional, tag="8")] pub proof_ops: ::core::option::Option, #[prost(int64, tag="9")] @@ -296,8 +296,8 @@ pub struct ResponseBeginBlock { pub struct ResponseCheckTx { #[prost(uint32, tag="1")] pub code: u32, - #[prost(bytes="vec", tag="2")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="2")] + pub data: ::prost::bytes::Bytes, /// nondeterministic #[prost(string, tag="3")] pub log: ::prost::alloc::string::String, @@ -325,8 +325,8 @@ pub struct ResponseCheckTx { pub struct ResponseDeliverTx { #[prost(uint32, tag="1")] pub code: u32, - #[prost(bytes="vec", tag="2")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="2")] + pub data: ::prost::bytes::Bytes, /// nondeterministic #[prost(string, tag="3")] pub log: ::prost::alloc::string::String, @@ -355,8 +355,8 @@ pub struct ResponseEndBlock { #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResponseCommit { /// reserve 1 - #[prost(bytes="vec", tag="2")] - pub data: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="2")] + pub data: ::prost::bytes::Bytes, #[prost(int64, tag="3")] pub retain_height: i64, } @@ -407,8 +407,8 @@ pub mod response_offer_snapshot { } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResponseLoadSnapshotChunk { - #[prost(bytes="vec", tag="1")] - pub chunk: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub chunk: ::prost::bytes::Bytes, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResponseApplySnapshotChunk { @@ -502,10 +502,10 @@ pub struct Event { /// EventAttribute is a single key-value pair, associated with an event. #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventAttribute { - #[prost(bytes="vec", tag="1")] - pub key: ::prost::alloc::vec::Vec, - #[prost(bytes="vec", tag="2")] - pub value: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub key: ::prost::bytes::Bytes, + #[prost(bytes="bytes", tag="2")] + pub value: ::prost::bytes::Bytes, /// nondeterministic #[prost(bool, tag="3")] pub index: bool, @@ -519,8 +519,8 @@ pub struct TxResult { pub height: i64, #[prost(uint32, tag="2")] pub index: u32, - #[prost(bytes="vec", tag="3")] - pub tx: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="3")] + pub tx: ::prost::bytes::Bytes, #[prost(message, optional, tag="4")] pub result: ::core::option::Option, } @@ -531,8 +531,8 @@ pub struct TxResult { #[derive(Clone, PartialEq, ::prost::Message)] pub struct Validator { /// The first 20 bytes of SHA256(public key) - #[prost(bytes="vec", tag="1")] - pub address: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="1")] + pub address: ::prost::bytes::Bytes, /// PubKey pub_key = 2 \[(gogoproto.nullable)=false\]; /// /// The voting power @@ -589,11 +589,11 @@ pub struct Snapshot { #[prost(uint32, tag="3")] pub chunks: u32, /// Arbitrary snapshot hash, equal only if identical - #[prost(bytes="vec", tag="4")] - pub hash: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="4")] + pub hash: ::prost::bytes::Bytes, /// Arbitrary application metadata - #[prost(bytes="vec", tag="5")] - pub metadata: ::prost::alloc::vec::Vec, + #[prost(bytes="bytes", tag="5")] + pub metadata: ::prost::bytes::Bytes, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] diff --git a/proto/src/prost/tendermint.crypto.rs b/proto/src/prost/tendermint.crypto.rs index 4b47ef593..0fac07973 100644 --- a/proto/src/prost/tendermint.crypto.rs +++ b/proto/src/prost/tendermint.crypto.rs @@ -1,24 +1,3 @@ -/// PublicKey defines the keys available for use with Tendermint Validators -#[derive(::serde::Deserialize, ::serde::Serialize)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PublicKey { - #[prost(oneof="public_key::Sum", tags="1, 2")] - pub sum: ::core::option::Option, -} -/// Nested message and enum types in `PublicKey`. -pub mod public_key { - #[derive(::serde::Deserialize, ::serde::Serialize)] - #[serde(tag = "type", content = "value")] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Sum { - #[prost(bytes, tag="1")] - #[serde(rename = "tendermint/PubKeyEd25519", with = "crate::serializers::bytes::base64string")] - Ed25519(::prost::alloc::vec::Vec), - #[prost(bytes, tag="2")] - #[serde(rename = "tendermint/PubKeySecp256k1", with = "crate::serializers::bytes::base64string")] - Secp256k1(::prost::alloc::vec::Vec), - } -} #[derive(::serde::Deserialize, ::serde::Serialize)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Proof { @@ -71,3 +50,24 @@ pub struct ProofOps { #[prost(message, repeated, tag="1")] pub ops: ::prost::alloc::vec::Vec, } +/// PublicKey defines the keys available for use with Tendermint Validators +#[derive(::serde::Deserialize, ::serde::Serialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublicKey { + #[prost(oneof="public_key::Sum", tags="1, 2")] + pub sum: ::core::option::Option, +} +/// Nested message and enum types in `PublicKey`. +pub mod public_key { + #[derive(::serde::Deserialize, ::serde::Serialize)] + #[serde(tag = "type", content = "value")] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Sum { + #[prost(bytes, tag="1")] + #[serde(rename = "tendermint/PubKeyEd25519", with = "crate::serializers::bytes::base64string")] + Ed25519(::prost::alloc::vec::Vec), + #[prost(bytes, tag="2")] + #[serde(rename = "tendermint/PubKeySecp256k1", with = "crate::serializers::bytes::base64string")] + Secp256k1(::prost::alloc::vec::Vec), + } +} diff --git a/proto/src/prost/tendermint.p2p.rs b/proto/src/prost/tendermint.p2p.rs index 8ed7b702f..4dc33809c 100644 --- a/proto/src/prost/tendermint.p2p.rs +++ b/proto/src/prost/tendermint.p2p.rs @@ -43,29 +43,6 @@ pub struct DefaultNodeInfoOther { pub rpc_address: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] -pub struct PexRequest { -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PexAddrs { - #[prost(message, repeated, tag="1")] - pub addrs: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Message { - #[prost(oneof="message::Sum", tags="1, 2")] - pub sum: ::core::option::Option, -} -/// Nested message and enum types in `Message`. -pub mod message { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Sum { - #[prost(message, tag="1")] - PexRequest(super::PexRequest), - #[prost(message, tag="2")] - PexAddrs(super::PexAddrs), - } -} -#[derive(Clone, PartialEq, ::prost::Message)] pub struct PacketPing { } #[derive(Clone, PartialEq, ::prost::Message)] @@ -104,3 +81,26 @@ pub struct AuthSigMessage { #[prost(bytes="vec", tag="2")] pub sig: ::prost::alloc::vec::Vec, } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PexRequest { +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PexAddrs { + #[prost(message, repeated, tag="1")] + pub addrs: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Message { + #[prost(oneof="message::Sum", tags="1, 2")] + pub sum: ::core::option::Option, +} +/// Nested message and enum types in `Message`. +pub mod message { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Sum { + #[prost(message, tag="1")] + PexRequest(super::PexRequest), + #[prost(message, tag="2")] + PexAddrs(super::PexAddrs), + } +} diff --git a/proto/src/prost/tendermint.types.rs b/proto/src/prost/tendermint.types.rs index 3888d22a6..04368cf8f 100644 --- a/proto/src/prost/tendermint.types.rs +++ b/proto/src/prost/tendermint.types.rs @@ -302,62 +302,6 @@ impl SignedMsgType { } } } -#[derive(::serde::Deserialize, ::serde::Serialize)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct CanonicalBlockId { - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag="2")] - #[serde(alias = "parts")] - pub part_set_header: ::core::option::Option, -} -#[derive(::serde::Deserialize, ::serde::Serialize)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct CanonicalPartSetHeader { - #[prost(uint32, tag="1")] - pub total: u32, - #[prost(bytes="vec", tag="2")] - pub hash: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct CanonicalProposal { - /// type alias for byte - #[prost(enumeration="SignedMsgType", tag="1")] - pub r#type: i32, - /// canonicalization requires fixed size encoding here - #[prost(sfixed64, tag="2")] - pub height: i64, - /// canonicalization requires fixed size encoding here - #[prost(sfixed64, tag="3")] - pub round: i64, - #[prost(int64, tag="4")] - pub pol_round: i64, - #[prost(message, optional, tag="5")] - pub block_id: ::core::option::Option, - #[prost(message, optional, tag="6")] - pub timestamp: ::core::option::Option, - #[prost(string, tag="7")] - pub chain_id: ::prost::alloc::string::String, -} -#[derive(::serde::Deserialize, ::serde::Serialize)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct CanonicalVote { - /// type alias for byte - #[prost(enumeration="SignedMsgType", tag="1")] - pub r#type: i32, - /// canonicalization requires fixed size encoding here - #[prost(sfixed64, tag="2")] - pub height: i64, - /// canonicalization requires fixed size encoding here - #[prost(sfixed64, tag="3")] - pub round: i64, - #[prost(message, optional, tag="4")] - pub block_id: ::core::option::Option, - #[prost(message, optional, tag="5")] - pub timestamp: ::core::option::Option, - #[prost(string, tag="6")] - pub chain_id: ::prost::alloc::string::String, -} #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventDataRoundState { #[prost(int64, tag="1")] @@ -509,6 +453,62 @@ pub struct EvidenceList { } #[derive(::serde::Deserialize, ::serde::Serialize)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct CanonicalBlockId { + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag="2")] + #[serde(alias = "parts")] + pub part_set_header: ::core::option::Option, +} +#[derive(::serde::Deserialize, ::serde::Serialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CanonicalPartSetHeader { + #[prost(uint32, tag="1")] + pub total: u32, + #[prost(bytes="vec", tag="2")] + pub hash: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CanonicalProposal { + /// type alias for byte + #[prost(enumeration="SignedMsgType", tag="1")] + pub r#type: i32, + /// canonicalization requires fixed size encoding here + #[prost(sfixed64, tag="2")] + pub height: i64, + /// canonicalization requires fixed size encoding here + #[prost(sfixed64, tag="3")] + pub round: i64, + #[prost(int64, tag="4")] + pub pol_round: i64, + #[prost(message, optional, tag="5")] + pub block_id: ::core::option::Option, + #[prost(message, optional, tag="6")] + pub timestamp: ::core::option::Option, + #[prost(string, tag="7")] + pub chain_id: ::prost::alloc::string::String, +} +#[derive(::serde::Deserialize, ::serde::Serialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CanonicalVote { + /// type alias for byte + #[prost(enumeration="SignedMsgType", tag="1")] + pub r#type: i32, + /// canonicalization requires fixed size encoding here + #[prost(sfixed64, tag="2")] + pub height: i64, + /// canonicalization requires fixed size encoding here + #[prost(sfixed64, tag="3")] + pub round: i64, + #[prost(message, optional, tag="4")] + pub block_id: ::core::option::Option, + #[prost(message, optional, tag="5")] + pub timestamp: ::core::option::Option, + #[prost(string, tag="6")] + pub chain_id: ::prost::alloc::string::String, +} +#[derive(::serde::Deserialize, ::serde::Serialize)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Block { #[prost(message, optional, tag="1")] pub header: ::core::option::Option
, diff --git a/proto/src/tendermint.rs b/proto/src/tendermint.rs index 895a9c663..e4d9b7e46 100644 --- a/proto/src/tendermint.rs +++ b/proto/src/tendermint.rs @@ -1,63 +1,63 @@ //! Tendermint-proto auto-generated sub-modules for Tendermint -pub mod consensus { - include!("prost/tendermint.consensus.rs"); +pub mod statesync { + include!("prost/tendermint.statesync.rs"); } -pub mod types { - include!("prost/tendermint.types.rs"); +pub mod abci { + include!("prost/tendermint.abci.rs"); } -pub mod mempool { - include!("prost/tendermint.mempool.rs"); +pub mod store { + include!("prost/tendermint.store.rs"); } -pub mod rpc { - pub mod grpc { - include!("prost/tendermint.rpc.grpc.rs"); - } +pub mod version { + include!("prost/tendermint.version.rs"); } -pub mod blockchain { - include!("prost/tendermint.blockchain.rs"); +pub mod types { + include!("prost/tendermint.types.rs"); } -pub mod libs { - pub mod bits { - include!("prost/tendermint.libs.bits.rs"); - } +pub mod consensus { + include!("prost/tendermint.consensus.rs"); } -pub mod state { - include!("prost/tendermint.state.rs"); +pub mod p2p { + include!("prost/tendermint.p2p.rs"); } -pub mod version { - include!("prost/tendermint.version.rs"); +pub mod privval { + include!("prost/tendermint.privval.rs"); } -pub mod store { - include!("prost/tendermint.store.rs"); +pub mod blockchain { + include!("prost/tendermint.blockchain.rs"); } -pub mod privval { - include!("prost/tendermint.privval.rs"); +pub mod crypto { + include!("prost/tendermint.crypto.rs"); } -pub mod statesync { - include!("prost/tendermint.statesync.rs"); +pub mod mempool { + include!("prost/tendermint.mempool.rs"); } -pub mod p2p { - include!("prost/tendermint.p2p.rs"); +pub mod state { + include!("prost/tendermint.state.rs"); } -pub mod abci { - include!("prost/tendermint.abci.rs"); +pub mod libs { + pub mod bits { + include!("prost/tendermint.libs.bits.rs"); + } } -pub mod crypto { - include!("prost/tendermint.crypto.rs"); +pub mod rpc { + pub mod grpc { + include!("prost/tendermint.rpc.grpc.rs"); + } } pub mod meta { diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 547c49dff..0f115c11e 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -79,6 +79,7 @@ subtle-encoding = { version = "0.5", default-features = false, features = ["bech url = { version = "2.2", default-features = false } walkdir = { version = "2.3", default-features = false } flex-error = { version = "0.4.4", default-features = false } +subtle = { version = "2", default-features = false } # Optional dependencies async-trait = { version = "0.1", optional = true, default-features = false } diff --git a/rpc/src/abci.rs b/rpc/src/abci.rs new file mode 100644 index 000000000..2cbb63df4 --- /dev/null +++ b/rpc/src/abci.rs @@ -0,0 +1,34 @@ +//! Old ABCI structures, formerly defined in `tendermint::abci`. +//! +//! The original contents of `tendermint::abci` were created only to model RPC +//! responses, not to model ABCI itself: +//! +//! > NOTE: This module contains types for ABCI responses as consumed from RPC +//! endpoints. It does not contain an ABCI protocol implementation. +//! +//! The old types should be eliminated and +//! merged with the new ABCI domain types. Moving them here in the meantime +//! disentangles improving the ABCI domain modeling from changes to the RPC +//! interface. + +mod code; +mod data; +mod gas; +mod info; +mod log; +mod path; + +pub mod responses; +pub mod tag; +pub mod transaction; + +pub use self::{ + code::Code, + data::Data, + gas::Gas, + info::Info, + log::Log, + path::Path, + responses::{DeliverTx, Event, Responses}, + transaction::Transaction, +}; diff --git a/tendermint/src/abci/code.rs b/rpc/src/abci/code.rs similarity index 91% rename from tendermint/src/abci/code.rs rename to rpc/src/abci/code.rs index cdf7e0067..81c379570 100644 --- a/tendermint/src/abci/code.rs +++ b/rpc/src/abci/code.rs @@ -1,4 +1,4 @@ -use core::fmt; +use core::{fmt, num::NonZeroU32}; use serde::{ de::{Deserialize, Deserializer, Visitor}, @@ -18,7 +18,7 @@ pub enum Code { Ok, /// Error codes - Err(u32), + Err(NonZeroU32), } impl Default for Code { @@ -49,9 +49,9 @@ impl Code { impl From for Code { fn from(value: u32) -> Code { - match value { - 0 => Code::Ok, - err => Code::Err(err), + match NonZeroU32::new(value) { + Some(value) => Code::Err(value), + None => Code::Ok, } } } @@ -60,7 +60,7 @@ impl From for u32 { fn from(code: Code) -> u32 { match code { Code::Ok => 0, - Code::Err(err) => err, + Code::Err(err) => err.get(), } } } diff --git a/tendermint/src/abci/data.rs b/rpc/src/abci/data.rs similarity index 94% rename from tendermint/src/abci/data.rs rename to rpc/src/abci/data.rs index 76bcbea3d..97df5e776 100644 --- a/tendermint/src/abci/data.rs +++ b/rpc/src/abci/data.rs @@ -8,7 +8,7 @@ use crate::prelude::*; /// application-specific rules. #[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] #[serde(transparent)] -pub struct Data(#[serde(with = "crate::serializers::bytes::base64string")] Vec); +pub struct Data(#[serde(with = "tendermint::serializers::bytes::base64string")] Vec); impl From> for Data { fn from(value: Vec) -> Self { diff --git a/tendermint/src/abci/gas.rs b/rpc/src/abci/gas.rs similarity index 96% rename from tendermint/src/abci/gas.rs rename to rpc/src/abci/gas.rs index b086c3941..433c86613 100644 --- a/tendermint/src/abci/gas.rs +++ b/rpc/src/abci/gas.rs @@ -11,8 +11,9 @@ use core::{ }; use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; +use tendermint::error::Error; -use crate::{error::Error, prelude::*}; +use crate::prelude::*; /// Gas: representation of transaction processing resource costs #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] diff --git a/tendermint/src/abci/info.rs b/rpc/src/abci/info.rs similarity index 100% rename from tendermint/src/abci/info.rs rename to rpc/src/abci/info.rs diff --git a/tendermint/src/abci/log.rs b/rpc/src/abci/log.rs similarity index 100% rename from tendermint/src/abci/log.rs rename to rpc/src/abci/log.rs diff --git a/tendermint/src/abci/path.rs b/rpc/src/abci/path.rs similarity index 90% rename from tendermint/src/abci/path.rs rename to rpc/src/abci/path.rs index 849de2cec..040ab26d2 100644 --- a/tendermint/src/abci/path.rs +++ b/rpc/src/abci/path.rs @@ -6,8 +6,9 @@ use core::{ }; use serde::{Deserialize, Serialize}; +use tendermint::error::Error; -use crate::{error::Error, prelude::*}; +use crate::prelude::*; /// Path to ABCI data #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] diff --git a/tendermint/src/abci/responses.rs b/rpc/src/abci/responses.rs similarity index 98% rename from tendermint/src/abci/responses.rs rename to rpc/src/abci/responses.rs index cca348473..2f5c156ed 100644 --- a/tendermint/src/abci/responses.rs +++ b/rpc/src/abci/responses.rs @@ -3,9 +3,10 @@ use core::fmt::{self, Display}; use serde::{Deserialize, Deserializer, Serialize}; +use tendermint::{consensus, serializers, validator}; use super::{code::Code, data::Data, gas::Gas, info::Info, log::Log, tag::Tag}; -use crate::{consensus, prelude::*, serializers, validator}; +use crate::prelude::*; /// Responses for ABCI calls which occur during block processing. /// diff --git a/tendermint/src/abci/tag.rs b/rpc/src/abci/tag.rs similarity index 77% rename from tendermint/src/abci/tag.rs rename to rpc/src/abci/tag.rs index feb840675..c791688eb 100644 --- a/tendermint/src/abci/tag.rs +++ b/rpc/src/abci/tag.rs @@ -3,9 +3,10 @@ use core::{fmt, str::FromStr}; use serde::{Deserialize, Serialize}; -use tendermint_proto::serializers::bytes::base64string; +use tendermint::error::Error; -use crate::{error::Error, prelude::*}; +use crate::prelude::*; +use crate::serializers::bytes::base64string; /// Tags #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -76,16 +77,3 @@ impl fmt::Display for Value { write!(f, "{}", &self.0) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn tag_serde() { - let json = r#"{"key": "cGFja2V0X3RpbWVvdXRfaGVpZ2h0", "value": "MC00ODQw"}"#; - let tag: Tag = serde_json::from_str(json).unwrap(); - assert_eq!("packet_timeout_height", tag.key.0); - assert_eq!("0-4840", tag.value.0); - } -} diff --git a/tendermint/src/abci/transaction.rs b/rpc/src/abci/transaction.rs similarity index 100% rename from tendermint/src/abci/transaction.rs rename to rpc/src/abci/transaction.rs diff --git a/tendermint/src/abci/transaction/hash.rs b/rpc/src/abci/transaction/hash.rs similarity index 98% rename from tendermint/src/abci/transaction/hash.rs rename to rpc/src/abci/transaction/hash.rs index 030547e4f..54a7a9571 100644 --- a/tendermint/src/abci/transaction/hash.rs +++ b/rpc/src/abci/transaction/hash.rs @@ -8,8 +8,9 @@ use core::{ use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use subtle::{self, ConstantTimeEq}; use subtle_encoding::hex; +use tendermint::error::Error; -use crate::{error::Error, prelude::*}; +use crate::prelude::*; /// Size of a transaction hash in bytes pub const LENGTH: usize = 32; diff --git a/rpc/src/client.rs b/rpc/src/client.rs index 9351469ed..c163c6b9f 100644 --- a/rpc/src/client.rs +++ b/rpc/src/client.rs @@ -9,12 +9,7 @@ use core::{fmt, time::Duration}; use async_trait::async_trait; use serde::{de::DeserializeOwned, Serialize}; -use tendermint::{ - abci::{self, Transaction}, - block::Height, - evidence::Evidence, - Genesis, -}; +use tendermint::{block::Height, evidence::Evidence, Genesis}; use tokio::time; #[cfg(feature = "http-client")] pub use transport::http::{HttpClient, HttpClientUrl}; @@ -25,6 +20,7 @@ pub use transport::websocket::{ }; use crate::{ + abci::{self, Transaction}, endpoint::{validators::DEFAULT_VALIDATORS_PER_PAGE, *}, paging::Paging, prelude::*, diff --git a/rpc/src/client/bin/main.rs b/rpc/src/client/bin/main.rs index 7e6733ab0..a38539f7d 100644 --- a/rpc/src/client/bin/main.rs +++ b/rpc/src/client/bin/main.rs @@ -4,10 +4,11 @@ use core::str::FromStr; use futures::StreamExt; use structopt::StructOpt; -use tendermint::abci::{transaction::Hash, Path, Transaction}; use tendermint_rpc::{ - query::Query, Client, Error, HttpClient, Order, Paging, Scheme, Subscription, - SubscriptionClient, Url, WebSocketClient, + abci::{transaction::Hash, Path, Transaction}, + query::Query, + Client, Error, HttpClient, Order, Paging, Scheme, Subscription, SubscriptionClient, Url, + WebSocketClient, }; use tokio::time::Duration; use tracing::{error, info, level_filters::LevelFilter, warn}; diff --git a/rpc/src/endpoint/abci_info.rs b/rpc/src/endpoint/abci_info.rs index 8c5b415b5..d4b9568c2 100644 --- a/rpc/src/endpoint/abci_info.rs +++ b/rpc/src/endpoint/abci_info.rs @@ -2,6 +2,7 @@ use core::convert::{TryFrom, TryInto}; +use bytes::Bytes; use serde::{Deserialize, Serialize}; use tendermint::{block, Error}; use tendermint_proto::abci::ResponseInfo; @@ -48,7 +49,7 @@ pub struct AbciInfo { pub last_block_height: block::Height, /// Last app hash for the block - pub last_block_app_hash: Vec, + pub last_block_app_hash: Bytes, } impl TryFrom for AbciInfo { diff --git a/rpc/src/endpoint/abci_query.rs b/rpc/src/endpoint/abci_query.rs index cf6cfbedc..6577fcef2 100644 --- a/rpc/src/endpoint/abci_query.rs +++ b/rpc/src/endpoint/abci_query.rs @@ -1,15 +1,13 @@ //! `/abci_query` endpoint JSON-RPC wrapper use serde::{Deserialize, Serialize}; -use tendermint::{ +use tendermint::{block, merkle::proof::Proof, serializers}; + +use crate::{ abci::{Code, Log, Path}, - block, - merkle::proof::Proof, - serializers, + prelude::*, }; -use crate::prelude::*; - /// Query the ABCI application for information #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Request { diff --git a/rpc/src/endpoint/block_results.rs b/rpc/src/endpoint/block_results.rs index 9920f0e38..184346638 100644 --- a/rpc/src/endpoint/block_results.rs +++ b/rpc/src/endpoint/block_results.rs @@ -1,9 +1,9 @@ //! `/block_results` endpoint JSON-RPC wrapper use serde::{Deserialize, Serialize}; -use tendermint::{abci, block, consensus, validator}; +use tendermint::{block, consensus, validator}; -use crate::prelude::*; +use crate::{abci, prelude::*}; /// Get ABCI results at a given height. #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] diff --git a/rpc/src/endpoint/broadcast/tx_async.rs b/rpc/src/endpoint/broadcast/tx_async.rs index 2ab35abe7..0de93113e 100644 --- a/rpc/src/endpoint/broadcast/tx_async.rs +++ b/rpc/src/endpoint/broadcast/tx_async.rs @@ -1,7 +1,8 @@ //! `/broadcast_tx_async`: broadcast a transaction and return immediately. use serde::{Deserialize, Serialize}; -use tendermint::abci::{transaction, Code, Data, Log, Transaction}; + +use crate::abci::{transaction, Code, Data, Log, Transaction}; /// `/broadcast_tx_async`: broadcast a transaction and return immediately. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] diff --git a/rpc/src/endpoint/broadcast/tx_commit.rs b/rpc/src/endpoint/broadcast/tx_commit.rs index 35352a972..6af3e82ca 100644 --- a/rpc/src/endpoint/broadcast/tx_commit.rs +++ b/rpc/src/endpoint/broadcast/tx_commit.rs @@ -2,13 +2,13 @@ //! if we timeout waiting for tx to commit. use serde::{Deserialize, Serialize}; -use tendermint::{ +use tendermint::block; + +use crate::{ abci::{responses::Codespace, transaction, Code, Data, Event, Gas, Info, Log, Transaction}, - block, + prelude::*, }; -use crate::prelude::*; - /// `/broadcast_tx_commit`: only returns error if `mempool.CheckTx()` errs or /// if we timeout waiting for tx to commit. /// diff --git a/rpc/src/endpoint/broadcast/tx_sync.rs b/rpc/src/endpoint/broadcast/tx_sync.rs index 82b1e9d0b..e188525a4 100644 --- a/rpc/src/endpoint/broadcast/tx_sync.rs +++ b/rpc/src/endpoint/broadcast/tx_sync.rs @@ -1,7 +1,8 @@ //! `/broadcast_tx_sync`: returns with the response from `CheckTx`. use serde::{Deserialize, Serialize}; -use tendermint::abci::{transaction, Code, Data, Log, Transaction}; + +use crate::abci::{transaction, Code, Data, Log, Transaction}; /// `/broadcast_tx_sync`: returns with the response from `CheckTx`. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] diff --git a/rpc/src/endpoint/evidence.rs b/rpc/src/endpoint/evidence.rs index 9a5f46d9c..ba1bb0e30 100644 --- a/rpc/src/endpoint/evidence.rs +++ b/rpc/src/endpoint/evidence.rs @@ -1,9 +1,9 @@ //! `/broadcast_evidence`: broadcast an evidence. use serde::{Deserialize, Serialize}; -use tendermint::{abci::transaction, evidence::Evidence}; +use tendermint::evidence::Evidence; -use crate::Method; +use crate::{abci::transaction, Method}; /// `/broadcast_evidence`: broadcast an evidence. #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] diff --git a/rpc/src/endpoint/tx.rs b/rpc/src/endpoint/tx.rs index 120d6881b..71729bc03 100644 --- a/rpc/src/endpoint/tx.rs +++ b/rpc/src/endpoint/tx.rs @@ -1,10 +1,10 @@ //! `/tx` endpoint JSON-RPC wrapper use serde::{Deserialize, Serialize}; -use tendermint::{abci, block}; +use tendermint::block; use tendermint_proto::types::TxProof; -use crate::Method; +use crate::{abci, Method}; /// Request for finding a transaction by its hash. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] @@ -13,7 +13,7 @@ pub struct Request { /// /// Serialized internally into a base64-encoded string before sending to /// the RPC server. - #[serde(with = "tendermint::serializers::hash_base64")] + #[serde(with = "crate::serializers::hash_base64")] pub hash: abci::transaction::Hash, /// Whether or not to include the proofs of the transaction's inclusion in /// the block. diff --git a/rpc/src/event.rs b/rpc/src/event.rs index 2b2bc03bb..49055de82 100644 --- a/rpc/src/event.rs +++ b/rpc/src/event.rs @@ -3,13 +3,16 @@ use alloc::collections::BTreeMap as HashMap; use serde::{Deserialize, Serialize}; -use tendermint::{ +use tendermint::Block; + +use crate::{ abci::responses::{BeginBlock, EndBlock}, - Block, + prelude::*, + query::EventType, + response::Wrapper, + Response, }; -use crate::{prelude::*, query::EventType, response::Wrapper, Response}; - /// An incoming event produced by a [`Subscription`]. /// /// [`Subscription`]: ../struct.Subscription.html @@ -76,5 +79,5 @@ pub struct TxResult { pub log: Option, pub gas_wanted: Option, pub gas_used: Option, - pub events: Vec, + pub events: Vec, } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index bd9e0eb82..b10f14f50 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -44,6 +44,7 @@ pub use client::{HttpClient, HttpClientUrl}; #[cfg(feature = "websocket-client")] pub use client::{WebSocketClient, WebSocketClientDriver, WebSocketClientUrl, WebSocketConfig}; +pub mod abci; pub mod endpoint; pub mod error; pub mod event; @@ -56,6 +57,7 @@ pub mod request; pub mod response; pub mod response_error; mod rpc_url; +pub mod serializers; mod utils; mod version; diff --git a/rpc/src/prelude.rs b/rpc/src/prelude.rs index a5b91aa13..aa9a20b65 100644 --- a/rpc/src/prelude.rs +++ b/rpc/src/prelude.rs @@ -1,7 +1,7 @@ // Re-export according to alloc::prelude::v1 because it is not yet stabilized // https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html -pub use alloc::borrow::ToOwned; pub use alloc::{ + borrow::ToOwned, boxed::Box, format, string::{String, ToString}, diff --git a/rpc/src/serializers.rs b/rpc/src/serializers.rs new file mode 100644 index 000000000..025f02876 --- /dev/null +++ b/rpc/src/serializers.rs @@ -0,0 +1,10 @@ +//! Serde serializers +//! +//! Serializers and deserializers for a transparent developer experience. +//! +//! CAUTION: There are no guarantees for backwards compatibility, this module should be considered +//! an internal implementation detail which can vanish without further warning. Use at your own +//! risk. +pub use tendermint_proto::serializers::*; + +pub mod hash_base64; diff --git a/tendermint/src/serializers/hash_base64.rs b/rpc/src/serializers/hash_base64.rs similarity index 100% rename from tendermint/src/serializers/hash_base64.rs rename to rpc/src/serializers/hash_base64.rs diff --git a/rpc/tests/kvstore_fixtures.rs b/rpc/tests/kvstore_fixtures.rs index 564470195..fbabb5c00 100644 --- a/rpc/tests/kvstore_fixtures.rs +++ b/rpc/tests/kvstore_fixtures.rs @@ -1,17 +1,18 @@ //! Tendermint kvstore RPC endpoint testing. use core::str::FromStr; +use std::collections::BTreeMap as HashMap; use std::{fs, path::PathBuf}; use subtle_encoding::{base64, hex}; use tendermint::{ - abci::transaction::Hash, evidence::{Duration, Evidence}, public_key, vote::Vote, }; use tendermint_config::net::Address; use tendermint_rpc::{ + abci::transaction::Hash, endpoint, error::{Error, ErrorDetail}, request::Wrapper as RequestWrapper, @@ -322,7 +323,7 @@ fn incoming_fixtures() { let result = endpoint::abci_info::Response::from_string(content).unwrap(); assert_eq!(result.response.app_version, 1); assert_eq!(result.response.data, "{\"size\":0}"); - assert_eq!(result.response.last_block_app_hash, b"AAAAAAAAAAA="); + assert_eq!(result.response.last_block_app_hash, b"AAAAAAAAAAA="[..]); assert_eq!(result.response.version, "0.17.0"); }, "abci_query_with_existing_key" => { @@ -368,7 +369,7 @@ fn incoming_fixtures() { }, "block_at_height_1" => { let result = endpoint::block::Response::from_string(content).unwrap(); - assert!(result.block.data.iter().next().is_none()); + assert!(result.block.data.get(0).is_none()); assert!(result.block.evidence.iter().next().is_none()); assert!(result.block.header.app_hash.value().is_empty()); assert_eq!(result.block.header.chain_id.as_str(), CHAIN_ID); @@ -409,7 +410,7 @@ fn incoming_fixtures() { }, "block_at_height_10" => { let result = endpoint::block::Response::from_string(content).unwrap(); - assert!(result.block.data.iter().next().is_none()); + assert!(result.block.data.get(0).is_none()); assert!(result.block.evidence.iter().next().is_none()); assert_eq!(result.block.header.app_hash.value(), [0u8; 8]); assert_eq!(result.block.header.chain_id.as_str(), CHAIN_ID); @@ -466,43 +467,8 @@ fn incoming_fixtures() { "block_search" => { let result = endpoint::block_search::Response::from_string(content).unwrap(); assert_eq!(result.total_count as usize, result.blocks.len()); - // Test a few selected attributes of the results. - for block in result.blocks { - assert!(block.block.data.iter().next().is_none()); - assert!(block.block.evidence.iter().next().is_none()); - assert_eq!(block.block.header.app_hash.value(), [0u8; 8]); - assert_eq!(block.block.header.chain_id.as_str(), CHAIN_ID); - assert!(!block.block.header.consensus_hash.is_empty()); - assert_eq!(block.block.header.data_hash, empty_merkle_root_hash); - assert_eq!(block.block.header.evidence_hash, empty_merkle_root_hash); - assert!(block.block.header.height.value() > 1); - assert!(block.block.header.last_block_id.is_some()); - assert!(block.block.header.last_commit_hash.is_some()); - assert!(block.block.header.last_results_hash.is_some()); - assert!(!block.block.header.next_validators_hash.is_empty()); - assert_ne!( - block.block.header.proposer_address.as_bytes(), - [0u8; tendermint::account::LENGTH] - ); - assert!( - block - .block - .header - .time - .duration_since(informal_epoch) - .unwrap() - .as_secs() - > 0 - ); - assert!(!block.block.header.validators_hash.is_empty()); - assert_eq!( - block.block.header.version, - tendermint::block::header::Version { block: 11, app: 1 } - ); - assert!(block.block.last_commit.is_some()); - assert!(!block.block_id.hash.is_empty()); - assert!(!block.block_id.part_set_header.hash.is_empty()); - assert_eq!(block.block_id.part_set_header.total, 1); + for response in result.blocks { + assert!(response.block.header.height.value() > 1); } }, "block_search_evidence" => { @@ -543,21 +509,21 @@ fn incoming_fixtures() { }, "broadcast_tx_async" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, "broadcast_tx_commit" => { let result = endpoint::broadcast::tx_commit::Response::from_string(content).unwrap(); - assert_eq!(result.check_tx.code, tendermint::abci::Code::Ok); + assert_eq!(result.check_tx.code, tendermint_rpc::abci::Code::Ok); assert_eq!( result.check_tx.codespace, - tendermint::abci::responses::Codespace::default() + tendermint_rpc::abci::responses::Codespace::default() ); assert!(result.check_tx.data.is_none()); assert!(result.check_tx.events.is_empty()); @@ -566,69 +532,45 @@ fn incoming_fixtures() { // assert_eq!(result.check_tx.gas_wanted.value(), 1); assert!(result.check_tx.info.to_string().is_empty()); assert!(result.check_tx.log.value().is_empty()); - assert_eq!(result.deliver_tx.code, tendermint::abci::Code::Ok); + assert_eq!(result.deliver_tx.code, tendermint_rpc::abci::Code::Ok); assert_eq!( result.deliver_tx.codespace, - tendermint::abci::responses::Codespace::default() + tendermint_rpc::abci::responses::Codespace::default() ); assert!(result.deliver_tx.data.is_none()); assert_eq!(result.deliver_tx.events.len(), 1); assert_eq!(result.deliver_tx.events[0].attributes.len(), 4); assert_eq!( - result.deliver_tx.events[0].attributes[0] - .key - .to_string() - .as_bytes(), - base64::decode("Y3JlYXRvcg==").unwrap() + result.deliver_tx.events[0].attributes[0].key.as_ref(), + "creator" ); assert_eq!( - result.deliver_tx.events[0].attributes[0] - .value - .to_string() - .as_bytes(), - base64::decode("Q29zbW9zaGkgTmV0b3dva28=").unwrap() + result.deliver_tx.events[0].attributes[0].value.as_ref(), + "Cosmoshi Netowoko" ); assert_eq!( - result.deliver_tx.events[0].attributes[1] - .key - .to_string() - .as_bytes(), - base64::decode("a2V5").unwrap() + result.deliver_tx.events[0].attributes[1].key.as_ref(), + "key" ); assert_eq!( - result.deliver_tx.events[0].attributes[1] - .value - .to_string() - .as_bytes(), - base64::decode("Y29tbWl0LWtleQ==").unwrap() + result.deliver_tx.events[0].attributes[1].value.as_ref(), + "commit-key" ); assert_eq!( - result.deliver_tx.events[0].attributes[2] - .key - .to_string() - .as_bytes(), - base64::decode("aW5kZXhfa2V5").unwrap() + result.deliver_tx.events[0].attributes[2].key.as_ref(), + "index_key" ); assert_eq!( - result.deliver_tx.events[0].attributes[2] - .value - .to_string() - .as_bytes(), - base64::decode("aW5kZXggaXMgd29ya2luZw==").unwrap() + result.deliver_tx.events[0].attributes[2].value.as_ref(), + "index is working" ); assert_eq!( - result.deliver_tx.events[0].attributes[3] - .key - .to_string() - .as_bytes(), - base64::decode("bm9pbmRleF9rZXk=").unwrap() + result.deliver_tx.events[0].attributes[3].key.as_ref(), + "noindex_key" ); assert_eq!( - result.deliver_tx.events[0].attributes[3] - .value - .to_string() - .as_bytes(), - base64::decode("aW5kZXggaXMgd29ya2luZw==").unwrap() + result.deliver_tx.events[0].attributes[3].value.as_ref(), + "index is working" ); assert_eq!(result.deliver_tx.events[0].type_str, "app"); assert_eq!(result.deliver_tx.gas_used.value(), 0); @@ -637,16 +579,16 @@ fn incoming_fixtures() { assert!(result.deliver_tx.log.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); }, "broadcast_tx_sync" => { let result = endpoint::broadcast::tx_sync::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, @@ -904,7 +846,7 @@ fn incoming_fixtures() { } = result.data { let b = block.unwrap(); - assert!(b.data.iter().next().is_none()); + assert!(b.data.get(0).is_none()); assert!(b.evidence.iter().next().is_none()); assert!(!b.header.app_hash.value().is_empty()); assert_eq!(b.header.chain_id.as_str(), CHAIN_ID); @@ -959,7 +901,7 @@ fn incoming_fixtures() { } = result.data { let b = block.unwrap(); - assert!(b.data.iter().next().is_none()); + assert!(b.data.get(0).is_none()); assert!(b.evidence.iter().next().is_none()); assert!(!b.header.app_hash.value().is_empty()); assert_eq!(b.header.chain_id.as_str(), CHAIN_ID); @@ -1014,7 +956,7 @@ fn incoming_fixtures() { } = result.data { let b = block.unwrap(); - assert!(b.data.iter().next().is_none()); + assert!(b.data.get(0).is_none()); assert!(b.evidence.iter().next().is_none()); assert!(!b.header.app_hash.value().is_empty()); assert_eq!(b.header.chain_id.as_str(), CHAIN_ID); @@ -1069,7 +1011,7 @@ fn incoming_fixtures() { } = result.data { let b = block.unwrap(); - assert!(b.data.iter().next().is_none()); + assert!(b.data.get(0).is_none()); assert!(b.evidence.iter().next().is_none()); assert!(!b.header.app_hash.value().is_empty()); assert_eq!(b.header.chain_id.as_str(), CHAIN_ID); @@ -1124,7 +1066,7 @@ fn incoming_fixtures() { } = result.data { let b = block.unwrap(); - assert!(b.data.iter().next().is_none()); + assert!(b.data.get(0).is_none()); assert!(b.evidence.iter().next().is_none()); assert!(!b.header.app_hash.value().is_empty()); assert_eq!(b.header.chain_id.as_str(), CHAIN_ID); @@ -1202,19 +1144,7 @@ fn incoming_fixtures() { } else { panic!("not a tx"); } - for (k, v) in result.events.unwrap() { - assert_eq!(v.len(), 1); - match k.as_str() { - "app.creator" => assert_eq!(v[0], "Cosmoshi Netowoko"), - "app.index_key" => assert_eq!(v[0], "index is working"), - "app.key" => assert_eq!(v[0], "tx0"), - "app.noindex_key" => assert_eq!(v[0], "index is working"), - "tm.event" => assert_eq!(v[0], "Tx"), - "tx.hash" => assert_eq!(v[0].len(), 64), - "tx.height" => assert_eq!(v[0], height.to_string()), - _ => panic!("unknown event found {}", k), - } - } + check_event_attrs(&result.events.unwrap(), "tx0", height); assert_eq!(result.query, "tm.event = 'Tx'"); }, "subscribe_txs_1" => { @@ -1246,19 +1176,8 @@ fn incoming_fixtures() { } else { panic!("not a tx"); } - for (k, v) in result.events.unwrap() { - assert_eq!(v.len(), 1); - match k.as_str() { - "app.creator" => assert_eq!(v[0], "Cosmoshi Netowoko"), - "app.index_key" => assert_eq!(v[0], "index is working"), - "app.key" => assert_eq!(v[0], "tx1"), - "app.noindex_key" => assert_eq!(v[0], "index is working"), - "tm.event" => assert_eq!(v[0], "Tx"), - "tx.hash" => assert_eq!(v[0].len(), 64), - "tx.height" => assert_eq!(v[0], height.to_string()), - _ => panic!("unknown event found {}", k), - } - } + + check_event_attrs(&result.events.unwrap(), "tx1", height); assert_eq!(result.query, "tm.event = 'Tx'"); }, "subscribe_txs_2" => { @@ -1290,19 +1209,7 @@ fn incoming_fixtures() { } else { panic!("not a tx"); } - for (k, v) in result.events.unwrap() { - assert_eq!(v.len(), 1); - match k.as_str() { - "app.creator" => assert_eq!(v[0], "Cosmoshi Netowoko"), - "app.index_key" => assert_eq!(v[0], "index is working"), - "app.key" => assert_eq!(v[0], "tx2"), - "app.noindex_key" => assert_eq!(v[0], "index is working"), - "tm.event" => assert_eq!(v[0], "Tx"), - "tx.hash" => assert_eq!(v[0].len(), 64), - "tx.height" => assert_eq!(v[0], height.to_string()), - _ => panic!("unknown event found {}", k), - } - } + check_event_attrs(&result.events.unwrap(), "tx2", height); assert_eq!(result.query, "tm.event = 'Tx'"); }, "subscribe_txs_3" => { @@ -1334,19 +1241,7 @@ fn incoming_fixtures() { } else { panic!("not a tx"); } - for (k, v) in result.events.unwrap() { - assert_eq!(v.len(), 1); - match k.as_str() { - "app.creator" => assert_eq!(v[0], "Cosmoshi Netowoko"), - "app.index_key" => assert_eq!(v[0], "index is working"), - "app.key" => assert_eq!(v[0], "tx3"), - "app.noindex_key" => assert_eq!(v[0], "index is working"), - "tm.event" => assert_eq!(v[0], "Tx"), - "tx.hash" => assert_eq!(v[0].len(), 64), - "tx.height" => assert_eq!(v[0], height.to_string()), - _ => panic!("unknown event found {}", k), - } - } + check_event_attrs(&result.events.unwrap(), "tx3", height); assert_eq!(result.query, "tm.event = 'Tx'"); }, "subscribe_txs_4" => { @@ -1378,78 +1273,66 @@ fn incoming_fixtures() { } else { panic!("not a tx"); } - for (k, v) in result.events.unwrap() { - assert_eq!(v.len(), 1); - match k.as_str() { - "app.creator" => assert_eq!(v[0], "Cosmoshi Netowoko"), - "app.index_key" => assert_eq!(v[0], "index is working"), - "app.key" => assert_eq!(v[0], "tx4"), - "app.noindex_key" => assert_eq!(v[0], "index is working"), - "tm.event" => assert_eq!(v[0], "Tx"), - "tx.hash" => assert_eq!(v[0].len(), 64), - "tx.height" => assert_eq!(v[0], height.to_string()), - _ => panic!("unknown event found {}", k), - } - } + check_event_attrs(&result.events.unwrap(), "tx4", height); assert_eq!(result.query, "tm.event = 'Tx'"); }, "subscribe_txs_broadcast_tx_0" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, "subscribe_txs_broadcast_tx_1" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, "subscribe_txs_broadcast_tx_2" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, "subscribe_txs_broadcast_tx_3" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, "subscribe_txs_broadcast_tx_4" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, "subscribe_txs_broadcast_tx_5" => { let result = endpoint::broadcast::tx_async::Response::from_string(content).unwrap(); - assert_eq!(result.code, tendermint::abci::Code::Ok); + assert_eq!(result.code, tendermint_rpc::abci::Code::Ok); assert!(result.data.value().is_empty()); assert_ne!( result.hash, - tendermint::abci::transaction::Hash::new([0; 32]) + tendermint_rpc::abci::transaction::Hash::new([0; 32]) ); assert!(result.log.value().is_empty()); }, @@ -1470,7 +1353,7 @@ fn incoming_fixtures() { // Test a few selected attributes of the results. for tx in result.txs { assert_ne!(tx.hash.as_bytes(), [0; 32]); - assert_eq!(tx.tx_result.code, tendermint::abci::Code::Ok); + assert_eq!(tx.tx_result.code, tendermint_rpc::abci::Code::Ok); assert_eq!(tx.tx_result.events.len(), 1); assert_eq!(tx.tx_result.events[0].type_str, "app"); assert_eq!(tx.tx_result.gas_used.value(), 0); @@ -1486,7 +1369,7 @@ fn incoming_fixtures() { // Test a few selected attributes of the results. for tx in result.txs { assert_ne!(tx.hash.as_bytes(), [0; 32]); - assert_eq!(tx.tx_result.code, tendermint::abci::Code::Ok); + assert_eq!(tx.tx_result.code, tendermint_rpc::abci::Code::Ok); assert_eq!(tx.tx_result.events.len(), 1); assert_eq!(tx.tx_result.events[0].type_str, "app"); assert_eq!(tx.tx_result.gas_used.value(), 0); @@ -1505,3 +1388,19 @@ fn incoming_fixtures() { } } } + +fn check_event_attrs(events: &HashMap>, app_key: &str, height: i64) { + for (k, v) in events { + assert_eq!(v.len(), 1); + match k.as_str() { + "app.creator" => assert_eq!(v[0], "Cosmoshi Netowoko"), + "app.index_key" => assert_eq!(v[0], "index is working"), + "app.key" => assert_eq!(v[0], app_key), + "app.noindex_key" => assert_eq!(v[0], "index is working"), + "tm.event" => assert_eq!(v[0], "Tx"), + "tx.hash" => assert_eq!(v[0].len(), 64), + "tx.height" => assert_eq!(v[0], height.to_string()), + _ => panic!("unknown event found {}", k), + } + } +} diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 9b80ecdba..642a1a4a2 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -8,7 +8,7 @@ repository = "https://github.com/informalsystems/tendermint-rs/tree/main/tenderm readme = "../README.md" categories = ["cryptography", "cryptography::cryptocurrencies", "database"] keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] -edition = "2018" +edition = "2021" description = """ Tendermint is a high-performance blockchain consensus engine that powers @@ -31,7 +31,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] async-trait = { version = "0.1", default-features = false } -bytes = { version = "1.0", default-features = false } +bytes = { version = "1.0", default-features = false, features = ["serde"] } ed25519 = { version = "1.3", default-features = false } ed25519-dalek = { version = "1", default-features = false, features = ["u64_backend"] } futures = { version = "0.3", default-features = false } diff --git a/tendermint/src/abci.rs b/tendermint/src/abci.rs index 9a0f84761..cb039806d 100644 --- a/tendermint/src/abci.rs +++ b/tendermint/src/abci.rs @@ -1,29 +1,53 @@ -//! Application BlockChain Interface (ABCI) +//! Application BlockChain Interface ([ABCI]) is the interface between Tendermint +//! (a consensus engine for Byzantine-fault-tolerant replication of a state +//! machine) and an application (the state machine to be replicated). //! -//! NOTE: This module contains types for ABCI responses as consumed from RPC -//! endpoints. It does not contain an ABCI protocol implementation. +//! Using ABCI involves writing an application driven by ABCI methods, exposing +//! that application as an ABCI server, and having Tendermint connect to the +//! server as an ABCI client. //! -//! For that, see: +//! This module does not include an ABCI server implementation itself. Instead, +//! it provides a common set of Rust domain types that model the ABCI protocol, +//! which can be used by both ABCI applications and ABCI server implementations. //! -//! +//! One ABCI server implementation is provided by the [`tendermint_abci`][tmabci] +//! crate. +//! +//! Each ABCI method corresponds to a request/response pair. ABCI requests are +//! modeled by the [`Request`] enum, and responses are modeled by the +//! [`Response`] enum. As described in the [methods and types][mat] page, ABCI +//! methods are split into four categories. Tendermint opens one ABCI connection +//! for each category of messages. These categories are modeled by the +//! [`MethodKind`] enum and by per-category request and response enums: +//! +//! * [`ConsensusRequest`] / [`ConsensusResponse`] for [`MethodKind::Consensus`] methods; +//! * [`MempoolRequest`] / [`MempoolResponse`] for [`MethodKind::Mempool`] methods; +//! * [`InfoRequest`] / [`InfoResponse`] for [`MethodKind::Info`] methods; +//! * [`SnapshotRequest`] / [`SnapshotResponse`] for [`MethodKind::Snapshot`] methods. +//! +//! The domain types in this module have conversions to and from the Protobuf +//! types defined in the [`tendermint_proto`] crate. These conversions are +//! required for ABCI server implementations, which use the protobufs to +//! communicate with Tendermint, but should not be required for ABCI +//! applications, which should use the domain types in an interface defined by +//! their choice of ABCI server implementation. +//! +//! [ABCI]: https://docs.tendermint.com/master/spec/abci/ +//! [mat]: https://docs.tendermint.com/master/spec/abci/abci.html +//! [tmabci]: https://github.com/informalsystems/tendermint-rs/tree/master/abci + +mod event; +mod kind; + +pub mod request; +pub mod response; +pub mod types; -mod code; -mod data; -mod gas; -mod info; -mod log; -mod path; -pub mod responses; -pub mod tag; -pub mod transaction; +pub use event::{Event, EventAttribute, EventAttributeIndexExt}; +#[doc(inline)] pub use self::{ - code::Code, - data::Data, - gas::Gas, - info::Info, - log::Log, - path::Path, - responses::{DeliverTx, Event, Responses}, - transaction::Transaction, + kind::MethodKind, + request::{ConsensusRequest, InfoRequest, MempoolRequest, Request, SnapshotRequest}, + response::{ConsensusResponse, InfoResponse, MempoolResponse, Response, SnapshotResponse}, }; diff --git a/tendermint/src/abci/doc/request-applysnapshotchunk.md b/tendermint/src/abci/doc/request-applysnapshotchunk.md new file mode 100644 index 000000000..fe1c35598 --- /dev/null +++ b/tendermint/src/abci/doc/request-applysnapshotchunk.md @@ -0,0 +1,21 @@ +Applies a snapshot chunk. + +The application can choose to refetch chunks and/or ban P2P peers as +appropriate. Tendermint will not do this unless instructed by the +application. + +The application may want to verify each chunk, e.g., by attaching chunk +hashes in [`Snapshot::metadata`] and/or incrementally verifying contents +against `app_hash`. + +When all chunks have been accepted, Tendermint will make an ABCI [`Info`] +request to verify that `last_block_app_hash` and `last_block_height` match +the expected values, and record the `app_version` in the node state. It then +switches to fast sync or consensus and joins the network. + +If Tendermint is unable to retrieve the next chunk after some time (e.g., +because no suitable peers are available), it will reject the snapshot and try +a different one via `OfferSnapshot`. The application should be prepared to +reset and accept it or abort as appropriate. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#applysnapshotchunk) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-beginblock.md b/tendermint/src/abci/doc/request-beginblock.md new file mode 100644 index 000000000..44b98920a --- /dev/null +++ b/tendermint/src/abci/doc/request-beginblock.md @@ -0,0 +1,6 @@ +Signals the beginning of a new block. + +Called prior to any [`DeliverTx`]s. The `header` contains the height, +timestamp, and more -- it exactly matches the Tendermint block header. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#beginblock) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-checktx.md b/tendermint/src/abci/doc/request-checktx.md new file mode 100644 index 000000000..7d97a6287 --- /dev/null +++ b/tendermint/src/abci/doc/request-checktx.md @@ -0,0 +1,11 @@ +Check whether a transaction should be included in the mempool. + +`CheckTx` is not involved in processing blocks, only in deciding whether a +transaction should be included in the mempool. Every node runs `CheckTx` +before adding a transaction to its local mempool. The transaction may come +from an external user or another node. `CheckTx` need not execute the +transaction in full, but can instead perform lightweight or statateful +validation (e.g., checking signatures or account balances) instead of more +expensive checks (like running code in a virtual machine). + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#checktx) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-commit.md b/tendermint/src/abci/doc/request-commit.md new file mode 100644 index 000000000..0013b8302 --- /dev/null +++ b/tendermint/src/abci/doc/request-commit.md @@ -0,0 +1,4 @@ +Signals the application that it can write the queued state transitions +from the block to its state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#commit) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-delivertx.md b/tendermint/src/abci/doc/request-delivertx.md new file mode 100644 index 000000000..4d449cc56 --- /dev/null +++ b/tendermint/src/abci/doc/request-delivertx.md @@ -0,0 +1,3 @@ +Execute a transaction against the application state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#delivertx) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-echo.md b/tendermint/src/abci/doc/request-echo.md new file mode 100644 index 000000000..92658169f --- /dev/null +++ b/tendermint/src/abci/doc/request-echo.md @@ -0,0 +1,3 @@ +Echoes a string to test an ABCI implementation. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#echo) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-endblock.md b/tendermint/src/abci/doc/request-endblock.md new file mode 100644 index 000000000..6e23b6d7c --- /dev/null +++ b/tendermint/src/abci/doc/request-endblock.md @@ -0,0 +1,5 @@ +Signals the end of a block. + +Called after all transactions, and prior to each `Commit`. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#endblock) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-flush.md b/tendermint/src/abci/doc/request-flush.md new file mode 100644 index 000000000..c556d8b2d --- /dev/null +++ b/tendermint/src/abci/doc/request-flush.md @@ -0,0 +1,3 @@ +Indicates that any pending requests should be completed and their responses flushed. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#flush) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-info.md b/tendermint/src/abci/doc/request-info.md new file mode 100644 index 000000000..471ec36c7 --- /dev/null +++ b/tendermint/src/abci/doc/request-info.md @@ -0,0 +1,3 @@ +Requests information about the application state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#info) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-initchain.md b/tendermint/src/abci/doc/request-initchain.md new file mode 100644 index 000000000..49180f351 --- /dev/null +++ b/tendermint/src/abci/doc/request-initchain.md @@ -0,0 +1,3 @@ +Called on genesis to initialize chain state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#initchain) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-listsnapshots.md b/tendermint/src/abci/doc/request-listsnapshots.md new file mode 100644 index 000000000..bc89accad --- /dev/null +++ b/tendermint/src/abci/doc/request-listsnapshots.md @@ -0,0 +1,3 @@ +Asks the application for a list of snapshots. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#listsnapshots) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-loadsnapshotchunk.md b/tendermint/src/abci/doc/request-loadsnapshotchunk.md new file mode 100644 index 000000000..70b686d41 --- /dev/null +++ b/tendermint/src/abci/doc/request-loadsnapshotchunk.md @@ -0,0 +1,3 @@ +Used during state sync to retrieve snapshot chunks from peers. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#loadsnapshotchunk) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-offersnapshot.md b/tendermint/src/abci/doc/request-offersnapshot.md new file mode 100644 index 000000000..db0e60b17 --- /dev/null +++ b/tendermint/src/abci/doc/request-offersnapshot.md @@ -0,0 +1,20 @@ +Offers a list of snapshots to the application. + +`OfferSnapshot` is called when bootstrapping a node using state sync. The +application may accept or reject snapshots as appropriate. Upon accepting, +Tendermint will retrieve and apply snapshot chunks via +[`ApplySnapshotChunk`]. The application may also choose to reject a snapshot +in the chunk response, in which case it should be prepared to accept further +`OfferSnapshot` calls. + +Only `app_hash` can be trusted, as it has been verified by the light client. +Any other data can be spoofed by adversaries, so applications should employ +additional verification schemes to avoid denial-of-service attacks. The +verified `app_hash` is automatically checked against the restored application +at the end of snapshot restoration. + +See also the [`Snapshot`] data type and the [ABCI state sync documentation][ssd]. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#offersnapshot) + +[ssd]: https://docs.tendermint.com/master/spec/abci/apps.html#state-sync \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-query.md b/tendermint/src/abci/doc/request-query.md new file mode 100644 index 000000000..5d061c54e --- /dev/null +++ b/tendermint/src/abci/doc/request-query.md @@ -0,0 +1,3 @@ +Queries for data from the application at current or past height. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#query) \ No newline at end of file diff --git a/tendermint/src/abci/doc/request-setoption.md b/tendermint/src/abci/doc/request-setoption.md new file mode 100644 index 000000000..f3c869b34 --- /dev/null +++ b/tendermint/src/abci/doc/request-setoption.md @@ -0,0 +1 @@ +Undocumented, non-deterministic, was removed from Tendermint in 0.35. \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-applysnapshotchunk.md b/tendermint/src/abci/doc/response-applysnapshotchunk.md new file mode 100644 index 000000000..bffabe7af --- /dev/null +++ b/tendermint/src/abci/doc/response-applysnapshotchunk.md @@ -0,0 +1,7 @@ +Returns the result of applying a snapshot chunk and associated data. + +The application can choose to refetch chunks and/or ban P2P peers as +appropriate. Tendermint will not do this unless instructed by the +application. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#applysnapshotchunk) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-beginblock.md b/tendermint/src/abci/doc/response-beginblock.md new file mode 100644 index 000000000..255efd098 --- /dev/null +++ b/tendermint/src/abci/doc/response-beginblock.md @@ -0,0 +1,3 @@ +Returns events that occurred when beginning a new block. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#beginblock) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-checktx.md b/tendermint/src/abci/doc/response-checktx.md new file mode 100644 index 000000000..cd31b1703 --- /dev/null +++ b/tendermint/src/abci/doc/response-checktx.md @@ -0,0 +1,3 @@ +Returns the result of checking a transaction for mempool inclusion. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#checktx) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-commit.md b/tendermint/src/abci/doc/response-commit.md new file mode 100644 index 000000000..822aab48d --- /dev/null +++ b/tendermint/src/abci/doc/response-commit.md @@ -0,0 +1,3 @@ +Returns the result of persisting the application state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#commit) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-delivertx.md b/tendermint/src/abci/doc/response-delivertx.md new file mode 100644 index 000000000..cb83a6fd9 --- /dev/null +++ b/tendermint/src/abci/doc/response-delivertx.md @@ -0,0 +1,4 @@ +Returns events that occurred while executing a transaction against the +application state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#delivertx) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-echo.md b/tendermint/src/abci/doc/response-echo.md new file mode 100644 index 000000000..92658169f --- /dev/null +++ b/tendermint/src/abci/doc/response-echo.md @@ -0,0 +1,3 @@ +Echoes a string to test an ABCI implementation. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#echo) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-endblock.md b/tendermint/src/abci/doc/response-endblock.md new file mode 100644 index 000000000..062cabb84 --- /dev/null +++ b/tendermint/src/abci/doc/response-endblock.md @@ -0,0 +1,3 @@ +Returns validator updates that occur after the end of a block. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#endblock) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-exception.md b/tendermint/src/abci/doc/response-exception.md new file mode 100644 index 000000000..5d8fb6c67 --- /dev/null +++ b/tendermint/src/abci/doc/response-exception.md @@ -0,0 +1 @@ +Returns an exception (undocumented, nondeterministic). \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-flush.md b/tendermint/src/abci/doc/response-flush.md new file mode 100644 index 000000000..6c411e1bf --- /dev/null +++ b/tendermint/src/abci/doc/response-flush.md @@ -0,0 +1,3 @@ +Indicates that all pending requests have been completed with their responses flushed. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#flush) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-info.md b/tendermint/src/abci/doc/response-info.md new file mode 100644 index 000000000..e0c64b1f5 --- /dev/null +++ b/tendermint/src/abci/doc/response-info.md @@ -0,0 +1,3 @@ +Returns information about the application state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#info) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-initchain.md b/tendermint/src/abci/doc/response-initchain.md new file mode 100644 index 000000000..b7ea62de7 --- /dev/null +++ b/tendermint/src/abci/doc/response-initchain.md @@ -0,0 +1,3 @@ +Returned on genesis after initializing chain state. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#initchain) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-listsnapshots.md b/tendermint/src/abci/doc/response-listsnapshots.md new file mode 100644 index 000000000..48255b800 --- /dev/null +++ b/tendermint/src/abci/doc/response-listsnapshots.md @@ -0,0 +1,3 @@ +Returns a list of local state snapshots. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#listsnapshots) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-loadsnapshotchunk.md b/tendermint/src/abci/doc/response-loadsnapshotchunk.md new file mode 100644 index 000000000..2eaf1c614 --- /dev/null +++ b/tendermint/src/abci/doc/response-loadsnapshotchunk.md @@ -0,0 +1,3 @@ +Returns a snapshot chunk from the application. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#loadsnapshotchunk) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-offersnapshot.md b/tendermint/src/abci/doc/response-offersnapshot.md new file mode 100644 index 000000000..0da7a66fa --- /dev/null +++ b/tendermint/src/abci/doc/response-offersnapshot.md @@ -0,0 +1,7 @@ +Returns the application's response to a snapshot offer. + +See also the [`Snapshot`] data type and the [ABCI state sync documentation][ssd]. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#offersnapshot) + +[ssd]: https://docs.tendermint.com/master/spec/abci/apps.html#state-sync \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-query.md b/tendermint/src/abci/doc/response-query.md new file mode 100644 index 000000000..57eb3bf4a --- /dev/null +++ b/tendermint/src/abci/doc/response-query.md @@ -0,0 +1,3 @@ +Returns data queried from the application. + +[ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#query) \ No newline at end of file diff --git a/tendermint/src/abci/doc/response-setoption.md b/tendermint/src/abci/doc/response-setoption.md new file mode 100644 index 000000000..f3c869b34 --- /dev/null +++ b/tendermint/src/abci/doc/response-setoption.md @@ -0,0 +1 @@ +Undocumented, non-deterministic, was removed from Tendermint in 0.35. \ No newline at end of file diff --git a/tendermint/src/abci/event.rs b/tendermint/src/abci/event.rs new file mode 100644 index 000000000..13c47ae39 --- /dev/null +++ b/tendermint/src/abci/event.rs @@ -0,0 +1,185 @@ +use crate::prelude::*; + +/// An event that occurred while processing a request. +/// +/// Application developers can attach additional information to +/// [`BeginBlock`](super::response::BeginBlock), +/// [`EndBlock`](super::response::EndBlock), +/// [`CheckTx`](super::response::CheckTx), and +/// [`DeliverTx`](super::response::DeliverTx) responses. Later, transactions may +/// be queried using these events. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Event { + /// The kind of event. + /// + /// Tendermint calls this the `type`, but we use `kind` to avoid confusion + /// with Rust types and follow Rust conventions. + pub kind: String, + /// A list of [`EventAttribute`]s describing the event. + pub attributes: Vec, +} + +impl Event { + /// Construct an event from generic data. + /// + /// The `From` impls on [`EventAttribute`] and the [`EventAttributeIndexExt`] + /// trait allow ergonomic event construction, as in this example: + /// + /// ``` + /// use tendermint::abci::{Event, EventAttributeIndexExt}; + /// + /// let event = Event::new( + /// "app", + /// vec![ + /// ("key1", "value1").index(), + /// ("key2", "value2").index(), + /// ("key3", "value3").no_index(), // will not be indexed + /// ], + /// ); + /// ``` + // XXX(hdevalence): remove vec! from example after https://github.com/rust-lang/rust/pull/65819 + pub fn new(kind: K, attributes: I) -> Self + where + K: Into, + I: IntoIterator, + I::Item: Into, + { + Self { + kind: kind.into(), + attributes: attributes.into_iter().map(Into::into).collect(), + } + } +} + +/// A key-value pair describing an [`Event`]. +/// +/// Generic methods are provided for more ergonomic attribute construction, see +/// [`Event::new`] for details. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct EventAttribute { + /// The event key. + pub key: String, + /// The event value. + pub value: String, + /// Whether Tendermint's indexer should index this event. + /// + /// **This field is nondeterministic**. + pub index: bool, +} + +impl, V: Into> From<(K, V, bool)> for EventAttribute { + fn from((key, value, index): (K, V, bool)) -> Self { + EventAttribute { + key: key.into(), + value: value.into(), + index, + } + } +} + +/// Adds convenience methods to tuples for more ergonomic [`EventAttribute`] +/// construction. +/// +/// See [`Event::new`] for details. +#[allow(missing_docs)] +pub trait EventAttributeIndexExt: private::Sealed { + type Key; + type Value; + + /// Indicate that this key/value pair should be indexed by Tendermint. + fn index(self) -> (Self::Key, Self::Value, bool); + /// Indicate that this key/value pair should not be indexed by Tendermint. + fn no_index(self) -> (Self::Key, Self::Value, bool); +} + +impl, V: Into> EventAttributeIndexExt for (K, V) { + type Key = K; + type Value = V; + fn index(self) -> (K, V, bool) { + let (key, value) = self; + (key, value, true) + } + fn no_index(self) -> (K, V, bool) { + let (key, value) = self; + (key, value, false) + } +} + +mod private { + use crate::prelude::*; + + pub trait Sealed {} + + impl, V: Into> Sealed for (K, V) {} +} + +impl, V: Into> From<(K, V)> for EventAttribute { + fn from((key, value): (K, V)) -> Self { + (key, value, false).into() + } +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::EventAttribute { + fn from(event: EventAttribute) -> Self { + Self { + key: event.key.into(), + value: event.value.into(), + index: event.index, + } + } +} + +impl TryFrom for EventAttribute { + type Error = crate::Error; + + fn try_from(event: pb::EventAttribute) -> Result { + // We insist that keys and values are strings, like tm 0.35 did. + Ok(Self { + key: String::from_utf8(event.key.to_vec()) + .map_err(|e| crate::Error::parse(e.to_string()))?, + value: String::from_utf8(event.value.to_vec()) + .map_err(|e| crate::Error::parse(e.to_string()))?, + index: event.index, + }) + } +} + +impl Protobuf for EventAttribute {} + +impl From for pb::Event { + fn from(event: Event) -> Self { + Self { + r#type: event.kind, + attributes: event.attributes.into_iter().map(Into::into).collect(), + } + } +} + +impl TryFrom for Event { + type Error = crate::Error; + + fn try_from(event: pb::Event) -> Result { + Ok(Self { + kind: event.r#type, + attributes: event + .attributes + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +impl Protobuf for Event {} diff --git a/tendermint/src/abci/kind.rs b/tendermint/src/abci/kind.rs new file mode 100644 index 000000000..065a01c9a --- /dev/null +++ b/tendermint/src/abci/kind.rs @@ -0,0 +1,23 @@ +/// A category of ABCI method. +/// +/// ABCI methods are split into four categories. Tendermint opens one ABCI +/// connection for each category and refers to these categories as *connections*, +/// but nothing actually restricts an ABCI connection from calling methods in +/// multiple categories. +/// +/// This enum breaks out the `Flush` method as a distinct category, since it is +/// used to control the execution of other methods. +pub enum MethodKind { + /// A consensus method, driven by the consensus protocol and responsible for + /// block execution. + Consensus, + /// A mempool method, used for validating new transactions before they're + /// shared or included in a block. + Mempool, + /// A snapshot method, used for serving and restoring state snapshots. + Snapshot, + /// An info method, used for initialization and user queries. + Info, + /// The flush method requests that all pending method requests are fully executed. + Flush, +} diff --git a/tendermint/src/abci/request.rs b/tendermint/src/abci/request.rs new file mode 100644 index 000000000..990b567d9 --- /dev/null +++ b/tendermint/src/abci/request.rs @@ -0,0 +1,312 @@ +//! ABCI requests and request data. +//! +//! The [`Request`] enum records all possible ABCI requests. Requests that +//! contain data are modeled as a separate struct, to avoid duplication of field +//! definitions. + +// IMPORTANT NOTE ON DOCUMENTATION: +// +// The documentation for each request type is adapted from the ABCI Methods and +// Types spec document. However, the same logical request may appear three +// times, as a struct with the request data, as a Request variant, and as a +// CategoryRequest variant. +// +// To avoid duplication, this documentation is stored in the doc/ folder in +// individual .md files, which are pasted onto the relevant items using #[doc = +// include_str!(...)]. +// +// This is also why certain submodules have #[allow(unused)] imports to bring +// items into scope for doc links, rather than changing the doc links -- it +// allows the doc comments to be copied without editing. +use core::convert::{TryFrom, TryInto}; + +// bring into scope for doc links +#[allow(unused)] +use super::types::Snapshot; +use super::MethodKind; +use crate::{prelude::*, Error}; + +mod apply_snapshot_chunk; +mod begin_block; +mod check_tx; +mod deliver_tx; +mod echo; +mod end_block; +mod info; +mod init_chain; +mod load_snapshot_chunk; +mod offer_snapshot; +mod query; +mod set_option; + +pub use apply_snapshot_chunk::ApplySnapshotChunk; +pub use begin_block::BeginBlock; +pub use check_tx::{CheckTx, CheckTxKind}; +pub use deliver_tx::DeliverTx; +pub use echo::Echo; +pub use end_block::EndBlock; +pub use info::Info; +pub use init_chain::InitChain; +pub use load_snapshot_chunk::LoadSnapshotChunk; +pub use offer_snapshot::OfferSnapshot; +pub use query::Query; +pub use set_option::SetOption; + +/// All possible ABCI requests. +#[allow(clippy::large_enum_variant)] +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Request { + #[doc = include_str!("doc/request-echo.md")] + Echo(Echo), + #[doc = include_str!("doc/request-flush.md")] + Flush, + #[doc = include_str!("doc/request-info.md")] + Info(Info), + #[doc = include_str!("doc/request-setoption.md")] + SetOption(SetOption), + #[doc = include_str!("doc/request-initchain.md")] + InitChain(InitChain), + #[doc = include_str!("doc/request-query.md")] + Query(Query), + #[doc = include_str!("doc/request-beginblock.md")] + BeginBlock(BeginBlock), + #[doc = include_str!("doc/request-checktx.md")] + CheckTx(CheckTx), + #[doc = include_str!("doc/request-delivertx.md")] + DeliverTx(DeliverTx), + #[doc = include_str!("doc/request-endblock.md")] + EndBlock(EndBlock), + #[doc = include_str!("doc/request-commit.md")] + Commit, + #[doc = include_str!("doc/request-listsnapshots.md")] + ListSnapshots, + #[doc = include_str!("doc/request-offersnapshot.md")] + OfferSnapshot(OfferSnapshot), + #[doc = include_str!("doc/request-loadsnapshotchunk.md")] + LoadSnapshotChunk(LoadSnapshotChunk), + #[doc = include_str!("doc/request-applysnapshotchunk.md")] + ApplySnapshotChunk(ApplySnapshotChunk), +} + +impl Request { + /// Get the method kind for this request. + pub fn kind(&self) -> MethodKind { + use Request::*; + match self { + Flush => MethodKind::Flush, + InitChain(_) => MethodKind::Consensus, + BeginBlock(_) => MethodKind::Consensus, + DeliverTx(_) => MethodKind::Consensus, + EndBlock(_) => MethodKind::Consensus, + Commit => MethodKind::Consensus, + CheckTx(_) => MethodKind::Mempool, + ListSnapshots => MethodKind::Snapshot, + OfferSnapshot(_) => MethodKind::Snapshot, + LoadSnapshotChunk(_) => MethodKind::Snapshot, + ApplySnapshotChunk(_) => MethodKind::Snapshot, + Info(_) => MethodKind::Info, + Query(_) => MethodKind::Info, + Echo(_) => MethodKind::Info, + SetOption(_) => MethodKind::Info, + } + } +} + +/// The consensus category of ABCI requests. +#[allow(clippy::large_enum_variant)] +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ConsensusRequest { + #[doc = include_str!("doc/request-initchain.md")] + InitChain(InitChain), + #[doc = include_str!("doc/request-beginblock.md")] + BeginBlock(BeginBlock), + #[doc = include_str!("doc/request-delivertx.md")] + DeliverTx(DeliverTx), + #[doc = include_str!("doc/request-endblock.md")] + EndBlock(EndBlock), + #[doc = include_str!("doc/request-commit.md")] + Commit, +} + +impl From for Request { + fn from(req: ConsensusRequest) -> Self { + match req { + ConsensusRequest::InitChain(x) => Self::InitChain(x), + ConsensusRequest::BeginBlock(x) => Self::BeginBlock(x), + ConsensusRequest::DeliverTx(x) => Self::DeliverTx(x), + ConsensusRequest::EndBlock(x) => Self::EndBlock(x), + ConsensusRequest::Commit => Self::Commit, + } + } +} + +impl TryFrom for ConsensusRequest { + type Error = Error; + fn try_from(req: Request) -> Result { + match req { + Request::InitChain(x) => Ok(Self::InitChain(x)), + Request::BeginBlock(x) => Ok(Self::BeginBlock(x)), + Request::DeliverTx(x) => Ok(Self::DeliverTx(x)), + Request::EndBlock(x) => Ok(Self::EndBlock(x)), + Request::Commit => Ok(Self::Commit), + _ => Err(Error::invalid_abci_request_type()), + } + } +} + +/// The mempool category of ABCI requests. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum MempoolRequest { + #[doc = include_str!("doc/request-checktx.md")] + CheckTx(CheckTx), +} + +impl From for Request { + fn from(req: MempoolRequest) -> Self { + match req { + MempoolRequest::CheckTx(x) => Self::CheckTx(x), + } + } +} + +impl TryFrom for MempoolRequest { + type Error = Error; + fn try_from(req: Request) -> Result { + match req { + Request::CheckTx(x) => Ok(Self::CheckTx(x)), + _ => Err(Error::invalid_abci_request_type()), + } + } +} + +/// The info category of ABCI requests. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum InfoRequest { + #[doc = include_str!("doc/request-info.md")] + Info(Info), + #[doc = include_str!("doc/request-query.md")] + Query(Query), + #[doc = include_str!("doc/request-echo.md")] + Echo(Echo), + #[doc = include_str!("doc/request-setoption.md")] + SetOption(SetOption), +} + +impl From for Request { + fn from(req: InfoRequest) -> Self { + match req { + InfoRequest::Info(x) => Self::Info(x), + InfoRequest::Query(x) => Self::Query(x), + InfoRequest::Echo(x) => Self::Echo(x), + InfoRequest::SetOption(x) => Self::SetOption(x), + } + } +} + +impl TryFrom for InfoRequest { + type Error = Error; + fn try_from(req: Request) -> Result { + match req { + Request::Info(x) => Ok(Self::Info(x)), + Request::Query(x) => Ok(Self::Query(x)), + Request::Echo(x) => Ok(Self::Echo(x)), + Request::SetOption(x) => Ok(Self::SetOption(x)), + _ => Err(Error::invalid_abci_request_type()), + } + } +} + +/// The snapshot category of ABCI requests. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum SnapshotRequest { + #[doc = include_str!("doc/request-listsnapshots.md")] + ListSnapshots, + #[doc = include_str!("doc/request-offersnapshot.md")] + OfferSnapshot(OfferSnapshot), + #[doc = include_str!("doc/request-loadsnapshotchunk.md")] + LoadSnapshotChunk(LoadSnapshotChunk), + #[doc = include_str!("doc/request-applysnapshotchunk.md")] + ApplySnapshotChunk(ApplySnapshotChunk), +} + +impl From for Request { + fn from(req: SnapshotRequest) -> Self { + match req { + SnapshotRequest::ListSnapshots => Self::ListSnapshots, + SnapshotRequest::OfferSnapshot(x) => Self::OfferSnapshot(x), + SnapshotRequest::LoadSnapshotChunk(x) => Self::LoadSnapshotChunk(x), + SnapshotRequest::ApplySnapshotChunk(x) => Self::ApplySnapshotChunk(x), + } + } +} + +impl TryFrom for SnapshotRequest { + type Error = Error; + fn try_from(req: Request) -> Result { + match req { + Request::ListSnapshots => Ok(Self::ListSnapshots), + Request::OfferSnapshot(x) => Ok(Self::OfferSnapshot(x)), + Request::LoadSnapshotChunk(x) => Ok(Self::LoadSnapshotChunk(x)), + Request::ApplySnapshotChunk(x) => Ok(Self::ApplySnapshotChunk(x)), + _ => Err(Error::invalid_abci_request_type()), + } + } +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::Request { + fn from(request: Request) -> pb::Request { + use pb::request::Value; + let value = match request { + Request::Echo(x) => Some(Value::Echo(x.into())), + Request::Flush => Some(Value::Flush(Default::default())), + Request::Info(x) => Some(Value::Info(x.into())), + Request::SetOption(x) => Some(Value::SetOption(x.into())), + Request::InitChain(x) => Some(Value::InitChain(x.into())), + Request::Query(x) => Some(Value::Query(x.into())), + Request::BeginBlock(x) => Some(Value::BeginBlock(x.into())), + Request::CheckTx(x) => Some(Value::CheckTx(x.into())), + Request::DeliverTx(x) => Some(Value::DeliverTx(x.into())), + Request::EndBlock(x) => Some(Value::EndBlock(x.into())), + Request::Commit => Some(Value::Commit(Default::default())), + Request::ListSnapshots => Some(Value::ListSnapshots(Default::default())), + Request::OfferSnapshot(x) => Some(Value::OfferSnapshot(x.into())), + Request::LoadSnapshotChunk(x) => Some(Value::LoadSnapshotChunk(x.into())), + Request::ApplySnapshotChunk(x) => Some(Value::ApplySnapshotChunk(x.into())), + }; + pb::Request { value } + } +} + +impl TryFrom for Request { + type Error = Error; + + fn try_from(request: pb::Request) -> Result { + use pb::request::Value; + match request.value { + Some(Value::Echo(x)) => Ok(Request::Echo(x.try_into()?)), + Some(Value::Flush(pb::RequestFlush {})) => Ok(Request::Flush), + Some(Value::Info(x)) => Ok(Request::Info(x.try_into()?)), + Some(Value::SetOption(x)) => Ok(Request::SetOption(x.try_into()?)), + Some(Value::InitChain(x)) => Ok(Request::InitChain(x.try_into()?)), + Some(Value::Query(x)) => Ok(Request::Query(x.try_into()?)), + Some(Value::BeginBlock(x)) => Ok(Request::BeginBlock(x.try_into()?)), + Some(Value::CheckTx(x)) => Ok(Request::CheckTx(x.try_into()?)), + Some(Value::DeliverTx(x)) => Ok(Request::DeliverTx(x.try_into()?)), + Some(Value::EndBlock(x)) => Ok(Request::EndBlock(x.try_into()?)), + Some(Value::Commit(pb::RequestCommit {})) => Ok(Request::Commit), + Some(Value::ListSnapshots(pb::RequestListSnapshots {})) => Ok(Request::ListSnapshots), + Some(Value::OfferSnapshot(x)) => Ok(Request::OfferSnapshot(x.try_into()?)), + Some(Value::LoadSnapshotChunk(x)) => Ok(Request::LoadSnapshotChunk(x.try_into()?)), + Some(Value::ApplySnapshotChunk(x)) => Ok(Request::ApplySnapshotChunk(x.try_into()?)), + None => Err(crate::Error::missing_data()), + } + } +} + +impl Protobuf for Request {} diff --git a/tendermint/src/abci/request/apply_snapshot_chunk.rs b/tendermint/src/abci/request/apply_snapshot_chunk.rs new file mode 100644 index 000000000..f893cbb47 --- /dev/null +++ b/tendermint/src/abci/request/apply_snapshot_chunk.rs @@ -0,0 +1,69 @@ +use bytes::Bytes; + +// bring into scope for doc links +#[allow(unused)] +use super::{super::types::Snapshot, Info, LoadSnapshotChunk}; +use crate::prelude::*; + +/// Applies a snapshot chunk. +/// +/// The application can choose to refetch chunks and/or ban P2P peers as +/// appropriate. Tendermint will not do this unless instructed by the +/// application. +/// +/// The application may want to verify each chunk, e.g., by attaching chunk +/// hashes in [`Snapshot::metadata`] and/or incrementally verifying contents +/// against `app_hash`. +/// +/// When all chunks have been accepted, Tendermint will make an ABCI [`Info`] +/// request to verify that `last_block_app_hash` and `last_block_height` match +/// the expected values, and record the `app_version` in the node state. It then +/// switches to fast sync or consensus and joins the network. +/// +/// If Tendermint is unable to retrieve the next chunk after some time (e.g., +/// because no suitable peers are available), it will reject the snapshot and try +/// a different one via `OfferSnapshot`. The application should be prepared to +/// reset and accept it or abort as appropriate. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#applysnapshotchunk) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ApplySnapshotChunk { + /// The chunk index, starting from `0`. Tendermint applies chunks sequentially. + pub index: u32, + /// The binary chunk contents, as returned by [`LoadSnapshotChunk`]. + pub chunk: Bytes, + /// The P2P ID of the node who sent this chunk. + pub sender: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestApplySnapshotChunk { + fn from(apply_snapshot_chunk: ApplySnapshotChunk) -> Self { + Self { + index: apply_snapshot_chunk.index, + chunk: apply_snapshot_chunk.chunk, + sender: apply_snapshot_chunk.sender, + } + } +} + +impl TryFrom for ApplySnapshotChunk { + type Error = crate::Error; + + fn try_from(apply_snapshot_chunk: pb::RequestApplySnapshotChunk) -> Result { + Ok(Self { + index: apply_snapshot_chunk.index, + chunk: apply_snapshot_chunk.chunk, + sender: apply_snapshot_chunk.sender, + }) + } +} + +impl Protobuf for ApplySnapshotChunk {} diff --git a/tendermint/src/abci/request/begin_block.rs b/tendermint/src/abci/request/begin_block.rs new file mode 100644 index 000000000..13ad8301c --- /dev/null +++ b/tendermint/src/abci/request/begin_block.rs @@ -0,0 +1,73 @@ +use bytes::Bytes; + +use super::super::types::{Evidence, LastCommitInfo}; +// bring into scope for doc links +#[allow(unused)] +use super::DeliverTx; +use crate::{block, prelude::*, Error}; + +#[doc = include_str!("../doc/request-beginblock.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct BeginBlock { + /// The block's hash. + /// + /// This can be derived from the block header. + pub hash: Bytes, + /// The block header. + pub header: block::Header, + /// Information about the last commit. + /// + /// This includes the round, the list of validators, and which validators + /// signed the last block. + pub last_commit_info: LastCommitInfo, + /// Evidence of validator misbehavior. + pub byzantine_validators: Vec, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestBeginBlock { + fn from(begin_block: BeginBlock) -> Self { + Self { + hash: begin_block.hash, + header: Some(begin_block.header.into()), + last_commit_info: Some(begin_block.last_commit_info.into()), + byzantine_validators: begin_block + .byzantine_validators + .into_iter() + .map(Into::into) + .collect(), + } + } +} + +impl TryFrom for BeginBlock { + type Error = Error; + + fn try_from(begin_block: pb::RequestBeginBlock) -> Result { + Ok(Self { + hash: begin_block.hash, + header: begin_block + .header + .ok_or_else(Error::missing_header)? + .try_into()?, + last_commit_info: begin_block + .last_commit_info + .ok_or_else(Error::missing_last_commit_info)? + .try_into()?, + byzantine_validators: begin_block + .byzantine_validators + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +impl Protobuf for BeginBlock {} diff --git a/tendermint/src/abci/request/check_tx.rs b/tendermint/src/abci/request/check_tx.rs new file mode 100644 index 000000000..0fbf8f10f --- /dev/null +++ b/tendermint/src/abci/request/check_tx.rs @@ -0,0 +1,71 @@ +use bytes::Bytes; + +use crate::prelude::*; + +#[doc = include_str!("../doc/request-checktx.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct CheckTx { + /// The transaction bytes. + pub tx: Bytes, + /// The kind of check to perform. + /// + /// Note: this field is called `type` in the protobuf, but we call it `kind` + /// to avoid the Rust keyword. + pub kind: CheckTxKind, +} + +/// The possible kinds of [`CheckTx`] checks. +/// +/// Note: the +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#checktx) +/// calls this `CheckTxType`, but we follow the Rust convention and name it `CheckTxKind` +/// to avoid confusion with Rust types. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(i32)] +pub enum CheckTxKind { + /// A full check is required (the default). + New = 0, + /// Indicates that the mempool is initiating a recheck of the transaction. + Recheck = 1, +} + +impl Default for CheckTxKind { + fn default() -> Self { + CheckTxKind::New + } +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestCheckTx { + fn from(check_tx: CheckTx) -> Self { + Self { + tx: check_tx.tx, + r#type: check_tx.kind as i32, + } + } +} + +impl TryFrom for CheckTx { + type Error = crate::Error; + + fn try_from(check_tx: pb::RequestCheckTx) -> Result { + let kind = match check_tx.r#type { + 0 => CheckTxKind::New, + 1 => CheckTxKind::Recheck, + _ => return Err(crate::Error::unsupported_check_tx_type()), + }; + Ok(Self { + tx: check_tx.tx, + kind, + }) + } +} + +impl Protobuf for CheckTx {} diff --git a/tendermint/src/abci/request/deliver_tx.rs b/tendermint/src/abci/request/deliver_tx.rs new file mode 100644 index 000000000..4c9bc6d50 --- /dev/null +++ b/tendermint/src/abci/request/deliver_tx.rs @@ -0,0 +1,34 @@ +use bytes::Bytes; + +use crate::prelude::*; + +#[doc = include_str!("../doc/request-delivertx.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct DeliverTx { + /// The bytes of the transaction to execute. + pub tx: Bytes, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestDeliverTx { + fn from(deliver_tx: DeliverTx) -> Self { + Self { tx: deliver_tx.tx } + } +} + +impl TryFrom for DeliverTx { + type Error = crate::Error; + + fn try_from(deliver_tx: pb::RequestDeliverTx) -> Result { + Ok(Self { tx: deliver_tx.tx }) + } +} + +impl Protobuf for DeliverTx {} diff --git a/tendermint/src/abci/request/echo.rs b/tendermint/src/abci/request/echo.rs new file mode 100644 index 000000000..3ae62456e --- /dev/null +++ b/tendermint/src/abci/request/echo.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/request-echo.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Echo { + /// The message to send back. + pub message: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestEcho { + fn from(echo: Echo) -> Self { + Self { + message: echo.message, + } + } +} + +impl TryFrom for Echo { + type Error = crate::Error; + + fn try_from(echo: pb::RequestEcho) -> Result { + Ok(Self { + message: echo.message, + }) + } +} + +impl Protobuf for Echo {} diff --git a/tendermint/src/abci/request/end_block.rs b/tendermint/src/abci/request/end_block.rs new file mode 100644 index 000000000..7095ea39f --- /dev/null +++ b/tendermint/src/abci/request/end_block.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/request-endblock.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct EndBlock { + /// The height of the block just executed. + pub height: i64, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestEndBlock { + fn from(end_block: EndBlock) -> Self { + Self { + height: end_block.height, + } + } +} + +impl TryFrom for EndBlock { + type Error = crate::Error; + + fn try_from(end_block: pb::RequestEndBlock) -> Result { + Ok(Self { + height: end_block.height, + }) + } +} + +impl Protobuf for EndBlock {} diff --git a/tendermint/src/abci/request/info.rs b/tendermint/src/abci/request/info.rs new file mode 100644 index 000000000..1a2c900d8 --- /dev/null +++ b/tendermint/src/abci/request/info.rs @@ -0,0 +1,44 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/request-info.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Info { + /// The Tendermint software semantic version. + pub version: String, + /// The Tendermint block protocol version. + pub block_version: u64, + /// The Tendermint p2p protocol version. + pub p2p_version: u64, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestInfo { + fn from(info: Info) -> Self { + Self { + version: info.version, + block_version: info.block_version, + p2p_version: info.p2p_version, + } + } +} + +impl TryFrom for Info { + type Error = crate::Error; + + fn try_from(info: pb::RequestInfo) -> Result { + Ok(Self { + version: info.version, + block_version: info.block_version, + p2p_version: info.p2p_version, + }) + } +} + +impl Protobuf for Info {} diff --git a/tendermint/src/abci/request/init_chain.rs b/tendermint/src/abci/request/init_chain.rs new file mode 100644 index 000000000..bb50b553f --- /dev/null +++ b/tendermint/src/abci/request/init_chain.rs @@ -0,0 +1,73 @@ +use bytes::Bytes; + +use super::super::types::ValidatorUpdate; +use crate::{block, consensus, prelude::*, Time}; + +/// Called on genesis to initialize chain state. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#initchain) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct InitChain { + /// The genesis time. + pub time: Time, + /// The ID of the blockchain. + pub chain_id: String, + /// Initial consensus-critical parameters. + pub consensus_params: consensus::Params, + /// Initial genesis validators, sorted by voting power. + pub validators: Vec, + /// Serialized JSON bytes containing the initial application state. + pub app_state_bytes: Bytes, + /// Height of the initial block (typically `1`). + pub initial_height: block::Height, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +use crate::Error; + +impl From for pb::RequestInitChain { + fn from(init_chain: InitChain) -> Self { + Self { + time: Some(init_chain.time.into()), + chain_id: init_chain.chain_id, + consensus_params: Some(init_chain.consensus_params.into()), + validators: init_chain.validators.into_iter().map(Into::into).collect(), + app_state_bytes: init_chain.app_state_bytes, + initial_height: init_chain.initial_height.into(), + } + } +} + +impl TryFrom for InitChain { + type Error = Error; + + fn try_from(init_chain: pb::RequestInitChain) -> Result { + Ok(Self { + time: init_chain + .time + .ok_or_else(Error::missing_genesis_time)? + .try_into()?, + chain_id: init_chain.chain_id, + consensus_params: init_chain + .consensus_params + .ok_or_else(Error::missing_consensus_params)? + .try_into()?, + validators: init_chain + .validators + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + app_state_bytes: init_chain.app_state_bytes, + initial_height: init_chain.initial_height.try_into()?, + }) + } +} + +impl Protobuf for InitChain {} diff --git a/tendermint/src/abci/request/load_snapshot_chunk.rs b/tendermint/src/abci/request/load_snapshot_chunk.rs new file mode 100644 index 000000000..4c56ed6e3 --- /dev/null +++ b/tendermint/src/abci/request/load_snapshot_chunk.rs @@ -0,0 +1,44 @@ +use crate::{block, prelude::*}; + +#[doc = include_str!("../doc/request-loadsnapshotchunk.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct LoadSnapshotChunk { + /// The height of the snapshot the chunks belong to. + pub height: block::Height, + /// An application-specific identifier of the format of the snapshot chunk. + pub format: u32, + /// The chunk index, starting from `0` for the initial chunk. + pub chunk: u32, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestLoadSnapshotChunk { + fn from(load_snapshot_chunk: LoadSnapshotChunk) -> Self { + Self { + height: load_snapshot_chunk.height.into(), + format: load_snapshot_chunk.format, + chunk: load_snapshot_chunk.chunk, + } + } +} + +impl TryFrom for LoadSnapshotChunk { + type Error = crate::Error; + + fn try_from(load_snapshot_chunk: pb::RequestLoadSnapshotChunk) -> Result { + Ok(Self { + height: load_snapshot_chunk.height.try_into()?, + format: load_snapshot_chunk.format, + chunk: load_snapshot_chunk.chunk, + }) + } +} + +impl Protobuf for LoadSnapshotChunk {} diff --git a/tendermint/src/abci/request/offer_snapshot.rs b/tendermint/src/abci/request/offer_snapshot.rs new file mode 100644 index 000000000..db8f2fae0 --- /dev/null +++ b/tendermint/src/abci/request/offer_snapshot.rs @@ -0,0 +1,50 @@ +use bytes::Bytes; + +use super::super::types::Snapshot; +// bring into scope for doc links +#[allow(unused)] +use super::ApplySnapshotChunk; +use crate::prelude::*; + +#[doc = include_str!("../doc/request-offersnapshot.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct OfferSnapshot { + /// The snapshot offered for restoration. + pub snapshot: Snapshot, + /// The light client verified app hash for this height. + // XXX(hdevalence): replace with apphash + pub app_hash: Bytes, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestOfferSnapshot { + fn from(offer_snapshot: OfferSnapshot) -> Self { + Self { + snapshot: Some(offer_snapshot.snapshot.into()), + app_hash: offer_snapshot.app_hash, + } + } +} + +impl TryFrom for OfferSnapshot { + type Error = crate::Error; + + fn try_from(offer_snapshot: pb::RequestOfferSnapshot) -> Result { + Ok(Self { + snapshot: offer_snapshot + .snapshot + .ok_or_else(crate::Error::missing_data)? + .try_into()?, + app_hash: offer_snapshot.app_hash, + }) + } +} + +impl Protobuf for OfferSnapshot {} diff --git a/tendermint/src/abci/request/query.rs b/tendermint/src/abci/request/query.rs new file mode 100644 index 000000000..07dade853 --- /dev/null +++ b/tendermint/src/abci/request/query.rs @@ -0,0 +1,63 @@ +use bytes::Bytes; + +use crate::{block, prelude::*}; + +#[doc = include_str!("../doc/request-query.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Query { + /// Raw query bytes. + /// + /// Can be used with or in lieu of `path`. + pub data: Bytes, + /// Path of the request, like an HTTP `GET` path. + /// + /// Can be used with or in lieu of `data`. + /// + /// Applications MUST interpret `/store` as a query by key on the underlying + /// store. The key SHOULD be specified in the Data field. Applications SHOULD + /// allow queries over specific types like `/accounts/...` or `/votes/...`. + pub path: String, + /// The block height for which the query should be executed. + /// + /// The default `0` returns data for the latest committed block. Note that + /// this is the height of the block containing the application's Merkle root + /// hash, which represents the state as it was after committing the block at + /// `height - 1`. + pub height: block::Height, + /// Whether to return a Merkle proof with the response, if possible. + pub prove: bool, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestQuery { + fn from(query: Query) -> Self { + Self { + data: query.data, + path: query.path, + height: query.height.into(), + prove: query.prove, + } + } +} + +impl TryFrom for Query { + type Error = crate::Error; + + fn try_from(query: pb::RequestQuery) -> Result { + Ok(Self { + data: query.data, + path: query.path, + height: query.height.try_into()?, + prove: query.prove, + }) + } +} + +impl Protobuf for Query {} diff --git a/tendermint/src/abci/request/set_option.rs b/tendermint/src/abci/request/set_option.rs new file mode 100644 index 000000000..ffb4dd9bb --- /dev/null +++ b/tendermint/src/abci/request/set_option.rs @@ -0,0 +1,38 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/request-setoption.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct SetOption { + pub key: String, + pub value: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::RequestSetOption { + fn from(message: SetOption) -> Self { + Self { + key: message.key, + value: message.value, + } + } +} + +impl TryFrom for SetOption { + type Error = crate::Error; + + fn try_from(message: pb::RequestSetOption) -> Result { + Ok(Self { + key: message.key, + value: message.value, + }) + } +} + +impl Protobuf for SetOption {} diff --git a/tendermint/src/abci/response.rs b/tendermint/src/abci/response.rs new file mode 100644 index 000000000..5b55a7116 --- /dev/null +++ b/tendermint/src/abci/response.rs @@ -0,0 +1,295 @@ +//! ABCI responses and response data. +//! +//! The [`Response`] enum records all possible ABCI responses. Responses that +//! contain data are modeled as a separate struct, to avoid duplication of field +//! definitions. + +// IMPORTANT NOTE ON DOCUMENTATION: +// +// The documentation for each request type is adapted from the ABCI Methods and +// Types spec document. However, the same logical request may appear three +// times, as a struct with the request data, as a Request variant, and as a +// CategoryRequest variant. +// +// To avoid duplication, this documentation is stored in the doc/ folder in +// individual .md files, which are pasted onto the relevant items using #[doc = +// include_str!(...)]. +// +// This is also why certain submodules have #[allow(unused)] imports to bring +// items into scope for doc links, rather than changing the doc links -- it +// allows the doc comments to be copied without editing. +use core::convert::{TryFrom, TryInto}; + +// bring into scope for doc links +#[allow(unused)] +use super::types::Snapshot; +use crate::{prelude::*, Error}; + +mod apply_snapshot_chunk; +mod begin_block; +mod check_tx; +mod commit; +mod deliver_tx; +mod echo; +mod end_block; +mod exception; +mod info; +mod init_chain; +mod list_snapshots; +mod load_snapshot_chunk; +mod offer_snapshot; +mod query; +mod set_option; + +pub use apply_snapshot_chunk::{ApplySnapshotChunk, ApplySnapshotChunkResult}; +pub use begin_block::BeginBlock; +pub use check_tx::CheckTx; +pub use commit::Commit; +pub use deliver_tx::DeliverTx; +pub use echo::Echo; +pub use end_block::EndBlock; +pub use exception::Exception; +pub use info::Info; +pub use init_chain::InitChain; +pub use list_snapshots::ListSnapshots; +pub use load_snapshot_chunk::LoadSnapshotChunk; +pub use offer_snapshot::OfferSnapshot; +pub use query::Query; +pub use set_option::SetOption; + +/// All possible ABCI responses. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Response { + #[doc = include_str!("doc/response-exception.md")] + Exception(Exception), + #[doc = include_str!("doc/response-echo.md")] + Echo(Echo), + #[doc = include_str!("doc/response-flush.md")] + Flush, + #[doc = include_str!("doc/response-info.md")] + Info(Info), + #[doc = include_str!("doc/response-setoption.md")] + SetOption(SetOption), + #[doc = include_str!("doc/response-initchain.md")] + InitChain(InitChain), + #[doc = include_str!("doc/response-query.md")] + Query(Query), + #[doc = include_str!("doc/response-beginblock.md")] + BeginBlock(BeginBlock), + #[doc = include_str!("doc/response-checktx.md")] + CheckTx(CheckTx), + #[doc = include_str!("doc/response-delivertx.md")] + DeliverTx(DeliverTx), + #[doc = include_str!("doc/response-endblock.md")] + EndBlock(EndBlock), + #[doc = include_str!("doc/response-commit.md")] + Commit(Commit), + #[doc = include_str!("doc/response-listsnapshots.md")] + ListSnapshots(ListSnapshots), + #[doc = include_str!("doc/response-offersnapshot.md")] + OfferSnapshot(OfferSnapshot), + #[doc = include_str!("doc/response-loadsnapshotchunk.md")] + LoadSnapshotChunk(LoadSnapshotChunk), + #[doc = include_str!("doc/response-applysnapshotchunk.md")] + ApplySnapshotChunk(ApplySnapshotChunk), +} + +/// The consensus category of ABCI responses. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ConsensusResponse { + #[doc = include_str!("doc/response-initchain.md")] + InitChain(InitChain), + #[doc = include_str!("doc/response-beginblock.md")] + BeginBlock(BeginBlock), + #[doc = include_str!("doc/response-delivertx.md")] + DeliverTx(DeliverTx), + #[doc = include_str!("doc/response-endblock.md")] + EndBlock(EndBlock), + #[doc = include_str!("doc/response-commit.md")] + Commit(Commit), +} + +impl From for Response { + fn from(req: ConsensusResponse) -> Self { + match req { + ConsensusResponse::InitChain(x) => Self::InitChain(x), + ConsensusResponse::BeginBlock(x) => Self::BeginBlock(x), + ConsensusResponse::DeliverTx(x) => Self::DeliverTx(x), + ConsensusResponse::EndBlock(x) => Self::EndBlock(x), + ConsensusResponse::Commit(x) => Self::Commit(x), + } + } +} + +impl TryFrom for ConsensusResponse { + type Error = Error; + fn try_from(req: Response) -> Result { + match req { + Response::InitChain(x) => Ok(Self::InitChain(x)), + Response::BeginBlock(x) => Ok(Self::BeginBlock(x)), + Response::DeliverTx(x) => Ok(Self::DeliverTx(x)), + Response::EndBlock(x) => Ok(Self::EndBlock(x)), + Response::Commit(x) => Ok(Self::Commit(x)), + _ => Err(Error::invalid_abci_response_type()), + } + } +} + +/// The mempool category of ABCI responses. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum MempoolResponse { + #[doc = include_str!("doc/response-checktx.md")] + CheckTx(CheckTx), +} + +impl From for Response { + fn from(req: MempoolResponse) -> Self { + match req { + MempoolResponse::CheckTx(x) => Self::CheckTx(x), + } + } +} + +impl TryFrom for MempoolResponse { + type Error = Error; + fn try_from(req: Response) -> Result { + match req { + Response::CheckTx(x) => Ok(Self::CheckTx(x)), + _ => Err(Error::invalid_abci_response_type()), + } + } +} + +/// The info category of ABCI responses. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum InfoResponse { + #[doc = include_str!("doc/response-echo.md")] + Echo(Echo), + #[doc = include_str!("doc/response-info.md")] + Info(Info), + #[doc = include_str!("doc/response-query.md")] + Query(Query), + #[doc = include_str!("doc/response-setoption.md")] + SetOption(SetOption), +} + +impl From for Response { + fn from(req: InfoResponse) -> Self { + match req { + InfoResponse::Echo(x) => Self::Echo(x), + InfoResponse::Info(x) => Self::Info(x), + InfoResponse::Query(x) => Self::Query(x), + InfoResponse::SetOption(x) => Self::SetOption(x), + } + } +} + +impl TryFrom for InfoResponse { + type Error = Error; + fn try_from(req: Response) -> Result { + match req { + Response::Echo(x) => Ok(Self::Echo(x)), + Response::Info(x) => Ok(Self::Info(x)), + Response::Query(x) => Ok(Self::Query(x)), + Response::SetOption(x) => Ok(Self::SetOption(x)), + _ => Err(Error::invalid_abci_response_type()), + } + } +} + +/// The snapshot category of ABCI responses. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum SnapshotResponse { + #[doc = include_str!("doc/response-listsnapshots.md")] + ListSnapshots(ListSnapshots), + #[doc = include_str!("doc/response-offersnapshot.md")] + OfferSnapshot(OfferSnapshot), + #[doc = include_str!("doc/response-loadsnapshotchunk.md")] + LoadSnapshotChunk(LoadSnapshotChunk), + #[doc = include_str!("doc/response-applysnapshotchunk.md")] + ApplySnapshotChunk(ApplySnapshotChunk), +} + +impl From for Response { + fn from(req: SnapshotResponse) -> Self { + match req { + SnapshotResponse::ListSnapshots(x) => Self::ListSnapshots(x), + SnapshotResponse::OfferSnapshot(x) => Self::OfferSnapshot(x), + SnapshotResponse::LoadSnapshotChunk(x) => Self::LoadSnapshotChunk(x), + SnapshotResponse::ApplySnapshotChunk(x) => Self::ApplySnapshotChunk(x), + } + } +} + +impl TryFrom for SnapshotResponse { + type Error = Error; + fn try_from(req: Response) -> Result { + match req { + Response::ListSnapshots(x) => Ok(Self::ListSnapshots(x)), + Response::OfferSnapshot(x) => Ok(Self::OfferSnapshot(x)), + Response::LoadSnapshotChunk(x) => Ok(Self::LoadSnapshotChunk(x)), + Response::ApplySnapshotChunk(x) => Ok(Self::ApplySnapshotChunk(x)), + _ => Err(Error::invalid_abci_response_type()), + } + } +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::Response { + fn from(response: Response) -> pb::Response { + use pb::response::Value; + let value = match response { + Response::Exception(x) => Some(Value::Exception(x.into())), + Response::Echo(x) => Some(Value::Echo(x.into())), + Response::Flush => Some(Value::Flush(Default::default())), + Response::Info(x) => Some(Value::Info(x.into())), + Response::SetOption(x) => Some(Value::SetOption(x.into())), + Response::InitChain(x) => Some(Value::InitChain(x.into())), + Response::Query(x) => Some(Value::Query(x.into())), + Response::BeginBlock(x) => Some(Value::BeginBlock(x.into())), + Response::CheckTx(x) => Some(Value::CheckTx(x.into())), + Response::DeliverTx(x) => Some(Value::DeliverTx(x.into())), + Response::EndBlock(x) => Some(Value::EndBlock(x.into())), + Response::Commit(x) => Some(Value::Commit(x.into())), + Response::ListSnapshots(x) => Some(Value::ListSnapshots(x.into())), + Response::OfferSnapshot(x) => Some(Value::OfferSnapshot(x.into())), + Response::LoadSnapshotChunk(x) => Some(Value::LoadSnapshotChunk(x.into())), + Response::ApplySnapshotChunk(x) => Some(Value::ApplySnapshotChunk(x.into())), + }; + pb::Response { value } + } +} + +impl TryFrom for Response { + type Error = Error; + + fn try_from(response: pb::Response) -> Result { + use pb::response::Value; + match response.value { + Some(Value::Exception(x)) => Ok(Response::Exception(x.try_into()?)), + Some(Value::Echo(x)) => Ok(Response::Echo(x.try_into()?)), + Some(Value::Flush(_)) => Ok(Response::Flush), + Some(Value::Info(x)) => Ok(Response::Info(x.try_into()?)), + Some(Value::SetOption(x)) => Ok(Response::SetOption(x.try_into()?)), + Some(Value::InitChain(x)) => Ok(Response::InitChain(x.try_into()?)), + Some(Value::Query(x)) => Ok(Response::Query(x.try_into()?)), + Some(Value::BeginBlock(x)) => Ok(Response::BeginBlock(x.try_into()?)), + Some(Value::CheckTx(x)) => Ok(Response::CheckTx(x.try_into()?)), + Some(Value::DeliverTx(x)) => Ok(Response::DeliverTx(x.try_into()?)), + Some(Value::EndBlock(x)) => Ok(Response::EndBlock(x.try_into()?)), + Some(Value::Commit(x)) => Ok(Response::Commit(x.try_into()?)), + Some(Value::ListSnapshots(x)) => Ok(Response::ListSnapshots(x.try_into()?)), + Some(Value::OfferSnapshot(x)) => Ok(Response::OfferSnapshot(x.try_into()?)), + Some(Value::LoadSnapshotChunk(x)) => Ok(Response::LoadSnapshotChunk(x.try_into()?)), + Some(Value::ApplySnapshotChunk(x)) => Ok(Response::ApplySnapshotChunk(x.try_into()?)), + None => Err(crate::Error::missing_data()), + } + } +} + +impl Protobuf for Response {} diff --git a/tendermint/src/abci/response/apply_snapshot_chunk.rs b/tendermint/src/abci/response/apply_snapshot_chunk.rs new file mode 100644 index 000000000..b3502450a --- /dev/null +++ b/tendermint/src/abci/response/apply_snapshot_chunk.rs @@ -0,0 +1,88 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/response-applysnapshotchunk.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct ApplySnapshotChunk { + /// The result of applying the snapshot chunk. + pub result: ApplySnapshotChunkResult, + /// Refetch and reapply the given chunks, regardless of `result`. + /// + /// Only the listed chunks will be refetched, and reapplied in sequential + /// order. + pub refetch_chunks: Vec, + /// Reject the given P2P senders, regardless of `result`. + /// + /// Any chunks already applied will not be refetched unless explicitly + /// requested, but queued chunks from these senders will be discarded, and + /// new chunks or other snapshots rejected. + pub reject_senders: Vec, +} + +/// The result of applying a snapshot chunk. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(i32)] +pub enum ApplySnapshotChunkResult { + /// Unknown result, abort all snapshot restoration. + Unknown = 0, + /// The chunk was accepted. + Accept = 1, + /// Abort snapshot restoration, and don't try any other snapshots. + Abort = 2, + /// Reapply this chunk, combine with + /// [`refetch_chunks`](ApplySnapshotChunk::refetch_chunks) and + /// [`reject_senders`](ApplySnapshotChunk::reject_senders) as appropriate. + Retry = 3, + /// Restart this snapshot from + /// [`OfferSnapshot`](super::super::request::OfferSnapshot), + /// reusing chunks unless instructed otherwise. + RetrySnapshot = 4, + /// Reject this snapshot, try a different one. + RejectSnapshot = 5, +} + +impl Default for ApplySnapshotChunkResult { + fn default() -> Self { + Self::Unknown + } +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseApplySnapshotChunk { + fn from(apply_snapshot_chunk: ApplySnapshotChunk) -> Self { + Self { + result: apply_snapshot_chunk.result as i32, + refetch_chunks: apply_snapshot_chunk.refetch_chunks, + reject_senders: apply_snapshot_chunk.reject_senders, + } + } +} + +impl TryFrom for ApplySnapshotChunk { + type Error = crate::Error; + + fn try_from(apply_snapshot_chunk: pb::ResponseApplySnapshotChunk) -> Result { + let result = match apply_snapshot_chunk.result { + 0 => ApplySnapshotChunkResult::Unknown, + 1 => ApplySnapshotChunkResult::Accept, + 2 => ApplySnapshotChunkResult::Abort, + 3 => ApplySnapshotChunkResult::Retry, + 4 => ApplySnapshotChunkResult::RetrySnapshot, + 5 => ApplySnapshotChunkResult::RejectSnapshot, + _ => return Err(crate::Error::unsupported_apply_snapshot_chunk_result()), + }; + Ok(Self { + result, + refetch_chunks: apply_snapshot_chunk.refetch_chunks, + reject_senders: apply_snapshot_chunk.reject_senders, + }) + } +} + +impl Protobuf for ApplySnapshotChunk {} diff --git a/tendermint/src/abci/response/begin_block.rs b/tendermint/src/abci/response/begin_block.rs new file mode 100644 index 000000000..b576629bc --- /dev/null +++ b/tendermint/src/abci/response/begin_block.rs @@ -0,0 +1,41 @@ +use super::super::Event; +use crate::prelude::*; + +#[doc = include_str!("../doc/response-beginblock.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct BeginBlock { + /// Events that occurred while beginning the block. + pub events: Vec, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseBeginBlock { + fn from(begin_block: BeginBlock) -> Self { + Self { + events: begin_block.events.into_iter().map(Into::into).collect(), + } + } +} + +impl TryFrom for BeginBlock { + type Error = crate::Error; + + fn try_from(begin_block: pb::ResponseBeginBlock) -> Result { + Ok(Self { + events: begin_block + .events + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +impl Protobuf for BeginBlock {} diff --git a/tendermint/src/abci/response/check_tx.rs b/tendermint/src/abci/response/check_tx.rs new file mode 100644 index 000000000..0a1897fcb --- /dev/null +++ b/tendermint/src/abci/response/check_tx.rs @@ -0,0 +1,91 @@ +use bytes::Bytes; + +use super::super::Event; +use crate::prelude::*; + +#[doc = include_str!("../doc/response-checktx.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct CheckTx { + /// The response code. + /// + /// Transactions where `code != 0` will be rejected; these transactions will + /// not be broadcast to other nodes or included in a proposal block. + /// Tendermint attributes no other value to the response code. + pub code: u32, + /// Result bytes, if any. + pub data: Bytes, + /// The output of the application's logger. + /// + /// **May be non-deterministic**. + pub log: String, + /// Additional information. + /// + /// **May be non-deterministic**. + pub info: String, + /// Amount of gas requested for the transaction. + pub gas_wanted: i64, + /// Amount of gas consumed by the transaction. + pub gas_used: i64, + /// Events that occurred while checking the transaction. + pub events: Vec, + /// The namespace for the `code`. + pub codespace: String, + /// The transaction's sender (e.g. the signer). + pub sender: String, + /// The transaction's priority (for mempool ordering). + pub priority: i64, + // mempool_error is contained in the proto, but skipped here: + // > mempool_error is set by Tendermint. + // > ABCI applictions creating a ResponseCheckTX should not set mempool_error. +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseCheckTx { + fn from(check_tx: CheckTx) -> Self { + Self { + code: check_tx.code, + data: check_tx.data, + log: check_tx.log, + info: check_tx.info, + gas_wanted: check_tx.gas_wanted, + gas_used: check_tx.gas_used, + events: check_tx.events.into_iter().map(Into::into).collect(), + codespace: check_tx.codespace, + sender: check_tx.sender, + priority: check_tx.priority, + mempool_error: String::default(), + } + } +} + +impl TryFrom for CheckTx { + type Error = crate::Error; + + fn try_from(check_tx: pb::ResponseCheckTx) -> Result { + Ok(Self { + code: check_tx.code, + data: check_tx.data, + log: check_tx.log, + info: check_tx.info, + gas_wanted: check_tx.gas_wanted, + gas_used: check_tx.gas_used, + events: check_tx + .events + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + codespace: check_tx.codespace, + sender: check_tx.sender, + priority: check_tx.priority, + }) + } +} + +impl Protobuf for CheckTx {} diff --git a/tendermint/src/abci/response/commit.rs b/tendermint/src/abci/response/commit.rs new file mode 100644 index 000000000..5d6180da6 --- /dev/null +++ b/tendermint/src/abci/response/commit.rs @@ -0,0 +1,45 @@ +use bytes::Bytes; + +use crate::{block, prelude::*}; + +#[doc = include_str!("../doc/response-commit.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct Commit { + /// The Merkle root hash of the application state + /// + /// XXX(hdevalence) - is this different from an app hash? + /// XXX(hdevalence) - rename to app_hash ? + pub data: Bytes, + /// Blocks below this height may be removed. + pub retain_height: block::Height, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseCommit { + fn from(commit: Commit) -> Self { + Self { + data: commit.data, + retain_height: commit.retain_height.into(), + } + } +} + +impl TryFrom for Commit { + type Error = crate::Error; + + fn try_from(commit: pb::ResponseCommit) -> Result { + Ok(Self { + data: commit.data, + retain_height: commit.retain_height.try_into()?, + }) + } +} + +impl Protobuf for Commit {} diff --git a/tendermint/src/abci/response/deliver_tx.rs b/tendermint/src/abci/response/deliver_tx.rs new file mode 100644 index 000000000..60c41eea6 --- /dev/null +++ b/tendermint/src/abci/response/deliver_tx.rs @@ -0,0 +1,79 @@ +use bytes::Bytes; + +use super::super::Event; +use crate::prelude::*; + +#[doc = include_str!("../doc/response-delivertx.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct DeliverTx { + /// The response code. + /// + /// This code should be `0` only if the transaction is fully valid. However, + /// invalid transactions included in a block will still be executed against + /// the application state. + pub code: u32, + /// Result bytes, if any. + pub data: Bytes, + /// The output of the application's logger. + /// + /// **May be non-deterministic**. + pub log: String, + /// Additional information. + /// + /// **May be non-deterministic**. + pub info: String, + /// Amount of gas requested for the transaction. + pub gas_wanted: i64, + /// Amount of gas consumed by the transaction. + pub gas_used: i64, + /// Events that occurred while executing the transaction. + pub events: Vec, + /// The namespace for the `code`. + pub codespace: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseDeliverTx { + fn from(deliver_tx: DeliverTx) -> Self { + Self { + code: deliver_tx.code, + data: deliver_tx.data, + log: deliver_tx.log, + info: deliver_tx.info, + gas_wanted: deliver_tx.gas_wanted, + gas_used: deliver_tx.gas_used, + events: deliver_tx.events.into_iter().map(Into::into).collect(), + codespace: deliver_tx.codespace, + } + } +} + +impl TryFrom for DeliverTx { + type Error = crate::Error; + + fn try_from(deliver_tx: pb::ResponseDeliverTx) -> Result { + Ok(Self { + code: deliver_tx.code, + data: deliver_tx.data, + log: deliver_tx.log, + info: deliver_tx.info, + gas_wanted: deliver_tx.gas_wanted, + gas_used: deliver_tx.gas_used, + events: deliver_tx + .events + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + codespace: deliver_tx.codespace, + }) + } +} + +impl Protobuf for DeliverTx {} diff --git a/tendermint/src/abci/response/echo.rs b/tendermint/src/abci/response/echo.rs new file mode 100644 index 000000000..7e4777713 --- /dev/null +++ b/tendermint/src/abci/response/echo.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/response-echo.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct Echo { + /// The message sent in the request. + pub message: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseEcho { + fn from(echo: Echo) -> Self { + Self { + message: echo.message, + } + } +} + +impl TryFrom for Echo { + type Error = crate::Error; + + fn try_from(echo: pb::ResponseEcho) -> Result { + Ok(Self { + message: echo.message, + }) + } +} + +impl Protobuf for Echo {} diff --git a/tendermint/src/abci/response/end_block.rs b/tendermint/src/abci/response/end_block.rs new file mode 100644 index 000000000..e59fbd4e8 --- /dev/null +++ b/tendermint/src/abci/response/end_block.rs @@ -0,0 +1,62 @@ +use super::super::{types::ValidatorUpdate, Event}; +use crate::{consensus, prelude::*}; + +#[doc = include_str!("../doc/response-endblock.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct EndBlock { + /// Changes to the validator set, if any. + /// + /// Setting the voting power to 0 removes a validator. + pub validator_updates: Vec, + /// Changes to consensus parameters (optional). + pub consensus_param_updates: Option, + /// Events that occurred while ending the block. + pub events: Vec, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseEndBlock { + fn from(end_block: EndBlock) -> Self { + Self { + validator_updates: end_block + .validator_updates + .into_iter() + .map(Into::into) + .collect(), + consensus_param_updates: end_block.consensus_param_updates.map(Into::into), + events: end_block.events.into_iter().map(Into::into).collect(), + } + } +} + +impl TryFrom for EndBlock { + type Error = crate::Error; + + fn try_from(end_block: pb::ResponseEndBlock) -> Result { + Ok(Self { + validator_updates: end_block + .validator_updates + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + consensus_param_updates: end_block + .consensus_param_updates + .map(TryInto::try_into) + .transpose()?, + events: end_block + .events + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +impl Protobuf for EndBlock {} diff --git a/tendermint/src/abci/response/exception.rs b/tendermint/src/abci/response/exception.rs new file mode 100644 index 000000000..a6e784147 --- /dev/null +++ b/tendermint/src/abci/response/exception.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/response-exception.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Exception { + /// Undocumented. + pub error: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseException { + fn from(exception: Exception) -> Self { + Self { + error: exception.error, + } + } +} + +impl TryFrom for Exception { + type Error = crate::Error; + + fn try_from(exception: pb::ResponseException) -> Result { + Ok(Self { + error: exception.error, + }) + } +} + +impl Protobuf for Exception {} diff --git a/tendermint/src/abci/response/info.rs b/tendermint/src/abci/response/info.rs new file mode 100644 index 000000000..9f90630fc --- /dev/null +++ b/tendermint/src/abci/response/info.rs @@ -0,0 +1,55 @@ +use bytes::Bytes; + +use crate::{block, prelude::*, Error}; + +#[doc = include_str!("../doc/response-info.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Info { + /// Some arbitrary information. + pub data: String, + /// The application software semantic version. + pub version: String, + /// The application protocol version. + pub app_version: u64, + /// The latest block for which the app has called [`Commit`](super::super::Request::Commit). + pub last_block_height: block::Height, + /// The latest result of [`Commit`](super::super::Request::Commit). + // XXX(hdevalence): fix this, should be apphash? + pub last_block_app_hash: Bytes, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseInfo { + fn from(info: Info) -> Self { + Self { + data: info.data, + version: info.version, + app_version: info.app_version, + last_block_height: info.last_block_height.into(), + last_block_app_hash: info.last_block_app_hash, + } + } +} + +impl TryFrom for Info { + type Error = Error; + + fn try_from(info: pb::ResponseInfo) -> Result { + Ok(Self { + data: info.data, + version: info.version, + app_version: info.app_version, + last_block_height: info.last_block_height.try_into()?, + last_block_app_hash: info.last_block_app_hash, + }) + } +} + +impl Protobuf for Info {} diff --git a/tendermint/src/abci/response/init_chain.rs b/tendermint/src/abci/response/init_chain.rs new file mode 100644 index 000000000..9e5c3eae3 --- /dev/null +++ b/tendermint/src/abci/response/init_chain.rs @@ -0,0 +1,61 @@ +use bytes::Bytes; + +use super::super::types::ValidatorUpdate; +use crate::{consensus, prelude::*}; + +#[doc = include_str!("../doc/response-initchain.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct InitChain { + /// Initial consensus-critical parameters (optional). + pub consensus_params: Option, + /// Initial validator set (optional). + /// + /// If this list is empty, the initial validator set will be the one given in + /// [`request::InitChain::validators`](super::super::request::InitChain::validators). + /// + /// If this list is nonempty, it will be the initial validator set, instead + /// of the one given in + /// [`request::InitChain::validators`](super::super::request::InitChain::validators). + pub validators: Vec, + /// Initial application hash. + pub app_hash: Bytes, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseInitChain { + fn from(init_chain: InitChain) -> Self { + Self { + consensus_params: init_chain.consensus_params.map(Into::into), + validators: init_chain.validators.into_iter().map(Into::into).collect(), + app_hash: init_chain.app_hash, + } + } +} + +impl TryFrom for InitChain { + type Error = crate::Error; + + fn try_from(init_chain: pb::ResponseInitChain) -> Result { + Ok(Self { + consensus_params: init_chain + .consensus_params + .map(TryInto::try_into) + .transpose()?, + validators: init_chain + .validators + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + app_hash: init_chain.app_hash, + }) + } +} + +impl Protobuf for InitChain {} diff --git a/tendermint/src/abci/response/list_snapshots.rs b/tendermint/src/abci/response/list_snapshots.rs new file mode 100644 index 000000000..55a7a5f24 --- /dev/null +++ b/tendermint/src/abci/response/list_snapshots.rs @@ -0,0 +1,45 @@ +use super::super::types::Snapshot; +use crate::prelude::*; + +#[doc = include_str!("../doc/response-listsnapshots.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct ListSnapshots { + /// A list of local state snapshots. + pub snapshots: Vec, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseListSnapshots { + fn from(list_snapshots: ListSnapshots) -> Self { + Self { + snapshots: list_snapshots + .snapshots + .into_iter() + .map(Into::into) + .collect(), + } + } +} + +impl TryFrom for ListSnapshots { + type Error = crate::Error; + + fn try_from(list_snapshots: pb::ResponseListSnapshots) -> Result { + Ok(Self { + snapshots: list_snapshots + .snapshots + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +impl Protobuf for ListSnapshots {} diff --git a/tendermint/src/abci/response/load_snapshot_chunk.rs b/tendermint/src/abci/response/load_snapshot_chunk.rs new file mode 100644 index 000000000..fb8f2c129 --- /dev/null +++ b/tendermint/src/abci/response/load_snapshot_chunk.rs @@ -0,0 +1,41 @@ +use bytes::Bytes; + +use crate::prelude::*; + +#[doc = include_str!("../doc/response-loadsnapshotchunk.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct LoadSnapshotChunk { + /// The binary chunk contents, in an arbitrary format. + /// + /// Chunk messages cannot be larger than 16MB *including metadata*, so 10MB + /// is a good starting point. + pub chunk: Bytes, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseLoadSnapshotChunk { + fn from(load_snapshot_chunk: LoadSnapshotChunk) -> Self { + Self { + chunk: load_snapshot_chunk.chunk, + } + } +} + +impl TryFrom for LoadSnapshotChunk { + type Error = crate::Error; + + fn try_from(load_snapshot_chunk: pb::ResponseLoadSnapshotChunk) -> Result { + Ok(Self { + chunk: load_snapshot_chunk.chunk, + }) + } +} + +impl Protobuf for LoadSnapshotChunk {} diff --git a/tendermint/src/abci/response/offer_snapshot.rs b/tendermint/src/abci/response/offer_snapshot.rs new file mode 100644 index 000000000..47ee38861 --- /dev/null +++ b/tendermint/src/abci/response/offer_snapshot.rs @@ -0,0 +1,62 @@ +// bring into scope for doc links +#[allow(unused)] +use super::super::types::Snapshot; +use crate::prelude::*; + +#[doc = include_str!("../doc/response-offersnapshot.md")] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(i32)] +pub enum OfferSnapshot { + /// Unknown result, abort all snapshot restoration + Unknown = 0, + /// Snapshot accepted, apply chunks + Accept = 1, + /// Abort all snapshot restoration + Abort = 2, + /// Reject this specific snapshot, try others + Reject = 3, + /// Reject all snapshots of this format, try others + RejectFormat = 4, + /// Reject all snapshots from the sender(s), try others + RejectSender = 5, +} + +impl Default for OfferSnapshot { + fn default() -> Self { + Self::Unknown + } +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseOfferSnapshot { + fn from(offer_snapshot: OfferSnapshot) -> Self { + Self { + result: offer_snapshot as i32, + } + } +} + +impl TryFrom for OfferSnapshot { + type Error = crate::Error; + + fn try_from(offer_snapshot: pb::ResponseOfferSnapshot) -> Result { + Ok(match offer_snapshot.result { + 0 => OfferSnapshot::Unknown, + 1 => OfferSnapshot::Accept, + 2 => OfferSnapshot::Abort, + 3 => OfferSnapshot::Reject, + 4 => OfferSnapshot::RejectFormat, + 5 => OfferSnapshot::RejectSender, + _ => return Err(crate::Error::unsupported_offer_snapshot_chunk_result()), + }) + } +} + +impl Protobuf for OfferSnapshot {} diff --git a/tendermint/src/abci/response/query.rs b/tendermint/src/abci/response/query.rs new file mode 100644 index 000000000..2d8279c2d --- /dev/null +++ b/tendermint/src/abci/response/query.rs @@ -0,0 +1,81 @@ +use bytes::Bytes; + +/// XXX(hdevalence): hide merkle::proof and re-export its contents from merkle? +use crate::merkle::proof as merkle; +use crate::{block, prelude::*}; + +#[doc = include_str!("../doc/response-query.md")] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +pub struct Query { + /// The response code for the query. + pub code: u32, + /// The output of the application's logger. + /// + /// **May be non-deterministic**. + pub log: String, + /// Additional information. + /// + /// **May be non-deterministic**. + pub info: String, + /// The index of the key in the tree. + pub index: i64, + /// The key of the matching data. + pub key: Bytes, + /// The value of the matching data. + pub value: Bytes, + /// Serialized proof for the value data, if requested, to be verified against + /// the app hash for the given `height`. + pub proof: Option, + /// The block height from which data was derived. + /// + /// Note that this is the height of the block containing the application's + /// Merkle root hash, which represents the state as it was after committing + /// the block at `height - 1`. + pub height: block::Height, + /// The namespace for the `code`. + pub codespace: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::{TryFrom, TryInto}; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseQuery { + fn from(query: Query) -> Self { + Self { + code: query.code, + log: query.log, + info: query.info, + index: query.index, + key: query.key, + value: query.value, + proof_ops: query.proof.map(Into::into), + height: query.height.into(), + codespace: query.codespace, + } + } +} + +impl TryFrom for Query { + type Error = crate::Error; + + fn try_from(query: pb::ResponseQuery) -> Result { + Ok(Self { + code: query.code, + log: query.log, + info: query.info, + index: query.index, + key: query.key, + value: query.value, + proof: query.proof_ops.map(TryInto::try_into).transpose()?, + height: query.height.try_into()?, + codespace: query.codespace, + }) + } +} + +impl Protobuf for Query {} diff --git a/tendermint/src/abci/response/set_option.rs b/tendermint/src/abci/response/set_option.rs new file mode 100644 index 000000000..ce15ede66 --- /dev/null +++ b/tendermint/src/abci/response/set_option.rs @@ -0,0 +1,41 @@ +use crate::prelude::*; + +#[doc = include_str!("../doc/response-setoption.md")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct SetOption { + pub code: u32, + pub log: String, + pub info: String, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use core::convert::TryFrom; + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::ResponseSetOption { + fn from(message: SetOption) -> Self { + Self { + code: message.code, + log: message.log, + info: message.info, + } + } +} + +impl TryFrom for SetOption { + type Error = crate::Error; + + fn try_from(message: pb::ResponseSetOption) -> Result { + Ok(Self { + code: message.code, + log: message.log, + info: message.info, + }) + } +} + +impl Protobuf for SetOption {} diff --git a/tendermint/src/abci/types.rs b/tendermint/src/abci/types.rs new file mode 100644 index 000000000..54344d692 --- /dev/null +++ b/tendermint/src/abci/types.rs @@ -0,0 +1,311 @@ +//! ABCI-specific data types used in requests and responses. +//! +//! These types have changes from the core data structures to better accomodate +//! ABCI applications. +//! +//! [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#data-types) + +use core::convert::{TryFrom, TryInto}; + +use bytes::Bytes; + +use crate::{block, prelude::*, vote, Error, PublicKey, Time}; + +/// A validator address with voting power. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#validator) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Validator { + /// The validator's address (the first 20 bytes of `SHA256(public_key)`). + pub address: [u8; 20], + /// The voting power of the validator. + pub power: vote::Power, +} + +/// A change to the validator set. +/// +/// Used to inform Tendermint of changes to the validator set. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#validatorupdate) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ValidatorUpdate { + /// The validator's public key. + pub pub_key: PublicKey, + /// The validator's voting power. + pub power: vote::Power, +} + +/// Information about a whether a validator signed the last block. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#voteinfo) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct VoteInfo { + /// Identifies the validator. + pub validator: Validator, + /// Whether or not the validator signed the last block. + pub signed_last_block: bool, +} + +/// The possible kinds of [`Evidence`]. +/// +/// Note: the +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#evidencetype-2) +/// calls this `EvidenceType`, but we follow the Rust convention and name it `EvidenceKind` +/// to avoid confusion with Rust types. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(i32)] +pub enum EvidenceKind { + /// Unknown evidence type (proto default value). + Unknown = 0, + /// Evidence that the validator voted for two different blocks in the same + /// round of the same height. + DuplicateVote = 1, + /// Evidence that a validator attacked a light client. + LightClientAttack = 2, +} + +/// Evidence of validator misbehavior. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#evidence) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Evidence { + /// The kind of evidence. + /// + /// Note: this field is called `type` in the protobuf, but we call it `kind` + /// to avoid the Rust keyword. + pub kind: EvidenceKind, + /// The offending validator. + pub validator: Validator, + /// The height when the offense occurred. + pub height: block::Height, + /// The corresponding time when the offense occurred. + pub time: Time, + /// Total voting power of the validator set at `height`. + /// + /// This is included in case the ABCI application does not store historical + /// validators, cf. + /// [#4581](https://github.com/tendermint/tendermint/issues/4581) + pub total_voting_power: vote::Power, +} + +/// Information on the last block commit. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#lastcommitinfo) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct LastCommitInfo { + /// The commit round. + /// + /// Reflects the total number of rounds it took to come to consensus for the + /// current block. + pub round: block::Round, + /// The list of validator addresses in the last validator set, with their + /// voting power and whether or not they signed a vote. + pub votes: Vec, +} + +/// Used for state sync snapshots. +/// +/// When sent across the network, a `Snapshot` can be at most 4 MB. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#snapshot) +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Snapshot { + /// The height at which the snapshot was taken + pub height: block::Height, + /// The application-specific snapshot format identifier. + /// + /// This allows applications to version their snapshot data format and make + /// backwards-incompatible changes. Tendermint does not interpret this field. + pub format: u32, + /// The number of chunks in the snapshot. Must be at least 1. + pub chunks: u32, + /// An arbitrary snapshot hash. + /// + /// This hash must be equal only for identical snapshots across nodes. + /// Tendermint does not interpret the hash, only compares it with other + /// hashes. + pub hash: Bytes, + /// Arbitrary application metadata, e.g., chunk hashes or other verification data. + pub metadata: Bytes, +} + +// ============================================================================= +// Protobuf conversions +// ============================================================================= + +use tendermint_proto::{abci as pb, Protobuf}; + +impl From for pb::Validator { + fn from(v: Validator) -> Self { + Self { + address: Bytes::copy_from_slice(&v.address[..]), + power: v.power.into(), + } + } +} + +impl TryFrom for Validator { + type Error = Error; + + fn try_from(vu: pb::Validator) -> Result { + let address = if vu.address.len() == 20 { + let mut bytes = [0u8; 20]; + bytes.copy_from_slice(&vu.address); + bytes + } else { + return Err(Error::invalid_account_id_length()); + }; + + Ok(Self { + address, + power: vu.power.try_into()?, + }) + } +} + +impl Protobuf for Validator {} + +impl From for pb::ValidatorUpdate { + fn from(vu: ValidatorUpdate) -> Self { + Self { + pub_key: Some(vu.pub_key.into()), + power: vu.power.into(), + } + } +} + +impl TryFrom for ValidatorUpdate { + type Error = Error; + + fn try_from(vu: pb::ValidatorUpdate) -> Result { + Ok(Self { + pub_key: vu + .pub_key + .ok_or_else(Error::missing_public_key)? + .try_into()?, + power: vu.power.try_into()?, + }) + } +} + +impl Protobuf for ValidatorUpdate {} + +impl From for pb::VoteInfo { + fn from(vi: VoteInfo) -> Self { + Self { + validator: Some(vi.validator.into()), + signed_last_block: vi.signed_last_block, + } + } +} + +impl TryFrom for VoteInfo { + type Error = Error; + + fn try_from(vi: pb::VoteInfo) -> Result { + Ok(Self { + validator: vi + .validator + .ok_or_else(Error::missing_validator)? + .try_into()?, + signed_last_block: vi.signed_last_block, + }) + } +} + +impl Protobuf for VoteInfo {} + +impl From for pb::Evidence { + fn from(evidence: Evidence) -> Self { + Self { + r#type: evidence.kind as i32, + validator: Some(evidence.validator.into()), + height: evidence.height.into(), + time: Some(evidence.time.into()), + total_voting_power: evidence.total_voting_power.into(), + } + } +} + +impl TryFrom for Evidence { + type Error = Error; + + fn try_from(evidence: pb::Evidence) -> Result { + let kind = match evidence.r#type { + 0 => EvidenceKind::Unknown, + 1 => EvidenceKind::DuplicateVote, + 2 => EvidenceKind::LightClientAttack, + _ => return Err(Error::invalid_evidence()), + }; + + Ok(Self { + kind, + validator: evidence + .validator + .ok_or_else(Error::missing_validator)? + .try_into()?, + height: evidence.height.try_into()?, + time: evidence + .time + .ok_or_else(Error::missing_timestamp)? + .try_into()?, + total_voting_power: evidence.total_voting_power.try_into()?, + }) + } +} + +impl Protobuf for Evidence {} + +impl From for pb::LastCommitInfo { + fn from(lci: LastCommitInfo) -> Self { + Self { + round: lci.round.into(), + votes: lci.votes.into_iter().map(Into::into).collect(), + } + } +} + +impl TryFrom for LastCommitInfo { + type Error = Error; + + fn try_from(lci: pb::LastCommitInfo) -> Result { + Ok(Self { + round: lci.round.try_into()?, + votes: lci + .votes + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +impl Protobuf for LastCommitInfo {} + +impl From for pb::Snapshot { + fn from(snapshot: Snapshot) -> Self { + Self { + height: snapshot.height.into(), + format: snapshot.format, + chunks: snapshot.chunks, + hash: snapshot.hash, + metadata: snapshot.metadata, + } + } +} + +impl TryFrom for Snapshot { + type Error = Error; + + fn try_from(snapshot: pb::Snapshot) -> Result { + Ok(Self { + height: snapshot.height.try_into()?, + format: snapshot.format, + chunks: snapshot.chunks, + hash: snapshot.hash, + metadata: snapshot.metadata, + }) + } +} + +impl Protobuf for Snapshot {} diff --git a/tendermint/src/block.rs b/tendermint/src/block.rs index 5c29e8440..0f961c05c 100644 --- a/tendermint/src/block.rs +++ b/tendermint/src/block.rs @@ -26,7 +26,7 @@ pub use self::{ round::*, size::Size, }; -use crate::{abci::transaction, error::Error, evidence, prelude::*}; +use crate::{error::Error, evidence, prelude::*}; /// Blocks consist of a header, transactions, votes (the commit), and a list of /// evidence of malfeasance (i.e. signing conflicting votes). @@ -35,18 +35,18 @@ use crate::{abci::transaction, error::Error, evidence, prelude::*}; // Default serialization - all fields serialize; used by /block endpoint #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[non_exhaustive] +#[serde(try_from = "RawBlock", into = "RawBlock")] pub struct Block { /// Block header pub header: Header, /// Transaction data - pub data: transaction::Data, + pub data: Vec>, /// Evidence of malfeasance pub evidence: evidence::Data, /// Last commit - #[serde(with = "crate::serializers::optional")] pub last_commit: Option, } @@ -75,7 +75,7 @@ impl TryFrom for Block { //} Ok(Block { header, - data: value.data.ok_or_else(Error::missing_data)?.into(), + data: value.data.ok_or_else(Error::missing_data)?.txs, evidence: value .evidence .ok_or_else(Error::missing_evidence)? @@ -87,9 +87,10 @@ impl TryFrom for Block { impl From for RawBlock { fn from(value: Block) -> Self { + use tendermint_proto::types::Data as RawData; RawBlock { header: Some(value.header.into()), - data: Some(value.data.into()), + data: Some(RawData { txs: value.data }), evidence: Some(value.evidence.into()), last_commit: value.last_commit.map(Into::into), } @@ -100,7 +101,7 @@ impl Block { /// constructor pub fn new( header: Header, - data: transaction::Data, + data: Vec>, evidence: evidence::Data, last_commit: Option, ) -> Result { @@ -128,7 +129,7 @@ impl Block { } /// Get data - pub fn data(&self) -> &transaction::Data { + pub fn data(&self) -> &Vec> { &self.data } diff --git a/tendermint/src/block/size.rs b/tendermint/src/block/size.rs index 2e9c32c50..093cd580d 100644 --- a/tendermint/src/block/size.rs +++ b/tendermint/src/block/size.rs @@ -3,7 +3,7 @@ use core::convert::{TryFrom, TryInto}; use serde::{Deserialize, Serialize}; -use tendermint_proto::{abci::BlockParams as RawSize, Protobuf}; +use tendermint_proto::{abci::BlockParams as RawAbciSize, types::BlockParams as RawSize, Protobuf}; use crate::{error::Error, serializers}; @@ -42,7 +42,7 @@ impl TryFrom for Size { .try_into() .map_err(Error::integer_overflow)?, max_gas: value.max_gas, - time_iota_ms: Self::default_time_iota_ms(), + time_iota_ms: value.time_iota_ms, }) } } @@ -53,6 +53,34 @@ impl From for RawSize { RawSize { max_bytes: value.max_bytes as i64, max_gas: value.max_gas, + time_iota_ms: value.time_iota_ms, + } + } +} + +impl Protobuf for Size {} + +impl TryFrom for Size { + type Error = Error; + + fn try_from(value: RawAbciSize) -> Result { + Ok(Self { + max_bytes: value + .max_bytes + .try_into() + .map_err(Error::integer_overflow)?, + max_gas: value.max_gas, + time_iota_ms: Self::default_time_iota_ms(), + }) + } +} + +impl From for RawAbciSize { + fn from(value: Size) -> Self { + // Todo: make the struct more robust so this can become infallible. + RawAbciSize { + max_bytes: value.max_bytes as i64, + max_gas: value.max_gas, } } } diff --git a/tendermint/src/consensus/params.rs b/tendermint/src/consensus/params.rs index b0b969c3a..e03031ec4 100644 --- a/tendermint/src/consensus/params.rs +++ b/tendermint/src/consensus/params.rs @@ -4,25 +4,28 @@ use core::convert::{TryFrom, TryInto}; use serde::{Deserialize, Serialize}; use tendermint_proto::{ - abci::ConsensusParams as RawParams, - types::{ValidatorParams as RawValidatorParams, VersionParams as RawVersionParams}, + abci::ConsensusParams as RawAbciParams, + types::{ + ConsensusParams as RawParams, ValidatorParams as RawValidatorParams, + VersionParams as RawVersionParams, + }, Protobuf, }; use crate::{block, error::Error, evidence, prelude::*, public_key}; -/// Tendermint consensus parameters +/// All consensus-relevant parameters that can be adjusted by the ABCI app. +/// +/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#consensusparams) #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct Params { - /// Block size parameters + /// Parameters limiting the size of a block and time between consecutive blocks. pub block: block::Size, - - /// Evidence parameters + /// Parameters limiting the validity of evidence of byzantine behaviour. pub evidence: evidence::Params, - - /// Validator parameters + /// Parameters limiting the types of public keys validators can use. pub validator: ValidatorParams, - + /// The ABCI application version. /// Version parameters #[serde(skip)] // Todo: FIXME kvstore /genesis returns '{}' instead of '{app_version: "0"}' pub version: Option, @@ -63,10 +66,47 @@ impl From for RawParams { } } -/// Validator consensus parameters +impl Protobuf for Params {} + +impl TryFrom for Params { + type Error = Error; + + fn try_from(value: RawAbciParams) -> Result { + Ok(Self { + block: value + .block + .ok_or_else(|| Error::invalid_block("missing block".to_string()))? + .try_into()?, + evidence: value + .evidence + .ok_or_else(Error::invalid_evidence)? + .try_into()?, + validator: value + .validator + .ok_or_else(Error::invalid_validator_params)? + .try_into()?, + version: value.version.map(TryFrom::try_from).transpose()?, + }) + } +} + +impl From for RawAbciParams { + fn from(value: Params) -> Self { + RawAbciParams { + block: Some(value.block.into()), + evidence: Some(value.evidence.into()), + validator: Some(value.validator.into()), + version: value.version.map(From::from), + } + } +} + +/// ValidatorParams restrict the public key types validators can use. +/// +/// [Tendermint documentation](https://docs.tendermint.com/master/spec/core/data_structures.html#validatorparams) #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct ValidatorParams { - /// Allowed algorithms for validator signing + /// List of accepted public key types. pub pub_key_types: Vec, } @@ -109,10 +149,13 @@ impl From for RawValidatorParams { } /// Version Parameters +/// +/// [Tendermint documentation](https://docs.tendermint.com/master/spec/core/data_structures.html#versionparams) #[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Default)] pub struct VersionParams { + /// The ABCI application version. #[serde(with = "crate::serializers::from_str")] - app_version: u64, + pub app_version: u64, } impl Protobuf for VersionParams {} diff --git a/tendermint/src/error.rs b/tendermint/src/error.rs index 1d9f563e2..2d6e6cf80 100644 --- a/tendermint/src/error.rs +++ b/tendermint/src/error.rs @@ -112,6 +112,27 @@ define_error! { MissingTimestamp |_| { format_args!("missing timestamp field") }, + MissingVersion + |_| { format_args!("missing version") }, + + MissingMaxAgeDuration + |_| { format_args!("missing max_age_duration") }, + + MissingPublicKey + |_| { format_args!("missing public key") }, + + MissingValidator + |_| { format_args!("missing validator") }, + + MissingLastCommitInfo + |_| { format_args!("missing last commit info") }, + + MissingGenesisTime + |_| { format_args!("missing genesis time") }, + + MissingConsensusParams + |_| { format_args!("missing consensus params") }, + InvalidTimestamp { reason: String } | e | { format_args!("invalid timestamp: {}", e.reason) }, @@ -120,9 +141,6 @@ define_error! { { reason: String } | e | { format_args!("invalid block: {}", e.reason) }, - MissingVersion - |_| { format_args!("missing version") }, - InvalidFirstHeader |_| { format_args!("last_block_id is not null on first height") }, @@ -139,6 +157,18 @@ define_error! { InvalidEvidence |_| { format_args!("invalid evidence") }, + InvalidValidatorParams + |_| { format_args!("invalid validator parameters") }, + + InvalidVersionParams + |_| { format_args!("invalid version parameters") }, + + InvalidAbciRequestType + |_| { format_args!("invalid ABCI request type") }, + + InvalidAbciResponseType + |_| { format_args!("invalid ABCI response type") }, + BlockIdFlag |_| { format_args!("invalid block id flag") }, @@ -149,26 +179,23 @@ define_error! { UnsupportedKeyType |_| { format_args!("unsupported key type" ) }, - RawVotingPowerMismatch - { raw: vote::Power, computed: vote::Power } - |e| { format_args!("mismatch between raw voting ({0:?}) and computed one ({1:?})", e.raw, e.computed) }, + UnsupportedCheckTxType + |_| { format_args!("unsupported CheckTx type" ) }, - MissingPublicKey - |_| { format_args!("missing public key") }, + UnsupportedApplySnapshotChunkResult + |_| { format_args!("unsupported ApplySnapshotChunkResult type" ) }, - InvalidValidatorParams - |_| { format_args!("invalid validator parameters") }, + UnsupportedOfferSnapshotChunkResult + |_| { format_args!("unsupported OfferSnapshotChunkResult type" ) }, - InvalidVersionParams - |_| { format_args!("invalid version parameters") }, + RawVotingPowerMismatch + { raw: vote::Power, computed: vote::Power } + |e| { format_args!("mismatch between raw voting ({0:?}) and computed one ({1:?})", e.raw, e.computed) }, NegativeMaxAgeNum [ DisplayOnly ] |_| { format_args!("negative max_age_num_blocks") }, - MissingMaxAgeDuration - |_| { format_args!("missing max_age_duration") }, - ProposerNotFound { account: account::Id } |e| { format_args!("proposer with address '{0}' no found in validator set", e.account) }, @@ -195,3 +222,9 @@ define_error! { |_| { "trust threshold too small (must be >= 1/3)" }, } } + +impl From for Error { + fn from(_never: core::convert::Infallible) -> Error { + unreachable!("Infallible can never be constructed") + } +} diff --git a/tendermint/src/evidence.rs b/tendermint/src/evidence.rs index 172a54cfb..cf8d6b524 100644 --- a/tendermint/src/evidence.rs +++ b/tendermint/src/evidence.rs @@ -214,19 +214,28 @@ impl AsRef<[Evidence]> for Data { } } -/// Evidence collection parameters +/// EvidenceParams determine how we handle evidence of malfeasance. +/// +/// [Tendermint documentation](https://docs.tendermint.com/master/spec/core/data_structures.html#evidenceparams) #[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] // Todo: This struct is ready to be converted through tendermint_proto::types::EvidenceParams. // https://github.com/informalsystems/tendermint-rs/issues/741 pub struct Params { - /// Maximum allowed age for evidence to be collected + /// Max age of evidence, in blocks. #[serde(with = "serializers::from_str")] pub max_age_num_blocks: u64, - /// Max age duration + /// Max age of evidence, in time. + /// + /// It should correspond with an app's "unbonding period" or other similar + /// mechanism for handling [Nothing-At-Stake attacks][nas]. + /// + /// [nas]: https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed pub max_age_duration: Duration, - /// Max bytes + /// This sets the maximum size of total evidence in bytes that can be + /// committed in a single block, and should fall comfortably under the max + /// block bytes. The default is 1048576 or 1MB. #[serde(with = "serializers::from_str", default)] pub max_bytes: i64, } diff --git a/tendermint/src/hash.rs b/tendermint/src/hash.rs index b6f39026c..17de5c533 100644 --- a/tendermint/src/hash.rs +++ b/tendermint/src/hash.rs @@ -233,7 +233,11 @@ impl AsRef<[u8]> for AppHash { impl Debug for AppHash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "hash::AppHash({:?})", self.0) + write!( + f, + "AppHash({})", + Hex::upper_case().encode_to_string(&self.0).unwrap() + ) } } diff --git a/tendermint/src/prelude.rs b/tendermint/src/prelude.rs index a5b91aa13..aa9a20b65 100644 --- a/tendermint/src/prelude.rs +++ b/tendermint/src/prelude.rs @@ -1,7 +1,7 @@ // Re-export according to alloc::prelude::v1 because it is not yet stabilized // https://doc.rust-lang.org/src/alloc/prelude/v1.rs.html -pub use alloc::borrow::ToOwned; pub use alloc::{ + borrow::ToOwned, boxed::Box, format, string::{String, ToString}, diff --git a/tendermint/src/serializers.rs b/tendermint/src/serializers.rs index 697ceaa1c..8a0efc8b9 100644 --- a/tendermint/src/serializers.rs +++ b/tendermint/src/serializers.rs @@ -9,6 +9,5 @@ pub use tendermint_proto::serializers::*; pub mod apphash; pub mod hash; -pub mod hash_base64; pub mod option_hash; pub mod time; diff --git a/tendermint/src/time.rs b/tendermint/src/time.rs index f230df8cc..6dca5c592 100644 --- a/tendermint/src/time.rs +++ b/tendermint/src/time.rs @@ -34,7 +34,7 @@ use crate::{error::Error, prelude::*}; // For memory efficiency, the inner member is `PrimitiveDateTime`, with assumed // UTC offset. The `assume_utc` method is used to get the operational // `OffsetDateTime` value. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[serde(try_from = "Timestamp", into = "Timestamp")] pub struct Time(PrimitiveDateTime); @@ -117,6 +117,20 @@ impl Time { pub fn to_rfc3339(&self) -> String { timestamp::to_rfc3339_nanos(self.0.assume_utc()) } + + /// Computes `self + duration`, returning `None` if an overflow occurred. + pub fn checked_add(self, duration: Duration) -> Option { + let duration = duration.try_into().ok()?; + let t = self.0.checked_add(duration)?; + Self::from_utc(t.assume_utc()).ok() + } + + /// Computes `self - duration`, returning `None` if an overflow occurred. + pub fn checked_sub(self, duration: Duration) -> Option { + let duration = duration.try_into().ok()?; + let t = self.0.checked_sub(duration)?; + Self::from_utc(t.assume_utc()).ok() + } } impl fmt::Display for Time { @@ -151,19 +165,12 @@ impl Add for Time { type Output = Result; fn add(self, rhs: Duration) -> Self::Output { - // Work around not being able to depend on time 0.3.5 - // https://github.com/informalsystems/tendermint-rs/issues/1047 - let lhs_nanos = self.0.assume_utc().unix_timestamp_nanos(); - let rhs_nanos: i128 = rhs - .as_nanos() - .try_into() - .map_err(|_| Error::duration_out_of_range())?; - let res_nanos = lhs_nanos - .checked_add(rhs_nanos) + let duration = rhs.try_into().map_err(|_| Error::duration_out_of_range())?; + let t = self + .0 + .checked_add(duration) .ok_or_else(Error::duration_out_of_range)?; - let t = OffsetDateTime::from_unix_timestamp_nanos(res_nanos) - .map_err(|_| Error::duration_out_of_range())?; - Self::from_utc(t) + Self::from_utc(t.assume_utc()) } } @@ -171,19 +178,12 @@ impl Sub for Time { type Output = Result; fn sub(self, rhs: Duration) -> Self::Output { - // Work around not being able to depend on time 0.3.5 - // https://github.com/informalsystems/tendermint-rs/issues/1047 - let lhs_nanos = self.0.assume_utc().unix_timestamp_nanos(); - let rhs_nanos: i128 = rhs - .as_nanos() - .try_into() - .map_err(|_| Error::duration_out_of_range())?; - let res_nanos = lhs_nanos - .checked_sub(rhs_nanos) + let duration = rhs.try_into().map_err(|_| Error::duration_out_of_range())?; + let t = self + .0 + .checked_sub(duration) .ok_or_else(Error::duration_out_of_range)?; - let t = OffsetDateTime::from_unix_timestamp_nanos(res_nanos) - .map_err(|_| Error::duration_out_of_range())?; - Self::from_utc(t) + Self::from_utc(t.assume_utc()) } } @@ -375,31 +375,31 @@ mod tests { proptest! { #[test] - fn add_regular((dt, d) in args_for_regular_add()) { + fn checked_add_regular((dt, d) in args_for_regular_add()) { let t: Time = dt.try_into().unwrap(); - let t = (t + d).unwrap(); + let t = t.checked_add(d).unwrap(); let res: OffsetDateTime = t.into(); assert_eq!(res, dt + d); } #[test] - fn sub_regular((dt, d) in args_for_regular_sub()) { + fn checked_sub_regular((dt, d) in args_for_regular_sub()) { let t: Time = dt.try_into().unwrap(); - let t = (t - d).unwrap(); + let t = t.checked_sub(d).unwrap(); let res: OffsetDateTime = t.into(); assert_eq!(res, dt - d); } #[test] - fn add_overflow((dt, d) in args_for_overflowed_add()) { + fn checked_add_overflow((dt, d) in args_for_overflowed_add()) { let t: Time = dt.try_into().unwrap(); - assert!((t + d).is_err()); + assert_eq!(t.checked_add(d), None); } #[test] - fn sub_overflow((dt, d) in args_for_overflowed_sub()) { + fn checked_sub_overflow((dt, d) in args_for_overflowed_sub()) { let t: Time = dt.try_into().unwrap(); - assert!((t - d).is_err()); + assert_eq!(t.checked_sub(d), None); } } } diff --git a/tools/abci-test/src/main.rs b/tools/abci-test/src/main.rs index eae6abe32..8469f5172 100644 --- a/tools/abci-test/src/main.rs +++ b/tools/abci-test/src/main.rs @@ -2,11 +2,11 @@ use futures::StreamExt; use structopt::StructOpt; -use tendermint::abci::Transaction; use tendermint_config::net::Address; -use tendermint_rpc::{ - event::EventData, query::EventType, Client, SubscriptionClient, WebSocketClient, -}; +use tendermint_rpc::abci::Transaction; +use tendermint_rpc::event::EventData; +use tendermint_rpc::query::EventType; +use tendermint_rpc::{Client, SubscriptionClient, WebSocketClient}; use tokio::time::Duration; use tracing::{debug, error, info, level_filters::LevelFilter}; diff --git a/tools/kvstore-test/tests/light-client.rs b/tools/kvstore-test/tests/light-client.rs index 14e25a252..0abe2b713 100644 --- a/tools/kvstore-test/tests/light-client.rs +++ b/tools/kvstore-test/tests/light-client.rs @@ -14,7 +14,6 @@ use std::{convert::TryFrom, time::Duration}; -use tendermint::abci::transaction::Hash as TxHash; use tendermint_light_client::{ builder::{LightClientBuilder, SupervisorBuilder}, components::io::{AtHeight, Io, IoError, ProdIo}, @@ -33,7 +32,11 @@ struct TestEvidenceReporter; #[contracts::contract_trait] impl EvidenceReporter for TestEvidenceReporter { - fn report(&self, evidence: Evidence, peer: PeerId) -> Result { + fn report( + &self, + evidence: Evidence, + peer: PeerId, + ) -> Result { panic!( "unexpected fork detected for peer {} with evidence: {:?}", peer, evidence diff --git a/tools/kvstore-test/tests/tendermint.rs b/tools/kvstore-test/tests/tendermint.rs index 31e7606d9..a9c450231 100644 --- a/tools/kvstore-test/tests/tendermint.rs +++ b/tools/kvstore-test/tests/tendermint.rs @@ -22,11 +22,11 @@ mod rpc { use futures::StreamExt; use tendermint::{ - abci::{Code, Log, Transaction}, block::Height, merkle::simple_hash_from_byte_vectors, }; use tendermint_rpc::{ + abci::{Code, Log, Transaction}, endpoint::tx::Response as ResultTx, event::{Event, EventData, TxInfo}, query::{EventType, Query}, @@ -109,12 +109,7 @@ mod rpc { // Check for empty merkle root. // See: https://github.com/informalsystems/tendermint-rs/issues/562 let computed_data_hash = simple_hash_from_byte_vectors( - block_info - .block - .data - .iter() - .map(|t| t.to_owned().into()) - .collect(), + block_info.block.data.iter().map(|t| t.to_owned()).collect(), ); assert_eq!( computed_data_hash, @@ -480,7 +475,7 @@ mod rpc { http_client: &HttpClient, websocket_client: &mut WebSocketClient, tx: Transaction, - ) -> Result<(tendermint::abci::transaction::Hash, TxInfo), tendermint_rpc::Error> { + ) -> Result<(tendermint_rpc::abci::transaction::Hash, TxInfo), tendermint_rpc::Error> { let mut subs = websocket_client.subscribe(EventType::Tx.into()).await?; let r = http_client.broadcast_tx_async(tx.clone()).await?; diff --git a/tools/proto-compiler/src/constants.rs b/tools/proto-compiler/src/constants.rs index facaabe64..d46458e23 100644 --- a/tools/proto-compiler/src/constants.rs +++ b/tools/proto-compiler/src/constants.rs @@ -21,8 +21,7 @@ const HEXSTRING: &str = r#"#[serde(with = "crate::serializers::bytes::hexstring" const BASE64STRING: &str = r#"#[serde(with = "crate::serializers::bytes::base64string")]"#; const VEC_BASE64STRING: &str = r#"#[serde(with = "crate::serializers::bytes::vec_base64string")]"#; const OPTIONAL: &str = r#"#[serde(with = "crate::serializers::optional")]"#; -const VEC_SKIP_IF_EMPTY: &str = - r#"#[serde(skip_serializing_if = "::prost::alloc::vec::Vec::is_empty", with = "serde_bytes")]"#; +const BYTES_SKIP_IF_EMPTY: &str = r#"#[serde(skip_serializing_if = "bytes::Bytes::is_empty")]"#; const NULLABLEVECARRAY: &str = r#"#[serde(with = "crate::serializers::txs")]"#; const NULLABLE: &str = r#"#[serde(with = "crate::serializers::nullable")]"#; const ALIAS_POWER_QUOTED: &str = @@ -31,6 +30,7 @@ const PART_SET_HEADER_TOTAL: &str = r#"#[serde(with = "crate::serializers::part_set_header_total")]"#; const RENAME_EDPUBKEY: &str = r#"#[serde(rename = "tendermint/PubKeyEd25519", with = "crate::serializers::bytes::base64string")]"#; const RENAME_SECPPUBKEY: &str = r#"#[serde(rename = "tendermint/PubKeySecp256k1", with = "crate::serializers::bytes::base64string")]"#; +const RENAME_SRPUBKEY: &str = r#"#[serde(rename = "tendermint/PubKeySr25519", with = "crate::serializers::bytes::base64string")]"#; const RENAME_DUPLICATEVOTE: &str = r#"#[serde(rename = "tendermint/DuplicateVoteEvidence")]"#; const RENAME_LIGHTCLIENTATTACK: &str = r#"#[serde(rename = "tendermint/LightClientAttackEvidence")]"#; @@ -106,7 +106,7 @@ pub static CUSTOM_FIELD_ATTRIBUTES: &[(&str, &str)] = &[ (".tendermint.abci.ResponseInfo.last_block_app_hash", DEFAULT), ( ".tendermint.abci.ResponseInfo.last_block_app_hash", - VEC_SKIP_IF_EMPTY, + BYTES_SKIP_IF_EMPTY, ), (".tendermint.types.BlockID.hash", HEXSTRING), (".tendermint.types.BlockID.part_set_header", ALIAS_PARTS), @@ -169,6 +169,7 @@ pub static CUSTOM_FIELD_ATTRIBUTES: &[(&str, &str)] = &[ ".tendermint.crypto.PublicKey.sum.secp256k1", RENAME_SECPPUBKEY, ), + (".tendermint.crypto.PublicKey.sum.sr25519", RENAME_SRPUBKEY), ( ".tendermint.types.Evidence.sum.duplicate_vote_evidence", RENAME_DUPLICATEVOTE, diff --git a/tools/proto-compiler/src/functions.rs b/tools/proto-compiler/src/functions.rs index 6055dcf03..df2158019 100644 --- a/tools/proto-compiler/src/functions.rs +++ b/tools/proto-compiler/src/functions.rs @@ -13,7 +13,7 @@ use walkdir::WalkDir; /// Clone or open+fetch a repository and check out a specific commitish /// In case of an existing repository, the origin remote will be set to `url`. -pub fn get_commitish(dir: &PathBuf, url: &str, commitish: &str) { +pub fn get_commitish(dir: &Path, url: &str, commitish: &str) { let repo = if dir.exists() { fetch_existing(dir, url) } else { @@ -39,7 +39,7 @@ fn clone_new(dir: &Path, url: &str) -> Repository { builder.clone(url, dir).unwrap() } -fn fetch_existing(dir: &PathBuf, url: &str) -> Repository { +fn fetch_existing(dir: &Path, url: &str) -> Repository { println!( " [info] => Fetching from {} into existing {} folder", url, @@ -156,7 +156,7 @@ fn find_reference_or_commit<'a>( } /// Copy generated files to target folder -pub fn copy_files(src_dir: &PathBuf, target_dir: &PathBuf) { +pub fn copy_files(src_dir: &Path, target_dir: &Path) { // Remove old compiled files remove_dir_all(target_dir).unwrap_or_default(); create_dir_all(target_dir).unwrap(); @@ -208,7 +208,7 @@ pub fn find_proto_files(proto_paths: Vec) -> Vec { } /// Create tendermint.rs with library information -pub fn generate_tendermint_lib(prost_dir: &PathBuf, tendermint_lib_target: &PathBuf) { +pub fn generate_tendermint_lib(prost_dir: &Path, tendermint_lib_target: &Path) { let file_names = WalkDir::new(prost_dir) .into_iter() .filter_map(|e| e.ok()) diff --git a/tools/proto-compiler/src/main.rs b/tools/proto-compiler/src/main.rs index 53b5cb42d..ea65b32bf 100644 --- a/tools/proto-compiler/src/main.rs +++ b/tools/proto-compiler/src/main.rs @@ -55,8 +55,12 @@ fn main() { // List available proto files let protos = find_proto_files(proto_paths); - // Compile proto files with added annotations, exchange prost_types to our own let mut pb = prost_build::Config::new(); + + // Use shared Bytes buffers for ABCI messages: + pb.bytes(&[".tendermint.abci"]); + + // Compile proto files with added annotations, exchange prost_types to our own pb.out_dir(&out_dir); for type_attribute in CUSTOM_TYPE_ATTRIBUTES { pb.type_attribute(type_attribute.0, type_attribute.1);