Skip to content

Commit

Permalink
Add block_by_hash RPC endpoint and tests (#1089)
Browse files Browse the repository at this point in the history
* Add block_by_hash RPC endpoint and tests

* rpc-probe part of this

* Fix comments

* Update .changelog/unreleased/features/832-block-by-hash.md

Co-authored-by: Thane Thomson <connect@thanethomson.com>

* cargo fmt

Co-authored-by: Thane Thomson <connect@thanethomson.com>
  • Loading branch information
hansl and thanethomson committed Feb 15, 2022
1 parent c70f6ee commit 33a4841
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 0 deletions.
1 change: 1 addition & 0 deletions .changelog/unreleased/features/832-block-by-hash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `[tendermint-rpc]` Add support for the `/block_by_hash` RPC endpoint. See <https://docs.tendermint.com/master/rpc/#/Info/block_by_hash> for details ([#832](https://github.com/informalsystems/tendermint-rs/issues/832)).
8 changes: 8 additions & 0 deletions rpc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ pub trait Client {
self.perform(block::Request::new(height.into())).await
}

/// `/block_by_hash`: get block by hash.
async fn block_by_hash(
&self,
hash: tendermint::Hash,
) -> Result<block_by_hash::Response, Error> {
self.perform(block_by_hash::Request::new(hash)).await
}

/// `/block`: get the latest block.
async fn latest_block(&self) -> Result<block::Response, Error> {
self.perform(block::Request::default()).await
Expand Down
10 changes: 10 additions & 0 deletions rpc/src/client/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ enum ClientRequest {
},
/// Get a block at a given height.
Block { height: u32 },
/// Get a block by its hash.
BlockByHash { hash: String },
/// Get block headers between two heights (min <= height <= max).
Blockchain {
/// The minimum height
Expand Down Expand Up @@ -316,6 +318,14 @@ where
ClientRequest::Block { height } => {
serde_json::to_string_pretty(&client.block(height).await?).map_err(Error::serde)?
}
ClientRequest::BlockByHash { hash } => serde_json::to_string_pretty(
&client
.block_by_hash(
tendermint::Hash::from_str(&hash).map_err(|e| Error::parse(e.to_string()))?,
)
.await?,
)
.map_err(Error::serde)?,
ClientRequest::Blockchain { min, max } => {
serde_json::to_string_pretty(&client.blockchain(min, max).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 @@ -3,6 +3,7 @@
pub mod abci_info;
pub mod abci_query;
pub mod block;
pub mod block_by_hash;
pub mod block_results;
pub mod block_search;
pub mod blockchain;
Expand Down
47 changes: 47 additions & 0 deletions rpc/src/endpoint/block_by_hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! `/block_by_hash` endpoint JSON-RPC wrapper

use serde::{Deserialize, Serialize};

use tendermint::block::{self, Block};
use tendermint::Hash;

/// Get information about a specific block by its hash
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Request {
/// Hash of the block to request.
///
/// If no hash is provided, it will return no block (as if the hash
/// did not match any block).
pub hash: Option<Hash>,
}

impl Request {
/// Create a new request for information about a particular block
pub fn new<H: Into<Hash>>(hash: H) -> Self {
Self {
hash: Some(hash.into()),
}
}
}

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

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

impl crate::SimpleRequest for Request {}

/// Block responses
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Response {
/// Block ID
pub block_id: block::Id,

/// Block data
pub block: Option<Block>,
}

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 @@ -22,6 +22,9 @@ pub enum Method {
/// Get block info
Block,

/// Get block info by hash
BlockByHash,

/// Get ABCI results for a particular block
BlockResults,

Expand Down Expand Up @@ -87,6 +90,7 @@ impl Method {
Method::AbciInfo => "abci_info",
Method::AbciQuery => "abci_query",
Method::Block => "block",
Method::BlockByHash => "block_by_hash",
Method::BlockResults => "block_results",
Method::BlockSearch => "block_search",
Method::Blockchain => "blockchain",
Expand Down Expand Up @@ -118,6 +122,7 @@ impl FromStr for Method {
"abci_info" => Method::AbciInfo,
"abci_query" => Method::AbciQuery,
"block" => Method::Block,
"block_by_hash" => Method::BlockByHash,
"block_results" => Method::BlockResults,
"block_search" => Method::BlockSearch,
"blockchain" => Method::Blockchain,
Expand Down
18 changes: 18 additions & 0 deletions rpc/tests/kvstore_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ fn outgoing_fixtures() {
.unwrap();
assert_eq!(wrapped.params().height.unwrap().value(), 10);
}
"block_by_hash" => {
// First, get the hash at height 1.
let wrapped = serde_json::from_str::<
RequestWrapper<endpoint::block_by_hash::Request>,
>(&content)
.unwrap();
assert_eq!(
wrapped.params().hash.unwrap().to_string(),
"00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"
);
}
"block_results_at_height_10" => {
let wrapped = serde_json::from_str::<
RequestWrapper<endpoint::block_results::Request>,
Expand Down Expand Up @@ -472,6 +483,13 @@ fn incoming_fixtures() {
assert!(result.txs_results.is_none());
assert!(result.validator_updates.is_empty());
}
"block_by_hash" => {
let result = endpoint::block::Response::from_string(content).unwrap();
assert_eq!(
result.block_id.hash.to_string(),
"BCF3DB412E80A396D10BF5B5E6D3E63D3B06DEB25AA958BCB8CE18D023838042"
);
}
"block_search" => {
let result = endpoint::block_search::Response::from_string(content).unwrap();
assert_eq!(result.total_count as usize, result.blocks.len());
Expand Down
65 changes: 65 additions & 0 deletions rpc/tests/kvstore_fixtures/incoming/block_by_hash.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"id": "988f944f-4d85-486f-ad27-2f3574c2a4a3",
"jsonrpc": "2.0",
"result": {
"block": {
"data": {
"txs": []
},
"evidence": {
"evidence": []
},
"header": {
"app_hash": "0000000000000000",
"chain_id": "dockerchain",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"data_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"height": "10",
"last_block_id": {
"hash": "03979BC4F521D92D137F8A93B64D7D1A8589560F64C4543784DECCBCEDF1D13F",
"parts": {
"hash": "00DC8C2DE1DE6B66960CB5F15F802286D6423903C06804C35053E0F757B34E47",
"total": 1
}
},
"last_commit_hash": "B194E4E363E010ED4F80860FAF452B45D81C77D7C68C753424F44596A229D692",
"last_results_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"next_validators_hash": "D506A39182DDCC44917A3E7827E784B501B354789F99BAC8D56AE50ABE34972B",
"proposer_address": "6B3F66DCF73507BCE7148D6580DAC27074108628",
"time": "2021-11-25T17:04:38.160820316Z",
"validators_hash": "D506A39182DDCC44917A3E7827E784B501B354789F99BAC8D56AE50ABE34972B",
"version": {
"app": "1",
"block": "11"
}
},
"last_commit": {
"block_id": {
"hash": "03979BC4F521D92D137F8A93B64D7D1A8589560F64C4543784DECCBCEDF1D13F",
"parts": {
"hash": "00DC8C2DE1DE6B66960CB5F15F802286D6423903C06804C35053E0F757B34E47",
"total": 1
}
},
"height": "9",
"round": 0,
"signatures": [
{
"block_id_flag": 2,
"signature": "5yClL8UlPdvb2tzNguZu3UaTH5X5S8S635u9nBQjZQw3NFhrZklXm6Aw7Mxvhn3y7CL0yKHdRmH0FnPh8cs2Cg==",
"timestamp": "2021-11-25T17:04:38.160820316Z",
"validator_address": "6B3F66DCF73507BCE7148D6580DAC27074108628"
}
]
}
},
"block_id": {
"hash": "BCF3DB412E80A396D10BF5B5E6D3E63D3B06DEB25AA958BCB8CE18D023838042",
"parts": {
"hash": "7F02924A557B6B6AEB9390183F5458C88EAC02956AFDCCAAFC09B59A80D1EEA8",
"total": 1
}
}
}
}
8 changes: 8 additions & 0 deletions rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "a37bcc8e-6a32-4157-9200-a26be9981bbd",
"jsonrpc": "2.0",
"method": "block",
"params": {
"hash": "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"
}
}
26 changes: 26 additions & 0 deletions tools/kvstore-test/tests/tendermint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
///
/// (Make sure you install cargo-make using `cargo install cargo-make` first.)
mod rpc {
use std::str::FromStr;
use std::cmp::min;

use tendermint_rpc::{
Expand Down Expand Up @@ -120,6 +121,31 @@ mod rpc {
);
}

/// `/block_search` endpoint
#[tokio::test]
async fn block_by_hash() {
let res = localhost_http_client()
.block_by_hash(
tendermint::Hash::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap()
)
.await
.unwrap();
assert!(res.block.is_none());

// Reuse block(1) to get an existing hash.
let height = 1u64;
let block_info = localhost_http_client()
.block(Height::try_from(height).unwrap())
.await
.unwrap();
let res = localhost_http_client()
.block_by_hash(block_info.block_id.hash)
.await
.unwrap();
assert!(res.block.is_some());
assert_eq!(block_info.block.header.height.value(), height);
}

/// `/block_results` endpoint
#[tokio::test]
async fn block_results() {
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 @@ -30,6 +30,16 @@ pub fn block(height: u64) -> PlannedInteraction {
.into()
}

pub fn block_by_hash(hash: &str) -> PlannedInteraction {
Request::new(
"block_by_hash",
json!({
"hash": format!("{}", hash),
}),
)
.into()
}

pub fn block_search(query: &str, page: u32, per_page: u32, order_by: &str) -> PlannedInteraction {
Request::new(
"block_search",
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 @@ -21,6 +21,7 @@ pub fn quick_probe_plan(output_path: &Path, request_wait: Duration) -> Result<Pl
.with_min_height(10)
.with_name("block_at_height_10"),
block_results(10).with_name("block_results_at_height_10"),
block_by_hash("0x00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF").with_name("block_by_hash"),
block_search("block.height > 1", 1, 10, "asc").with_name("block_search"),
blockchain(1, 10).with_name("blockchain_from_1_to_10"),
commit(10).with_name("commit_at_height_10"),
Expand Down

0 comments on commit 33a4841

Please sign in to comment.