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 block_search RPC endpoint #991

Merged
merged 3 commits into from
Sep 28, 2021
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
3 changes: 3 additions & 0 deletions .changelog/unreleased/features/832-block-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[tendermint-rpc]` Add support for the `/block_search` RPC endpoint. See
<https://docs.tendermint.com/master/rpc/\#/Info/block_search> for details
([#832](https://github.com/informalsystems/tendermint-rs/issues/832))
12 changes: 12 additions & 0 deletions rpc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ pub trait Client {
self.perform(block_results::Request::default()).await
}

/// `/block_search`: search for blocks by BeginBlock and EndBlock events.
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<block_search::Response, Error> {
self.perform(block_search::Request::new(query, page, per_page, order))
.await
}

/// `/blockchain`: get block headers for `min` <= `height` <= `max`.
///
/// Block headers are returned in descending order (highest first).
Expand Down
21 changes: 21 additions & 0 deletions rpc/src/client/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ enum ClientRequest {
/// The height of the block you want.
height: u32,
},
/// Search for a block by way of a specific query. Uses the same
/// query syntax as the `subscribe` endpoint.
BlockSearch {
/// The query against which blocks should be matched.
query: Query,
#[structopt(long, default_value = "1")]
page: u32,
#[structopt(long, default_value = "10")]
per_page: u8,
#[structopt(long, default_value = "asc")]
order: Order,
},
// TODO(thane): Implement evidence broadcast
/// Broadcast a transaction asynchronously (without waiting for the ABCI
/// app to check it or for it to be committed).
Expand Down Expand Up @@ -313,6 +325,15 @@ where
serde_json::to_string_pretty(&client.block_results(height).await?)
.map_err(Error::serde)?
}
ClientRequest::BlockSearch {
query,
page,
per_page,
order,
} => {
serde_json::to_string_pretty(&client.block_search(query, page, per_page, order).await?)
.map_err(Error::serde)?
}
ClientRequest::BroadcastTxAsync { tx } => serde_json::to_string_pretty(
&client
.broadcast_tx_async(Transaction::from(tx.into_bytes()))
Expand Down
1 change: 1 addition & 0 deletions rpc/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod abci_info;
pub mod abci_query;
pub mod block;
pub mod block_results;
pub mod block_search;
pub mod blockchain;
pub mod broadcast;
pub mod commit;
Expand Down
48 changes: 48 additions & 0 deletions rpc/src/endpoint/block_search.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! `/block_search` endpoint JSON-RPC wrapper

pub use super::{block, block_results};

use crate::{Method, Order};
use serde::{Deserialize, Serialize};

/// Request for searching for blocks by their BeginBlock and EndBlock events.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Request {
pub query: String,
#[serde(with = "tendermint_proto::serializers::from_str")]
pub page: u32,
#[serde(with = "tendermint_proto::serializers::from_str")]
pub per_page: u8,
pub order_by: Order,
}

impl Request {
/// Constructor.
pub fn new(query: impl ToString, page: u32, per_page: u8, order_by: Order) -> Self {
Self {
query: query.to_string(),
page,
per_page,
order_by,
}
}
}

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

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

impl crate::SimpleRequest for Request {}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Response {
pub blocks: Vec<block::Response>,
#[serde(with = "tendermint_proto::serializers::from_str")]
pub total_count: u32,
}

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 @@ -24,6 +24,9 @@ pub enum Method {
/// Get ABCI results for a particular block
BlockResults,

/// Search for blocks by their BeginBlock and EndBlock events
BlockSearch,

/// Get blockchain info
Blockchain,

Expand Down Expand Up @@ -84,6 +87,7 @@ impl Method {
Method::AbciQuery => "abci_query",
Method::Block => "block",
Method::BlockResults => "block_results",
Method::BlockSearch => "block_search",
Method::Blockchain => "blockchain",
Method::BroadcastEvidence => "broadcast_evidence",
Method::BroadcastTxAsync => "broadcast_tx_async",
Expand Down Expand Up @@ -114,6 +118,7 @@ impl FromStr for Method {
"abci_query" => Method::AbciQuery,
"block" => Method::Block,
"block_results" => Method::BlockResults,
"block_search" => Method::BlockSearch,
"blockchain" => Method::Blockchain,
"broadcast_evidence" => Method::BroadcastEvidence,
"broadcast_tx_async" => Method::BroadcastTxAsync,
Expand Down
53 changes: 53 additions & 0 deletions rpc/tests/kvstore_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ fn outgoing_fixtures() {
.unwrap();
assert_eq!(wrapped.params().height.unwrap().value(), 10);
}
"block_search" => {
let wrapped =
serde_json::from_str::<RequestWrapper<endpoint::block_search::Request>>(
&content,
)
.unwrap();
assert_eq!(wrapped.params().query, "block.height > 1");
assert_eq!(wrapped.params().page, 1);
assert_eq!(wrapped.params().per_page, 10);
assert_eq!(wrapped.params().order_by, Order::Ascending);
}
"blockchain_from_1_to_10" => {
let wrapped =
serde_json::from_str::<RequestWrapper<endpoint::blockchain::Request>>(&content)
Expand Down Expand Up @@ -447,6 +458,48 @@ fn incoming_fixtures() {
assert!(result.txs_results.is_none());
assert!(result.validator_updates.is_empty());
}
"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!(block.block.header.data_hash.is_none());
assert!(block.block.header.evidence_hash.is_none());
assert_eq!(block.block.header.height.value(), 10);
assert!(block.block.header.last_block_id.is_some());
assert_eq!(block.block.header.last_commit_hash, empty_merkle_root_hash);
assert_eq!(block.block.header.last_results_hash, empty_merkle_root_hash);
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: 10, 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);
}
}
"blockchain_from_1_to_10" => {
let result = endpoint::blockchain::Response::from_string(content).unwrap();
assert_eq!(result.block_metas.len(), 10);
Expand Down
70 changes: 70 additions & 0 deletions rpc/tests/kvstore_fixtures/incoming/block_search.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"jsonrpc": "2.0",
"id": "",
"result": {
"blocks": [
{
"block_id": {
"hash": "4FFD15F274758E474898498A191EB8CA6FC6C466576255DA132908A12AC1674C",
"part_set_header": {
"total": 1,
"hash": "BBA710736635FA20CDB4F48732563869E90871D31FE9E7DE3D900CD4334D8775"
}
},
"block": {
"header": {
"version": {
"block": "10",
"app": "1"
},
"chain_id": "dockerchain",
"height": "10",
"time": "2020-03-15T16:57:08.151Z",
"last_block_id": {
"hash": "760E050B2404A4BC661635CA552FF45876BCD927C367ADF88961E389C01D32FF",
"part_set_header": {
"total": 1,
"hash": "485070D01F9543827B3F9BAF11BDCFFBFD2BDED0B63D7192FA55649B94A1D5DE"
}
},
"last_commit_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"data_hash": "",
"validators_hash": "3C0A744897A1E0DBF1DEDE1AF339D65EDDCF10E6338504368B20C508D6D578DC",
"next_validators_hash": "3C0A744897A1E0DBF1DEDE1AF339D65EDDCF10E6338504368B20C508D6D578DC",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"app_hash": "0000000000000000",
"last_results_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
"evidence_hash": "",
"proposer_address": "12CC3970B3AE9F19A4B1D98BE1799F2CB923E0A3"
},
"data": {
"txs": null
},
"evidence": {
"evidence": null
},
"last_commit": {
"height": "9",
"round": 0,
"block_id": {
"hash": "760E050B2404A4BC661635CA552FF45876BCD927C367ADF88961E389C01D32FF",
"part_set_header": {
"total": 1,
"hash": "485070D01F9543827B3F9BAF11BDCFFBFD2BDED0B63D7192FA55649B94A1D5DE"
}
},
"signatures": [
{
"block_id_flag": 2,
"validator_address": "12CC3970B3AE9F19A4B1D98BE1799F2CB923E0A3",
"timestamp": "2020-03-15T16:57:08.151Z",
"signature": "GRBX/UNaf19vs5byJfAuXk2FQ05soOHmaMFCbrNBhHdNZtFKHp6J9eFwZrrG+YCxKMdqPn2tQWAes6X8kpd1DA=="
}
]
}
}
}
],
"total_count": "1"
}
}
11 changes: 11 additions & 0 deletions rpc/tests/kvstore_fixtures/outgoing/block_search.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "9ee74828-e8f1-429d-9d53-254c833bae00",
"jsonrpc": "2.0",
"method": "block_search",
"params": {
"order_by": "asc",
"page": "1",
"per_page": "10",
"query": "block.height > 1"
}
}
14 changes: 14 additions & 0 deletions tools/kvstore-test/tests/tendermint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ mod rpc {
assert!(block_results.txs_results.is_none());
}

async fn block_search() {
let res = localhost_http_client()
.block_search(
Query::gt("block.height", "1"),
1,
1,
Order::Ascending,
)
.await
.unwrap();
assert!(res.total_count > 0);
assert_eq!(res.total_count as usize, res.blocks.len());
}

/// `/blockchain` endpoint
#[tokio::test]
async fn blockchain() {
Expand Down
1 change: 1 addition & 0 deletions tools/rpc-probe/src/quick.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_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"),
consensus_params(10),
Expand Down