2020-09-09 17:13:58 -07:00
|
|
|
use std::{
|
|
|
|
future::Future,
|
|
|
|
pin::Pin,
|
2020-09-09 23:07:47 -07:00
|
|
|
sync::Arc,
|
2020-09-09 17:13:58 -07:00
|
|
|
task::{Context, Poll},
|
2020-10-14 14:06:32 -07:00
|
|
|
time::Duration,
|
|
|
|
time::Instant,
|
2020-09-09 17:13:58 -07:00
|
|
|
};
|
2020-09-09 21:15:08 -07:00
|
|
|
|
2020-10-24 17:09:50 -07:00
|
|
|
use futures::future::FutureExt;
|
2020-11-16 16:05:35 -08:00
|
|
|
use non_finalized_state::{NonFinalizedState, QueuedBlocks};
|
2020-10-26 13:54:19 -07:00
|
|
|
use tokio::sync::oneshot;
|
2020-10-21 21:56:18 -07:00
|
|
|
use tower::{util::BoxService, Service};
|
2020-10-07 20:07:32 -07:00
|
|
|
use tracing::instrument;
|
2020-09-09 23:07:47 -07:00
|
|
|
use zebra_chain::{
|
|
|
|
block::{self, Block},
|
|
|
|
parameters::Network,
|
2020-11-01 10:49:34 -08:00
|
|
|
transaction,
|
|
|
|
transaction::Transaction,
|
|
|
|
transparent,
|
2020-09-09 23:07:47 -07:00
|
|
|
};
|
2020-09-09 17:13:58 -07:00
|
|
|
|
2020-10-09 01:37:24 -07:00
|
|
|
use crate::{
|
2020-11-16 16:05:35 -08:00
|
|
|
request::HashOrHeight, BoxError, CommitBlockError, Config, Request, Response,
|
2020-11-01 10:49:34 -08:00
|
|
|
ValidateContextError,
|
2020-10-09 01:37:24 -07:00
|
|
|
};
|
2020-10-07 20:07:32 -07:00
|
|
|
|
2020-11-16 16:05:35 -08:00
|
|
|
use self::finalized_state::FinalizedState;
|
|
|
|
|
2020-11-12 20:26:16 -08:00
|
|
|
mod check;
|
2020-11-16 16:05:35 -08:00
|
|
|
mod finalized_state;
|
|
|
|
mod non_finalized_state;
|
2020-11-13 10:19:47 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2020-10-14 14:06:32 -07:00
|
|
|
mod utxo;
|
2020-09-09 17:13:58 -07:00
|
|
|
|
2020-09-09 23:07:47 -07:00
|
|
|
// todo: put this somewhere
|
2020-09-24 15:46:04 -07:00
|
|
|
#[derive(Debug)]
|
2020-09-09 23:07:47 -07:00
|
|
|
pub struct QueuedBlock {
|
|
|
|
pub block: Arc<Block>,
|
|
|
|
// TODO: add these parameters when we can compute anchors.
|
|
|
|
// sprout_anchor: sprout::tree::Root,
|
|
|
|
// sapling_anchor: sapling::tree::Root,
|
2020-10-26 13:54:19 -07:00
|
|
|
pub rsp_tx: oneshot::Sender<Result<block::Hash, BoxError>>,
|
2020-09-09 23:07:47 -07:00
|
|
|
}
|
|
|
|
|
2020-09-09 17:13:58 -07:00
|
|
|
struct StateService {
|
|
|
|
/// Holds data relating to finalized chain state.
|
2020-11-17 15:26:21 -08:00
|
|
|
disk: FinalizedState,
|
2020-09-09 17:13:58 -07:00
|
|
|
/// Holds data relating to non-finalized chain state.
|
2020-10-07 20:07:32 -07:00
|
|
|
mem: NonFinalizedState,
|
|
|
|
/// Blocks awaiting their parent blocks for contextual verification.
|
|
|
|
queued_blocks: QueuedBlocks,
|
2020-10-14 14:06:32 -07:00
|
|
|
/// The set of outpoints with pending requests for their associated transparent::Output
|
|
|
|
pending_utxos: utxo::PendingUtxos,
|
2020-11-15 21:46:16 -08:00
|
|
|
/// The configured Zcash network
|
|
|
|
network: Network,
|
2020-10-14 14:06:32 -07:00
|
|
|
/// Instant tracking the last time `pending_utxos` was pruned
|
|
|
|
last_prune: Instant,
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
2020-09-09 17:13:58 -07:00
|
|
|
impl StateService {
|
2020-10-14 14:06:32 -07:00
|
|
|
const PRUNE_INTERVAL: Duration = Duration::from_secs(30);
|
|
|
|
|
2020-09-09 17:13:58 -07:00
|
|
|
pub fn new(config: Config, network: Network) -> Self {
|
2020-11-17 15:26:21 -08:00
|
|
|
let disk = FinalizedState::new(&config, network);
|
2020-10-07 20:07:32 -07:00
|
|
|
let mem = NonFinalizedState::default();
|
|
|
|
let queued_blocks = QueuedBlocks::default();
|
2020-10-14 14:06:32 -07:00
|
|
|
let pending_utxos = utxo::PendingUtxos::default();
|
2020-10-07 20:07:32 -07:00
|
|
|
|
|
|
|
Self {
|
2020-11-17 15:26:21 -08:00
|
|
|
disk,
|
2020-10-07 20:07:32 -07:00
|
|
|
mem,
|
|
|
|
queued_blocks,
|
2020-10-14 14:06:32 -07:00
|
|
|
pending_utxos,
|
2020-11-15 21:46:16 -08:00
|
|
|
network,
|
2020-10-14 14:06:32 -07:00
|
|
|
last_prune: Instant::now(),
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Queue a non finalized block for verification and check if any queued
|
|
|
|
/// blocks are ready to be verified and committed to the state.
|
|
|
|
///
|
|
|
|
/// This function encodes the logic for [committing non-finalized blocks][1]
|
|
|
|
/// in RFC0005.
|
|
|
|
///
|
|
|
|
/// [1]: https://zebra.zfnd.org/dev/rfcs/0005-state-updates.html#committing-non-finalized-blocks
|
2020-10-26 13:54:19 -07:00
|
|
|
#[instrument(skip(self, block))]
|
2020-11-20 19:52:44 -08:00
|
|
|
fn queue_and_commit_non_finalized(
|
2020-10-26 13:54:19 -07:00
|
|
|
&mut self,
|
|
|
|
block: Arc<Block>,
|
|
|
|
) -> oneshot::Receiver<Result<block::Hash, BoxError>> {
|
|
|
|
let hash = block.hash();
|
|
|
|
let parent_hash = block.header.previous_block_hash;
|
2020-10-07 20:07:32 -07:00
|
|
|
|
2020-10-26 13:54:19 -07:00
|
|
|
if self.contains_committed_block(&block) {
|
|
|
|
let (rsp_tx, rsp_rx) = oneshot::channel();
|
2020-11-12 11:43:17 -08:00
|
|
|
let _ = rsp_tx.send(Err("block is already committed to the state".into()));
|
2020-10-26 13:54:19 -07:00
|
|
|
return rsp_rx;
|
|
|
|
}
|
|
|
|
|
2020-11-12 11:43:17 -08:00
|
|
|
// Request::CommitBlock contract: a request to commit a block which has
|
|
|
|
// been queued but not yet committed to the state fails the older
|
|
|
|
// request and replaces it with the newer request.
|
2020-10-26 13:54:19 -07:00
|
|
|
let rsp_rx = if let Some(queued_block) = self.queued_blocks.get_mut(&hash) {
|
|
|
|
let (mut rsp_tx, rsp_rx) = oneshot::channel();
|
|
|
|
std::mem::swap(&mut queued_block.rsp_tx, &mut rsp_tx);
|
2020-11-12 11:43:17 -08:00
|
|
|
let _ = rsp_tx.send(Err("replaced by newer request".into()));
|
2020-10-26 13:54:19 -07:00
|
|
|
rsp_rx
|
|
|
|
} else {
|
|
|
|
let (rsp_tx, rsp_rx) = oneshot::channel();
|
|
|
|
self.queued_blocks.queue(QueuedBlock { block, rsp_tx });
|
|
|
|
rsp_rx
|
|
|
|
};
|
2020-10-07 20:07:32 -07:00
|
|
|
|
|
|
|
if !self.can_fork_chain_at(&parent_hash) {
|
2020-10-26 13:54:19 -07:00
|
|
|
return rsp_rx;
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
self.process_queued(parent_hash);
|
|
|
|
|
|
|
|
while self.mem.best_chain_len() > crate::constants::MAX_BLOCK_REORG_HEIGHT {
|
|
|
|
let finalized = self.mem.finalize();
|
2020-11-17 15:26:21 -08:00
|
|
|
self.disk
|
2020-10-07 20:07:32 -07:00
|
|
|
.commit_finalized_direct(finalized)
|
2020-11-17 15:26:21 -08:00
|
|
|
.expect("expected that disk errors would not occur");
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
self.queued_blocks
|
2020-11-17 15:26:21 -08:00
|
|
|
.prune_by_height(self.disk.finalized_tip_height().expect(
|
2020-10-07 20:07:32 -07:00
|
|
|
"Finalized state must have at least one block before committing non-finalized state",
|
|
|
|
));
|
2020-10-26 13:54:19 -07:00
|
|
|
|
|
|
|
rsp_rx
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Run contextual validation on `block` and add it to the non-finalized
|
|
|
|
/// state if it is contextually valid.
|
2020-10-09 01:37:24 -07:00
|
|
|
fn validate_and_commit(&mut self, block: Arc<Block>) -> Result<(), CommitBlockError> {
|
2020-10-07 20:07:32 -07:00
|
|
|
self.check_contextual_validity(&block)?;
|
|
|
|
let parent_hash = block.header.previous_block_hash;
|
|
|
|
|
2020-11-17 15:26:21 -08:00
|
|
|
if self.disk.finalized_tip_hash() == parent_hash {
|
2020-10-07 20:07:32 -07:00
|
|
|
self.mem.commit_new_chain(block);
|
|
|
|
} else {
|
|
|
|
self.mem.commit_block(block);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if `hash` is a valid previous block hash for new non-finalized blocks.
|
|
|
|
fn can_fork_chain_at(&self, hash: &block::Hash) -> bool {
|
2020-11-17 15:26:21 -08:00
|
|
|
self.mem.any_chain_contains(hash) || &self.disk.finalized_tip_hash() == hash
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
2020-10-26 13:54:19 -07:00
|
|
|
/// Returns true if the given hash has been committed to either the finalized
|
|
|
|
/// or non-finalized state.
|
|
|
|
fn contains_committed_block(&self, block: &Block) -> bool {
|
|
|
|
let hash = block.hash();
|
|
|
|
let height = block
|
|
|
|
.coinbase_height()
|
|
|
|
.expect("coinbase heights should be valid");
|
|
|
|
|
2020-11-17 15:26:21 -08:00
|
|
|
self.mem.any_chain_contains(&hash) || self.disk.hash(height) == Some(hash)
|
2020-10-26 13:54:19 -07:00
|
|
|
}
|
|
|
|
|
2020-10-07 20:07:32 -07:00
|
|
|
/// Attempt to validate and commit all queued blocks whose parents have
|
|
|
|
/// recently arrived starting from `new_parent`, in breadth-first ordering.
|
|
|
|
#[instrument(skip(self))]
|
|
|
|
fn process_queued(&mut self, new_parent: block::Hash) {
|
|
|
|
let mut new_parents = vec![new_parent];
|
|
|
|
|
|
|
|
while let Some(parent) = new_parents.pop() {
|
|
|
|
let queued_children = self.queued_blocks.dequeue_children(parent);
|
|
|
|
|
|
|
|
for QueuedBlock { block, rsp_tx } in queued_children {
|
|
|
|
let hash = block.hash();
|
|
|
|
let result = self
|
|
|
|
.validate_and_commit(block)
|
|
|
|
.map(|()| hash)
|
2020-10-26 13:54:19 -07:00
|
|
|
.map_err(BoxError::from);
|
2020-10-07 20:07:32 -07:00
|
|
|
let _ = rsp_tx.send(result);
|
|
|
|
new_parents.push(hash);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-16 16:55:24 -08:00
|
|
|
/// Check that `block` is contextually valid for the configured network,
|
|
|
|
/// based on the committed finalized and non-finalized state.
|
2020-10-07 20:07:32 -07:00
|
|
|
fn check_contextual_validity(&mut self, block: &Block) -> Result<(), ValidateContextError> {
|
2020-11-16 16:55:24 -08:00
|
|
|
check::block_is_contextually_valid(
|
|
|
|
block,
|
|
|
|
self.network,
|
2020-11-17 15:26:21 -08:00
|
|
|
self.disk.finalized_tip_height(),
|
2020-11-16 16:55:24 -08:00
|
|
|
self.chain(block.header.previous_block_hash),
|
|
|
|
)?;
|
2020-11-15 21:46:16 -08:00
|
|
|
|
2020-10-07 20:07:32 -07:00
|
|
|
Ok(())
|
2020-09-09 17:13:58 -07:00
|
|
|
}
|
2020-11-01 10:49:34 -08:00
|
|
|
|
|
|
|
/// Create a block locator for the current best chain.
|
|
|
|
fn block_locator(&self) -> Option<Vec<block::Hash>> {
|
|
|
|
let tip_height = self.tip()?.0;
|
|
|
|
|
|
|
|
let heights = crate::util::block_locator_heights(tip_height);
|
|
|
|
let mut hashes = Vec::with_capacity(heights.len());
|
|
|
|
|
|
|
|
for height in heights {
|
|
|
|
if let Some(hash) = self.hash(height) {
|
|
|
|
hashes.push(hash);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(hashes)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the tip of the current best chain.
|
|
|
|
pub fn tip(&self) -> Option<(block::Height, block::Hash)> {
|
2020-11-17 15:26:21 -08:00
|
|
|
self.mem.tip().or_else(|| self.disk.tip())
|
2020-11-01 10:49:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the depth of block `hash` in the current best chain.
|
|
|
|
pub fn depth(&self, hash: block::Hash) -> Option<u32> {
|
|
|
|
let tip = self.tip()?.0;
|
2020-11-17 15:26:21 -08:00
|
|
|
let height = self.mem.height(hash).or_else(|| self.disk.height(hash))?;
|
2020-11-01 10:49:34 -08:00
|
|
|
|
|
|
|
Some(tip.0 - height.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the block identified by either its `height` or `hash` if it exists
|
|
|
|
/// in the current best chain.
|
|
|
|
pub fn block(&self, hash_or_height: HashOrHeight) -> Option<Arc<Block>> {
|
|
|
|
self.mem
|
|
|
|
.block(hash_or_height)
|
2020-11-17 15:26:21 -08:00
|
|
|
.or_else(|| self.disk.block(hash_or_height))
|
2020-11-01 10:49:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the transaction identified by `hash` if it exists in the current
|
|
|
|
/// best chain.
|
|
|
|
pub fn transaction(&self, hash: transaction::Hash) -> Option<Arc<Transaction>> {
|
|
|
|
self.mem
|
|
|
|
.transaction(hash)
|
2020-11-17 15:26:21 -08:00
|
|
|
.or_else(|| self.disk.transaction(hash))
|
2020-11-01 10:49:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the hash for the block at `height` in the current best chain.
|
|
|
|
pub fn hash(&self, height: block::Height) -> Option<block::Hash> {
|
2020-11-17 15:26:21 -08:00
|
|
|
self.mem.hash(height).or_else(|| self.disk.hash(height))
|
2020-11-01 10:49:34 -08:00
|
|
|
}
|
|
|
|
|
2020-11-15 18:22:53 -08:00
|
|
|
/// Return the height for the block at `hash` in any chain.
|
|
|
|
pub fn height_by_hash(&self, hash: block::Hash) -> Option<block::Height> {
|
|
|
|
self.mem
|
|
|
|
.height_by_hash(hash)
|
2020-11-17 15:26:21 -08:00
|
|
|
.or_else(|| self.disk.height(hash))
|
2020-11-15 18:22:53 -08:00
|
|
|
}
|
|
|
|
|
2020-11-01 10:49:34 -08:00
|
|
|
/// Return the utxo pointed to by `outpoint` if it exists in any chain.
|
|
|
|
pub fn utxo(&self, outpoint: &transparent::OutPoint) -> Option<transparent::Output> {
|
2020-11-17 15:26:21 -08:00
|
|
|
self.mem.utxo(outpoint).or_else(|| self.disk.utxo(outpoint))
|
2020-11-01 10:49:34 -08:00
|
|
|
}
|
2020-11-15 18:22:53 -08:00
|
|
|
|
|
|
|
/// Return an iterator over the relevant chain of the block identified by
|
|
|
|
/// `hash`.
|
|
|
|
///
|
|
|
|
/// The block identified by `hash` is included in the chain of blocks yielded
|
|
|
|
/// by the iterator.
|
|
|
|
pub fn chain(&self, hash: block::Hash) -> Iter<'_> {
|
|
|
|
Iter {
|
|
|
|
service: self,
|
|
|
|
state: IterState::NonFinalized(hash),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Iter<'a> {
|
|
|
|
service: &'a StateService,
|
|
|
|
state: IterState,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum IterState {
|
|
|
|
NonFinalized(block::Hash),
|
|
|
|
Finalized(block::Height),
|
|
|
|
Finished,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iter<'_> {
|
|
|
|
fn next_non_finalized_block(&mut self) -> Option<Arc<Block>> {
|
|
|
|
let Iter { service, state } = self;
|
|
|
|
|
|
|
|
let hash = match state {
|
|
|
|
IterState::NonFinalized(hash) => *hash,
|
|
|
|
IterState::Finalized(_) | IterState::Finished => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(block) = service.mem.block_by_hash(hash) {
|
|
|
|
let hash = block.header.previous_block_hash;
|
|
|
|
self.state = IterState::NonFinalized(hash);
|
|
|
|
Some(block)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn next_finalized_block(&mut self) -> Option<Arc<Block>> {
|
|
|
|
let Iter { service, state } = self;
|
|
|
|
|
|
|
|
let hash_or_height: HashOrHeight = match *state {
|
|
|
|
IterState::Finalized(height) => height.into(),
|
|
|
|
IterState::NonFinalized(hash) => hash.into(),
|
|
|
|
IterState::Finished => unreachable!(),
|
|
|
|
};
|
|
|
|
|
2020-11-17 15:26:21 -08:00
|
|
|
if let Some(block) = service.disk.block(hash_or_height) {
|
2020-11-15 18:22:53 -08:00
|
|
|
let height = block
|
|
|
|
.coinbase_height()
|
|
|
|
.expect("valid blocks have a coinbase height");
|
|
|
|
|
|
|
|
if let Some(next_height) = height - 1 {
|
|
|
|
self.state = IterState::Finalized(next_height);
|
|
|
|
} else {
|
|
|
|
self.state = IterState::Finished;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(block)
|
|
|
|
} else {
|
|
|
|
self.state = IterState::Finished;
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for Iter<'_> {
|
|
|
|
type Item = Arc<Block>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
match self.state {
|
|
|
|
IterState::NonFinalized(_) => self
|
|
|
|
.next_non_finalized_block()
|
|
|
|
.or_else(|| self.next_finalized_block()),
|
|
|
|
IterState::Finalized(_) => self.next_finalized_block(),
|
|
|
|
IterState::Finished => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
let len = self.len();
|
|
|
|
(len, Some(len))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::iter::FusedIterator for Iter<'_> {}
|
|
|
|
|
|
|
|
impl ExactSizeIterator for Iter<'_> {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
match self.state {
|
|
|
|
IterState::NonFinalized(hash) => self
|
|
|
|
.service
|
|
|
|
.height_by_hash(hash)
|
|
|
|
.map(|height| (height.0 + 1) as _)
|
|
|
|
.unwrap_or(0),
|
|
|
|
IterState::Finalized(height) => (height.0 + 1) as _,
|
|
|
|
IterState::Finished => 0,
|
|
|
|
}
|
|
|
|
}
|
2020-09-09 17:13:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Service<Request> for StateService {
|
|
|
|
type Response = Response;
|
|
|
|
type Error = BoxError;
|
|
|
|
type Future =
|
|
|
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
|
|
|
|
|
|
|
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
2020-10-14 14:06:32 -07:00
|
|
|
let now = Instant::now();
|
|
|
|
|
|
|
|
if self.last_prune + Self::PRUNE_INTERVAL < now {
|
|
|
|
self.pending_utxos.prune();
|
|
|
|
self.last_prune = now;
|
|
|
|
}
|
|
|
|
|
2020-09-09 17:13:58 -07:00
|
|
|
Poll::Ready(Ok(()))
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:12:30 -08:00
|
|
|
#[instrument(name = "state", skip(self, req))]
|
2020-09-09 17:13:58 -07:00
|
|
|
fn call(&mut self, req: Request) -> Self::Future {
|
|
|
|
match req {
|
2020-10-07 20:07:32 -07:00
|
|
|
Request::CommitBlock { block } => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "commit_block");
|
|
|
|
|
2020-10-14 14:06:32 -07:00
|
|
|
self.pending_utxos.check_block(&block);
|
2020-11-20 19:52:44 -08:00
|
|
|
let rsp_rx = self.queue_and_commit_non_finalized(block);
|
2020-10-07 20:07:32 -07:00
|
|
|
|
|
|
|
async move {
|
|
|
|
rsp_rx
|
|
|
|
.await
|
2020-10-09 01:37:24 -07:00
|
|
|
.expect("sender is not dropped")
|
2020-10-07 20:07:32 -07:00
|
|
|
.map(Response::Committed)
|
2020-10-09 01:37:24 -07:00
|
|
|
.map_err(Into::into)
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
2020-09-09 21:15:08 -07:00
|
|
|
Request::CommitFinalizedBlock { block } => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "commit_finalized_block");
|
|
|
|
|
2020-10-26 13:54:19 -07:00
|
|
|
let (rsp_tx, rsp_rx) = oneshot::channel();
|
2020-09-09 23:07:47 -07:00
|
|
|
|
2020-10-14 14:06:32 -07:00
|
|
|
self.pending_utxos.check_block(&block);
|
2020-11-17 15:26:21 -08:00
|
|
|
self.disk
|
2020-11-20 19:52:44 -08:00
|
|
|
.queue_and_commit_finalized(QueuedBlock { block, rsp_tx });
|
2020-09-09 21:15:08 -07:00
|
|
|
|
2020-09-09 23:07:47 -07:00
|
|
|
async move {
|
|
|
|
rsp_rx
|
|
|
|
.await
|
2020-10-09 01:37:24 -07:00
|
|
|
.expect("sender is not dropped")
|
2020-09-10 10:52:51 -07:00
|
|
|
.map(Response::Committed)
|
2020-10-09 01:37:24 -07:00
|
|
|
.map_err(Into::into)
|
2020-09-09 23:07:47 -07:00
|
|
|
}
|
|
|
|
.boxed()
|
2020-09-09 21:15:08 -07:00
|
|
|
}
|
|
|
|
Request::Depth(hash) => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "depth");
|
2020-11-01 10:49:34 -08:00
|
|
|
let rsp = Ok(self.depth(hash)).map(Response::Depth);
|
2020-10-24 17:09:50 -07:00
|
|
|
async move { rsp }.boxed()
|
2020-09-09 21:15:08 -07:00
|
|
|
}
|
|
|
|
Request::Tip => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "tip");
|
2020-11-01 10:49:34 -08:00
|
|
|
let rsp = Ok(self.tip()).map(Response::Tip);
|
2020-10-24 17:09:50 -07:00
|
|
|
async move { rsp }.boxed()
|
2020-09-09 21:15:08 -07:00
|
|
|
}
|
|
|
|
Request::BlockLocator => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "block_locator");
|
2020-11-01 10:49:34 -08:00
|
|
|
let rsp = Ok(self.block_locator().unwrap_or_default()).map(Response::BlockLocator);
|
|
|
|
async move { rsp }.boxed()
|
|
|
|
}
|
|
|
|
Request::Transaction(hash) => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "transaction");
|
2020-11-01 10:49:34 -08:00
|
|
|
let rsp = Ok(self.transaction(hash)).map(Response::Transaction);
|
2020-10-24 17:09:50 -07:00
|
|
|
async move { rsp }.boxed()
|
2020-09-09 21:15:08 -07:00
|
|
|
}
|
2020-09-10 10:19:45 -07:00
|
|
|
Request::Block(hash_or_height) => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "block");
|
2020-11-01 10:49:34 -08:00
|
|
|
let rsp = Ok(self.block(hash_or_height)).map(Response::Block);
|
2020-10-24 17:09:50 -07:00
|
|
|
async move { rsp }.boxed()
|
2020-09-10 10:19:45 -07:00
|
|
|
}
|
2020-10-14 14:06:32 -07:00
|
|
|
Request::AwaitUtxo(outpoint) => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "await_utxo");
|
|
|
|
|
2020-10-14 14:06:32 -07:00
|
|
|
let fut = self.pending_utxos.queue(outpoint);
|
|
|
|
|
2020-11-01 10:49:34 -08:00
|
|
|
if let Some(utxo) = self.utxo(&outpoint) {
|
|
|
|
self.pending_utxos.respond(outpoint, utxo);
|
2020-10-14 14:06:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fut.boxed()
|
|
|
|
}
|
2020-09-09 17:13:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-09 17:51:08 -07:00
|
|
|
/// Initialize a state service from the provided [`Config`].
|
2020-09-09 17:13:58 -07:00
|
|
|
///
|
2020-11-17 15:26:21 -08:00
|
|
|
/// Each `network` has its own separate on-disk database.
|
2020-09-09 17:13:58 -07:00
|
|
|
///
|
2020-10-21 21:56:18 -07:00
|
|
|
/// To share access to the state, wrap the returned service in a `Buffer`. It's
|
|
|
|
/// possible to construct multiple state services in the same application (as
|
|
|
|
/// long as they, e.g., use different storage locations), but doing so is
|
|
|
|
/// probably not what you want.
|
|
|
|
pub fn init(config: Config, network: Network) -> BoxService<Request, Response, BoxError> {
|
|
|
|
BoxService::new(StateService::new(config, network))
|
2020-09-09 17:13:58 -07:00
|
|
|
}
|