cache transaction outputs during block accept
This commit is contained in:
parent
0f6ff605d7
commit
d0b0249ca2
|
@ -42,7 +42,9 @@ pub use duplex_store::{DuplexTransactionOutputProvider, NoopStore};
|
|||
pub use error::Error;
|
||||
pub use store::{AsSubstore, Store, SharedStore, CanonStore};
|
||||
pub use transaction_meta::TransactionMeta;
|
||||
pub use transaction_provider::{TransactionProvider, TransactionOutputProvider, TransactionMetaProvider};
|
||||
pub use transaction_provider::{
|
||||
TransactionProvider, TransactionOutputProvider, TransactionMetaProvider, CachedTransactionOutputProvider,
|
||||
};
|
||||
pub use nullifier_tracker::NullifierTracker;
|
||||
pub use tree_state::{TreeState, H32 as H32TreeDim, Dim as TreeDim, SproutTreeState, SaplingTreeState};
|
||||
pub use tree_state_provider::TreeStateProvider;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use parking_lot::RwLock;
|
||||
use hash::H256;
|
||||
use bytes::Bytes;
|
||||
use chain::{IndexedTransaction, OutPoint, TransactionOutput};
|
||||
|
@ -32,3 +34,40 @@ pub trait TransactionMetaProvider: Send + Sync {
|
|||
/// Otherwise returns transaction meta object
|
||||
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
|
||||
}
|
||||
|
||||
/// Transaction output provider that caches all read outputs.
|
||||
///
|
||||
/// Not intended for long-lasting life, because it never clears its internal
|
||||
/// cache. The backing storage is considered readonly for the cache lifetime.
|
||||
pub struct CachedTransactionOutputProvider<'a> {
|
||||
backend: &'a TransactionOutputProvider,
|
||||
cached_outputs: RwLock<HashMap<OutPoint, Option<TransactionOutput>>>,
|
||||
}
|
||||
|
||||
impl<'a> CachedTransactionOutputProvider<'a> {
|
||||
/// Create new cached tx output provider backed by passed provider.
|
||||
pub fn new(backend: &'a TransactionOutputProvider) -> Self {
|
||||
CachedTransactionOutputProvider {
|
||||
backend,
|
||||
cached_outputs: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TransactionOutputProvider for CachedTransactionOutputProvider<'a> {
|
||||
fn transaction_output(&self, outpoint: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
||||
let cached_value = self.cached_outputs.read().get(outpoint).cloned();
|
||||
match cached_value {
|
||||
Some(cached_value) => cached_value,
|
||||
None => {
|
||||
let value_from_backend = self.backend.transaction_output(outpoint, transaction_index);
|
||||
self.cached_outputs.write().insert(outpoint.clone(), value_from_backend.clone());
|
||||
value_from_backend
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_spent(&self, outpoint: &OutPoint) -> bool {
|
||||
self.backend.is_spent(outpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use rayon::prelude::{IntoParallelRefIterator, IndexedParallelIterator, ParallelIterator};
|
||||
use storage::{DuplexTransactionOutputProvider, Store};
|
||||
use storage::{
|
||||
DuplexTransactionOutputProvider, TransactionOutputProvider, TransactionMetaProvider,
|
||||
BlockHeaderProvider, TreeStateProvider, NullifierTracker,
|
||||
};
|
||||
use network::ConsensusParams;
|
||||
use error::Error;
|
||||
use canon::CanonBlock;
|
||||
|
@ -16,29 +19,40 @@ pub struct ChainAcceptor<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ChainAcceptor<'a> {
|
||||
pub fn new(store: &'a Store, consensus: &'a ConsensusParams, verification_level: VerificationLevel, block: CanonBlock<'a>, height: u32, time: u32, deployments: &'a BlockDeployments) -> Self {
|
||||
pub fn new(
|
||||
tx_out_provider: &'a TransactionOutputProvider,
|
||||
tx_meta_provider: &'a TransactionMetaProvider,
|
||||
header_provider: &'a BlockHeaderProvider,
|
||||
tree_state_provider: &'a TreeStateProvider,
|
||||
nullifier_tracker: &'a NullifierTracker,
|
||||
consensus: &'a ConsensusParams,
|
||||
verification_level: VerificationLevel,
|
||||
block: CanonBlock<'a>,
|
||||
height: u32,
|
||||
time: u32,
|
||||
deployments: &'a BlockDeployments,
|
||||
) -> Self {
|
||||
trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
|
||||
let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
|
||||
let headers = store.as_block_header_provider();
|
||||
let output_store = DuplexTransactionOutputProvider::new(tx_out_provider, block.raw());
|
||||
|
||||
ChainAcceptor {
|
||||
block: BlockAcceptor::new(
|
||||
store.as_transaction_output_provider(),
|
||||
store.as_tree_state_provider(),
|
||||
tx_out_provider,
|
||||
tree_state_provider,
|
||||
consensus,
|
||||
block,
|
||||
height,
|
||||
deployments,
|
||||
headers,
|
||||
header_provider,
|
||||
),
|
||||
header: HeaderAcceptor::new(headers, consensus, block.header(), height, time, deployments),
|
||||
header: HeaderAcceptor::new(header_provider, consensus, block.header(), height, time, deployments),
|
||||
transactions: block.transactions()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(tx_index, tx)| TransactionAcceptor::new(
|
||||
store.as_transaction_meta_provider(),
|
||||
tx_meta_provider,
|
||||
output_store,
|
||||
store.as_nullifier_tracker(),
|
||||
nullifier_tracker,
|
||||
consensus,
|
||||
tx,
|
||||
verification_level,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use chain::{IndexedBlock, IndexedBlockHeader, IndexedTransaction};
|
||||
use storage::{SharedStore, TransactionOutputProvider, BlockHeaderProvider, BlockOrigin,
|
||||
DuplexTransactionOutputProvider, NoopStore};
|
||||
DuplexTransactionOutputProvider, NoopStore, CachedTransactionOutputProvider};
|
||||
use network::ConsensusParams;
|
||||
use error::{Error, TransactionError};
|
||||
use canon::{CanonBlock, CanonTransaction};
|
||||
|
@ -41,42 +41,91 @@ impl BackwardsCompatibleChainVerifier {
|
|||
|
||||
assert_eq!(Some(self.store.best_block().hash), self.store.block_hash(self.store.best_block().number));
|
||||
let block_origin = self.store.block_origin(&block.header)?;
|
||||
trace!(target: "verification", "verify_block: {:?} best_block: {:?} block_origin: {:?}", block.hash().reversed(), self.store.best_block(), block_origin);
|
||||
trace!(
|
||||
target: "verification",
|
||||
"verify_block: {:?} best_block: {:?} block_origin: {:?}",
|
||||
block.hash().reversed(),
|
||||
self.store.best_block(),
|
||||
block_origin,
|
||||
);
|
||||
|
||||
let canon_block = CanonBlock::new(block);
|
||||
match block_origin {
|
||||
BlockOrigin::KnownBlock => {
|
||||
// there should be no known blocks at this point
|
||||
unreachable!();
|
||||
},
|
||||
BlockOrigin::CanonChain { block_number } => {
|
||||
let tx_out_provider = CachedTransactionOutputProvider::new(self.store.as_store().as_transaction_output_provider());
|
||||
let tx_meta_provider = self.store.as_store().as_transaction_meta_provider();
|
||||
let header_provider = self.store.as_store().as_block_header_provider();
|
||||
let tree_state_provider = self.store.as_store().as_tree_state_provider();
|
||||
let nullifier_tracker = self.store.as_store().as_nullifier_tracker();
|
||||
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
||||
let canon_block = CanonBlock::new(block);
|
||||
let chain_acceptor = ChainAcceptor::new(self.store.as_store(), &self.consensus, verification_level,
|
||||
canon_block, block_number, block.header.raw.time, &deployments);
|
||||
let chain_acceptor = ChainAcceptor::new(
|
||||
&tx_out_provider,
|
||||
tx_meta_provider,
|
||||
header_provider,
|
||||
tree_state_provider,
|
||||
nullifier_tracker,
|
||||
&self.consensus,
|
||||
verification_level,
|
||||
canon_block,
|
||||
block_number,
|
||||
block.header.raw.time,
|
||||
&deployments,
|
||||
);
|
||||
chain_acceptor.check()?;
|
||||
},
|
||||
BlockOrigin::SideChain(origin) => {
|
||||
let block_number = origin.block_number;
|
||||
let header_provider = self.store.as_store().as_block_header_provider();
|
||||
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
||||
let fork = self.store.fork(origin)?;
|
||||
let canon_block = CanonBlock::new(block);
|
||||
let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_block,
|
||||
block_number, block.header.raw.time, &deployments);
|
||||
let tx_out_provider = CachedTransactionOutputProvider::new(fork.store().as_transaction_output_provider());
|
||||
let tx_meta_provider = fork.store().as_transaction_meta_provider();
|
||||
let header_provider = fork.store().as_block_header_provider();
|
||||
let tree_state_provider = fork.store().as_tree_state_provider();
|
||||
let nullifier_tracker = fork.store().as_nullifier_tracker();
|
||||
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
||||
let chain_acceptor = ChainAcceptor::new(
|
||||
&tx_out_provider,
|
||||
tx_meta_provider,
|
||||
header_provider,
|
||||
tree_state_provider,
|
||||
nullifier_tracker,
|
||||
&self.consensus,
|
||||
verification_level,
|
||||
canon_block,
|
||||
block_number,
|
||||
block.header.raw.time,
|
||||
&deployments,
|
||||
);
|
||||
chain_acceptor.check()?;
|
||||
},
|
||||
BlockOrigin::SideChainBecomingCanonChain(origin) => {
|
||||
let block_number = origin.block_number;
|
||||
let header_provider = self.store.as_store().as_block_header_provider();
|
||||
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
||||
let fork = self.store.fork(origin)?;
|
||||
let canon_block = CanonBlock::new(block);
|
||||
let chain_acceptor = ChainAcceptor::new(fork.store(), &self.consensus, verification_level, canon_block,
|
||||
block_number, block.header.raw.time, &deployments);
|
||||
let tx_out_provider = CachedTransactionOutputProvider::new(fork.store().as_transaction_output_provider());
|
||||
let tx_meta_provider = fork.store().as_transaction_meta_provider();
|
||||
let header_provider = fork.store().as_block_header_provider();
|
||||
let tree_state_provider = fork.store().as_tree_state_provider();
|
||||
let nullifier_tracker = fork.store().as_nullifier_tracker();
|
||||
let deployments = BlockDeployments::new(&self.deployments, block_number, header_provider, &self.consensus);
|
||||
let chain_acceptor = ChainAcceptor::new(
|
||||
&tx_out_provider,
|
||||
tx_meta_provider,
|
||||
header_provider,
|
||||
tree_state_provider,
|
||||
nullifier_tracker,
|
||||
&self.consensus,
|
||||
verification_level,
|
||||
canon_block,
|
||||
block_number,
|
||||
block.header.raw.time,
|
||||
&deployments,
|
||||
);
|
||||
chain_acceptor.check()?;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(Some(self.store.best_block().hash), self.store.block_hash(self.store.best_block().number));
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue