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},
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
time::{Duration, 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::{
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
request::HashOrHeight, BoxError, CommitBlockError, Config, FinalizedBlock, PreparedBlock,
|
2020-11-23 12:02:57 -08:00
|
|
|
Request, Response, Utxo, ValidateContextError,
|
2020-10-09 01:37:24 -07:00
|
|
|
};
|
2020-10-07 20:07:32 -07:00
|
|
|
|
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-23 12:54:27 -08:00
|
|
|
mod pending_utxos;
|
2020-11-13 10:19:47 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2020-11-23 12:54:27 -08:00
|
|
|
|
|
|
|
use self::{finalized_state::FinalizedState, pending_utxos::PendingUtxos};
|
2020-09-09 17:13:58 -07:00
|
|
|
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
pub type QueuedBlock = (
|
|
|
|
PreparedBlock,
|
|
|
|
oneshot::Sender<Result<block::Hash, BoxError>>,
|
|
|
|
);
|
|
|
|
pub type QueuedFinalized = (
|
|
|
|
FinalizedBlock,
|
|
|
|
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
|
2020-11-23 12:54:27 -08:00
|
|
|
pending_utxos: 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-11-23 12:54:27 -08:00
|
|
|
let pending_utxos = 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-11-21 10:41:53 -08:00
|
|
|
#[instrument(level = "debug", skip(self, prepared))]
|
2020-11-20 19:52:44 -08:00
|
|
|
fn queue_and_commit_non_finalized(
|
2020-10-26 13:54:19 -07:00
|
|
|
&mut self,
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
prepared: PreparedBlock,
|
2020-10-26 13:54:19 -07:00
|
|
|
) -> oneshot::Receiver<Result<block::Hash, BoxError>> {
|
2020-11-21 10:41:53 -08:00
|
|
|
tracing::debug!(block = %prepared.block, "queueing block for contextual verification");
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
let parent_hash = prepared.block.header.previous_block_hash;
|
2020-10-07 20:07:32 -07:00
|
|
|
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
if self.mem.any_chain_contains(&prepared.hash) || self.disk.hash(prepared.height).is_some()
|
|
|
|
{
|
2020-10-26 13:54:19 -07:00
|
|
|
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.
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
let rsp_rx = if let Some((_, old_rsp_tx)) = self.queued_blocks.get_mut(&prepared.hash) {
|
2020-11-20 19:58:07 -08:00
|
|
|
tracing::debug!("replacing older queued request with new request");
|
2020-10-26 13:54:19 -07:00
|
|
|
let (mut rsp_tx, rsp_rx) = oneshot::channel();
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
std::mem::swap(old_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();
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
self.queued_blocks.queue((prepared, rsp_tx));
|
2020-10-26 13:54:19 -07:00
|
|
|
rsp_rx
|
|
|
|
};
|
2020-10-07 20:07:32 -07:00
|
|
|
|
|
|
|
if !self.can_fork_chain_at(&parent_hash) {
|
2020-11-20 19:58:07 -08:00
|
|
|
tracing::trace!("unready to verify, returning early");
|
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 {
|
2020-11-20 19:58:07 -08:00
|
|
|
tracing::trace!("finalizing block past the reorg limit");
|
2020-10-07 20:07:32 -07:00
|
|
|
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
|
|
|
|
2020-11-20 19:58:07 -08:00
|
|
|
tracing::trace!("finished processing queued block");
|
2020-10-26 13:54:19 -07:00
|
|
|
rsp_rx
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
/// Run contextual validation on the prepared block and add it to the
|
|
|
|
/// non-finalized state if it is contextually valid.
|
|
|
|
fn validate_and_commit(&mut self, prepared: PreparedBlock) -> Result<(), CommitBlockError> {
|
|
|
|
self.check_contextual_validity(&prepared)?;
|
|
|
|
let parent_hash = prepared.block.header.previous_block_hash;
|
2020-10-07 20:07:32 -07:00
|
|
|
|
2020-11-17 15:26:21 -08:00
|
|
|
if self.disk.finalized_tip_hash() == parent_hash {
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
self.mem.commit_new_chain(prepared);
|
2020-10-07 20:07:32 -07:00
|
|
|
} else {
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
self.mem.commit_block(prepared);
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempt to validate and commit all queued blocks whose parents have
|
|
|
|
/// recently arrived starting from `new_parent`, in breadth-first ordering.
|
|
|
|
fn process_queued(&mut self, new_parent: block::Hash) {
|
|
|
|
let mut new_parents = vec![new_parent];
|
|
|
|
|
2020-11-20 19:54:57 -08:00
|
|
|
while let Some(parent_hash) = new_parents.pop() {
|
|
|
|
let queued_children = self.queued_blocks.dequeue_children(parent_hash);
|
2020-10-07 20:07:32 -07:00
|
|
|
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
for (child, rsp_tx) in queued_children {
|
2020-11-22 19:38:25 -08:00
|
|
|
let child_hash = child.hash;
|
2020-11-20 19:54:57 -08:00
|
|
|
tracing::trace!(?child_hash, "validating queued child");
|
2020-10-07 20:07:32 -07:00
|
|
|
let result = self
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
.validate_and_commit(child)
|
2020-11-20 19:54:57 -08:00
|
|
|
.map(|()| child_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);
|
2020-11-20 19:54:57 -08:00
|
|
|
new_parents.push(child_hash);
|
2020-10-07 20:07:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
/// Check that the prepared block is contextually valid for the configured
|
|
|
|
/// network, based on the committed finalized and non-finalized state.
|
|
|
|
fn check_contextual_validity(
|
|
|
|
&mut self,
|
|
|
|
prepared: &PreparedBlock,
|
|
|
|
) -> Result<(), ValidateContextError> {
|
2020-11-16 16:55:24 -08:00
|
|
|
check::block_is_contextually_valid(
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
prepared,
|
2020-11-16 16:55:24 -08:00
|
|
|
self.network,
|
2020-11-17 15:26:21 -08:00
|
|
|
self.disk.finalized_tip_height(),
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
self.chain(prepared.block.header.previous_block_hash),
|
2020-11-16 16:55:24 -08:00
|
|
|
)?;
|
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-23 12:02:57 -08:00
|
|
|
/// Return the [`Utxo`] pointed to by `outpoint` if it exists in any chain.
|
|
|
|
pub fn utxo(&self, outpoint: &transparent::OutPoint) -> Option<Utxo> {
|
2020-11-20 22:50:09 -08:00
|
|
|
self.mem
|
|
|
|
.utxo(outpoint)
|
|
|
|
.or_else(|| self.queued_blocks.utxo(outpoint))
|
2020-11-23 12:02:57 -08:00
|
|
|
.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 {
|
2020-11-25 23:26:10 -08:00
|
|
|
let tip = self.tip();
|
|
|
|
let old_len = self.pending_utxos.len();
|
|
|
|
|
2020-10-14 14:06:32 -07:00
|
|
|
self.pending_utxos.prune();
|
|
|
|
self.last_prune = now;
|
2020-11-25 23:26:10 -08:00
|
|
|
|
|
|
|
let new_len = self.pending_utxos.len();
|
|
|
|
let prune_count = old_len
|
|
|
|
.checked_sub(new_len)
|
|
|
|
.expect("prune does not add any utxo requests");
|
|
|
|
if prune_count > 0 {
|
|
|
|
tracing::info!(
|
|
|
|
?old_len,
|
|
|
|
?new_len,
|
|
|
|
?prune_count,
|
|
|
|
?tip,
|
|
|
|
"pruned utxo requests"
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
tracing::debug!(len = ?old_len, ?tip, "no utxo requests needed pruning");
|
|
|
|
}
|
2020-10-14 14:06:32 -07:00
|
|
|
}
|
|
|
|
|
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 {
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
Request::CommitBlock(prepared) => {
|
2020-11-20 13:27:57 -08:00
|
|
|
metrics::counter!("state.requests", 1, "type" => "commit_block");
|
|
|
|
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
self.pending_utxos.check_against(&prepared.new_outputs);
|
|
|
|
let rsp_rx = self.queue_and_commit_non_finalized(prepared);
|
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()
|
|
|
|
}
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
Request::CommitFinalizedBlock(finalized) => {
|
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-11-23 12:02:57 -08:00
|
|
|
self.pending_utxos.check_against(&finalized.new_outputs);
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
self.disk.queue_and_commit_finalized((finalized, 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) {
|
state: introduce PreparedBlock, FinalizedBlock
This change introduces two new types:
- `PreparedBlock`, representing a block which has undergone semantic
validation and has been prepared for contextual validation;
- `FinalizedBlock`, representing a block which is ready to be finalized
immediately;
and changes the `Request::CommitBlock`,`Request::CommitFinalizedBlock`
variants to use these types instead of their previous fields.
This change solves the problem of passing data between semantic
validation and contextual validation, and cleans up the state code by
allowing it to pass around a bundle of data. Previously, the state code
just passed around an `Arc<Block>`, which forced it to needlessly
recompute block hashes and other data, and was incompatible with the
already-known but not-yet-implemented data transfer requirements, namely
passing in the Sprout and Sapling anchors computed during contextual
validation.
This commit propagates the `PreparedBlock` and `FinalizedBlock` types
through the state code but only uses their data opportunistically, e.g.,
changing .hash() computations to use the precomputed hash. In the
future, these structures can be extended to pass data through the
verification pipeline for reuse as appropriate. For instance, these
changes allow the sprout and sapling anchors to be propagated through
the state.
2020-11-21 01:16:14 -08:00
|
|
|
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
|
|
|
}
|