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

feat(rpc): add support for verbosity=1 in getblock #4511

Merged
merged 1 commit into from
May 27, 2022
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
6 changes: 6 additions & 0 deletions zebra-chain/src/block/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,9 @@ impl AsRef<[u8]> for SerializedBlock {
self.bytes.as_ref()
}
}

impl From<Vec<u8>> for SerializedBlock {
fn from(bytes: Vec<u8>) -> Self {
Self { bytes }
}
}
39 changes: 32 additions & 7 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,18 @@ pub trait Rpc {
/// # Parameters
///
/// - `height`: (string, required) The height number for the block to be returned.
/// - `verbosity`: (numeric, optional, default=1) 0 for hex encoded data, 1 for a json object,
/// and 2 for json object with transaction data.
///
/// # Notes
///
/// We only expose the `data` field as lightwalletd uses the non-verbose
/// mode for all getblock calls: <https://github.com/zcash/lightwalletd/blob/v0.4.9/common/common.go#L232>
/// With verbosity=1, [`lightwalletd` only reads the `tx` field of the
/// result](https://github.com/zcash/lightwalletd/blob/dfac02093d85fb31fb9a8475b884dd6abca966c7/common/common.go#L152),
/// so we only return that for now.
///
/// `lightwalletd` only requests blocks by height, so we don't support
/// getting blocks by hash. (But we parse the height as a JSON string, not an integer).
///
/// The `verbosity` parameter is ignored but required in the call.
/// `lightwalletd` also does not use verbosity=2, so we don't support it.
#[rpc(name = "getblock")]
fn get_block(&self, height: String, verbosity: u8) -> BoxFuture<Result<GetBlock>>;

Expand Down Expand Up @@ -515,7 +517,7 @@ where
.boxed()
}

fn get_block(&self, height: String, _verbosity: u8) -> BoxFuture<Result<GetBlock>> {
fn get_block(&self, height: String, verbosity: u8) -> BoxFuture<Result<GetBlock>> {
let mut state = self.state.clone();

async move {
Expand All @@ -538,7 +540,21 @@ where
})?;

match response {
zebra_state::ReadResponse::Block(Some(block)) => Ok(GetBlock(block.into())),
zebra_state::ReadResponse::Block(Some(block)) => match verbosity {
0 => Ok(GetBlock::Raw(block.into())),
1 => Ok(GetBlock::Object {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.collect(),
}),
_ => Err(Error {
code: ErrorCode::InvalidParams,
message: "Invalid verbosity value".to_string(),
data: None,
}),
},
zebra_state::ReadResponse::Block(None) => Err(Error {
code: MISSING_BLOCK_ERROR_CODE,
message: "Block not found".to_string(),
Expand Down Expand Up @@ -1070,7 +1086,16 @@ pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash);
///
/// See the notes for the [`Rpc::get_block` method].
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct GetBlock(#[serde(with = "hex")] SerializedBlock);
#[serde(untagged)]
pub enum GetBlock {
/// The request block, hex-encoded.
Raw(#[serde(with = "hex")] SerializedBlock),
/// The block object.
Object {
/// Vector of hex-encoded TXIDs of the transactions of the block
tx: Vec<String>,
},
}

/// Response to a `getbestblockhash` RPC request.
///
Expand Down
14 changes: 13 additions & 1 deletion zebra-rpc/src/methods/tests/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,21 @@ async fn test_rpc_response_data_for_network(network: Network) {
.expect("We should have an AddressBalance struct");
snapshot_rpc_getaddressbalance(get_address_balance, &settings);

// `getblock`
// `getblock`, verbosity=0
const BLOCK_HEIGHT: u32 = 1;
let get_block = rpc
.get_block(BLOCK_HEIGHT.to_string(), 0u8)
.await
.expect("We should have a GetBlock struct");
snapshot_rpc_getblock(get_block, block_data.get(&BLOCK_HEIGHT).unwrap(), &settings);

// `getblock`, verbosity=1
let get_block = rpc
.get_block(BLOCK_HEIGHT.to_string(), 1u8)
.await
.expect("We should have a GetBlock struct");
snapshot_rpc_getblock_verbose(get_block, &settings);

// `getbestblockhash`
let get_best_block_hash = rpc
.get_best_block_hash()
Expand Down Expand Up @@ -211,6 +218,11 @@ fn snapshot_rpc_getblock(block: GetBlock, block_data: &[u8], settings: &insta::S
});
}

/// Check `getblock` response with verbosity=1, using `cargo insta` and JSON serialization.
fn snapshot_rpc_getblock_verbose(block: GetBlock, settings: &insta::Settings) {
settings.bind(|| insta::assert_json_snapshot!("get_block_verbose", block));
}

/// Snapshot `getbestblockhash` response, using `cargo insta` and JSON serialization.
fn snapshot_rpc_getbestblockhash(tip_hash: GetBestBlockHash, settings: &insta::Settings) {
settings.bind(|| insta::assert_json_snapshot!("get_best_block_hash", tip_hash));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"tx": [
"851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"tx": [
"f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75"
]
}
25 changes: 22 additions & 3 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,33 @@ async fn rpc_getblock() {
Mainnet,
);

// Make calls and check response
for (i, block) in blocks.into_iter().enumerate() {
// Make calls with verbosity=0 and check response
for (i, block) in blocks.iter().enumerate() {
let get_block = rpc
.get_block(i.to_string(), 0u8)
.await
.expect("We should have a GetBlock struct");

assert_eq!(get_block.0, block.into());
assert_eq!(get_block, GetBlock::Raw(block.clone().into()));
}

// Make calls with verbosity=1 and check response
for (i, block) in blocks.iter().enumerate() {
let get_block = rpc
.get_block(i.to_string(), 1u8)
.await
.expect("We should have a GetBlock struct");

assert_eq!(
get_block,
GetBlock::Object {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.collect()
}
);
}

mempool.expect_no_requests().await;
Expand Down
19 changes: 18 additions & 1 deletion zebra-rpc/src/tests/vectors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::methods::GetRawTransaction;
use crate::methods::{GetBlock, GetRawTransaction};

#[test]
pub fn test_transaction_serialization() {
Expand All @@ -17,3 +17,20 @@ pub fn test_transaction_serialization() {

assert_eq!(j, expected_json);
}

#[test]
pub fn test_block_serialization() {
let expected_tx = GetBlock::Raw(vec![0x42].into());
let expected_json = r#""42""#;
let j = serde_json::to_string(&expected_tx).unwrap();

assert_eq!(j, expected_json);

let expected_tx = GetBlock::Object {
tx: vec!["42".into()],
};
let expected_json = r#"{"tx":["42"]}"#;
let j = serde_json::to_string(&expected_tx).unwrap();

assert_eq!(j, expected_json);
}