From dda8f686c827d85538270ec7a09e79f37a1dbb06 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 10 Feb 2022 11:55:22 -0800 Subject: [PATCH 1/9] Add block_by_hash RPC endpoint and tests Signed-off-by: Felix C. Morency <1102868+fmorency@users.noreply.github.com> --- .../unreleased/features/832-block-by-hash.md | 1 + rpc/src/client.rs | 8 +++ rpc/src/client/bin/main.rs | 9 +++ rpc/src/endpoint.rs | 1 + rpc/src/endpoint/block_by_hash.rs | 46 +++++++++++++ rpc/src/method.rs | 5 ++ rpc/tests/kvstore_fixtures.rs | 14 ++++ .../incoming/block_by_hash.json | 65 +++++++++++++++++++ .../outgoing/block_by_hash.json | 8 +++ tools/kvstore-test/tests/tendermint.rs | 25 +++++++ 10 files changed, 182 insertions(+) create mode 100644 .changelog/unreleased/features/832-block-by-hash.md create mode 100644 rpc/src/endpoint/block_by_hash.rs create mode 100644 rpc/tests/kvstore_fixtures/incoming/block_by_hash.json create mode 100644 rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json diff --git a/.changelog/unreleased/features/832-block-by-hash.md b/.changelog/unreleased/features/832-block-by-hash.md new file mode 100644 index 000000000..0834eb397 --- /dev/null +++ b/.changelog/unreleased/features/832-block-by-hash.md @@ -0,0 +1 @@ +- `[tendermint-rpc]` Add support for the `/block_by_hash` RPC endpoint. See for details. Links to ([#832](https://github.com/informalsystems/tendermint-rs/issues/832)). diff --git a/rpc/src/client.rs b/rpc/src/client.rs index c163c6b9f..604b90258 100644 --- a/rpc/src/client.rs +++ b/rpc/src/client.rs @@ -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 { + self.perform(block_by_hash::Request::new(hash)).await + } + /// `/block`: get the latest block. async fn latest_block(&self) -> Result { self.perform(block::Request::default()).await diff --git a/rpc/src/client/bin/main.rs b/rpc/src/client/bin/main.rs index a38539f7d..f16566825 100644 --- a/rpc/src/client/bin/main.rs +++ b/rpc/src/client/bin/main.rs @@ -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 @@ -316,6 +318,13 @@ 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)? diff --git a/rpc/src/endpoint.rs b/rpc/src/endpoint.rs index 8720be10b..2e749aaa6 100644 --- a/rpc/src/endpoint.rs +++ b/rpc/src/endpoint.rs @@ -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; diff --git a/rpc/src/endpoint/block_by_hash.rs b/rpc/src/endpoint/block_by_hash.rs new file mode 100644 index 000000000..1b1f0f825 --- /dev/null +++ b/rpc/src/endpoint/block_by_hash.rs @@ -0,0 +1,46 @@ +//! `/block` endpoint JSON-RPC wrapper + +use serde::{Deserialize, Serialize}; + +use tendermint::Hash; +use tendermint::block::{self, Block}; + +/// Get information about a specific block +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct Request { + /// Hash of the block to request. + /// + /// If no hash is provided, it will fetch results for the latest block. + pub hash: Option, +} + +impl Request { + /// Create a new request for information about a particular block + pub fn new(hash: Hash) -> Self { + Self { + hash: Some(hash), + } + } +} + +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, +} + +impl crate::Response for Response {} diff --git a/rpc/src/method.rs b/rpc/src/method.rs index 287f93bb9..aaffd85c6 100644 --- a/rpc/src/method.rs +++ b/rpc/src/method.rs @@ -23,6 +23,9 @@ pub enum Method { /// Get block info Block, + /// Get block info by hash + BlockByHash, + /// Get ABCI results for a particular block BlockResults, @@ -88,6 +91,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", @@ -119,6 +123,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, diff --git a/rpc/tests/kvstore_fixtures.rs b/rpc/tests/kvstore_fixtures.rs index fbabb5c00..252bbb8b6 100644 --- a/rpc/tests/kvstore_fixtures.rs +++ b/rpc/tests/kvstore_fixtures.rs @@ -95,6 +95,16 @@ 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::>(&content) + .unwrap(); + assert_eq!( + wrapped.params().hash.unwrap().to_string(), + "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF" + ); + }, "block_results_at_height_10" => { let wrapped = serde_json::from_str::< RequestWrapper, @@ -464,6 +474,10 @@ 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()); diff --git a/rpc/tests/kvstore_fixtures/incoming/block_by_hash.json b/rpc/tests/kvstore_fixtures/incoming/block_by_hash.json new file mode 100644 index 000000000..975884a09 --- /dev/null +++ b/rpc/tests/kvstore_fixtures/incoming/block_by_hash.json @@ -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 + } + } + } +} \ No newline at end of file diff --git a/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json b/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json new file mode 100644 index 000000000..5560f514b --- /dev/null +++ b/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json @@ -0,0 +1,8 @@ +{ + "id": "a37bcc8e-6a32-4157-9200-a26be9981bbd", + "jsonrpc": "2.0", + "method": "block", + "params": { + "hash": "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF" + } +} diff --git a/tools/kvstore-test/tests/tendermint.rs b/tools/kvstore-test/tests/tendermint.rs index a9c450231..b681d892b 100644 --- a/tools/kvstore-test/tests/tendermint.rs +++ b/tools/kvstore-test/tests/tendermint.rs @@ -122,6 +122,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() { From 1a196519927b5b48ab44f5672bc7aba654500fd5 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 10 Feb 2022 12:17:30 -0800 Subject: [PATCH 2/9] rpc-probe part of this Signed-off-by: Felix C. Morency <1102868+fmorency@users.noreply.github.com> --- tools/rpc-probe/src/common.rs | 10 ++++++++++ tools/rpc-probe/src/kvstore.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/tools/rpc-probe/src/common.rs b/tools/rpc-probe/src/common.rs index 3397f2015..eb8bd88ad 100644 --- a/tools/rpc-probe/src/common.rs +++ b/tools/rpc-probe/src/common.rs @@ -33,6 +33,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", diff --git a/tools/rpc-probe/src/kvstore.rs b/tools/rpc-probe/src/kvstore.rs index 413e08f91..9e103d267 100644 --- a/tools/rpc-probe/src/kvstore.rs +++ b/tools/rpc-probe/src/kvstore.rs @@ -26,6 +26,7 @@ pub fn quick_probe_plan(output_path: &Path, request_wait: Duration) -> Result 1", 1, 100, "asc").with_name("block_search"), blockchain(1, 10).with_name("blockchain_from_1_to_10"), commit(10).with_name("commit_at_height_10"), From 160514f8fdbc38d7a98ae4213000e612b30e0416 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 10 Feb 2022 13:13:53 -0800 Subject: [PATCH 3/9] Fix comments Signed-off-by: Felix C. Morency <1102868+fmorency@users.noreply.github.com> --- rpc/src/endpoint/block_by_hash.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rpc/src/endpoint/block_by_hash.rs b/rpc/src/endpoint/block_by_hash.rs index 1b1f0f825..2b885eb18 100644 --- a/rpc/src/endpoint/block_by_hash.rs +++ b/rpc/src/endpoint/block_by_hash.rs @@ -1,24 +1,25 @@ -//! `/block` endpoint JSON-RPC wrapper +//! `/block_by_hash` endpoint JSON-RPC wrapper use serde::{Deserialize, Serialize}; use tendermint::Hash; use tendermint::block::{self, Block}; -/// Get information about a specific block +/// 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 fetch results for the latest block. + /// If no hash is provided, it will return no block (as if the hash + /// did not match any block). pub hash: Option, } impl Request { /// Create a new request for information about a particular block - pub fn new(hash: Hash) -> Self { + pub fn new>(hash: H) -> Self { Self { - hash: Some(hash), + hash: Some(hash.into()), } } } From 0c487d077eeb4e4826621930bbdce5d81f783b89 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 15 Feb 2022 09:05:05 -0800 Subject: [PATCH 4/9] Update .changelog/unreleased/features/832-block-by-hash.md Co-authored-by: Thane Thomson Signed-off-by: Felix C. Morency <1102868+fmorency@users.noreply.github.com> --- .changelog/unreleased/features/832-block-by-hash.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/unreleased/features/832-block-by-hash.md b/.changelog/unreleased/features/832-block-by-hash.md index 0834eb397..28f17c6b3 100644 --- a/.changelog/unreleased/features/832-block-by-hash.md +++ b/.changelog/unreleased/features/832-block-by-hash.md @@ -1 +1 @@ -- `[tendermint-rpc]` Add support for the `/block_by_hash` RPC endpoint. See for details. Links to ([#832](https://github.com/informalsystems/tendermint-rs/issues/832)). +- `[tendermint-rpc]` Add support for the `/block_by_hash` RPC endpoint. See for details ([#832](https://github.com/informalsystems/tendermint-rs/issues/832)). From 424cf54e74192485a9eef506d72084cc83b5c443 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 15 Feb 2022 09:08:24 -0800 Subject: [PATCH 5/9] cargo fmt Signed-off-by: Felix C. Morency <1102868+fmorency@users.noreply.github.com> --- rpc/src/client/bin/main.rs | 15 ++++++++------- rpc/src/endpoint/block_by_hash.rs | 2 +- rpc/tests/kvstore_fixtures.rs | 12 ++++++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/rpc/src/client/bin/main.rs b/rpc/src/client/bin/main.rs index f16566825..e4176da12 100644 --- a/rpc/src/client/bin/main.rs +++ b/rpc/src/client/bin/main.rs @@ -318,13 +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::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)? diff --git a/rpc/src/endpoint/block_by_hash.rs b/rpc/src/endpoint/block_by_hash.rs index 2b885eb18..1775ca507 100644 --- a/rpc/src/endpoint/block_by_hash.rs +++ b/rpc/src/endpoint/block_by_hash.rs @@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize}; -use tendermint::Hash; 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)] diff --git a/rpc/tests/kvstore_fixtures.rs b/rpc/tests/kvstore_fixtures.rs index 252bbb8b6..e85fbd733 100644 --- a/rpc/tests/kvstore_fixtures.rs +++ b/rpc/tests/kvstore_fixtures.rs @@ -97,9 +97,10 @@ fn outgoing_fixtures() { }, "block_by_hash" => { // First, get the hash at height 1. - let wrapped = - serde_json::from_str::>(&content) - .unwrap(); + let wrapped = serde_json::from_str::< + RequestWrapper, + >(&content) + .unwrap(); assert_eq!( wrapped.params().hash.unwrap().to_string(), "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF" @@ -476,7 +477,10 @@ fn incoming_fixtures() { }, "block_by_hash" => { let result = endpoint::block::Response::from_string(content).unwrap(); - assert_eq!(result.block_id.hash.to_string(), "BCF3DB412E80A396D10BF5B5E6D3E63D3B06DEB25AA958BCB8CE18D023838042"); + assert_eq!( + result.block_id.hash.to_string(), + "BCF3DB412E80A396D10BF5B5E6D3E63D3B06DEB25AA958BCB8CE18D023838042" + ); }, "block_search" => { let result = endpoint::block_search::Response::from_string(content).unwrap(); From f5d90a0eeb9987ab0e74c15da0a1d17e32266926 Mon Sep 17 00:00:00 2001 From: "Felix C. Morency" <1102868+fmorency@users.noreply.github.com> Date: Mon, 3 Oct 2022 10:44:57 -0400 Subject: [PATCH 6/9] cargo fmt --- rpc/src/abci/tag.rs | 3 +-- rpc/src/endpoint/block_by_hash.rs | 7 ++++--- rpc/tests/kvstore_fixtures.rs | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/rpc/src/abci/tag.rs b/rpc/src/abci/tag.rs index c791688eb..26acd4a9c 100644 --- a/rpc/src/abci/tag.rs +++ b/rpc/src/abci/tag.rs @@ -5,8 +5,7 @@ use core::{fmt, str::FromStr}; use serde::{Deserialize, Serialize}; use tendermint::error::Error; -use crate::prelude::*; -use crate::serializers::bytes::base64string; +use crate::{prelude::*, serializers::bytes::base64string}; /// Tags #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] diff --git a/rpc/src/endpoint/block_by_hash.rs b/rpc/src/endpoint/block_by_hash.rs index 1775ca507..7fecf15e0 100644 --- a/rpc/src/endpoint/block_by_hash.rs +++ b/rpc/src/endpoint/block_by_hash.rs @@ -1,9 +1,10 @@ //! `/block_by_hash` endpoint JSON-RPC wrapper use serde::{Deserialize, Serialize}; - -use tendermint::block::{self, Block}; -use tendermint::Hash; +use tendermint::{ + block::{self, Block}, + Hash, +}; /// Get information about a specific block by its hash #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] diff --git a/rpc/tests/kvstore_fixtures.rs b/rpc/tests/kvstore_fixtures.rs index e85fbd733..aef3f63f9 100644 --- a/rpc/tests/kvstore_fixtures.rs +++ b/rpc/tests/kvstore_fixtures.rs @@ -1,8 +1,7 @@ //! Tendermint kvstore RPC endpoint testing. use core::str::FromStr; -use std::collections::BTreeMap as HashMap; -use std::{fs, path::PathBuf}; +use std::{collections::BTreeMap as HashMap, fs, path::PathBuf}; use subtle_encoding::{base64, hex}; use tendermint::{ From d890cd62446217f2497f24c04b4bacc000efbe6e Mon Sep 17 00:00:00 2001 From: "Felix C. Morency" <1102868+fmorency@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:39:39 -0400 Subject: [PATCH 7/9] Fix TM hash encoding - Add base64 `tendermint::hash::Hash` encoding/decoding support - Add base64 `Option` encoding/decoding support - Add missing `FromStr` import - Rename `hash_base64` serializer to `tx_hash_base64` Relates #942 Links #832 --- .../bug-fixes/832-block-by-hash-encoding.md | 4 +++ rpc/src/endpoint/block_by_hash.rs | 5 +++ rpc/src/endpoint/tx.rs | 2 +- rpc/src/serializers.rs | 4 ++- rpc/src/serializers/opt_tm_hash_base64.rs | 25 ++++++++++++++ rpc/src/serializers/tm_hash_base64.rs | 34 +++++++++++++++++++ .../{hash_base64.rs => tx_hash_base64.rs} | 4 +-- tools/kvstore-test/tests/tendermint.rs | 1 + 8 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 .changelog/unreleased/bug-fixes/832-block-by-hash-encoding.md create mode 100644 rpc/src/serializers/opt_tm_hash_base64.rs create mode 100644 rpc/src/serializers/tm_hash_base64.rs rename rpc/src/serializers/{hash_base64.rs => tx_hash_base64.rs} (87%) diff --git a/.changelog/unreleased/bug-fixes/832-block-by-hash-encoding.md b/.changelog/unreleased/bug-fixes/832-block-by-hash-encoding.md new file mode 100644 index 000000000..883fa55c6 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/832-block-by-hash-encoding.md @@ -0,0 +1,4 @@ +- `[tendermint-rpc]` The encoding of the `hash` field for requests to the `/block_by_hash` + endpoint has been changed to base64 (from hex) to accommodate discrepancies in + how the Tendermint RPC encodes this field for different RPC interfaces + ([#942](https://github.com/informalsystems/tendermint-rs/issues/942)) \ No newline at end of file diff --git a/rpc/src/endpoint/block_by_hash.rs b/rpc/src/endpoint/block_by_hash.rs index 7fecf15e0..09100fce6 100644 --- a/rpc/src/endpoint/block_by_hash.rs +++ b/rpc/src/endpoint/block_by_hash.rs @@ -13,6 +13,11 @@ pub struct Request { /// /// If no hash is provided, it will return no block (as if the hash /// did not match any block). + /// + /// Serialized internally into a base64-encoded string before sending to + /// the RPC server. + #[serde(default)] + #[serde(with = "crate::serializers::opt_tm_hash_base64")] pub hash: Option, } diff --git a/rpc/src/endpoint/tx.rs b/rpc/src/endpoint/tx.rs index 71729bc03..22735b789 100644 --- a/rpc/src/endpoint/tx.rs +++ b/rpc/src/endpoint/tx.rs @@ -13,7 +13,7 @@ pub struct Request { /// /// Serialized internally into a base64-encoded string before sending to /// the RPC server. - #[serde(with = "crate::serializers::hash_base64")] + #[serde(with = "crate::serializers::tx_hash_base64")] pub hash: abci::transaction::Hash, /// Whether or not to include the proofs of the transaction's inclusion in /// the block. diff --git a/rpc/src/serializers.rs b/rpc/src/serializers.rs index 025f02876..d55dff33b 100644 --- a/rpc/src/serializers.rs +++ b/rpc/src/serializers.rs @@ -7,4 +7,6 @@ //! risk. pub use tendermint_proto::serializers::*; -pub mod hash_base64; +pub mod opt_tm_hash_base64; +pub mod tm_hash_base64; +pub mod tx_hash_base64; diff --git a/rpc/src/serializers/opt_tm_hash_base64.rs b/rpc/src/serializers/opt_tm_hash_base64.rs new file mode 100644 index 000000000..3491ec648 --- /dev/null +++ b/rpc/src/serializers/opt_tm_hash_base64.rs @@ -0,0 +1,25 @@ +//! Encoding/decoding Option Tendermint hashes to/from base64. +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use tendermint::hash::Hash; + +use crate::prelude::*; + +#[derive(Serialize, Deserialize)] +struct Helper(#[serde(with = "crate::serializers::tm_hash_base64")] Hash); + +/// Deserialize base64-encoded string into an Option +pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let helper: Option = Option::deserialize(deserializer)?; + Ok(helper.map(|Helper(hash)| hash)) +} + +/// Serialize from an Option into a base64-encoded string +pub fn serialize(value: &Option, serializer: S) -> Result +where + S: Serializer, +{ + value.map(Helper).serialize(serializer) +} diff --git a/rpc/src/serializers/tm_hash_base64.rs b/rpc/src/serializers/tm_hash_base64.rs new file mode 100644 index 000000000..b077ddbe4 --- /dev/null +++ b/rpc/src/serializers/tm_hash_base64.rs @@ -0,0 +1,34 @@ +//! Encoding/decoding Tendermint hashes to/from base64. + +use serde::{Deserialize, Deserializer, Serializer}; +use subtle_encoding::base64; +use tendermint::hash::{Algorithm::Sha256, Hash, SHA256_HASH_SIZE}; + +use crate::prelude::*; + +/// Deserialize a base64-encoded string into an tendermint::Hash +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = Option::::deserialize(deserializer)?.unwrap_or_default(); + let decoded = base64::decode(&s).map_err(serde::de::Error::custom)?; + if decoded.len() != SHA256_HASH_SIZE { + return Err(serde::de::Error::custom( + "unexpected transaction length for hash", + )); + } + let mut decoded_bytes = [0u8; SHA256_HASH_SIZE]; + decoded_bytes.copy_from_slice(decoded.as_ref()); + Ok(Hash::from_bytes(Sha256, &decoded_bytes).map_err(serde::de::Error::custom)?) +} + +/// Serialize from a tendermint::Hash into a base64-encoded string +pub fn serialize(value: &Hash, serializer: S) -> Result +where + S: Serializer, +{ + let base64_bytes = base64::encode(value.as_bytes()); + let base64_string = String::from_utf8(base64_bytes).map_err(serde::ser::Error::custom)?; + serializer.serialize_str(&base64_string) +} diff --git a/rpc/src/serializers/hash_base64.rs b/rpc/src/serializers/tx_hash_base64.rs similarity index 87% rename from rpc/src/serializers/hash_base64.rs rename to rpc/src/serializers/tx_hash_base64.rs index edb832085..e896cbd44 100644 --- a/rpc/src/serializers/hash_base64.rs +++ b/rpc/src/serializers/tx_hash_base64.rs @@ -8,7 +8,7 @@ use crate::{ prelude::*, }; -/// Deserialize a base64-encoded string into a Hash +/// Deserialize a base64-encoded string into an abci::transaction::Hash pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -25,7 +25,7 @@ where Ok(Hash::new(decoded_bytes)) } -/// Serialize from a Hash into a base64-encoded string +/// Serialize from an abci::transaction::Hash into a base64-encoded string pub fn serialize(value: &Hash, serializer: S) -> Result where S: Serializer, diff --git a/tools/kvstore-test/tests/tendermint.rs b/tools/kvstore-test/tests/tendermint.rs index b681d892b..bcdfecb15 100644 --- a/tools/kvstore-test/tests/tendermint.rs +++ b/tools/kvstore-test/tests/tendermint.rs @@ -17,6 +17,7 @@ mod rpc { use std::{ cmp::min, convert::TryFrom, + str::FromStr, sync::atomic::{AtomicU8, Ordering}, }; From de469d9de16feab0af50afa3a5619ba8b1a67cf9 Mon Sep 17 00:00:00 2001 From: "Felix C. Morency" <1102868+fmorency@users.noreply.github.com> Date: Wed, 5 Oct 2022 15:07:06 -0400 Subject: [PATCH 8/9] Fix outgoing kvstore `block_by_hash` fixture --- rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json b/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json index 5560f514b..e7a164013 100644 --- a/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json +++ b/rpc/tests/kvstore_fixtures/outgoing/block_by_hash.json @@ -1,8 +1,8 @@ { "id": "a37bcc8e-6a32-4157-9200-a26be9981bbd", "jsonrpc": "2.0", - "method": "block", + "method": "block_by_hash", "params": { - "hash": "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF" + "hash": "ABEiM0RVZneImaq7zN3u/wARIjNEVWZ3iJmqu8zd7v8=" } } From 018f1db5f6af19987d557865a2c927f92778e896 Mon Sep 17 00:00:00 2001 From: "Felix C. Morency" <1102868+fmorency@users.noreply.github.com> Date: Wed, 5 Oct 2022 16:05:14 -0400 Subject: [PATCH 9/9] Fix clippy --- rpc/src/serializers/tm_hash_base64.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/src/serializers/tm_hash_base64.rs b/rpc/src/serializers/tm_hash_base64.rs index b077ddbe4..b8626a825 100644 --- a/rpc/src/serializers/tm_hash_base64.rs +++ b/rpc/src/serializers/tm_hash_base64.rs @@ -20,7 +20,7 @@ where } let mut decoded_bytes = [0u8; SHA256_HASH_SIZE]; decoded_bytes.copy_from_slice(decoded.as_ref()); - Ok(Hash::from_bytes(Sha256, &decoded_bytes).map_err(serde::de::Error::custom)?) + Hash::from_bytes(Sha256, &decoded_bytes).map_err(serde::de::Error::custom) } /// Serialize from a tendermint::Hash into a base64-encoded string