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

View File

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

View File

@ -859,12 +859,6 @@ impl Service<Request> for ReadStateService {
.boxed() .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 // TODO: implement for lightwalletd as part of these tickets
// get_raw_transaction (#3145) // get_raw_transaction (#3145)
@ -885,6 +879,9 @@ impl Service<Request> for ReadStateService {
// Out of Scope // Out of Scope
// TODO: delete when splitting the Request enum // 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. // These requests don't need better performance at the moment.
Request::FindBlockHashes { Request::FindBlockHashes {
known_blocks: _, known_blocks: _,

View File

@ -20,7 +20,9 @@ use zebra_chain::{
}; };
use crate::{ 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; 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( pub async fn populated_state(
blocks: impl IntoIterator<Item = Arc<Block>>, blocks: impl IntoIterator<Item = Arc<Block>>,
network: Network, network: Network,
) -> Buffer<BoxService<Request, Response, BoxError>, Request> { ) -> (
Buffer<BoxService<Request, Response, BoxError>, Request>,
ReadStateService,
LatestChainTip,
ChainTipChange,
) {
let requests = blocks let requests = blocks
.into_iter() .into_iter()
.map(|block| Request::CommitFinalizedBlock(block.into())); .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(); let mut responses = FuturesUnordered::new();
@ -188,5 +201,5 @@ pub async fn populated_state(
rsp.expect("blocks should commit just fine"); 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] #[tokio::main]
async fn populate_and_check(blocks: Vec<Arc<Block>>) -> Result<()> { 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?; test_populated_state_responds_correctly(state).await?;
Ok(()) Ok(())
} }