Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /header RPC endpoint #1101

Closed
wants to merge 11 commits into from
Closed
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
3 changes: 3 additions & 0 deletions .changelog/unreleased/features/832-header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[tendermint-rpc]` Add support for the `/header` RPC endpoint. See
<https://docs.tendermint.com/v0.35/rpc/#/Info/header> for details
([#832](https://github.com/informalsystems/tendermint-rs/issues/832)).
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# will have compiled files and executables
target/

# IDE specific files
.idea/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
Expand Down
2 changes: 1 addition & 1 deletion config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl LogLevel {
{
self.components
.get(key.as_ref())
.or_else(|| self.global.as_ref())
.or(self.global.as_ref())
.map(AsRef::as_ref)
}

Expand Down
3 changes: 3 additions & 0 deletions light-client/src/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
//!
//! [1]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/verification/verification.md

// FIXME(hu55a1n1): Remove below line once clippy errors in `contracts::post` derive macro are fixed
#![allow(clippy::nonminimal_bool)]

use contracts::*;
use core::fmt;

Expand Down
3 changes: 3 additions & 0 deletions light-client/src/peer_list.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Provides a peer list for use within the `Supervisor`

// FIXME(hu55a1n1): Remove below line once clippy errors in `contracts::post` derive macro are fixed
#![allow(clippy::nonminimal_bool)]

use contracts::{post, pre};
use std::collections::{BTreeSet, HashMap};

Expand Down
3 changes: 3 additions & 0 deletions p2p/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Error types

// FIXME(hu55a1n1): Remove below line once flex-error solves this clippy error
#![allow(clippy::use_self)]

use flex_error::{define_error, DisplayOnly};
use prost::DecodeError;
use signature::Error as SignatureError;
Expand Down
8 changes: 8 additions & 0 deletions rpc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ pub trait Client {
.response)
}

/// `/header`: get header at a given height.
async fn header<H>(&self, height: H) -> Result<header::Response, Error>
where
H: Into<Height> + Send,
{
self.perform(header::Request::new(height.into())).await
}

/// `/block`: get block at a given height.
async fn block<H>(&self, height: H) -> Result<block::Response, Error>
where
Expand Down
5 changes: 5 additions & 0 deletions rpc/src/client/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ enum ClientRequest {
#[structopt(long)]
prove: bool,
},
/// Get a header at a given height.
Header { height: u32 },
/// Get a block at a given height.
Block { height: u32 },
/// Get block headers between two heights (min <= height <= max).
Expand Down Expand Up @@ -314,6 +316,9 @@ where
.await?,
)
.map_err(Error::serde)?,
ClientRequest::Header { height } => {
serde_json::to_string_pretty(&client.header(height).await?).map_err(Error::serde)?
}
ClientRequest::Block { height } => {
serde_json::to_string_pretty(&client.block(height).await?).map_err(Error::serde)?
}
Expand Down
1 change: 1 addition & 0 deletions rpc/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod consensus_params;
pub mod consensus_state;
pub mod evidence;
pub mod genesis;
pub mod header;
pub mod health;
pub mod net_info;
pub mod status;
Expand Down
43 changes: 43 additions & 0 deletions rpc/src/endpoint/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! `/header` endpoint JSON-RPC wrapper

use serde::{Deserialize, Serialize};

use tendermint::block::{Header, Height};

/// Get information about a specific header
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Request {
/// Height of the header to request.
///
/// If no height is provided, it will fetch results for the latest header.
pub height: Option<Height>,
}

impl Request {
/// Create a new request for information about a particular header
pub fn new(height: Height) -> Self {
Self {
height: Some(height),
}
}
}

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

fn method(&self) -> crate::Method {
crate::Method::Header
}
}

impl crate::SimpleRequest for Request {}

/// Header responses
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(transparent)]
pub struct Response {
/// Header
pub header: Header,
}

impl crate::Response for Response {}
5 changes: 5 additions & 0 deletions rpc/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub enum Method {
/// Get ABCI query
AbciQuery,

/// Get block header
Header,

/// Get block info
Block,

Expand Down Expand Up @@ -86,6 +89,7 @@ impl Method {
match self {
Method::AbciInfo => "abci_info",
Method::AbciQuery => "abci_query",
Method::Header => "header",
Method::Block => "block",
Method::BlockResults => "block_results",
Method::BlockSearch => "block_search",
Expand Down Expand Up @@ -117,6 +121,7 @@ impl FromStr for Method {
Ok(match s {
"abci_info" => Method::AbciInfo,
"abci_query" => Method::AbciQuery,
"header" => Method::Header,
"block" => Method::Block,
"block_results" => Method::BlockResults,
"block_search" => Method::BlockSearch,
Expand Down
37 changes: 37 additions & 0 deletions rpc/tests/kvstore_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ fn outgoing_fixtures() {
assert!(wrapped.params().height.is_none());
assert!(!wrapped.params().prove);
}
"header_at_height_1" => {
let wrapped =
serde_json::from_str::<RequestWrapper<endpoint::header::Request>>(&content)
.unwrap();
assert_eq!(wrapped.params().height.unwrap().value(), 1);
}
"block_at_height_0" => {
let wrapped =
serde_json::from_str::<RequestWrapper<endpoint::block::Request>>(&content)
Expand Down Expand Up @@ -346,6 +352,37 @@ fn incoming_fixtures() {
assert!(result.response.proof.is_none());
assert!(result.response.value.is_empty());
}
"header_at_height_1" => {
let result = endpoint::header::Response::from_string(content).unwrap();
assert!(result.header.app_hash.value().is_empty());
assert_eq!(result.header.chain_id.as_str(), CHAIN_ID);
assert!(!result.header.consensus_hash.is_empty());
assert_eq!(result.header.data_hash, empty_merkle_root_hash);
assert_eq!(result.header.evidence_hash, empty_merkle_root_hash);
assert_eq!(result.header.height.value(), 1);
assert!(result.header.last_block_id.is_none());
assert_eq!(result.header.last_commit_hash, empty_merkle_root_hash);
assert_eq!(result.header.last_results_hash, empty_merkle_root_hash);
assert!(!result.header.next_validators_hash.is_empty());
assert_ne!(
result.header.proposer_address.as_bytes(),
[0u8; tendermint::account::LENGTH]
);
assert!(
result
.header
.time
.duration_since(informal_epoch)
.unwrap()
.as_secs()
> 0
);
assert!(!result.header.validators_hash.is_empty());
assert_eq!(
result.header.version,
tendermint::block::header::Version { block: 11, app: 1 }
);
}
"block_at_height_0" => {
let res = endpoint::block::Response::from_string(&content);

Expand Down
29 changes: 29 additions & 0 deletions rpc/tests/kvstore_fixtures/incoming/header_at_height_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"id": "0166b641-4967-4b3c-a36a-73ea4e5a737a",
"jsonrpc": "2.0",
"result": {
"app_hash": "",
"chain_id": "dockerchain",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"data_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"height": "1",
"last_block_id": {
"hash": "",
"parts": {
"hash": "",
"total": 0
}
},
"last_commit_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"last_results_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"next_validators_hash": "ADFA3B40824D69EAD7828B9A78D16D80DFA93499D1DB0EC362916AE61182A64D",
"proposer_address": "ABA577531E6D6F4119E7E1E0EE1909B908A8346D",
"time": "2021-07-16T12:16:29.232984022Z",
"validators_hash": "ADFA3B40824D69EAD7828B9A78D16D80DFA93499D1DB0EC362916AE61182A64D",
"version": {
"app": "1",
"block": "11"
}
}
}
8 changes: 8 additions & 0 deletions rpc/tests/kvstore_fixtures/outgoing/header_at_height_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "0166b641-4967-4b3c-a36a-73ea4e5a737a",
"jsonrpc": "2.0",
"method": "header",
"params": {
"height": "1"
}
}
2 changes: 1 addition & 1 deletion tendermint/src/abci/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl Data {

impl AsRef<[Transaction]> for Data {
fn as_ref(&self) -> &[Transaction] {
self.txs.as_deref().unwrap_or_else(|| &[])
self.txs.as_deref().unwrap_or(&[])
}
}

Expand Down
2 changes: 1 addition & 1 deletion tendermint/src/evidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ impl Data {

impl AsRef<[Evidence]> for Data {
fn as_ref(&self) -> &[Evidence] {
self.evidence.as_deref().unwrap_or_else(|| &[])
self.evidence.as_deref().unwrap_or(&[])
}
}

Expand Down
11 changes: 11 additions & 0 deletions tools/kvstore-test/tests/tendermint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ mod rpc {
assert_eq!(abci_query.codespace, String::new());
}

/// `/header` endpoint
#[tokio::test]
async fn header() {
let height = 1u64;
let res = localhost_http_client()
.header(Height::try_from(height).unwrap())
.await
.unwrap();
assert_eq!(res.header.height.value(), height);
}

/// `/block` endpoint
#[tokio::test]
async fn block() {
Expand Down
10 changes: 10 additions & 0 deletions tools/rpc-probe/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ pub fn abci_query(key: &str) -> PlannedInteraction {
.into()
}

pub fn header(height: u64) -> PlannedInteraction {
Request::new(
"header",
json!({
"height": format!("{}", height),
}),
)
.into()
}

pub fn block(height: u64) -> PlannedInteraction {
Request::new(
"block",
Expand Down
1 change: 1 addition & 0 deletions tools/rpc-probe/src/kvstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn quick_probe_plan(output_path: &Path, request_wait: Duration) -> Result<Pl
in_series(vec![
abci_info(),
abci_query("non_existent_key").with_name("abci_query_with_non_existent_key"),
header(1).with_name("header_at_height_1"),
block(0).with_name("block_at_height_0").expect_error(),
block(1).with_name("block_at_height_1"),
block(10)
Expand Down