Skip to content
This repository has been archived by the owner on Jun 3, 2020. It is now read-only.

tendermint-rs: /abci_query RPC endpoint (closes #287) #296

Merged
merged 1 commit into from
Jul 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ yubihsm = { version = "0.26", features = ["setup", "usb"], optional = true }
zeroize = "0.9"

[dependencies.abscissa_core]
version = "0.2.0"
version = "0.2"

[dependencies.tendermint]
version = "0.9"
Expand All @@ -59,7 +59,7 @@ tempfile = "3"
rand = "0.6"

[dev-dependencies.abscissa_core]
version = "0.2.0"
version = "0.2"
features = ["testing"]

[features]
Expand Down
8 changes: 6 additions & 2 deletions tendermint-rs/src/abci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ mod info;
#[cfg(feature = "rpc")]
mod log;
#[cfg(feature = "rpc")]
mod path;
#[cfg(feature = "rpc")]
mod proof;
#[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,
code::Code, data::Data, gas::Gas, info::Info, log::Log, path::Path, proof::Proof,
responses::Responses, transaction::Transaction,
};
26 changes: 26 additions & 0 deletions tendermint-rs/src/abci/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! Paths to ABCI data

use crate::error::Error;
use serde::{Deserialize, Serialize};
use std::{
fmt::{self, Display},
str::FromStr,
};

/// Path to ABCI data
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Path(String);

impl Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.0)
}
}

impl FromStr for Path {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
Ok(Path(s.to_owned()))
}
}
51 changes: 51 additions & 0 deletions tendermint-rs/src/abci/proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! ABCI Merkle proofs

use crate::error::Error;
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt::{self, Display},
str::FromStr,
};
use subtle_encoding::{Encoding, Hex};

/// ABCI Merkle proofs
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Proof(Vec<u8>);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ebuchman this seems like it might have some overlap with the stuff you're working on? That said, is this an OK start?


impl AsRef<[u8]> for Proof {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}

impl Display for Proof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
&Hex::upper_case().encode_to_string(&self.0).unwrap()
)
}
}

impl FromStr for Proof {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
let bytes = Hex::upper_case().decode(s)?;
Ok(Proof(bytes))
}
}

impl<'de> Deserialize<'de> for Proof {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let hex = String::deserialize(deserializer)?;
Ok(Self::from_str(&hex).map_err(|e| D::Error::custom(format!("{}", e)))?)
}
}

impl Serialize for Proof {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}
2 changes: 1 addition & 1 deletion tendermint-rs/src/block/height.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
/// Block height for a particular chain (i.e. number of blocks created since
/// the chain began)
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Height(pub u64);
pub struct Height(u64);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With pub on the u64 this wraps, the Height type can't maintain invariants (i.e. >0 or >=0)


impl Height {
/// Convert `u64` to block height.
Expand Down
18 changes: 17 additions & 1 deletion tendermint-rs/src/rpc/client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Tendermint RPC client

use crate::{
abci::Transaction,
abci::{self, Transaction},
block::Height,
net,
rpc::{self, endpoint::*, Error, Response},
Expand Down Expand Up @@ -33,6 +33,22 @@ impl Client {
Ok(self.perform(abci_info::Request)?.response)
}

/// `/abci_query`: query the ABCI application
pub fn abci_query<D>(
&self,
path: Option<abci::Path>,
data: D,
height: Option<Height>,
prove: bool,
) -> Result<abci_query::AbciQuery, Error>
where
D: Into<Vec<u8>>,
{
Ok(self
.perform(abci_query::Request::new(path, data, height, prove))?
.response)
}

/// `/block`: get block at a given height.
pub fn block<H>(&self, height: H) -> Result<block::Response, Error>
where
Expand Down
1 change: 1 addition & 0 deletions tendermint-rs/src/rpc/endpoint.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Tendermint JSONRPC endpoints

pub mod abci_info;
pub mod abci_query;
pub mod block;
pub mod block_results;
pub mod blockchain;
Expand Down
95 changes: 95 additions & 0 deletions tendermint-rs/src/rpc/endpoint/abci_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! `/abci_query` endpoint JSONRPC wrapper

use crate::{
abci::{Code, Log, Path, Proof},
block, rpc, serializers,
};
use serde::{Deserialize, Serialize};

/// Query the ABCI application for information
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Request {
/// Path to the data
path: Option<Path>,

/// Data to query
data: Vec<u8>,

/// Block height
height: Option<block::Height>,

/// Include proof in response
prove: bool,
}

impl Request {
/// Create a new ABCI query request
pub fn new<D>(path: Option<Path>, data: D, height: Option<block::Height>, prove: bool) -> Self
where
D: Into<Vec<u8>>,
{
Self {
path,
data: data.into(),
height,
prove,
}
}
}

impl rpc::Request for Request {
type Response = Response;

fn method(&self) -> rpc::Method {
rpc::Method::AbciInfo
}
}

/// ABCI query response wrapper
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Response {
/// ABCI query results
pub response: AbciQuery,
}

impl rpc::Response for Response {}

/// ABCI query results
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AbciQuery {
/// Response code
pub code: Code,

/// Log value
pub log: Log,

/// Info value
pub info: Option<String>,

/// Index
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "serializers::serialize_i64",
deserialize_with = "serializers::parse_i64"
)
)]
pub index: i64,

/// Key
// TODO(tarcieri): parse to Vec<u8>?
pub key: String,

/// Value
// TODO(tarcieri): parse to Vec<u8>?
pub value: String,

/// Proof (if requested)
pub proof: Option<Proof>,

/// Block height
pub height: block::Height,

/// Codespace
pub codespace: Option<String>,
}
6 changes: 6 additions & 0 deletions tendermint-rs/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ mod rpc {
assert_eq!(&abci_info.data, "GaiaApp");
}

/// `/abci_query` endpoint
#[test]
fn abci_query() {
// TODO(tarcieri): write integration test for this endpoint
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike the other endpoints it wasn't clear how to immediately test this in an integration test (with gaiad)

}

/// `/block` endpoint
#[test]
fn block() {
Expand Down
9 changes: 9 additions & 0 deletions tendermint-rs/tests/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ mod endpoints {
assert_eq!(response.last_block_height.value(), 488120);
}

#[test]
fn abci_query() {
let response = endpoint::abci_query::Response::from_json(&read_json_fixture("abci_query"))
.unwrap()
.response;

assert_eq!(response.height.value(), 1);
}

#[test]
fn block() {
let response = endpoint::block::Response::from_json(&read_json_fixture("block")).unwrap();
Expand Down
15 changes: 15 additions & 0 deletions tendermint-rs/tests/support/rpc/abci_query.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike the other JSON examples found in this directory, this one wasn't taken live from a gaiad RPC response, but is copy/paste from https://tendermint.com/rpc/#abciquery

"jsonrpc": "2.0",
"id": "",
"result": {
"response": {
"log": "exists",
"height": "1",
"proof": "010114FED0DAD959F36091AD761C922ABA3CBF1D8349990101020103011406AA2262E2F448242DF2C2607C3CDC705313EE3B0001149D16177BC71E445476174622EA559715C293740C",
"value": "61626364",
"key": "61626364",
"index": "-1",
"code": "0"
}
}
}