diff --git a/README.md b/README.md index 1209015..579f679 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Crate][crate-image]][crate-link] [![Build Status][build-image]][build-link] [![Apache 2.0 Licensed][license-image]][license-link] +![Rust 1.35+][rustc-image] Key Management System for [Tendermint] applications, initially targeting [Cosmos Validators]. @@ -215,6 +216,7 @@ limitations under the License. [build-link]: https://circleci.com/gh/tendermint/kms [license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg [license-link]: https://github.com/tendermint/kms/blob/master/LICENSE +[rustc-image]: https://img.shields.io/badge/rustc-1.35+-blue.svg [Tendermint]: https://tendermint.com/ [Cosmos Validators]: https://cosmos.network/docs/gaia/validators/validator-faq.html [YubiHSM 2]: https://github.com/tendermint/kms/blob/master/README.yubihsm.md diff --git a/tendermint-rs/README.md b/tendermint-rs/README.md index 8c6452e..2a0d40d 100644 --- a/tendermint-rs/README.md +++ b/tendermint-rs/README.md @@ -4,25 +4,21 @@ [![Docs][docs-image]][docs-link] [![Build Status][build-image]][build-link] [![Apache 2.0 Licensed][license-image]][license-link] +![Rust 1.35+][rustc-image] -[crate-image]: https://img.shields.io/crates/v/tendermint.svg -[crate-link]: https://crates.io/crates/tendermint -[docs-image]: https://docs.rs/tendermint/badge.svg -[docs-link]: https://docs.rs/tendermint/ -[build-image]: https://circleci.com/gh/tendermint/kms.svg?style=shield -[build-link]: https://circleci.com/gh/tendermint/kms -[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg -[license-link]: https://github.com/tendermint/kms/blob/master/LICENSE - -Rust crate for interacting with Tendermint: a high-performance blockchain +Rust crate for interacting with [Tendermint]: a high-performance blockchain consensus engine that powers Byzantine fault tolerant applications written in any programming language. [Documentation][docs-link] +## Requirements + +- Rust 1.35+ + ## License -Copyright © 2018 Tendermint +Copyright © 2018-2019 Tendermint Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -35,3 +31,19 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/tendermint.svg +[crate-link]: https://crates.io/crates/tendermint +[docs-image]: https://docs.rs/tendermint/badge.svg +[docs-link]: https://docs.rs/tendermint/ +[build-image]: https://circleci.com/gh/tendermint/kms.svg?style=shield +[build-link]: https://circleci.com/gh/tendermint/kms +[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg +[license-link]: https://github.com/tendermint/kms/blob/master/LICENSE +[rustc-image]: https://img.shields.io/badge/rustc-1.35+-blue.svg + +[//]: # (general links) + +[Tendermint]: https://github.com/tendermint/tendermint diff --git a/tendermint-rs/src/abci.rs b/tendermint-rs/src/abci.rs new file mode 100644 index 0000000..76e0572 --- /dev/null +++ b/tendermint-rs/src/abci.rs @@ -0,0 +1,28 @@ +//! Application BlockChain Interface (ABCI) +//! +//! NOTE: This module contains types for ABCI responses as consumed from RPC +//! endpoints. It does not contain an ABCI protocol implementation. +//! +//! For that, see: +//! +//! + +#[cfg(feature = "rpc")] +mod code; +#[cfg(feature = "rpc")] +mod data; +#[cfg(feature = "rpc")] +mod gas; +#[cfg(feature = "rpc")] +mod info; +#[cfg(feature = "rpc")] +mod log; +#[cfg(feature = "rpc")] +mod responses; +pub mod transaction; + +#[cfg(feature = "rpc")] +pub use self::{ + code::Code, data::Data, gas::Gas, info::Info, log::Log, responses::Responses, + transaction::Transaction, +}; diff --git a/tendermint-rs/src/abci/code.rs b/tendermint-rs/src/abci/code.rs new file mode 100644 index 0000000..45d6b37 --- /dev/null +++ b/tendermint-rs/src/abci/code.rs @@ -0,0 +1,71 @@ +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; + +/// ABCI application response codes. +/// +/// These presently use 0 for success and non-zero for errors: +/// +/// +/// +/// Note that in the future there may potentially be non-zero success codes. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum Code { + /// Success + Ok, + + /// Error codes + Err(u32), +} + +impl Code { + /// Was the response OK? + pub fn is_ok(self) -> bool { + match self { + Code::Ok => true, + Code::Err(_) => false, + } + } + + /// Was the response an error? + pub fn is_err(self) -> bool { + !self.is_ok() + } + + /// Get the integer error value for this code + pub fn value(self) -> u32 { + u32::from(self) + } +} + +impl From for Code { + fn from(value: u32) -> Code { + match value { + 0 => Code::Ok, + err => Code::Err(err), + } + } +} + +impl From for u32 { + fn from(code: Code) -> u32 { + match code { + Code::Ok => 0, + Code::Err(err) => err, + } + } +} + +impl<'de> Deserialize<'de> for Code { + fn deserialize>(deserializer: D) -> Result { + Ok(Code::from( + String::deserialize(deserializer)? + .parse::() + .map_err(|e| D::Error::custom(format!("{}", e)))?, + )) + } +} + +impl Serialize for Code { + fn serialize(&self, serializer: S) -> Result { + self.value().serialize(serializer) + } +} diff --git a/tendermint-rs/src/abci/data.rs b/tendermint-rs/src/abci/data.rs new file mode 100644 index 0000000..dc9c06e --- /dev/null +++ b/tendermint-rs/src/abci/data.rs @@ -0,0 +1,64 @@ +use crate::Error; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; +use subtle_encoding::hex; + +/// ABCI transaction data. +/// +/// Transactions are opaque binary blobs which are validated according to +/// application-specific rules. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Data(Vec); + +impl Data { + /// Borrow the data as bytes + pub fn as_bytes(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8]> for Data { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl Display for Data { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in &self.0 { + write!(f, "{:02X}", byte)?; + } + Ok(()) + } +} + +impl FromStr for Data { + type Err = Error; + + fn from_str(s: &str) -> Result { + // Accept either upper or lower case hex + let bytes = hex::decode_upper(s) + .or_else(|_| hex::decode(s)) + .map_err(|_| Error::Parse)?; + + Ok(Data(bytes)) + } +} + +impl<'de> Deserialize<'de> for Data { + fn deserialize>(deserializer: D) -> Result { + let bytes = hex::decode(String::deserialize(deserializer)?.as_bytes()) + .map_err(|e| D::Error::custom(format!("{}", e)))?; + + Ok(Self(bytes)) + } +} + +impl Serialize for Data { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } +} diff --git a/tendermint-rs/src/abci/gas.rs b/tendermint-rs/src/abci/gas.rs new file mode 100644 index 0000000..ed2bc75 --- /dev/null +++ b/tendermint-rs/src/abci/gas.rs @@ -0,0 +1,58 @@ +//! Gas: abstract representation for the cost of resources used by nodes when +//! processing transactions. +//! +//! For more information, see: +//! +//! + +use crate::Error; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; + +/// Gas: representation of transaction processing resource costs +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] +pub struct Gas(u64); + +impl From for Gas { + fn from(amount: u64) -> Gas { + Gas(amount) + } +} + +impl From for u64 { + fn from(gas: Gas) -> u64 { + gas.0 + } +} + +impl Display for Gas { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl FromStr for Gas { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(Self::from(s.parse::().map_err(|_| Error::Parse)?)) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Gas { + fn deserialize>(deserializer: D) -> Result { + Ok(Self::from_str(&String::deserialize(deserializer)?) + .map_err(|e| D::Error::custom(format!("{}", e)))?) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Gas { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } +} diff --git a/tendermint-rs/src/abci/info.rs b/tendermint-rs/src/abci/info.rs new file mode 100644 index 0000000..48c700f --- /dev/null +++ b/tendermint-rs/src/abci/info.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +/// ABCI info +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Info(String); + +impl AsRef for Info { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl Display for Info { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/tendermint-rs/src/abci/log.rs b/tendermint-rs/src/abci/log.rs new file mode 100644 index 0000000..9286f66 --- /dev/null +++ b/tendermint-rs/src/abci/log.rs @@ -0,0 +1,28 @@ +#[cfg(feature = "serde_json")] +use crate::Error; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +/// ABCI log data +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Log(String); + +impl Log { + /// Parse the log data as JSON, returning a `serde_json::Value` + #[cfg(feature = "serde_json")] + pub fn parse_json(&self) -> Result { + serde_json::from_str(&self.0).map_err(|_| Error::Parse) + } +} + +impl AsRef for Log { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl Display for Log { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/tendermint-rs/src/abci/responses.rs b/tendermint-rs/src/abci/responses.rs new file mode 100644 index 0000000..a8fa26a --- /dev/null +++ b/tendermint-rs/src/abci/responses.rs @@ -0,0 +1,124 @@ +//! ABCI response types used by the `/block_results` RPC endpoint. + +use super::{code::Code, data::Data, gas::Gas, info::Info, log::Log}; +use crate::{consensus, validator}; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +/// Responses for ABCI calls which occur during block processing. +/// +/// Returned from the `/block_results` RPC endpoint. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Responses { + /// Deliver TX response. + // TODO(tarcieri): remove the `rename` attribute when this lands upstream: + // + #[serde(rename = "DeliverTx")] + pub deliver_tx: Option, + + /// Begin block response. + // TODO(tarcieri): remove the `rename` attribute when this lands upstream: + // + #[serde(rename = "BeginBlock")] + pub begin_block: Option, + + /// End block response. + // TODO(tarcieri): remove the `rename` attribute when this lands upstream: + // + #[serde(rename = "EndBlock")] + pub end_block: Option, +} + +/// Deliver TX response. +/// +/// This type corresponds to the `ResponseDeliverTx` proto from: +/// +/// +// TODO(tarcieri): generate this automatically from the proto +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DeliverTx { + /// ABCI application response code + pub code: Option, + + /// ABCI application data + pub data: Option, + + /// ABCI log data (nondeterministic) + pub log: Option, + + /// ABCI info (nondeterministic) + pub info: Option, + + /// Amount of gas wanted + #[serde(default, rename = "gasWanted")] + pub gas_wanted: Gas, + + /// Amount of gas used + #[serde(default, rename = "gasUsed")] + pub gas_used: Gas, + + /// Tags + #[serde(default)] + pub tags: Vec, + + /// Codespace + pub codespace: Option, +} + +/// Begin block response. +/// +/// This type corresponds to the `ResponseBeginBlock` proto from: +/// +/// +// TODO(tarcieri): generate this automatically from the proto +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BeginBlock { + /// Tags + #[serde(default)] + pub tags: Vec, +} + +/// End block response. +/// +/// This type corresponds to the `ResponseEndBlock` proto from: +/// +/// +// TODO(tarcieri): generate this automatically from the proto +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EndBlock { + /// Validator updates + pub validator_updates: Option>, + + /// New consensus params + pub consensus_param_updates: Option, + + /// Tags + #[serde(default)] + pub tags: Vec, +} + +/// Tags +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Tag { + /// Key + pub key: String, + + /// Value + pub value: String, +} + +/// Codespace +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Codespace(String); + +impl AsRef for Codespace { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl Display for Codespace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/tendermint-rs/src/transaction.rs b/tendermint-rs/src/abci/transaction.rs similarity index 96% rename from tendermint-rs/src/transaction.rs rename to tendermint-rs/src/abci/transaction.rs index c82a7cd..bd645dd 100644 --- a/tendermint-rs/src/transaction.rs +++ b/tendermint-rs/src/abci/transaction.rs @@ -6,7 +6,7 @@ pub use self::hash::Hash; use std::slice; #[cfg(feature = "serde")] use { - serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}, + serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}, subtle_encoding::base64, }; diff --git a/tendermint-rs/src/transaction/hash.rs b/tendermint-rs/src/abci/transaction/hash.rs similarity index 100% rename from tendermint-rs/src/transaction/hash.rs rename to tendermint-rs/src/abci/transaction/hash.rs diff --git a/tendermint-rs/src/block.rs b/tendermint-rs/src/block.rs index 1a54b2c..c8aa313 100644 --- a/tendermint-rs/src/block.rs +++ b/tendermint-rs/src/block.rs @@ -16,7 +16,7 @@ pub use self::{ meta::Meta, size::Size, }; -use crate::{evidence, transaction}; +use crate::{abci::transaction, evidence}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; diff --git a/tendermint-rs/src/block/height.rs b/tendermint-rs/src/block/height.rs index bd05e77..65974e9 100644 --- a/tendermint-rs/src/block/height.rs +++ b/tendermint-rs/src/block/height.rs @@ -1,6 +1,6 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{self, Debug, Display}, str::FromStr, @@ -101,7 +101,7 @@ impl<'de> Deserialize<'de> for Height { #[cfg(feature = "serde")] impl Serialize for Height { fn serialize(&self, serializer: S) -> Result { - self.value().to_string().serialize(serializer) + self.to_string().serialize(serializer) } } diff --git a/tendermint-rs/src/chain/id.rs b/tendermint-rs/src/chain/id.rs index 30684e0..adcb84f 100644 --- a/tendermint-rs/src/chain/id.rs +++ b/tendermint-rs/src/chain/id.rs @@ -2,7 +2,7 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ cmp::Ordering, fmt::{self, Debug, Display}, diff --git a/tendermint-rs/src/evidence.rs b/tendermint-rs/src/evidence.rs index 0ae7d3a..41ca291 100644 --- a/tendermint-rs/src/evidence.rs +++ b/tendermint-rs/src/evidence.rs @@ -4,7 +4,7 @@ use std::slice; #[cfg(feature = "serde")] use { crate::serializers, - serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}, + serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}, subtle_encoding::base64, }; diff --git a/tendermint-rs/src/hash.rs b/tendermint-rs/src/hash.rs index 4b33ca6..5d4774c 100644 --- a/tendermint-rs/src/hash.rs +++ b/tendermint-rs/src/hash.rs @@ -2,7 +2,7 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{self, Debug, Display}, str::FromStr, diff --git a/tendermint-rs/src/lib.rs b/tendermint-rs/src/lib.rs index 2e4602f..637e713 100644 --- a/tendermint-rs/src/lib.rs +++ b/tendermint-rs/src/lib.rs @@ -24,6 +24,7 @@ extern crate prost_amino as prost; #[macro_use] extern crate prost_amino_derive as prost_derive; +pub mod abci; pub mod account; #[cfg(feature = "amino-types")] pub mod amino_types; @@ -49,7 +50,6 @@ pub mod secret_connection; mod serializers; pub mod signature; pub mod time; -pub mod transaction; pub mod validator; mod version; pub mod vote; @@ -66,7 +66,6 @@ pub use crate::{ public_key::{PublicKey, TendermintKey}, signature::Signature, time::Time, - transaction::Transaction, version::Version, vote::Vote, }; diff --git a/tendermint-rs/src/net.rs b/tendermint-rs/src/net.rs index 020b23d..1206fdf 100644 --- a/tendermint-rs/src/net.rs +++ b/tendermint-rs/src/net.rs @@ -3,7 +3,7 @@ use crate::node; use failure::{bail, Error}; #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{self, Display}, path::PathBuf, diff --git a/tendermint-rs/src/public_key.rs b/tendermint-rs/src/public_key.rs index 9553b8e..78be64e 100644 --- a/tendermint-rs/src/public_key.rs +++ b/tendermint-rs/src/public_key.rs @@ -2,7 +2,7 @@ use crate::error::Error; #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use signatory::{ecdsa::curve::secp256k1, ed25519}; use std::{ fmt::{self, Display}, diff --git a/tendermint-rs/src/rpc/client.rs b/tendermint-rs/src/rpc/client.rs index 6bb60f7..cb77e0b 100644 --- a/tendermint-rs/src/rpc/client.rs +++ b/tendermint-rs/src/rpc/client.rs @@ -1,10 +1,11 @@ //! Tendermint RPC client use crate::{ + abci::Transaction, block::Height, net, rpc::{self, endpoint::*, Error, Response}, - Genesis, Transaction, + Genesis, }; use hyper::header; use std::io::Read; @@ -40,11 +41,24 @@ impl Client { self.perform(block::Request::new(height.into())) } - /// `/block`: get the latest block + /// `/block`: get the latest block. pub fn latest_block(&self) -> Result { self.perform(block::Request::default()) } + /// `/block_results`: get ABCI results for a block at a particular height. + pub fn block_results(&self, height: H) -> Result + where + H: Into, + { + self.perform(block_results::Request::new(height.into())) + } + + /// `/block_results`: get ABCI results for the latest block. + pub fn latest_block_results(&self) -> Result { + self.perform(block_results::Request::default()) + } + /// `/blockchain`: get block headers for `min` <= `height` <= `max`. /// /// Block headers are returned in descending order (highest first). diff --git a/tendermint-rs/src/rpc/endpoint.rs b/tendermint-rs/src/rpc/endpoint.rs index 125413d..ad612a2 100644 --- a/tendermint-rs/src/rpc/endpoint.rs +++ b/tendermint-rs/src/rpc/endpoint.rs @@ -2,6 +2,7 @@ pub mod abci_info; pub mod block; +pub mod block_results; pub mod blockchain; pub mod broadcast; pub mod commit; diff --git a/tendermint-rs/src/rpc/endpoint/abci_info.rs b/tendermint-rs/src/rpc/endpoint/abci_info.rs index ccdf660..16d4c23 100644 --- a/tendermint-rs/src/rpc/endpoint/abci_info.rs +++ b/tendermint-rs/src/rpc/endpoint/abci_info.rs @@ -1,7 +1,7 @@ //! `/abci_info` endpoint JSONRPC wrapper use crate::{block, hash, rpc, Hash}; -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use subtle_encoding::base64; /// Request ABCI information from a node diff --git a/tendermint-rs/src/rpc/endpoint/block.rs b/tendermint-rs/src/rpc/endpoint/block.rs index d833ad6..34aa0f5 100644 --- a/tendermint-rs/src/rpc/endpoint/block.rs +++ b/tendermint-rs/src/rpc/endpoint/block.rs @@ -9,7 +9,9 @@ use serde::{Deserialize, Serialize}; /// Get information about a specific block #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct Request { - /// Height of the block to request + /// Height of the block to request. + /// + /// If no height is provided, it will fetch results for the latest block. height: Option, } diff --git a/tendermint-rs/src/rpc/endpoint/block_results.rs b/tendermint-rs/src/rpc/endpoint/block_results.rs new file mode 100644 index 0000000..91e1757 --- /dev/null +++ b/tendermint-rs/src/rpc/endpoint/block_results.rs @@ -0,0 +1,42 @@ +//! `/block_results` endpoint JSONRPC wrapper + +use crate::{abci, block, rpc}; +use serde::{Deserialize, Serialize}; + +/// Get ABCI results at a given height. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct Request { + /// Height of the block to request. + /// + /// If no height is provided, it will fetch results for the latest block. + height: Option, +} + +impl Request { + /// Create a new request for information about a particular block + pub fn new(height: block::Height) -> Self { + Self { + height: Some(height), + } + } +} + +impl rpc::Request for Request { + type Response = Response; + + fn method(&self) -> rpc::Method { + rpc::Method::BlockResults + } +} + +/// ABCI result response. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Response { + /// Block height + pub height: block::Height, + + /// Block results + pub results: abci::Responses, +} + +impl rpc::Response for Response {} diff --git a/tendermint-rs/src/rpc/endpoint/broadcast.rs b/tendermint-rs/src/rpc/endpoint/broadcast.rs index f198e66..f45593e 100644 --- a/tendermint-rs/src/rpc/endpoint/broadcast.rs +++ b/tendermint-rs/src/rpc/endpoint/broadcast.rs @@ -3,143 +3,3 @@ pub mod tx_async; pub mod tx_commit; pub mod tx_sync; - -use crate::Error; -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; -use std::{ - fmt::{self, Display}, - str::FromStr, -}; -use subtle_encoding::hex; - -/// Transaction broadcast response codes. -/// -/// These presently use 0 for success and non-zero for errors, however there -/// is ample discussion about potentially supporting non-zero success cases -/// as well. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum Code { - /// Success - Ok, - - /// Error codes - Err(u32), -} - -impl Code { - /// Was the response OK? - pub fn is_ok(self) -> bool { - match self { - Code::Ok => true, - Code::Err(_) => false, - } - } - - /// Was the response an error? - pub fn is_err(self) -> bool { - !self.is_ok() - } - - /// Get the integer error value for this code - pub fn value(self) -> u32 { - u32::from(self) - } -} - -impl From for Code { - fn from(value: u32) -> Code { - match value { - 0 => Code::Ok, - err => Code::Err(err), - } - } -} - -impl From for u32 { - fn from(code: Code) -> u32 { - match code { - Code::Ok => 0, - Code::Err(err) => err, - } - } -} - -impl<'de> Deserialize<'de> for Code { - fn deserialize>(deserializer: D) -> Result { - Ok(Code::from( - String::deserialize(deserializer)? - .parse::() - .map_err(|e| D::Error::custom(format!("{}", e)))?, - )) - } -} - -impl Serialize for Code { - fn serialize(&self, serializer: S) -> Result { - self.value().serialize(serializer) - } -} - -/// Transaction data -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Data(Vec); - -impl Data { - /// Borrow the data as bytes - pub fn as_bytes(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8]> for Data { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl Display for Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for byte in &self.0 { - write!(f, "{:02X}", byte)?; - } - Ok(()) - } -} - -impl FromStr for Data { - type Err = Error; - - fn from_str(s: &str) -> Result { - // Accept either upper or lower case hex - let bytes = hex::decode_upper(s) - .or_else(|_| hex::decode(s)) - .map_err(|_| Error::Parse)?; - - Ok(Data(bytes)) - } -} - -impl<'de> Deserialize<'de> for Data { - fn deserialize>(deserializer: D) -> Result { - let bytes = hex::decode(String::deserialize(deserializer)?.as_bytes()) - .map_err(|e| D::Error::custom(format!("{}", e)))?; - - Ok(Self(bytes)) - } -} - -impl Serialize for Data { - fn serialize(&self, serializer: S) -> Result { - self.to_string().serialize(serializer) - } -} - -/// Transaction log -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Log(String); - -impl Display for Log { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} diff --git a/tendermint-rs/src/rpc/endpoint/broadcast/tx_async.rs b/tendermint-rs/src/rpc/endpoint/broadcast/tx_async.rs index 76853eb..69fec93 100644 --- a/tendermint-rs/src/rpc/endpoint/broadcast/tx_async.rs +++ b/tendermint-rs/src/rpc/endpoint/broadcast/tx_async.rs @@ -1,7 +1,9 @@ //! `/broadcast_tx_async`: broadcast a transaction and return immediately. -use super::{Code, Data, Log}; -use crate::{rpc, transaction, Transaction}; +use crate::{ + abci::{transaction, Code, Data, Log, Transaction}, + rpc, +}; use serde::{Deserialize, Serialize}; /// `/broadcast_tx_async`: broadcast a transaction and return immediately. diff --git a/tendermint-rs/src/rpc/endpoint/broadcast/tx_commit.rs b/tendermint-rs/src/rpc/endpoint/broadcast/tx_commit.rs index 92cd8f5..2561889 100644 --- a/tendermint-rs/src/rpc/endpoint/broadcast/tx_commit.rs +++ b/tendermint-rs/src/rpc/endpoint/broadcast/tx_commit.rs @@ -1,8 +1,10 @@ //! `/broadcast_tx_commit`: only returns error if `mempool.CheckTx()` errs or //! if we timeout waiting for tx to commit. -use super::{Code, Data, Log}; -use crate::{block, rpc, transaction, Transaction}; +use crate::{ + abci::{transaction, Code, Data, Log, Transaction}, + block, rpc, +}; use serde::{Deserialize, Serialize}; /// `/broadcast_tx_commit`: only returns error if `mempool.CheckTx()` errs or diff --git a/tendermint-rs/src/rpc/endpoint/broadcast/tx_sync.rs b/tendermint-rs/src/rpc/endpoint/broadcast/tx_sync.rs index b1c7e8a..8fc48ca 100644 --- a/tendermint-rs/src/rpc/endpoint/broadcast/tx_sync.rs +++ b/tendermint-rs/src/rpc/endpoint/broadcast/tx_sync.rs @@ -1,7 +1,9 @@ //! `/broadcast_tx_sync`: returns with the response from `CheckTx`. -use super::{Code, Data, Log}; -use crate::{rpc, transaction, Transaction}; +use crate::{ + abci::{transaction, Code, Data, Log, Transaction}, + rpc, +}; use serde::{Deserialize, Serialize}; /// `/broadcast_tx_sync`: returns with the response from `CheckTx`. diff --git a/tendermint-rs/src/rpc/method.rs b/tendermint-rs/src/rpc/method.rs index b61b324..344c62e 100644 --- a/tendermint-rs/src/rpc/method.rs +++ b/tendermint-rs/src/rpc/method.rs @@ -1,7 +1,7 @@ //! JSONRPC request methods use super::Error; -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{self, Display}, str::FromStr, @@ -18,6 +18,9 @@ pub enum Method { /// Get block info Block, + /// Get ABCI results for a particular block + BlockResults, + /// Get blockchain info Blockchain, @@ -55,6 +58,7 @@ impl Method { match self { Method::AbciInfo => "abci_info", Method::Block => "block", + Method::BlockResults => "block_results", Method::Blockchain => "blockchain", Method::BroadcastTxAsync => "broadcast_tx_async", Method::BroadcastTxSync => "broadcast_tx_sync", @@ -76,6 +80,7 @@ impl FromStr for Method { Ok(match s { "abci_info" => Method::AbciInfo, "block" => Method::Block, + "block_results" => Method::BlockResults, "blockchain" => Method::Blockchain, "broadcast_tx_async" => Method::BroadcastTxAsync, "broadcast_tx_sync" => Method::BroadcastTxSync, diff --git a/tendermint-rs/src/serializers.rs b/tendermint-rs/src/serializers.rs index 34a5e9d..0fa06ad 100644 --- a/tendermint-rs/src/serializers.rs +++ b/tendermint-rs/src/serializers.rs @@ -1,6 +1,6 @@ //! Serde serializers -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "rpc")] use std::time::Duration; diff --git a/tendermint-rs/src/signature.rs b/tendermint-rs/src/signature.rs index 0b5bfbb..f61c998 100644 --- a/tendermint-rs/src/signature.rs +++ b/tendermint-rs/src/signature.rs @@ -1,7 +1,7 @@ //! Cryptographic (a.k.a. digital) signatures #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] use signatory::Signature as SignatureTrait; #[cfg(feature = "serde")] diff --git a/tendermint-rs/src/validator.rs b/tendermint-rs/src/validator.rs index 5301400..e612a68 100644 --- a/tendermint-rs/src/validator.rs +++ b/tendermint-rs/src/validator.rs @@ -2,7 +2,7 @@ use crate::{account, vote, PublicKey}; #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; /// Validator information #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -55,3 +55,14 @@ impl Serialize for ProposerPriority { self.0.to_string().serialize(serializer) } } + +/// Updates to the validator set +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug)] +pub struct Update { + /// Validator public key + pub pub_key: PublicKey, + + /// New voting power + pub power: vote::Power, +} diff --git a/tendermint-rs/src/vote.rs b/tendermint-rs/src/vote.rs index 41cb689..6a4ebbe 100644 --- a/tendermint-rs/src/vote.rs +++ b/tendermint-rs/src/vote.rs @@ -7,7 +7,7 @@ use crate::{account, block, Signature, Time}; #[cfg(feature = "serde")] use { crate::serializers, - serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}, + serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}, }; /// Votes are signed messages from validators for a particular block which diff --git a/tendermint-rs/src/vote/power.rs b/tendermint-rs/src/vote/power.rs index f0ffe19..e037712 100644 --- a/tendermint-rs/src/vote/power.rs +++ b/tendermint-rs/src/vote/power.rs @@ -1,7 +1,7 @@ //! Votes #[cfg(feature = "serde")] -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; /// Voting power #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] diff --git a/tendermint-rs/tests/integration.rs b/tendermint-rs/tests/integration.rs index c949c6f..0e51504 100644 --- a/tendermint-rs/tests/integration.rs +++ b/tendermint-rs/tests/integration.rs @@ -30,6 +30,14 @@ mod rpc { assert_eq!(block_info.block_meta.header.height.value(), height); } + /// `/block_results` endpoint + #[test] + fn block_results() { + let height = 1u64; + let block_results = localhost_rpc_client().block_results(height).unwrap(); + assert_eq!(block_results.height.value(), height); + } + /// `/blockchain` endpoint #[test] fn blockchain() {