refactor(rpc): use ChainTip for get_best_block_hash (#3864)

* Use ChainTip for get_best_block_hash RPC

* Use ReadStateService and LatestChainTip in tests

* Mark Request::Tip as out of scope for ReadStateService
This commit is contained in:
teor 2022-03-16 14:01:59 +10:00 committed by GitHub
parent bf1f7aa973
commit 93c681fd6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 50 deletions

View File

@ -96,7 +96,7 @@ pub trait Rpc {
///
/// zcashd reference: [`getbestblockhash`](https://zcash.github.io/rpc/getbestblockhash.html)
#[rpc(name = "getbestblockhash")]
fn get_best_block_hash(&self) -> BoxFuture<Result<GetBestBlockHash>>;
fn get_best_block_hash(&self) -> Result<GetBestBlockHash>;
/// Returns all transaction ids in the memory pool, as a JSON array.
///
@ -126,7 +126,6 @@ where
state: State,
/// Allows efficient access to the best tip of the blockchain.
#[allow(dead_code)]
latest_chain_tip: Tip,
/// The configured network for this RPC service.
@ -282,32 +281,15 @@ where
.boxed()
}
fn get_best_block_hash(&self) -> BoxFuture<Result<GetBestBlockHash>> {
let mut state = self.state.clone();
async move {
let request = zebra_state::Request::Tip;
let response = state
.ready()
.and_then(|service| service.call(request))
.await
.map_err(|error| Error {
code: ErrorCode::ServerError(0),
message: error.to_string(),
data: None,
})?;
match response {
zebra_state::Response::Tip(Some((_height, hash))) => Ok(GetBestBlockHash(hash)),
zebra_state::Response::Tip(None) => Err(Error {
code: ErrorCode::ServerError(0),
message: "No blocks in state".to_string(),
data: None,
}),
_ => unreachable!("unmatched response to a tip request"),
}
}
.boxed()
fn get_best_block_hash(&self) -> Result<GetBestBlockHash> {
self.latest_chain_tip
.best_tip_hash()
.map(GetBestBlockHash)
.ok_or(Error {
code: ErrorCode::ServerError(0),
message: "No blocks in state".to_string(),
data: None,
})
}
fn get_raw_mempool(&self) -> BoxFuture<Result<Vec<String>>> {

View File

@ -5,9 +5,7 @@ use std::sync::Arc;
use tower::buffer::Buffer;
use zebra_chain::{
block::Block,
chain_tip::NoChainTip,
parameters::Network::{self, *},
block::Block, chain_tip::NoChainTip, parameters::Network::*,
serialization::ZcashDeserializeInto,
};
use zebra_network::constants::USER_AGENT;
@ -61,14 +59,15 @@ async fn rpc_getblock() {
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
// Create a populated state service
let state = zebra_state::populated_state(blocks.clone(), Network::Mainnet).await;
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
zebra_state::populated_state(blocks.clone(), Mainnet).await;
// Init RPC
let rpc = RpcImpl::new(
"RPC test",
Buffer::new(mempool.clone(), 1),
state,
NoChainTip,
read_state,
latest_chain_tip,
Mainnet,
);
@ -131,21 +130,21 @@ async fn rpc_getbestblockhash() {
// Get a mempool handle
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
// Create a populated state service, the tip will be in `NUMBER_OF_BLOCKS`.
let state = zebra_state::populated_state(blocks.clone(), Network::Mainnet).await;
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
zebra_state::populated_state(blocks.clone(), Mainnet).await;
// Init RPC
let rpc = RpcImpl::new(
"RPC test",
Buffer::new(mempool.clone(), 1),
state,
NoChainTip,
read_state,
latest_chain_tip,
Mainnet,
);
// Get the tip hash using RPC method `get_best_block_hash`
let get_best_block_hash = rpc
.get_best_block_hash()
.await
.expect("We should have a GetBestBlockHash struct");
let response_hash = get_best_block_hash.0;

View File

@ -859,12 +859,6 @@ impl Service<Request> for ReadStateService {
.boxed()
}
// Used by get_best_block_hash & get_blockchain_info (#3143) RPCs.
//
// These RPC methods could use the ChainTip struct instead,
// if that's easier or more consistent.
Request::Tip => unimplemented!("ReadStateService doesn't Tip yet"),
// TODO: implement for lightwalletd as part of these tickets
// get_raw_transaction (#3145)
@ -885,6 +879,9 @@ impl Service<Request> for ReadStateService {
// Out of Scope
// TODO: delete when splitting the Request enum
// Use ChainTip instead.
Request::Tip => unreachable!("ReadStateService doesn't need to Tip"),
// These requests don't need better performance at the moment.
Request::FindBlockHashes {
known_blocks: _,

View File

@ -20,7 +20,9 @@ use zebra_chain::{
};
use crate::{
arbitrary::Prepare, init_test, service::check, BoxError, PreparedBlock, Request, Response,
arbitrary::Prepare,
service::{check, ReadStateService, StateService},
BoxError, ChainTipChange, Config, LatestChainTip, PreparedBlock, Request, Response,
};
pub use zebra_chain::block::arbitrary::MAX_PARTIAL_CHAIN_BLOCKS;
@ -166,16 +168,27 @@ impl Strategy for PreparedChain {
}
}
/// Initialize a state service with blocks.
/// Initialize a state service with blocks, and return:
/// - a read-write [`StateService`]
/// - a read-only [`ReadStateService`]
/// - a [`LatestChainTip`]
/// - a [`ChainTipChange`] tracker
pub async fn populated_state(
blocks: impl IntoIterator<Item = Arc<Block>>,
network: Network,
) -> Buffer<BoxService<Request, Response, BoxError>, Request> {
) -> (
Buffer<BoxService<Request, Response, BoxError>, Request>,
ReadStateService,
LatestChainTip,
ChainTipChange,
) {
let requests = blocks
.into_iter()
.map(|block| Request::CommitFinalizedBlock(block.into()));
let mut state = init_test(network);
let (state, read_state, latest_chain_tip, chain_tip_change) =
StateService::new(Config::ephemeral(), network);
let mut state = Buffer::new(BoxService::new(state), 1);
let mut responses = FuturesUnordered::new();
@ -188,5 +201,5 @@ pub async fn populated_state(
rsp.expect("blocks should commit just fine");
}
state
(state, read_state, latest_chain_tip, chain_tip_change)
}

View File

@ -196,7 +196,7 @@ async fn test_populated_state_responds_correctly(
#[tokio::main]
async fn populate_and_check(blocks: Vec<Arc<Block>>) -> Result<()> {
let state = populated_state(blocks, Network::Mainnet).await;
let (state, _, _, _) = populated_state(blocks, Network::Mainnet).await;
test_populated_state_responds_correctly(state).await?;
Ok(())
}