Skip to content

Commit

Permalink
Split state requests into Request and ReadRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
teor2345 committed Mar 14, 2022
1 parent 7dd079c commit 162a24b
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 63 deletions.
2 changes: 1 addition & 1 deletion zebra-state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub use service::{
pub use service::{
arbitrary::populated_state,
chain_tip::{ChainTipBlock, ChainTipSender},
init_test,
init_test, init_test_services,
};

pub(crate) use request::ContextuallyValidBlock;
109 changes: 48 additions & 61 deletions zebra-state/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ use crate::{
pending_utxos::PendingUtxos,
watch_receiver::WatchReceiver,
},
BoxError, CloneError, CommitBlockError, Config, FinalizedBlock, PreparedBlock, Request,
Response, ValidateContextError,
BoxError, CloneError, CommitBlockError, Config, FinalizedBlock, PreparedBlock, ReadRequest,
ReadResponse, Request, Response, ValidateContextError,
};

pub mod block_iter;
Expand Down Expand Up @@ -902,8 +902,8 @@ impl Service<Request> for StateService {
}
}

impl Service<Request> for ReadStateService {
type Response = Response;
impl Service<ReadRequest> for ReadStateService {
type Response = ReadResponse;
type Error = BoxError;
type Future =
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
Expand All @@ -913,10 +913,19 @@ impl Service<Request> for ReadStateService {
}

#[instrument(name = "read_state", skip(self))]
fn call(&mut self, req: Request) -> Self::Future {
fn call(&mut self, req: ReadRequest) -> Self::Future {
match req {
// TODO: implement these new ReadRequests for lightwalletd, as part of these tickets

// z_get_tree_state (#3156)

// depends on transparent address indexes (#3150)
// get_address_tx_ids (#3147)
// get_address_balance (#3157)
// get_address_utxos (#3158)

// Used by get_block RPC.
Request::Block(hash_or_height) => {
ReadRequest::Block(hash_or_height) => {
metrics::counter!(
"state.requests",
1,
Expand All @@ -931,13 +940,13 @@ impl Service<Request> for ReadStateService {
read::block(best_chain, &state.db, hash_or_height)
});

Ok(Response::Block(block))
Ok(ReadResponse::Block(block))
}
.boxed()
}

// For the get_raw_transaction RPC, to be implemented in #3145.
Request::Transaction(hash) => {
ReadRequest::Transaction(hash) => {
metrics::counter!(
"state.requests",
1,
Expand All @@ -952,60 +961,10 @@ impl Service<Request> for ReadStateService {
read::transaction(best_chain, &state.db, hash)
});

Ok(Response::Transaction(transaction))
Ok(ReadResponse::Transaction(transaction))
}
.boxed()
}

// TODO: split the Request enum, then implement these new ReadRequests for lightwalletd
// as part of these tickets

// z_get_tree_state (#3156)

// depends on transparent address indexes (#3150)
// get_address_tx_ids (#3147)
// get_address_balance (#3157)
// get_address_utxos (#3158)

// 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: _,
stop: _,
} => {
unreachable!("ReadStateService doesn't need to FindBlockHashes")
}
Request::FindBlockHeaders {
known_blocks: _,
stop: _,
} => {
unreachable!("ReadStateService doesn't need to FindBlockHeaders")
}

// Some callers of this request need to wait for queued blocks.
Request::Depth(_hash) => unreachable!("ReadStateService could change depth behaviour"),

// This request needs to wait for queued blocks.
Request::BlockLocator => {
unreachable!("ReadStateService should not be used for block locators")
}

// Impossible Requests

// The read-only service doesn't have the shared internal state
// needed to await UTXOs.
Request::AwaitUtxo(_outpoint) => unreachable!("ReadStateService can't await UTXOs"),

// The read-only service can't write.
Request::CommitBlock(_prepared) => unreachable!("ReadStateService can't commit blocks"),
Request::CommitFinalizedBlock(_finalized) => {
unreachable!("ReadStateService can't commit blocks")
}
}
}
}
Expand Down Expand Up @@ -1042,12 +1001,40 @@ pub fn init(
)
}

/// Initialize a state service with an ephemeral [`Config`] and a buffer with a single slot.
/// Returns a [`StateService`] with an ephemeral [`Config`] and a buffer with a single slot.
///
/// This can be used to create a state service for testing. See also [`init`].
/// This can be used to create a state service for testing.
///
/// See also [`init`].
#[cfg(any(test, feature = "proptest-impl"))]
pub fn init_test(network: Network) -> Buffer<BoxService<Request, Response, BoxError>, Request> {
let (state_service, _, _, _) = StateService::new(Config::ephemeral(), network);

Buffer::new(BoxService::new(state_service), 1)
}

/// Initializes a state service with an ephemeral [`Config`] and a buffer with a single slot,
/// then returns the read-write service, read-only service, and tip watch channels.
///
/// This can be used to create a state service for testing. See also [`init`].
#[cfg(any(test, feature = "proptest-impl"))]
pub fn init_test_services(
network: Network,
) -> (
Buffer<BoxService<Request, Response, BoxError>, Request>,
ReadStateService,
LatestChainTip,
ChainTipChange,
) {
let (state_service, read_state_service, latest_chain_tip, chain_tip_change) =
StateService::new(Config::ephemeral(), network);

let state_service = Buffer::new(BoxService::new(state_service), 1);

(
state_service,
read_state_service,
latest_chain_tip,
chain_tip_change,
)
}
3 changes: 3 additions & 0 deletions zebra-state/src/service/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use crate::{
HashOrHeight,
};

#[cfg(test)]
mod tests;

/// Returns the [`Block`] with [`block::Hash`](zebra_chain::block::Hash) or
/// [`Height`](zebra_chain::block::Height),
/// if it exists in the non-finalized `chain` or finalized `db`.
Expand Down
3 changes: 3 additions & 0 deletions zebra-state/src/service/read/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! Tests for the ReadStateService.
mod vectors;
102 changes: 102 additions & 0 deletions zebra-state/src/service/read/tests/vectors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Fixed test vectors for the ReadStateService.
use std::sync::Arc;

use zebra_chain::{
block::Block, parameters::Network::*, serialization::ZcashDeserializeInto, transaction,
};

use zebra_test::{
prelude::Result,
transcript::{ExpectedTranscriptError, Transcript},
};

use crate::{init_test_services, populated_state, ReadRequest, ReadResponse};

/// Test that ReadStateService responds correctly when empty.
#[tokio::test]
async fn empty_read_state_still_responds_to_requests() -> Result<()> {
zebra_test::init();

let transcript = Transcript::from(empty_state_test_cases());

let network = Mainnet;
let (_state, read_state, _latest_chain_tip, _chain_tip_change) = init_test_services(network);

transcript.check(read_state).await?;

Ok(())
}

/// Test that ReadStateService responds correctly when the state contains blocks.
#[tokio::test]
async fn populated_read_state_responds_correctly() -> Result<()> {
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 (_state, read_state, _latest_chain_tip, _chain_tip_change) =
populated_state(blocks.clone(), Mainnet).await;

let empty_cases = Transcript::from(empty_state_test_cases());
empty_cases.check(read_state.clone()).await?;

for block in blocks {
let block_cases = vec![
(
ReadRequest::Block(block.hash().into()),
Ok(ReadResponse::Block(Some(block.clone()))),
),
(
ReadRequest::Block(block.coinbase_height().unwrap().into()),
Ok(ReadResponse::Block(Some(block.clone()))),
),
];

let block_cases = Transcript::from(block_cases);
block_cases.check(read_state.clone()).await?;

// Spec: transactions in the genesis block are ignored.
if block.coinbase_height().unwrap().0 == 0 {
continue;
}

for transaction in &block.transactions {
let transaction_cases = vec![(
ReadRequest::Transaction(transaction.hash()),
Ok(ReadResponse::Transaction(Some(transaction.clone()))),
)];

let transaction_cases = Transcript::from(transaction_cases);
transaction_cases.check(read_state.clone()).await?;
}
}

Ok(())
}

/// Returns test cases for the empty state and missing blocks.
fn empty_state_test_cases() -> Vec<(ReadRequest, Result<ReadResponse, ExpectedTranscriptError>)> {
let block: Arc<Block> = zebra_test::vectors::BLOCK_MAINNET_419200_BYTES
.zcash_deserialize_into()
.unwrap();

vec![
(
ReadRequest::Transaction(transaction::Hash([0; 32])),
Ok(ReadResponse::Transaction(None)),
),
(
ReadRequest::Block(block.hash().into()),
Ok(ReadResponse::Block(None)),
),
(
ReadRequest::Block(block.coinbase_height().unwrap().into()),
Ok(ReadResponse::Block(None)),
),
]
}
5 changes: 5 additions & 0 deletions zebra-state/src/service/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! StateService test vectors.
//!
//! TODO: move these tests into tests::vectors and tests::prop modules.
use std::{convert::TryInto, env, sync::Arc};

use tower::{buffer::Buffer, util::BoxService};
Expand All @@ -11,6 +15,7 @@ use zebra_chain::{
transaction, transparent,
value_balance::ValueBalance,
};

use zebra_test::{prelude::*, transcript::Transcript};

use crate::{
Expand Down
6 changes: 5 additions & 1 deletion zebra-state/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! Basic integration tests for zebra-state
use std::sync::Arc;

use color_eyre::eyre::Report;
use once_cell::sync::Lazy;
use std::sync::Arc;

use zebra_chain::{block::Block, parameters::Network, serialization::ZcashDeserialize};
use zebra_test::transcript::{ExpectedTranscriptError, Transcript};

Expand Down
3 changes: 3 additions & 0 deletions zebra-test/src/transcript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl ExpectedTranscriptError {
}

/// Check the actual error `e` against this expected error.
#[track_caller]
fn check(&self, e: BoxError) -> Result<(), Report> {
match self {
ExpectedTranscriptError::Any => Ok(()),
Expand Down Expand Up @@ -89,6 +90,7 @@ where
S: Debug + Eq,
{
/// Check this transcript against the responses from the `to_check` service
#[track_caller]
pub async fn check<C>(mut self, mut to_check: C) -> Result<(), Report>
where
C: Service<R, Response = S>,
Expand Down Expand Up @@ -169,6 +171,7 @@ where
Poll::Ready(Ok(()))
}

#[track_caller]
fn call(&mut self, request: R) -> Self::Future {
if let Some((expected_request, response)) = self.messages.next() {
match response {
Expand Down

0 comments on commit 162a24b

Please sign in to comment.