From 4aa00fa0a56fde967dc8735f393ca5ae1780b1c0 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 4 Apr 2024 10:46:36 -0400 Subject: [PATCH 1/4] Returns block times from the getblock RPC when used with verbosity = 1 (it's already included with verbosity = 0 but this makes it easier to use). --- zebra-rpc/src/methods.rs | 91 +++++++++++++------ ...k_verbose_hash_verbosity_1@mainnet_10.snap | 1 + ...k_verbose_hash_verbosity_1@testnet_10.snap | 1 + ...ose_hash_verbosity_default@mainnet_10.snap | 1 + ...ose_hash_verbosity_default@testnet_10.snap | 1 + ...verbose_height_verbosity_1@mainnet_10.snap | 1 + ...verbose_height_verbosity_1@testnet_10.snap | 1 + ...e_height_verbosity_default@mainnet_10.snap | 1 + ...e_height_verbosity_default@testnet_10.snap | 1 + zebra-rpc/src/methods/tests/vectors.rs | 4 + zebra-state/src/request.rs | 23 +++++ zebra-state/src/response.rs | 7 ++ zebra-state/src/service.rs | 26 ++++++ 13 files changed, 130 insertions(+), 29 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index b4105c29dcd..1b33a8abf4d 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -789,11 +789,38 @@ where let request = zebra_state::ReadRequest::TransactionIdsForBlock(hash.into()); let tx_ids_response_fut = state.clone().oneshot(request); + // Sapling trees + // + // # Concurrency + // + // We look up by block hash so the hash, transaction IDs, block header, and confirmations + // are consistent. + let request = zebra_state::ReadRequest::SaplingTree(hash.into()); + let sapling_tree_response_fut = state.clone().oneshot(request); + + // Orchard trees + // + // # Concurrency + // + // We look up by block hash so the hash, transaction IDs, block header, and confirmations + // are consistent. + let request = zebra_state::ReadRequest::OrchardTree(hash.into()); + let orchard_tree_response_fut = state.clone().oneshot(request); + + // Block header + // + // # Concurrency + // + // We look up by block hash so the hash, transaction IDs, block header, and confirmations + // are consistent. + let request = zebra_state::ReadRequest::BlockHeader(hash.into()); + let block_header_response_fut = state.clone().oneshot(request); + // Get block confirmations from the block height index // // # Concurrency // - // We look up by block hash so the hash, transaction IDs, and confirmations + // We look up by block hash so the hash, transaction IDs, block header, and confirmations // are consistent. // // All possible responses are valid, even if a block is added to the chain, or @@ -807,34 +834,17 @@ where let request = zebra_state::ReadRequest::Depth(hash); let depth_response_fut = state.clone().oneshot(request); - // Sapling trees - // - // # Concurrency - // - // We look up by block hash so the hash, transaction IDs, and confirmations - // are consistent. - let request = zebra_state::ReadRequest::SaplingTree(hash.into()); - let sapling_tree_response_fut = state.clone().oneshot(request); - - // Orchard trees - // - // # Concurrency - // - // We look up by block hash so the hash, transaction IDs, and confirmations - // are consistent. - let request = zebra_state::ReadRequest::OrchardTree(hash.into()); - let orchard_tree_response_fut = state.clone().oneshot(request); - let mut futs = FuturesOrdered::new(); futs.push_back(tx_ids_response_fut); futs.push_back(sapling_tree_response_fut); futs.push_back(orchard_tree_response_fut); + futs.push_back(block_header_response_fut); futs.push_back(depth_response_fut); let tx_ids_response = futs .next() .await - .expect("should have 4 items in futs") + .expect("should have 5 items in futs") .map_err(|error| Error { code: ErrorCode::ServerError(0), message: error.to_string(), @@ -844,7 +854,7 @@ where let sapling_tree_response = futs .next() .await - .expect("should have 3 items in futs") + .expect("should have 4 items in futs") .map_err(|error| Error { code: ErrorCode::ServerError(0), message: error.to_string(), @@ -852,6 +862,16 @@ where })?; let orchard_tree_response = futs + .next() + .await + .expect("should have 3 items in futs") + .map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + let block_header_response = futs .next() .await .expect("should have 2 items in futs") @@ -889,14 +909,6 @@ where _ => unreachable!("unmatched response to a SaplingTree request"), }; - let confirmations = match depth_response { - // Confirmations are one more than the depth. - // Depth is limited by height, so it will never overflow an i64. - zebra_state::ReadResponse::Depth(Some(depth)) => i64::from(depth) + 1, - zebra_state::ReadResponse::Depth(None) => NOT_IN_BEST_CHAIN_CONFIRMATIONS, - _ => unreachable!("unmatched response to a depth request"), - }; - // TODO: look up the height if we only have a hash, // this needs a new state request for the height -> hash index let height = hash_or_height.height(); @@ -907,6 +919,21 @@ where _ => unreachable!("unmatched response to a OrchardTree request"), }; + let block_header = match block_header_response { + zebra_state::ReadResponse::BlockHeader(header) => header, + _ => unreachable!("unmatched response to a BlockHeader request"), + }; + + let time = block_header.map(|header| header.time.timestamp()); + + let confirmations = match depth_response { + // Confirmations are one more than the depth. + // Depth is limited by height, so it will never overflow an i64. + zebra_state::ReadResponse::Depth(Some(depth)) => i64::from(depth) + 1, + zebra_state::ReadResponse::Depth(None) => NOT_IN_BEST_CHAIN_CONFIRMATIONS, + _ => unreachable!("unmatched response to a depth request"), + }; + let sapling = SaplingTrees { size: sapling_note_commitment_tree_count, }; @@ -921,6 +948,7 @@ where hash: GetBlockHash(hash), confirmations, height, + time, tx, trees, }) @@ -1639,6 +1667,10 @@ pub enum GetBlock { #[serde(skip_serializing_if = "Option::is_none")] height: Option, + /// The height of the requested block. + #[serde(skip_serializing_if = "Option::is_none")] + time: Option, + /// List of transaction IDs in block order, hex-encoded. // // TODO: use a typed Vec here @@ -1655,6 +1687,7 @@ impl Default for GetBlock { hash: GetBlockHash::default(), confirmations: 0, height: None, + time: None, tx: Vec::new(), trees: GetBlockTrees::default(), } diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap index 6bed7d59cd2..3bcf968bed5 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap @@ -5,6 +5,7 @@ expression: block { "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, + "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap index fe2c9527562..7ea021d3382 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap @@ -5,6 +5,7 @@ expression: block { "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, + "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap index 6bed7d59cd2..3bcf968bed5 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap @@ -5,6 +5,7 @@ expression: block { "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, + "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap index fe2c9527562..7ea021d3382 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap @@ -5,6 +5,7 @@ expression: block { "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, + "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap index 3d66b2dffa2..f18b879f6b3 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap @@ -6,6 +6,7 @@ expression: block "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, "height": 1, + "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap index f79a4283b50..013a4c09b23 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap @@ -6,6 +6,7 @@ expression: block "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, "height": 1, + "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap index 3d66b2dffa2..f18b879f6b3 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap @@ -6,6 +6,7 @@ expression: block "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, "height": 1, + "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap index f79a4283b50..013a4c09b23 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap @@ -6,6 +6,7 @@ expression: block "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, "height": 1, + "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 28ca7b198ff..c6cbb4289f2 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -140,6 +140,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: Some(Height(i.try_into().expect("valid u32"))), + time: Some(block.header.time.timestamp()), tx: block .transactions .iter() @@ -163,6 +164,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: None, + time: Some(block.header.time.timestamp()), tx: block .transactions .iter() @@ -186,6 +188,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: Some(Height(i.try_into().expect("valid u32"))), + time: Some(block.header.time.timestamp()), tx: block .transactions .iter() @@ -209,6 +212,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: None, + time: Some(block.header.time.timestamp()), tx: block .transactions .iter() diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 2a6b3b1a5b8..6f785a9d250 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -614,6 +614,16 @@ pub enum Request { /// [`block::Height`] using `.into()`. Block(HashOrHeight), + /// Looks up a block header by hash or height in the current best chain. + /// + /// Returns + /// + /// [`Response::BlockHeader(block::Header)`](Response::BlockHeader). + /// + /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or + /// [`block::Height`] using `.into()`. + BlockHeader(HashOrHeight), + /// Request a UTXO identified by the given [`OutPoint`](transparent::OutPoint), /// waiting until it becomes available if it is unknown. /// @@ -725,6 +735,7 @@ impl Request { Request::Transaction(_) => "transaction", Request::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo", Request::Block(_) => "block", + Request::BlockHeader(_) => "block_header", Request::FindBlockHashes { .. } => "find_block_hashes", Request::FindBlockHeaders { .. } => "find_block_headers", Request::CheckBestChainTipNullifiersAndAnchors(_) => { @@ -776,6 +787,16 @@ pub enum ReadRequest { /// [`block::Height`] using `.into()`. Block(HashOrHeight), + /// Looks up a block header by hash or height in the current best chain. + /// + /// Returns + /// + /// [`Response::BlockHeader(block::Header)`](Response::BlockHeader). + /// + /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or + /// [`block::Height`] using `.into()`. + BlockHeader(HashOrHeight), + /// Looks up a transaction by hash in the current best chain. /// /// Returns @@ -999,6 +1020,7 @@ impl ReadRequest { ReadRequest::Tip => "tip", ReadRequest::Depth(_) => "depth", ReadRequest::Block(_) => "block", + ReadRequest::BlockHeader(_) => "block_header", ReadRequest::Transaction(_) => "transaction", ReadRequest::TransactionIdsForBlock(_) => "transaction_ids_for_block", ReadRequest::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo", @@ -1052,6 +1074,7 @@ impl TryFrom for ReadRequest { Request::BestChainBlockHash(hash) => Ok(ReadRequest::BestChainBlockHash(hash)), Request::Block(hash_or_height) => Ok(ReadRequest::Block(hash_or_height)), + Request::BlockHeader(hash_or_height) => Ok(ReadRequest::BlockHeader(hash_or_height)), Request::Transaction(tx_hash) => Ok(ReadRequest::Transaction(tx_hash)), Request::UnspentBestChainUtxo(outpoint) => { Ok(ReadRequest::UnspentBestChainUtxo(outpoint)) diff --git a/zebra-state/src/response.rs b/zebra-state/src/response.rs index ad75287d8f1..242191f8259 100644 --- a/zebra-state/src/response.rs +++ b/zebra-state/src/response.rs @@ -50,6 +50,9 @@ pub enum Response { /// Response to [`Request::Block`] with the specified block. Block(Option>), + /// The response to a `BlockHeader` request. + BlockHeader(Option>), + /// The response to a `AwaitUtxo` request, from any non-finalized chains, finalized chain, /// pending unverified blocks, or blocks received after the request was sent. Utxo(transparent::Utxo), @@ -131,6 +134,9 @@ pub enum ReadResponse { /// Response to [`ReadRequest::Block`] with the specified block. Block(Option>), + /// The response to a `BlockHeader` request. + BlockHeader(Option>), + /// Response to [`ReadRequest::Transaction`] with the specified transaction. Transaction(Option), @@ -265,6 +271,7 @@ impl TryFrom for Response { ReadResponse::BlockHash(hash) => Ok(Response::BlockHash(hash)), ReadResponse::Block(block) => Ok(Response::Block(block)), + ReadResponse::BlockHeader(header) => Ok(Response::BlockHeader(header)), ReadResponse::Transaction(tx_info) => { Ok(Response::Transaction(tx_info.map(|tx_info| tx_info.tx))) } diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index bd0abe8670e..203c1d34c70 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -1120,6 +1120,7 @@ impl Service for StateService { | Request::Transaction(_) | Request::UnspentBestChainUtxo(_) | Request::Block(_) + | Request::BlockHeader(_) | Request::FindBlockHashes { .. } | Request::FindBlockHeaders { .. } | Request::CheckBestChainTipNullifiersAndAnchors(_) => { @@ -1288,6 +1289,31 @@ impl Service for ReadStateService { .wait_for_panics() } + // Used by the get_block (verbose) RPC and the StateService. + ReadRequest::BlockHeader(hash_or_height) => { + let state = self.clone(); + + tokio::task::spawn_blocking(move || { + span.in_scope(move || { + let header = state.non_finalized_state_receiver.with_watch_data( + |non_finalized_state| { + read::block_header( + non_finalized_state.best_chain(), + &state.db, + hash_or_height, + ) + }, + ); + + // The work is done in the future. + timer.finish(module_path!(), line!(), "ReadRequest::Block"); + + Ok(ReadResponse::BlockHeader(header)) + }) + }) + .wait_for_panics() + } + // For the get_raw_transaction RPC and the StateService. ReadRequest::Transaction(hash) => { let state = self.clone(); From 8513d31733a74b9c668f2f379c5591f74691b3d0 Mon Sep 17 00:00:00 2001 From: Arya Date: Mon, 8 Apr 2024 22:55:50 -0400 Subject: [PATCH 2/4] cleanup/refactor, adds MapServerError and OkOrServerError traits --- zebra-rpc/src/methods.rs | 243 +++++++++++++++++---------------------- 1 file changed, 105 insertions(+), 138 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 1b33a8abf4d..59b464ecb84 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -675,10 +675,12 @@ where } // TODO: - // - use a generic error constructor (#5548) // - use `height_from_signed_int()` to handle negative heights // (this might be better in the state request, because it needs the state height) // - create a function that handles block hashes or heights, and use it in `z_get_treestate()` + // - Type second argument as an enum with variant for each verbosity level and a `Fields` variant + // for specifying exactly which fields should be included in the return value. Consider replacing + // `ReadRequest::BlockHeader` with a `PartialBlockData` request that accepts the fields? fn get_block( &self, hash_or_height: String, @@ -776,157 +778,85 @@ where } }; - // Get transaction IDs from the transaction index by block hash - // - // # Concurrency - // - // We look up by block hash so the hash, transaction IDs, and confirmations - // are consistent. - // - // A block's transaction IDs are never modified, so all possible responses are - // valid. Clients that query block heights must be able to handle chain forks, - // including getting transaction IDs from any chain fork. - let request = zebra_state::ReadRequest::TransactionIdsForBlock(hash.into()); - let tx_ids_response_fut = state.clone().oneshot(request); - - // Sapling trees - // - // # Concurrency - // - // We look up by block hash so the hash, transaction IDs, block header, and confirmations - // are consistent. - let request = zebra_state::ReadRequest::SaplingTree(hash.into()); - let sapling_tree_response_fut = state.clone().oneshot(request); - - // Orchard trees - // - // # Concurrency - // - // We look up by block hash so the hash, transaction IDs, block header, and confirmations - // are consistent. - let request = zebra_state::ReadRequest::OrchardTree(hash.into()); - let orchard_tree_response_fut = state.clone().oneshot(request); - - // Block header - // - // # Concurrency - // - // We look up by block hash so the hash, transaction IDs, block header, and confirmations - // are consistent. - let request = zebra_state::ReadRequest::BlockHeader(hash.into()); - let block_header_response_fut = state.clone().oneshot(request); + // TODO: look up the height if we only have a hash, + // this needs a new state request for the height -> hash index + let height = hash_or_height.height(); - // Get block confirmations from the block height index - // // # Concurrency // - // We look up by block hash so the hash, transaction IDs, block header, and confirmations + // We look up by block hash so the hash, transaction IDs, and confirmations // are consistent. - // - // All possible responses are valid, even if a block is added to the chain, or - // the best chain changes. Clients must be able to handle chain forks, including - // different confirmation values before or after added blocks, and switching - // between -1 and multiple different confirmation values. - - // From - const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1; - - let request = zebra_state::ReadRequest::Depth(hash); - let depth_response_fut = state.clone().oneshot(request); + let requests = [ + // Get transaction IDs from the transaction index by block hash + // + // # Concurrency + // + // A block's transaction IDs are never modified, so all possible responses are + // valid. Clients that query block heights must be able to handle chain forks, + // including getting transaction IDs from any chain fork. + zebra_state::ReadRequest::TransactionIdsForBlock(hash.into()), + // Sapling trees + zebra_state::ReadRequest::SaplingTree(hash.into()), + // Orchard trees + zebra_state::ReadRequest::OrchardTree(hash.into()), + // Get block confirmations from the block height index + // + // # Concurrency + // + // All possible responses are valid, even if a block is added to the chain, or + // the best chain changes. Clients must be able to handle chain forks, including + // different confirmation values before or after added blocks, and switching + // between -1 and multiple different confirmation values. + zebra_state::ReadRequest::Depth(hash), + // Block header + zebra_state::ReadRequest::BlockHeader(hash.into()), + ]; let mut futs = FuturesOrdered::new(); - futs.push_back(tx_ids_response_fut); - futs.push_back(sapling_tree_response_fut); - futs.push_back(orchard_tree_response_fut); - futs.push_back(block_header_response_fut); - futs.push_back(depth_response_fut); - - let tx_ids_response = futs - .next() - .await - .expect("should have 5 items in futs") - .map_err(|error| Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - })?; - - let sapling_tree_response = futs - .next() - .await - .expect("should have 4 items in futs") - .map_err(|error| Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - })?; - - let orchard_tree_response = futs - .next() - .await - .expect("should have 3 items in futs") - .map_err(|error| Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - })?; - - let block_header_response = futs - .next() - .await - .expect("should have 2 items in futs") - .map_err(|error| Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - })?; - let depth_response = futs - .next() - .await - .expect("should have an item in futs") - .map_err(|error| Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - })?; + for request in requests { + futs.push_back(state.clone().oneshot(request)); + } - let tx = match tx_ids_response { - zebra_state::ReadResponse::TransactionIdsForBlock(Some(tx_ids)) => { - tx_ids.iter().map(|tx_id| tx_id.encode_hex()).collect() - } - zebra_state::ReadResponse::TransactionIdsForBlock(None) => Err(Error { - code: MISSING_BLOCK_ERROR_CODE, - message: "Block not found".to_string(), - data: None, - })?, + let tx_ids_response = futs.next().await.expect("`futs` should not be empty"); + let tx = match tx_ids_response.map_server_error()? { + zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => tx_ids + .ok_or_server_error("Block not found")? + .iter() + .map(|tx_id| tx_id.encode_hex()) + .collect(), _ => unreachable!("unmatched response to a transaction_ids_for_block request"), }; - let sapling_note_commitment_tree_count = match sapling_tree_response { - zebra_state::ReadResponse::SaplingTree(Some(nct)) => nct.count(), - zebra_state::ReadResponse::SaplingTree(None) => 0, - _ => unreachable!("unmatched response to a SaplingTree request"), - }; - - // TODO: look up the height if we only have a hash, - // this needs a new state request for the height -> hash index - let height = hash_or_height.height(); - - let orchard_note_commitment_tree_count = match orchard_tree_response { - zebra_state::ReadResponse::OrchardTree(Some(nct)) => nct.count(), - zebra_state::ReadResponse::OrchardTree(None) => 0, - _ => unreachable!("unmatched response to a OrchardTree request"), - }; - - let block_header = match block_header_response { - zebra_state::ReadResponse::BlockHeader(header) => header, + let sapling_tree_response = futs.next().await.expect("`futs` should not be empty"); + let sapling_note_commitment_tree_count = + match sapling_tree_response.map_server_error()? { + zebra_state::ReadResponse::SaplingTree(Some(nct)) => nct.count(), + zebra_state::ReadResponse::SaplingTree(None) => 0, + _ => unreachable!("unmatched response to a SaplingTree request"), + }; + + let orchard_tree_response = futs.next().await.expect("`futs` should not be empty"); + let orchard_note_commitment_tree_count = + match orchard_tree_response.map_server_error()? { + zebra_state::ReadResponse::OrchardTree(Some(nct)) => nct.count(), + zebra_state::ReadResponse::OrchardTree(None) => 0, + _ => unreachable!("unmatched response to a OrchardTree request"), + }; + + let block_header_response = futs.next().await.expect("`futs` should not be empty"); + let block_header = match block_header_response.map_server_error()? { + zebra_state::ReadResponse::BlockHeader(header) => { + header.ok_or_server_error("Block not found")? + } _ => unreachable!("unmatched response to a BlockHeader request"), }; - let time = block_header.map(|header| header.time.timestamp()); + // From + const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1; - let confirmations = match depth_response { + let depth_response = futs.next().await.expect("`futs` should not be empty"); + let confirmations = match depth_response.map_server_error()? { // Confirmations are one more than the depth. // Depth is limited by height, so it will never overflow an i64. zebra_state::ReadResponse::Depth(Some(depth)) => i64::from(depth) + 1, @@ -948,7 +878,7 @@ where hash: GetBlockHash(hash), confirmations, height, - time, + time: Some(block_header.time.timestamp()), tx, trees, }) @@ -1987,3 +1917,40 @@ pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result Ok(Height(sanitized_height)) } } + +trait MapServerError { + fn map_server_error(self) -> std::result::Result; +} + +trait OkOrServerError { + fn ok_or_server_error( + self, + message: S, + ) -> std::result::Result; +} + +impl MapServerError for std::result::Result +where + E: ToString, +{ + fn map_server_error(self) -> std::result::Result { + self.map_err(|error| jsonrpc_core::Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + }) + } +} + +impl OkOrServerError for Option { + fn ok_or_server_error( + self, + message: S, + ) -> std::result::Result { + self.ok_or(jsonrpc_core::Error { + code: ErrorCode::ServerError(0), + message: message.to_string(), + data: None, + }) + } +} From 5da0ca6093f0eedd2967bd202ab0d94115b3981e Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 9 Apr 2024 19:50:57 -0400 Subject: [PATCH 3/4] moves rpc error conversion traits to their own module --- zebra-rpc/src/methods.rs | 41 ++++----------------------------- zebra-rpc/src/methods/errors.rs | 37 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 37 deletions(-) create mode 100644 zebra-rpc/src/methods/errors.rs diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 59b464ecb84..21c12182a07 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -38,6 +38,10 @@ use crate::{ queue::Queue, }; +mod errors; + +use errors::{MapServerError, OkOrServerError}; + // We don't use a types/ module here, because it is redundant. pub mod trees; @@ -1917,40 +1921,3 @@ pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result Ok(Height(sanitized_height)) } } - -trait MapServerError { - fn map_server_error(self) -> std::result::Result; -} - -trait OkOrServerError { - fn ok_or_server_error( - self, - message: S, - ) -> std::result::Result; -} - -impl MapServerError for std::result::Result -where - E: ToString, -{ - fn map_server_error(self) -> std::result::Result { - self.map_err(|error| jsonrpc_core::Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - }) - } -} - -impl OkOrServerError for Option { - fn ok_or_server_error( - self, - message: S, - ) -> std::result::Result { - self.ok_or(jsonrpc_core::Error { - code: ErrorCode::ServerError(0), - message: message.to_string(), - data: None, - }) - } -} diff --git a/zebra-rpc/src/methods/errors.rs b/zebra-rpc/src/methods/errors.rs new file mode 100644 index 00000000000..be9231d058d --- /dev/null +++ b/zebra-rpc/src/methods/errors.rs @@ -0,0 +1,37 @@ +//! Error conversions for Zebra's RPC methods. + +use jsonrpc_core::ErrorCode; + +pub(crate) trait MapServerError { + fn map_server_error(self) -> std::result::Result; +} + +pub(crate) trait OkOrServerError { + fn ok_or_server_error( + self, + message: S, + ) -> std::result::Result; +} + +impl MapServerError for Result +where + E: ToString, +{ + fn map_server_error(self) -> Result { + self.map_err(|error| jsonrpc_core::Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + }) + } +} + +impl OkOrServerError for Option { + fn ok_or_server_error(self, message: S) -> Result { + self.ok_or(jsonrpc_core::Error { + code: ErrorCode::ServerError(0), + message: message.to_string(), + data: None, + }) + } +} From c4fe4739aada4c5ab329829df1e386e14f875fd2 Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 9 Apr 2024 23:44:32 -0400 Subject: [PATCH 4/4] Only returns block time for verbosity = 2, updates snapshots --- zebra-rpc/src/methods.rs | 43 +++++++++------ zebra-rpc/src/methods/tests/snapshot.rs | 21 +++++++- ...cessive_height_verbosity_2@mainnet_10.snap | 10 ++++ ...cessive_height_verbosity_2@testnet_10.snap | 10 ++++ ...k_verbose_hash_verbosity_1@mainnet_10.snap | 1 - ...k_verbose_hash_verbosity_1@testnet_10.snap | 1 - ...k_verbose_hash_verbosity_2@mainnet_10.snap | 13 +++++ ...k_verbose_hash_verbosity_2@testnet_10.snap | 13 +++++ ...ose_hash_verbosity_default@mainnet_10.snap | 1 - ...ose_hash_verbosity_default@testnet_10.snap | 1 - ...verbose_height_verbosity_1@mainnet_10.snap | 1 - ...verbose_height_verbosity_1@testnet_10.snap | 1 - ...verbose_height_verbosity_2@mainnet_10.snap | 14 +++++ ...verbose_height_verbosity_2@testnet_10.snap | 14 +++++ ...e_height_verbosity_default@mainnet_10.snap | 1 - ...e_height_verbosity_default@testnet_10.snap | 1 - zebra-rpc/src/methods/tests/vectors.rs | 54 +++++++++++++++++-- 17 files changed, 172 insertions(+), 28 deletions(-) create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@mainnet_10.snap create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@testnet_10.snap create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 21c12182a07..8fd68272512 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -682,9 +682,6 @@ where // - use `height_from_signed_int()` to handle negative heights // (this might be better in the state request, because it needs the state height) // - create a function that handles block hashes or heights, and use it in `z_get_treestate()` - // - Type second argument as an enum with variant for each verbosity level and a `Fields` variant - // for specifying exactly which fields should be included in the return value. Consider replacing - // `ReadRequest::BlockHeader` with a `PartialBlockData` request that accepts the fields? fn get_block( &self, hash_or_height: String, @@ -733,7 +730,7 @@ where }), _ => unreachable!("unmatched response to a block request"), } - } else if verbosity == 1 { + } else if verbosity == 1 || verbosity == 2 { // # Performance // // This RPC is used in `lightwalletd`'s initial sync of 2 million blocks, @@ -754,6 +751,8 @@ where // must be able to handle chain forks, including a hash for a block that is // later discovered to be on a side chain. + let should_read_block_header = verbosity == 2; + let hash = match hash_or_height { HashOrHeight::Hash(hash) => hash, HashOrHeight::Height(height) => { @@ -790,7 +789,7 @@ where // // We look up by block hash so the hash, transaction IDs, and confirmations // are consistent. - let requests = [ + let mut requests = vec![ // Get transaction IDs from the transaction index by block hash // // # Concurrency @@ -812,10 +811,13 @@ where // different confirmation values before or after added blocks, and switching // between -1 and multiple different confirmation values. zebra_state::ReadRequest::Depth(hash), - // Block header - zebra_state::ReadRequest::BlockHeader(hash.into()), ]; + if should_read_block_header { + // Block header + requests.push(zebra_state::ReadRequest::BlockHeader(hash.into())) + } + let mut futs = FuturesOrdered::new(); for request in requests { @@ -848,14 +850,6 @@ where _ => unreachable!("unmatched response to a OrchardTree request"), }; - let block_header_response = futs.next().await.expect("`futs` should not be empty"); - let block_header = match block_header_response.map_server_error()? { - zebra_state::ReadResponse::BlockHeader(header) => { - header.ok_or_server_error("Block not found")? - } - _ => unreachable!("unmatched response to a BlockHeader request"), - }; - // From const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1; @@ -868,6 +862,23 @@ where _ => unreachable!("unmatched response to a depth request"), }; + let time = if should_read_block_header { + let block_header_response = + futs.next().await.expect("`futs` should not be empty"); + + match block_header_response.map_server_error()? { + zebra_state::ReadResponse::BlockHeader(header) => Some( + header + .ok_or_server_error("Block not found")? + .time + .timestamp(), + ), + _ => unreachable!("unmatched response to a BlockHeader request"), + } + } else { + None + }; + let sapling = SaplingTrees { size: sapling_note_commitment_tree_count, }; @@ -882,7 +893,7 @@ where hash: GetBlockHash(hash), confirmations, height, - time: Some(block_header.time.timestamp()), + time, tx, trees, }) diff --git a/zebra-rpc/src/methods/tests/snapshot.rs b/zebra-rpc/src/methods/tests/snapshot.rs index 94e12cc806f..e1c84f4a520 100644 --- a/zebra-rpc/src/methods/tests/snapshot.rs +++ b/zebra-rpc/src/methods/tests/snapshot.rs @@ -2,7 +2,7 @@ //! //! To update these snapshots, run: //! ```sh -//! cargo insta test --review +//! cargo insta test --review -p zebra-rpc --lib -- test_rpc_response_data //! ``` use std::{collections::BTreeMap, sync::Arc}; @@ -167,6 +167,25 @@ async fn test_rpc_response_data_for_network(network: &Network) { .expect("We should have a GetBlock struct"); snapshot_rpc_getblock_verbose("hash_verbosity_1", get_block, &settings); + // `getblock`, verbosity=2, height + let get_block = rpc + .get_block(BLOCK_HEIGHT.to_string(), Some(2u8)) + .await + .expect("We should have a GetBlock struct"); + snapshot_rpc_getblock_verbose("height_verbosity_2", get_block, &settings); + + let get_block = rpc + .get_block(EXCESSIVE_BLOCK_HEIGHT.to_string(), Some(2u8)) + .await; + snapshot_rpc_getblock_invalid("excessive_height_verbosity_2", get_block, &settings); + + // `getblock`, verbosity=2, hash + let get_block = rpc + .get_block(block_hash.to_string(), Some(2u8)) + .await + .expect("We should have a GetBlock struct"); + snapshot_rpc_getblock_verbose("hash_verbosity_2", get_block, &settings); + // `getblock`, no verbosity - defaults to 1, height let get_block = rpc .get_block(BLOCK_HEIGHT.to_string(), None) diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@mainnet_10.snap new file mode 100644 index 00000000000..5c2e6892a7d --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@mainnet_10.snap @@ -0,0 +1,10 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: response +--- +{ + "Err": { + "code": -8, + "message": "block height not in best chain" + } +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@testnet_10.snap new file mode 100644 index 00000000000..5c2e6892a7d --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_invalid_excessive_height_verbosity_2@testnet_10.snap @@ -0,0 +1,10 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: response +--- +{ + "Err": { + "code": -8, + "message": "block height not in best chain" + } +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap index 3bcf968bed5..6bed7d59cd2 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap @@ -5,7 +5,6 @@ expression: block { "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, - "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap index 7ea021d3382..fe2c9527562 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap @@ -5,7 +5,6 @@ expression: block { "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, - "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap new file mode 100644 index 00000000000..3bcf968bed5 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap @@ -0,0 +1,13 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: block +--- +{ + "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", + "confirmations": 10, + "time": 1477671596, + "tx": [ + "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" + ], + "trees": {} +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap new file mode 100644 index 00000000000..7ea021d3382 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap @@ -0,0 +1,13 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: block +--- +{ + "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", + "confirmations": 10, + "time": 1477674473, + "tx": [ + "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" + ], + "trees": {} +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap index 3bcf968bed5..6bed7d59cd2 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap @@ -5,7 +5,6 @@ expression: block { "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, - "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap index 7ea021d3382..fe2c9527562 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap @@ -5,7 +5,6 @@ expression: block { "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, - "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap index f18b879f6b3..3d66b2dffa2 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap @@ -6,7 +6,6 @@ expression: block "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, "height": 1, - "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap index 013a4c09b23..f79a4283b50 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap @@ -6,7 +6,6 @@ expression: block "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, "height": 1, - "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap new file mode 100644 index 00000000000..f18b879f6b3 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap @@ -0,0 +1,14 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: block +--- +{ + "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", + "confirmations": 10, + "height": 1, + "time": 1477671596, + "tx": [ + "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" + ], + "trees": {} +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap new file mode 100644 index 00000000000..013a4c09b23 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap @@ -0,0 +1,14 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +expression: block +--- +{ + "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", + "confirmations": 10, + "height": 1, + "time": 1477674473, + "tx": [ + "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" + ], + "trees": {} +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap index f18b879f6b3..3d66b2dffa2 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap @@ -6,7 +6,6 @@ expression: block "hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283", "confirmations": 10, "height": 1, - "time": 1477671596, "tx": [ "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609" ], diff --git a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap index 013a4c09b23..f79a4283b50 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap @@ -6,7 +6,6 @@ expression: block "hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23", "confirmations": 10, "height": 1, - "time": 1477674473, "tx": [ "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75" ], diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index c6cbb4289f2..1591ab69057 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -140,7 +140,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: Some(Height(i.try_into().expect("valid u32"))), - time: Some(block.header.time.timestamp()), + time: None, tx: block .transactions .iter() @@ -158,6 +158,54 @@ async fn rpc_getblock() { .await .expect("We should have a GetBlock struct"); + assert_eq!( + get_block, + GetBlock::Object { + hash: GetBlockHash(block.hash()), + confirmations: (blocks.len() - i).try_into().expect("valid i64"), + height: None, + time: None, + tx: block + .transactions + .iter() + .map(|tx| tx.hash().encode_hex()) + .collect(), + trees, + } + ); + } + + // Make height calls with verbosity=2 and check response + for (i, block) in blocks.iter().enumerate() { + let get_block = rpc + .get_block(i.to_string(), Some(2u8)) + .await + .expect("We should have a GetBlock struct"); + + assert_eq!( + get_block, + GetBlock::Object { + hash: GetBlockHash(block.hash()), + confirmations: (blocks.len() - i).try_into().expect("valid i64"), + height: Some(Height(i.try_into().expect("valid u32"))), + time: Some(block.header.time.timestamp()), + tx: block + .transactions + .iter() + .map(|tx| tx.hash().encode_hex()) + .collect(), + trees, + } + ); + } + + // Make hash calls with verbosity=2 and check response + for (i, block) in blocks.iter().enumerate() { + let get_block = rpc + .get_block(blocks[i].hash().to_string(), Some(2u8)) + .await + .expect("We should have a GetBlock struct"); + assert_eq!( get_block, GetBlock::Object { @@ -188,7 +236,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: Some(Height(i.try_into().expect("valid u32"))), - time: Some(block.header.time.timestamp()), + time: None, tx: block .transactions .iter() @@ -212,7 +260,7 @@ async fn rpc_getblock() { hash: GetBlockHash(block.hash()), confirmations: (blocks.len() - i).try_into().expect("valid i64"), height: None, - time: Some(block.header.time.timestamp()), + time: None, tx: block .transactions .iter()