Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge-queue: embarking main (b563b2a) and #3865 together #3894

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 95 additions & 17 deletions zebra-state/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,12 +464,10 @@ impl StateService {
read::block(self.mem.best_chain(), self.disk.db(), hash_or_height)
}

/// Return the transaction identified by `hash` if it exists in the current
/// best chain.
/// Returns the [`Transaction`] with [`transaction::Hash`],
/// if it exists in the current best chain.
pub fn best_transaction(&self, hash: transaction::Hash) -> Option<Arc<Transaction>> {
self.mem
.best_transaction(hash)
.or_else(|| self.disk.db().transaction(hash))
read::transaction(self.mem.best_chain(), self.disk.db(), hash)
}

/// Return the hash for the block at `height` in the current best chain.
Expand Down Expand Up @@ -739,7 +737,12 @@ impl Service<Request> for StateService {
fn call(&mut self, req: Request) -> Self::Future {
match req {
Request::CommitBlock(prepared) => {
metrics::counter!("state.requests", 1, "type" => "commit_block");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "commit_block",
);

self.assert_block_can_be_validated(&prepared);

Expand All @@ -757,7 +760,12 @@ impl Service<Request> for StateService {
.boxed()
}
Request::CommitFinalizedBlock(finalized) => {
metrics::counter!("state.requests", 1, "type" => "commit_finalized_block");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "commit_finalized_block",
);

self.pending_utxos.check_against(&finalized.new_outputs);
let rsp_rx = self.queue_and_commit_finalized(finalized);
Expand All @@ -772,32 +780,67 @@ impl Service<Request> for StateService {
.boxed()
}
Request::Depth(hash) => {
metrics::counter!("state.requests", 1, "type" => "depth");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "depth",
);

let rsp = Ok(self.best_depth(hash)).map(Response::Depth);
async move { rsp }.boxed()
}
Request::Tip => {
metrics::counter!("state.requests", 1, "type" => "tip");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "tip",
);

let rsp = Ok(self.best_tip()).map(Response::Tip);
async move { rsp }.boxed()
}
Request::BlockLocator => {
metrics::counter!("state.requests", 1, "type" => "block_locator");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "block_locator",
);

let rsp = Ok(self.block_locator().unwrap_or_default()).map(Response::BlockLocator);
async move { rsp }.boxed()
}
Request::Transaction(hash) => {
metrics::counter!("state.requests", 1, "type" => "transaction");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "transaction",
);

let rsp = Ok(self.best_transaction(hash)).map(Response::Transaction);
async move { rsp }.boxed()
}
Request::Block(hash_or_height) => {
metrics::counter!("state.requests", 1, "type" => "block");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "block",
);

let rsp = Ok(self.best_block(hash_or_height)).map(Response::Block);
async move { rsp }.boxed()
}
Request::AwaitUtxo(outpoint) => {
metrics::counter!("state.requests", 1, "type" => "await_utxo");
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "await_utxo",
);

let fut = self.pending_utxos.queue(outpoint);

Expand All @@ -808,12 +851,26 @@ impl Service<Request> for StateService {
fut.boxed()
}
Request::FindBlockHashes { known_blocks, stop } => {
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "find_block_hashes",
);

const MAX_FIND_BLOCK_HASHES_RESULTS: usize = 500;
let res =
self.find_best_chain_hashes(known_blocks, stop, MAX_FIND_BLOCK_HASHES_RESULTS);
async move { Ok(Response::BlockHashes(res)) }.boxed()
}
Request::FindBlockHeaders { known_blocks, stop } => {
metrics::counter!(
"state.requests",
1,
"service" => "state",
"type" => "find_block_headers",
);

const MAX_FIND_BLOCK_HEADERS_RESULTS: usize = 160;
// Zcashd will blindly request more block headers as long as it
// got 160 block headers in response to a previous query, EVEN
Expand Down Expand Up @@ -860,6 +917,13 @@ impl Service<Request> for ReadStateService {
match req {
// Used by get_block RPC.
Request::Block(hash_or_height) => {
metrics::counter!(
"state.requests",
1,
"service" => "read_state",
"type" => "block",
);

let state = self.clone();

async move {
Expand All @@ -872,11 +936,25 @@ impl Service<Request> for ReadStateService {
.boxed()
}

// TODO: implement for lightwalletd as part of these tickets
// For the get_raw_transaction RPC, to be implemented in #3145.
Request::Transaction(hash) => {
metrics::counter!(
"state.requests",
1,
"service" => "read_state",
"type" => "transaction",
);

let state = self.clone();

// get_raw_transaction (#3145)
Request::Transaction(_hash) => {
unimplemented!("ReadStateService doesn't Transaction yet")
async move {
let transaction = state.best_chain_receiver.with_watch_data(|best_chain| {
read::transaction(best_chain, &state.db, hash)
});

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

// TODO: split the Request enum, then implement these new ReadRequests for lightwalletd
Expand Down
5 changes: 3 additions & 2 deletions zebra-state/src/service/finalized_state/zebra_db/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl ZebraDb {
self.db.zs_get(height_by_hash, &hash)
}

/// Returns the [`Block`] with [`Hash`](zebra_chain::block::Hash) or
/// Returns the [`Block`] with [`block::Hash`](zebra_chain::block::Hash) or
/// [`Height`](zebra_chain::block::Height), if it exists in the finalized chain.
pub fn block(&self, hash_or_height: HashOrHeight) -> Option<Arc<Block>> {
let height_by_hash = self.db.cf_handle("height_by_hash").unwrap();
Expand Down Expand Up @@ -102,7 +102,8 @@ impl ZebraDb {

// Read transaction methods

/// Returns the given transaction if it exists.
/// Returns the [`Transaction`] with [`transaction::Hash`],
/// if it exists in the finalized chain.
pub fn transaction(&self, hash: transaction::Hash) -> Option<Arc<Transaction>> {
let tx_by_hash = self.db.cf_handle("tx_by_hash").unwrap();
self.db
Expand Down
13 changes: 1 addition & 12 deletions zebra-state/src/service/non_finalized_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use zebra_chain::{
history_tree::HistoryTree,
orchard,
parameters::Network,
sapling, sprout,
transaction::{self, Transaction},
transparent,
sapling, sprout, transparent,
};

use crate::{
Expand Down Expand Up @@ -347,15 +345,6 @@ impl NonFinalizedState {
None
}

/// Returns the given transaction if it exists in the best chain.
pub fn best_transaction(&self, hash: transaction::Hash) -> Option<Arc<Transaction>> {
let best_chain = self.best_chain()?;
best_chain
.tx_by_hash
.get(&hash)
.map(|(height, index)| best_chain.blocks[height].block.transactions[*index].clone())
}

/// Returns `true` if the best chain contains `sprout_nullifier`.
#[cfg(test)]
pub fn best_contains_sprout_nullifier(&self, sprout_nullifier: &sprout::Nullifier) -> bool {
Expand Down
13 changes: 11 additions & 2 deletions zebra-state/src/service/non_finalized_state/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
cmp::Ordering,
collections::{BTreeMap, HashMap, HashSet},
ops::Deref,
sync::Arc,
};

use mset::MultiSet;
Expand All @@ -17,8 +18,9 @@ use zebra_chain::{
orchard,
parameters::Network,
primitives::Groth16Proof,
sapling, sprout, transaction,
sapling, sprout,
transaction::Transaction::*,
transaction::{self, Transaction},
transparent,
value_balance::ValueBalance,
work::difficulty::PartialCumulativeWork,
Expand Down Expand Up @@ -318,7 +320,7 @@ impl Chain {
Ok(Some(forked))
}

/// Returns the [`ContextuallyValidBlock`] with [`Hash`](zebra_chain::block::Hash) or
/// Returns the [`ContextuallyValidBlock`] with [`block::Hash`] or
/// [`Height`](zebra_chain::block::Height), if it exists in this chain.
pub fn block(&self, hash_or_height: HashOrHeight) -> Option<&ContextuallyValidBlock> {
let height =
Expand All @@ -327,6 +329,13 @@ impl Chain {
self.blocks.get(&height)
}

/// Returns the [`Transaction`] with [`transaction::Hash`], if it exists in this chain.
pub fn transaction(&self, hash: transaction::Hash) -> Option<&Arc<Transaction>> {
self.tx_by_hash
.get(&hash)
.map(|(height, index)| &self.blocks[height].block.transactions[*index])
}

/// Returns the block hash of the tip block.
pub fn non_finalized_tip_hash(&self) -> block::Hash {
self.blocks
Expand Down
31 changes: 29 additions & 2 deletions zebra-state/src/service/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@

use std::sync::Arc;

use zebra_chain::block::Block;
use zebra_chain::{
block::Block,
transaction::{self, Transaction},
};

use crate::{
service::{finalized_state::ZebraDb, non_finalized_state::Chain},
HashOrHeight,
};

/// Returns the [`Block`] with [`Hash`](zebra_chain::block::Hash) or
/// 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`.
pub(crate) fn block<C>(
Expand All @@ -38,3 +41,27 @@ where
.map(|contextual| contextual.block.clone())
.or_else(|| db.block(hash_or_height))
}

/// Returns the [`Transaction`] with [`transaction::Hash`],
/// if it exists in the non-finalized `chain` or finalized `db`.
pub(crate) fn transaction<C>(
chain: Option<C>,
db: &ZebraDb,
hash: transaction::Hash,
) -> Option<Arc<Transaction>>
where
C: AsRef<Chain>,
{
// # Correctness
//
// The StateService commits blocks to the finalized state before updating the latest chain,
// and it can commit additional blocks after we've cloned this `chain` variable.
//
// Since transactions are the same in the finalized and non-finalized state,
// we check the most efficient alternative first.
// (`chain` is always in memory, but `db` stores transactions on disk, with a memory cache.)
chain
.as_ref()
.and_then(|chain| chain.as_ref().transaction(hash).cloned())
.or_else(|| db.transaction(hash))
}