Skip to content

Commit

Permalink
updates getblockheader response type to hex-encode fields, adds ToHex…
Browse files Browse the repository at this point in the history
… impl for sapling::tree::Root, adds snapshot and vector tests for new RPC method, adds snapshots.
  • Loading branch information
arya2 committed Oct 25, 2024
1 parent 91a199c commit d693a49
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 2 deletions.
20 changes: 20 additions & 0 deletions zebra-chain/src/sapling/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ impl TryFrom<[u8; 32]> for Root {
}
}

impl ToHex for &Root {
fn encode_hex<T: FromIterator<char>>(&self) -> T {
<[u8; 32]>::from(*self).encode_hex()
}

fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
<[u8; 32]>::from(*self).encode_hex_upper()
}
}

impl ToHex for Root {
fn encode_hex<T: FromIterator<char>>(&self) -> T {
(&self).encode_hex()
}

fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
(&self).encode_hex_upper()
}
}

impl ZcashSerialize for Root {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&<[u8; 32]>::from(*self)[..])?;
Expand Down
7 changes: 5 additions & 2 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ where
height,
version: header.version,
merkle_root: header.merkle_root,
final_sapling_root: sapling_tree,
final_sapling_root: sapling_tree.root(),
time: header.time.timestamp(),
nonce: *header.nonce,
bits: header.difficulty_threshold,
Expand Down Expand Up @@ -1756,15 +1756,18 @@ pub struct GetBlockHeaderObject {
pub version: u32,

/// The merkle root of the requesteed block.
#[serde(with = "hex")]
pub merkle_root: block::merkle::Root,

/// The root of the Sapling commitment tree after applying this block.
pub final_sapling_root: Arc<zebra_chain::sapling::tree::NoteCommitmentTree>,
#[serde(with = "hex")]
pub final_sapling_root: zebra_chain::sapling::tree::Root,

/// The block time of the requested block header in non-leap seconds since Jan 1 1970 GMT.
pub time: i64,

/// The nonce of the requested block header
#[serde(with = "hex")]
pub nonce: [u8; 32],

/// The difficulty threshold of the requested block header displayed in compact form.
Expand Down
37 changes: 37 additions & 0 deletions zebra-rpc/src/methods/tests/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,34 @@ async fn test_rpc_response_data_for_network(network: &Network) {
.expect("We should have a GetBlock struct");
snapshot_rpc_getblock_verbose("hash_verbosity_default", get_block, &settings);

// `getblockheader(hash, verbose = false)`
let get_block_header = rpc
.get_block_header(block_hash.to_string(), None)
.await
.expect("We should have a GetBlock struct");
snapshot_rpc_getblockheader("hash", get_block_header, &settings);

// `getblockheader(height, verbose = false)`
let get_block_header = rpc
.get_block_header(BLOCK_HEIGHT.to_string(), None)
.await
.expect("We should have a GetBlock struct");
snapshot_rpc_getblockheader("height", get_block_header, &settings);

// `getblockheader(hash, verbose = true)`
let get_block_header = rpc
.get_block_header(block_hash.to_string(), Some(true))
.await
.expect("We should have a GetBlock struct");
snapshot_rpc_getblockheader("hash", get_block_header, &settings);

// `getblockheader(height, verbose = true)`
let get_block_header = rpc
.get_block_header(BLOCK_HEIGHT.to_string(), Some(true))
.await
.expect("We should have a GetBlock struct");
snapshot_rpc_getblockheader("height", get_block_header, &settings);

// `getbestblockhash`
let get_best_block_hash = rpc
.get_best_block_hash()
Expand Down Expand Up @@ -608,6 +636,15 @@ fn snapshot_rpc_getblock_verbose(
settings.bind(|| insta::assert_json_snapshot!(format!("get_block_verbose_{variant}"), block));
}

/// Check valid `getblockheader` response using `cargo insta`.
fn snapshot_rpc_getblockheader(
variant: &'static str,
block: GetBlockHeader,
settings: &insta::Settings,
) {
settings.bind(|| insta::assert_json_snapshot!(format!("get_block_header_{variant}"), block));
}

/// Check invalid height `getblock` response using `cargo insta`.
fn snapshot_rpc_getblock_invalid(
variant: &'static str,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283",
"confirmations": 10,
"height": 1,
"version": 4,
"merkle_root": "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
"final_sapling_root": "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
"time": 1477671596,
"nonce": "7534e8cf161ff2e49d54bdb3bfbcde8cdbf2fc5963c9ec7d86aed4a67e975790",
"bits": "1f07ffff",
"difficulty": "0007ffff00000000000000000000000000000000000000000000000000000000",
"previousblockhash": "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08",
"nextblockhash": "0002a26c902619fc964443264feb16f1e3e2d71322fc53dcb81cc5d797e273ed"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23",
"confirmations": 10,
"height": 1,
"version": 4,
"merkle_root": "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
"final_sapling_root": "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
"time": 1477674473,
"nonce": "000056c2264c31261d597c6fcea7c5e00160cf6be1cd89ca96a0389473e50000",
"bits": "2007ffff",
"difficulty": "07ffff0000000000000000000000000000000000000000000000000000000000",
"previousblockhash": "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38",
"nextblockhash": "00f1a49e54553ac3ef735f2eb1d8247c9a87c22a47dbd7823ae70adcd6c21a18"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283",
"confirmations": 10,
"height": 1,
"version": 4,
"merkle_root": "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
"final_sapling_root": "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
"time": 1477671596,
"nonce": "7534e8cf161ff2e49d54bdb3bfbcde8cdbf2fc5963c9ec7d86aed4a67e975790",
"bits": "1f07ffff",
"difficulty": "0007ffff00000000000000000000000000000000000000000000000000000000",
"previousblockhash": "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08",
"nextblockhash": "0002a26c902619fc964443264feb16f1e3e2d71322fc53dcb81cc5d797e273ed"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23",
"confirmations": 10,
"height": 1,
"version": 4,
"merkle_root": "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
"final_sapling_root": "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
"time": 1477674473,
"nonce": "000056c2264c31261d597c6fcea7c5e00160cf6be1cd89ca96a0389473e50000",
"bits": "2007ffff",
"difficulty": "07ffff0000000000000000000000000000000000000000000000000000000000",
"previousblockhash": "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38",
"nextblockhash": "00f1a49e54553ac3ef735f2eb1d8247c9a87c22a47dbd7823ae70adcd6c21a18"
}
94 changes: 94 additions & 0 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,100 @@ async fn rpc_getblock_missing_error() {
assert!(rpc_tx_queue_task_result.is_none());
}

#[tokio::test(flavor = "multi_thread")]
async fn rpc_getblockheader() {
let _init_guard = zebra_test::init();

// Create a continuous chain of mainnet blocks from genesis
let blocks: Vec<Arc<Block>> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS
.iter()
.map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap())
.collect();

let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
// Create a populated state service
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
zebra_state::populated_state(blocks.clone(), &Mainnet).await;

// Init RPC
let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new(
"RPC test",
"RPC test",
Mainnet,
false,
true,
Buffer::new(mempool.clone(), 1),
read_state.clone(),
latest_chain_tip,
);

// Make height calls with verbose=false and check response
for (i, block) in blocks.iter().enumerate() {
let expected_result = GetBlockHeader::Raw(HexData(
block
.header
.clone()
.zcash_serialize_to_vec()
.expect("test block header should serialize"),
));

let hash = block.hash();
let height = Height(i as u32);

for hash_or_height in [HashOrHeight::from(height), hash.into()] {
let get_block_header = rpc
.get_block_header(hash_or_height.to_string(), None)
.await
.expect("we should have a GetBlockHeader struct");
assert_eq!(get_block_header, expected_result);
}

let zebra_state::ReadResponse::SaplingTree(sapling_tree) = read_state
.clone()
.oneshot(zebra_state::ReadRequest::SaplingTree(height.into()))
.await
.expect("should have sapling tree for block hash")
else {
panic!("unexpected response to SaplingTree request")
};

let expected_result = GetBlockHeader::Object(Box::new(GetBlockHeaderObject {
hash: GetBlockHash(hash),
confirmations: 11 - i as i64,
height,
version: 4,
merkle_root: block.header.merkle_root,
final_sapling_root: sapling_tree
.expect("should always have sapling root")
.root(),
time: block.header.time.timestamp(),
nonce: *block.header.nonce,
bits: block.header.difficulty_threshold,
difficulty: block
.header
.difficulty_threshold
.to_expanded()
.expect("should have valid difficulty"),
previous_block_hash: GetBlockHash(block.header.previous_block_hash),
next_block_hash: blocks.get(i + 1).map(|b| GetBlockHash(b.hash())),
}));

for hash_or_height in [HashOrHeight::from(Height(i as u32)), block.hash().into()] {
let get_block_header = rpc
.get_block_header(hash_or_height.to_string(), Some(true))
.await
.expect("we should have a GetBlockHeader struct");
assert_eq!(get_block_header, expected_result);
}
}

mempool.expect_no_requests().await;

// The queue task should continue without errors or panics
let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never();
assert!(rpc_tx_queue_task_result.is_none());
}

#[tokio::test(flavor = "multi_thread")]
async fn rpc_getbestblockhash() {
let _init_guard = zebra_test::init();
Expand Down
9 changes: 9 additions & 0 deletions zebra-state/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ impl HashOrHeight {
}
}

impl std::fmt::Display for HashOrHeight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HashOrHeight::Hash(hash) => write!(f, "{hash}"),
HashOrHeight::Height(height) => write!(f, "{}", height.0),
}
}
}

impl From<block::Hash> for HashOrHeight {
fn from(hash: block::Hash) -> Self {
Self::Hash(hash)
Expand Down

0 comments on commit d693a49

Please sign in to comment.