consolidated TransactionOutputProvider and TransactionOutputObserver

This commit is contained in:
debris 2017-04-07 12:54:40 +07:00
parent 2074076c41
commit 21fdfabb0e
15 changed files with 135 additions and 180 deletions

View File

@ -19,8 +19,8 @@ use kv::{
use best_block::BestBlock;
use {
BlockRef, Error, BlockHeaderProvider, BlockProvider, BlockOrigin, TransactionMeta, IndexedBlockProvider,
TransactionMetaProvider, TransactionProvider, PreviousTransactionOutputProvider, BlockChain, Store,
SideChainOrigin, ForkChain, TransactionOutputObserver, Forkable, CanonStore
TransactionMetaProvider, TransactionProvider, TransactionOutputProvider, BlockChain, Store,
SideChainOrigin, ForkChain, Forkable, CanonStore
};
const COL_COUNT: u32 = 10;
@ -463,17 +463,15 @@ impl<T> TransactionProvider for BlockChainDatabase<T> where T: KeyValueDatabase
}
}
impl<T> PreviousTransactionOutputProvider for BlockChainDatabase<T> where T: KeyValueDatabase {
fn previous_transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
impl<T> TransactionOutputProvider for BlockChainDatabase<T> where T: KeyValueDatabase {
fn transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
// return previous transaction outputs only for canon chain transactions
self.transaction_meta(&prevout.hash)
.and_then(|_| self.transaction(&prevout.hash))
.and_then(|tx| tx.outputs.into_iter().nth(prevout.index as usize))
}
}
impl<T> TransactionOutputObserver for BlockChainDatabase<T> where T: KeyValueDatabase {
fn is_spent(&self, prevout: &OutPoint) -> bool {
fn is_double_spent(&self, prevout: &OutPoint) -> bool {
self.transaction_meta(&prevout.hash)
.and_then(|meta| meta.is_spent(prevout.index as usize))
.unwrap_or(false)

View File

@ -1,6 +1,6 @@
use std::cmp;
use chain::{OutPoint, TransactionOutput, IndexedBlock, IndexedTransaction};
use transaction_provider::PreviousTransactionOutputProvider;
use transaction_meta_provider::TransactionOutputObserver;
use {TransactionOutputProvider};
fn transaction_output(transactions: &[IndexedTransaction], prevout: &OutPoint) -> Option<TransactionOutput> {
transactions.iter()
@ -9,7 +9,7 @@ fn transaction_output(transactions: &[IndexedTransaction], prevout: &OutPoint) -
.cloned()
}
fn is_spent(transactions: &[IndexedTransaction], prevout: &OutPoint) -> bool {
fn is_double_spent(transactions: &[IndexedTransaction], prevout: &OutPoint) -> bool {
// the code below is valid, but has rather poor performance
// if previous transaction output appears more than once than we can safely
@ -23,14 +23,13 @@ fn is_spent(transactions: &[IndexedTransaction], prevout: &OutPoint) -> bool {
spends == 2
}
impl PreviousTransactionOutputProvider for IndexedBlock {
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
transaction_output(&self.transactions[..transaction_index], prevout)
impl TransactionOutputProvider for IndexedBlock {
fn transaction_output(&self, outpoint: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
let take = cmp::min(transaction_index, self.transactions.len());
transaction_output(&self.transactions[..take], outpoint)
}
}
impl TransactionOutputObserver for IndexedBlock {
fn is_spent(&self, prevout: &OutPoint) -> bool {
is_spent(&self.transactions, prevout)
fn is_double_spent(&self, outpoint: &OutPoint) -> bool {
is_double_spent(&self.transactions, outpoint)
}
}

View File

@ -17,10 +17,9 @@ mod block_ref;
mod block_chain;
mod block_chain_db;
mod error;
mod impls;
mod block_impls;
mod store;
mod transaction_meta;
mod transaction_meta_provider;
mod transaction_provider;
pub use primitives::{hash, bytes};
@ -34,6 +33,5 @@ pub use block_chain_db::{BlockChainDatabase, ForkChainDatabase};
pub use error::Error;
pub use store::{AsSubstore, Store, SharedStore, CanonStore};
pub use transaction_meta::TransactionMeta;
pub use transaction_meta_provider::{TransactionMetaProvider, TransactionOutputObserver};
pub use transaction_provider::{TransactionProvider, PreviousTransactionOutputProvider};
pub use transaction_provider::{TransactionProvider, TransactionOutputProvider, TransactionMetaProvider};

View File

@ -2,8 +2,7 @@ use std::sync::Arc;
use chain::BlockHeader;
use {
BestBlock, BlockProvider, BlockHeaderProvider, TransactionProvider, TransactionMetaProvider,
PreviousTransactionOutputProvider, BlockChain, IndexedBlockProvider, TransactionOutputObserver,
Forkable
TransactionOutputProvider, BlockChain, IndexedBlockProvider, Forkable
};
pub trait CanonStore: Store + Forkable {
@ -23,21 +22,19 @@ pub trait Store: AsSubstore {
}
/// Allows casting Arc<Store> to reference to any substore type
pub trait AsSubstore: BlockChain + IndexedBlockProvider + TransactionProvider + TransactionMetaProvider + PreviousTransactionOutputProvider + TransactionOutputObserver {
pub trait AsSubstore: BlockChain + IndexedBlockProvider + TransactionProvider + TransactionMetaProvider + TransactionOutputProvider {
fn as_block_provider(&self) -> &BlockProvider;
fn as_block_header_provider(&self) -> &BlockHeaderProvider;
fn as_transaction_provider(&self) -> &TransactionProvider;
fn as_previous_transaction_output_provider(&self) -> &PreviousTransactionOutputProvider;
fn as_transaction_output_provider(&self) -> &TransactionOutputProvider;
fn as_transaction_meta_provider(&self) -> &TransactionMetaProvider;
fn as_transaction_output_observer(&self) -> &TransactionOutputObserver;
}
impl<T> AsSubstore for T where T: BlockChain + IndexedBlockProvider + TransactionProvider + TransactionMetaProvider + PreviousTransactionOutputProvider + TransactionOutputObserver {
impl<T> AsSubstore for T where T: BlockChain + IndexedBlockProvider + TransactionProvider + TransactionMetaProvider + TransactionOutputProvider {
fn as_block_provider(&self) -> &BlockProvider {
&*self
}
@ -50,17 +47,13 @@ impl<T> AsSubstore for T where T: BlockChain + IndexedBlockProvider + Transactio
&*self
}
fn as_previous_transaction_output_provider(&self) -> &PreviousTransactionOutputProvider {
fn as_transaction_output_provider(&self) -> &TransactionOutputProvider {
&*self
}
fn as_transaction_meta_provider(&self) -> &TransactionMetaProvider {
&*self
}
fn as_transaction_output_observer(&self) -> &TransactionOutputObserver {
&*self
}
}
pub type SharedStore = Arc<CanonStore + Send + Sync>;

View File

@ -1,16 +0,0 @@
use primitives::hash::H256;
use chain::OutPoint;
use transaction_meta::TransactionMeta;
/// Transaction output observers track if output has been spent
pub trait TransactionOutputObserver: Send + Sync {
/// Returns true if we know that output has been spent
fn is_spent(&self, prevout: &OutPoint) -> bool;
}
/// Transaction meta provider stores transaction meta information
pub trait TransactionMetaProvider: Send + Sync {
/// Returns None if transactin with given hash does not exist
/// Otherwise returns transaction meta object
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
}

View File

@ -1,23 +1,34 @@
use hash::H256;
use bytes::Bytes;
use chain::{Transaction, OutPoint, TransactionOutput};
use {TransactionMeta};
/// Should be used to obtain all transactions from canon chain and forks.
pub trait TransactionProvider {
/// returns true if store contains given transaction
/// Returns true if store contains given transaction.
fn contains_transaction(&self, hash: &H256) -> bool {
self.transaction(hash).is_some()
}
/// resolves transaction body bytes by transaction hash
/// Resolves transaction body bytes by transaction hash.
fn transaction_bytes(&self, hash: &H256) -> Option<Bytes>;
/// resolves serialized transaction info by transaction hash
/// Resolves serialized transaction info by transaction hash.
fn transaction(&self, hash: &H256) -> Option<Transaction>;
}
/// During transaction verifiction the only part of old transaction that we need is `TransactionOutput`.
/// Structures like `IndexedBlock` or `MemoryPool` already have it in memory, so it would be
/// a shame to clone the whole transaction just to get single output.
pub trait PreviousTransactionOutputProvider: Send + Sync {
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput>;
/// Should be used to get canon chain transaction outputs.
pub trait TransactionOutputProvider: Send + Sync {
/// Returns transaction output.
fn transaction_output(&self, outpoint: &OutPoint, transaction_index: usize) -> Option<TransactionOutput>;
/// Returns true if we know that output is double spent.
fn is_double_spent(&self, outpoint: &OutPoint) -> bool;
}
/// Transaction meta provider stores transaction meta information
pub trait TransactionMetaProvider: Send + Sync {
/// Returns None if transactin with given hash does not exist
/// Otherwise returns transaction meta object
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
}

View File

@ -2,7 +2,7 @@ use std::collections::HashSet;
use primitives::hash::H256;
use primitives::compact::Compact;
use chain::{OutPoint, TransactionOutput, IndexedTransaction};
use db::{SharedStore, PreviousTransactionOutputProvider};
use db::{SharedStore, TransactionOutputProvider};
use network::Magic;
use memory_pool::{MemoryPool, OrderingStrategy, Entry};
use verification::{work_required, block_reward_satoshi, transaction_sigops};
@ -133,7 +133,7 @@ impl Default for BlockAssembler {
/// Iterator iterating over mempool transactions and yielding only those which fit the block
struct FittingTransactionsIterator<'a, T> {
/// Shared store is used to query previous transaction outputs from database
store: &'a PreviousTransactionOutputProvider,
store: &'a TransactionOutputProvider,
/// Memory pool transactions iterator
iter: T,
/// New block height
@ -153,7 +153,7 @@ struct FittingTransactionsIterator<'a, T> {
}
impl<'a, T> FittingTransactionsIterator<'a, T> where T: Iterator<Item = &'a Entry> {
fn new(store: &'a PreviousTransactionOutputProvider, iter: T, max_block_size: u32, max_block_sigops: u32, block_height: u32, block_time: u32) -> Self {
fn new(store: &'a TransactionOutputProvider, iter: T, max_block_size: u32, max_block_sigops: u32, block_height: u32, block_time: u32) -> Self {
FittingTransactionsIterator {
store: store,
iter: iter,
@ -169,9 +169,9 @@ impl<'a, T> FittingTransactionsIterator<'a, T> where T: Iterator<Item = &'a Entr
}
}
impl<'a, T> PreviousTransactionOutputProvider for FittingTransactionsIterator<'a, T> where T: Send + Sync {
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
self.store.previous_transaction_output(prevout, transaction_index)
impl<'a, T> TransactionOutputProvider for FittingTransactionsIterator<'a, T> where T: Send + Sync {
fn transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
self.store.transaction_output(prevout, transaction_index)
.or_else(|| {
self.previous_entries.iter()
.find(|e| e.hash == prevout.hash)
@ -179,6 +179,10 @@ impl<'a, T> PreviousTransactionOutputProvider for FittingTransactionsIterator<'a
.cloned()
})
}
fn is_double_spent(&self, _outpoint: &OutPoint) -> bool {
unimplemented!();
}
}
impl<'a, T> Iterator for FittingTransactionsIterator<'a, T> where T: Iterator<Item = &'a Entry> + Send + Sync {
@ -252,7 +256,7 @@ impl BlockAssembler {
let mut transactions = Vec::new();
let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore);
let tx_iter = FittingTransactionsIterator::new(store.as_previous_transaction_output_provider(), mempool_iter, self.max_block_size, self.max_block_sigops, height, time);
let tx_iter = FittingTransactionsIterator::new(store.as_transaction_output_provider(), mempool_iter, self.max_block_size, self.max_block_sigops, height, time);
for entry in tx_iter {
// miner_fee is i64, but we can safely cast it to u64
// memory pool should restrict miner fee to be positive

View File

@ -5,7 +5,7 @@
//! transactions.
//! It also guarantees that ancestor-descendant relation won't break during ordered removal (ancestors always removed
//! before descendants). Removal using `remove_by_hash` can break this rule.
use db::{TransactionProvider, PreviousTransactionOutputProvider};
use db::{TransactionProvider, TransactionOutputProvider};
use primitives::bytes::Bytes;
use primitives::hash::H256;
use chain::{IndexedTransaction, Transaction, OutPoint, TransactionOutput};
@ -812,12 +812,16 @@ impl TransactionProvider for MemoryPool {
}
}
impl PreviousTransactionOutputProvider for MemoryPool {
fn previous_transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
impl TransactionOutputProvider for MemoryPool {
fn transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
self.get(&prevout.hash)
.and_then(|tx| tx.outputs.get(prevout.index as usize))
.cloned()
}
fn is_double_spent(&self, outpoint: &OutPoint) -> bool {
self.is_spent(outpoint)
}
}
impl HeapSizeOf for MemoryPool {

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use chain::{Transaction, TransactionOutput, OutPoint};
use db::{PreviousTransactionOutputProvider, TransactionOutputObserver};
use db::TransactionOutputProvider;
use miner::{DoubleSpendCheckResult, HashedOutPoint, NonFinalDoubleSpendSet};
use verification::TransactionError;
use super::super::types::{MemoryPoolRef, StorageRef};
@ -32,7 +32,7 @@ impl MemoryPoolTransactionOutputProvider {
mempool_inputs: transaction.inputs.iter()
.map(|input| (
input.previous_output.clone().into(),
memory_pool.previous_transaction_output(&input.previous_output, usize::max_value()),
memory_pool.transaction_output(&input.previous_output, usize::max_value()),
)).collect(),
nonfinal_spends: None,
}),
@ -42,7 +42,7 @@ impl MemoryPoolTransactionOutputProvider {
mempool_inputs: transaction.inputs.iter()
.map(|input| (
input.previous_output.clone().into(),
memory_pool.previous_transaction_output(&input.previous_output, usize::max_value()),
memory_pool.transaction_output(&input.previous_output, usize::max_value()),
)).collect(),
nonfinal_spends: Some(nonfinal_spends),
}),
@ -50,23 +50,8 @@ impl MemoryPoolTransactionOutputProvider {
}
}
impl TransactionOutputObserver for MemoryPoolTransactionOutputProvider {
fn is_spent(&self, prevout: &OutPoint) -> bool {
// check if this output is spent by some non-final mempool transaction
if let Some(ref nonfinal_spends) = self.nonfinal_spends {
if nonfinal_spends.double_spends.contains(&prevout.clone().into()) {
return false;
}
}
// we can omit memory_pool check here, because it has been completed in `for_transaction` method
// => just check spending in storage
self.storage_provider.is_spent(prevout)
}
}
impl PreviousTransactionOutputProvider for MemoryPoolTransactionOutputProvider {
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
impl TransactionOutputProvider for MemoryPoolTransactionOutputProvider {
fn transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
let hashed_prevout: HashedOutPoint = prevout.clone().into();
// check if that is output of some transaction, which is vitually removed from memory pool
@ -87,7 +72,20 @@ impl PreviousTransactionOutputProvider for MemoryPoolTransactionOutputProvider {
}
// now check in storage
self.storage_provider.previous_transaction_output(prevout, transaction_index)
self.storage_provider.transaction_output(prevout, transaction_index)
}
fn is_double_spent(&self, prevout: &OutPoint) -> bool {
// check if this output is spent by some non-final mempool transaction
if let Some(ref nonfinal_spends) = self.nonfinal_spends {
if nonfinal_spends.double_spends.contains(&prevout.clone().into()) {
return false;
}
}
// we can omit memory_pool check here, because it has been completed in `for_transaction` method
// => just check spending in storage
self.storage_provider.is_double_spent(prevout)
}
}
@ -96,7 +94,7 @@ mod tests {
use std::sync::Arc;
use parking_lot::RwLock;
use chain::OutPoint;
use db::{TransactionOutputObserver, PreviousTransactionOutputProvider, BlockChainDatabase};
use db::{TransactionOutputProvider, BlockChainDatabase};
use miner::MemoryPool;
use test_data;
use super::MemoryPoolTransactionOutputProvider;
@ -128,11 +126,11 @@ mod tests {
// =>
// if t3 is also depending on t1[0] || t2[0], it will be rejected by verification as missing inputs
let provider = MemoryPoolTransactionOutputProvider::for_transaction(storage, &memory_pool, &dchain.at(3)).unwrap();
assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(0).hash(), index: 0, }), false);
assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), false);
assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(2).hash(), index: 0, }), false);
assert_eq!(provider.previous_transaction_output(&OutPoint { hash: dchain.at(0).hash(), index: 0, }, 0), Some(dchain.at(0).outputs[0].clone()));
assert_eq!(provider.previous_transaction_output(&OutPoint { hash: dchain.at(1).hash(), index: 0, }, 0), None);
assert_eq!(provider.previous_transaction_output(&OutPoint { hash: dchain.at(2).hash(), index: 0, }, 0), None);
assert_eq!(provider.is_double_spent(&OutPoint { hash: dchain.at(0).hash(), index: 0, }), false);
assert_eq!(provider.is_double_spent(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), false);
assert_eq!(provider.is_double_spent(&OutPoint { hash: dchain.at(2).hash(), index: 0, }), false);
assert_eq!(provider.transaction_output(&OutPoint { hash: dchain.at(0).hash(), index: 0, }, 0), Some(dchain.at(0).outputs[0].clone()));
assert_eq!(provider.transaction_output(&OutPoint { hash: dchain.at(1).hash(), index: 0, }, 0), None);
assert_eq!(provider.transaction_output(&OutPoint { hash: dchain.at(2).hash(), index: 0, }, 0), None);
}
}

View File

@ -1,5 +1,5 @@
use network::{Magic, ConsensusParams};
use db::PreviousTransactionOutputProvider;
use db::TransactionOutputProvider;
use sigops::transaction_sigops;
use work::block_reward_satoshi;
use duplex_store::DuplexTransactionOutputProvider;
@ -15,7 +15,7 @@ pub struct BlockAcceptor<'a> {
}
impl<'a> BlockAcceptor<'a> {
pub fn new(store: &'a PreviousTransactionOutputProvider, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
pub fn new(store: &'a TransactionOutputProvider, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
let params = network.consensus_params();
BlockAcceptor {
finality: BlockFinality::new(block, height),
@ -56,13 +56,13 @@ impl<'a> BlockFinality<'a> {
pub struct BlockSigops<'a> {
block: CanonBlock<'a>,
store: &'a PreviousTransactionOutputProvider,
store: &'a TransactionOutputProvider,
consensus_params: ConsensusParams,
max_sigops: usize,
}
impl<'a> BlockSigops<'a> {
fn new(block: CanonBlock<'a>, store: &'a PreviousTransactionOutputProvider, consensus_params: ConsensusParams, max_sigops: usize) -> Self {
fn new(block: CanonBlock<'a>, store: &'a TransactionOutputProvider, consensus_params: ConsensusParams, max_sigops: usize) -> Self {
BlockSigops {
block: block,
store: store,
@ -88,12 +88,12 @@ impl<'a> BlockSigops<'a> {
pub struct BlockCoinbaseClaim<'a> {
block: CanonBlock<'a>,
store: &'a PreviousTransactionOutputProvider,
store: &'a TransactionOutputProvider,
height: u32,
}
impl<'a> BlockCoinbaseClaim<'a> {
fn new(block: CanonBlock<'a>, store: &'a PreviousTransactionOutputProvider, height: u32) -> Self {
fn new(block: CanonBlock<'a>, store: &'a TransactionOutputProvider, height: u32) -> Self {
BlockCoinbaseClaim {
block: block,
store: store,
@ -111,7 +111,7 @@ impl<'a> BlockCoinbaseClaim<'a> {
let mut incoming: u64 = 0;
for input in tx.raw.inputs.iter() {
let (sum, overflow) = incoming.overflowing_add(
store.previous_transaction_output(&input.previous_output, tx_idx).map(|o| o.value).unwrap_or(0));
store.transaction_output(&input.previous_output, tx_idx).map(|o| o.value).unwrap_or(0));
if overflow {
return Err(Error::ReferencedInputsSumOverflow);
}

View File

@ -6,7 +6,7 @@ use canon::CanonBlock;
use accept_block::BlockAcceptor;
use accept_header::HeaderAcceptor;
use accept_transaction::TransactionAcceptor;
use duplex_store::{DuplexTransactionOutputProvider, DuplexTransactionOutputObserver};
use duplex_store::DuplexTransactionOutputProvider;
pub struct ChainAcceptor<'a> {
pub block: BlockAcceptor<'a>,
@ -17,18 +17,16 @@ pub struct ChainAcceptor<'a> {
impl<'a> ChainAcceptor<'a> {
pub fn new(store: &'a Store, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
let prevouts = DuplexTransactionOutputProvider::new(store.as_previous_transaction_output_provider(), block.raw());
let spents = DuplexTransactionOutputObserver::new(store.as_transaction_output_observer(), block.raw());
let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
ChainAcceptor {
block: BlockAcceptor::new(store.as_previous_transaction_output_provider(), network, block, height),
block: BlockAcceptor::new(store.as_transaction_output_provider(), network, block, height),
header: HeaderAcceptor::new(store.as_block_header_provider(), network, block.header(), height),
transactions: block.transactions()
.into_iter()
.enumerate()
.map(|(tx_index, tx)| TransactionAcceptor::new(
store.as_transaction_meta_provider(),
prevouts,
spents,
output_store,
network,
tx,
block.hash(),

View File

@ -1,8 +1,8 @@
use primitives::hash::H256;
use db::{TransactionMetaProvider, PreviousTransactionOutputProvider, TransactionOutputObserver};
use db::{TransactionMetaProvider, TransactionOutputProvider};
use network::{Magic, ConsensusParams};
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
use duplex_store::{DuplexTransactionOutputProvider, DuplexTransactionOutputObserver};
use duplex_store::DuplexTransactionOutputProvider;
use sigops::transaction_sigops;
use canon::CanonTransaction;
use constants::{COINBASE_MATURITY, MAX_BLOCK_SIGOPS};
@ -23,10 +23,7 @@ impl<'a> TransactionAcceptor<'a> {
meta_store: &'a TransactionMetaProvider,
// previous transaction outputs
// in case of block validation, that's database and currently processed block
prevout_store: DuplexTransactionOutputProvider<'a>,
// in case of block validation, that's database and currently processed block
//spent_store: &'a TransactionOutputObserver,
spent_store: DuplexTransactionOutputObserver<'a>,
output_store: DuplexTransactionOutputProvider<'a>,
network: Magic,
transaction: CanonTransaction<'a>,
block_hash: &'a H256,
@ -38,11 +35,11 @@ impl<'a> TransactionAcceptor<'a> {
let params = network.consensus_params();
TransactionAcceptor {
bip30: TransactionBip30::new_for_sync(transaction, meta_store, params.clone(), block_hash, height),
missing_inputs: TransactionMissingInputs::new(transaction, prevout_store, transaction_index),
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
maturity: TransactionMaturity::new(transaction, meta_store, height),
overspent: TransactionOverspent::new(transaction, prevout_store),
double_spent: TransactionDoubleSpend::new(transaction, spent_store),
eval: TransactionEval::new(transaction, prevout_store, params, height, time),
overspent: TransactionOverspent::new(transaction, output_store),
double_spent: TransactionDoubleSpend::new(transaction, output_store),
eval: TransactionEval::new(transaction, output_store, params, height, time),
}
}
@ -71,10 +68,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
// TODO: in case of memory pool it should be db and memory pool
meta_store: &'a TransactionMetaProvider,
// in case of memory pool it should be db and memory pool
prevout_store: DuplexTransactionOutputProvider<'a>,
// in case of memory pool it should be db and memory pool
//spent_store: &'a TransactionOutputObserver,
spent_store: DuplexTransactionOutputObserver<'a>,
output_store: DuplexTransactionOutputProvider<'a>,
network: Magic,
transaction: CanonTransaction<'a>,
height: u32,
@ -84,12 +78,12 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
let params = network.consensus_params();
let transaction_index = 0;
MemoryPoolTransactionAcceptor {
missing_inputs: TransactionMissingInputs::new(transaction, prevout_store, transaction_index),
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
maturity: TransactionMaturity::new(transaction, meta_store, height),
overspent: TransactionOverspent::new(transaction, prevout_store),
sigops: TransactionSigops::new(transaction, prevout_store, params.clone(), MAX_BLOCK_SIGOPS, time),
double_spent: TransactionDoubleSpend::new(transaction, spent_store),
eval: TransactionEval::new(transaction, prevout_store, params, height, time),
overspent: TransactionOverspent::new(transaction, output_store),
sigops: TransactionSigops::new(transaction, output_store, params.clone(), MAX_BLOCK_SIGOPS, time),
double_spent: TransactionDoubleSpend::new(transaction, output_store),
eval: TransactionEval::new(transaction, output_store, params, height, time),
}
}
@ -167,7 +161,7 @@ impl<'a> TransactionMissingInputs<'a> {
let missing_index = self.transaction.raw.inputs.iter()
.position(|input| {
let is_not_null = !input.previous_output.is_null();
let is_missing = self.store.previous_transaction_output(&input.previous_output, self.transaction_index).is_none();
let is_missing = self.store.transaction_output(&input.previous_output, self.transaction_index).is_none();
is_not_null && is_missing
});
@ -228,7 +222,7 @@ impl<'a> TransactionOverspent<'a> {
}
let available = self.transaction.raw.inputs.iter()
.map(|input| self.store.previous_transaction_output(&input.previous_output, usize::max_value()).map(|o| o.value).unwrap_or(0))
.map(|input| self.store.transaction_output(&input.previous_output, usize::max_value()).map(|o| o.value).unwrap_or(0))
.sum::<u64>();
let spends = self.transaction.raw.total_spends();
@ -310,7 +304,7 @@ impl<'a> TransactionEval<'a> {
};
for (index, input) in self.transaction.raw.inputs.iter().enumerate() {
let output = self.store.previous_transaction_output(&input.previous_output, usize::max_value())
let output = self.store.transaction_output(&input.previous_output, usize::max_value())
.ok_or_else(|| TransactionError::UnknownReference(input.previous_output.hash.clone()))?;
checker.input_index = index;
@ -331,13 +325,11 @@ impl<'a> TransactionEval<'a> {
pub struct TransactionDoubleSpend<'a> {
transaction: CanonTransaction<'a>,
//store: &'a TransactionOutputObserver,
store: DuplexTransactionOutputObserver<'a>,
store: DuplexTransactionOutputProvider<'a>,
}
impl<'a> TransactionDoubleSpend<'a> {
//fn new(transaction: CanonTransaction<'a>, store: &'a TransactionOutputObserver) -> Self {
fn new(transaction: CanonTransaction<'a>, store: DuplexTransactionOutputObserver<'a>) -> Self {
fn new(transaction: CanonTransaction<'a>, store: DuplexTransactionOutputProvider<'a>) -> Self {
TransactionDoubleSpend {
transaction: transaction,
store: store,
@ -346,7 +338,7 @@ impl<'a> TransactionDoubleSpend<'a> {
fn check(&self) -> Result<(), TransactionError> {
for input in &self.transaction.raw.inputs {
if self.store.is_spent(&input.previous_output) {
if self.store.is_double_spent(&input.previous_output) {
return Err(TransactionError::UsingSpentOutput(
input.previous_output.hash.clone(),
input.previous_output.index

View File

@ -2,14 +2,11 @@
use hash::H256;
use chain::{IndexedBlock, IndexedBlockHeader, BlockHeader, Transaction};
use db::{
SharedStore, PreviousTransactionOutputProvider, BlockHeaderProvider, TransactionOutputObserver,
BlockOrigin
};
use db::{SharedStore, TransactionOutputProvider, BlockHeaderProvider, BlockOrigin};
use network::Magic;
use error::{Error, TransactionError};
use canon::{CanonBlock, CanonTransaction};
use duplex_store::{DuplexTransactionOutputObserver, DuplexTransactionOutputProvider, NoopStore};
use duplex_store::{DuplexTransactionOutputProvider, NoopStore};
use verify_chain::ChainVerifier;
use verify_header::HeaderVerifier;
use verify_transaction::MemoryPoolTransactionVerifier;
@ -89,7 +86,7 @@ impl BackwardsCompatibleChainVerifier {
height: u32,
time: u32,
transaction: &Transaction,
) -> Result<(), TransactionError> where T: PreviousTransactionOutputProvider + TransactionOutputObserver {
) -> Result<(), TransactionError> where T: TransactionOutputProvider {
let indexed_tx = transaction.clone().into();
// let's do preverification first
let tx_verifier = MemoryPoolTransactionVerifier::new(&indexed_tx);
@ -98,12 +95,10 @@ impl BackwardsCompatibleChainVerifier {
let canon_tx = CanonTransaction::new(&indexed_tx);
// now let's do full verification
let noop = NoopStore;
let prevouts = DuplexTransactionOutputProvider::new(prevout_provider, &noop);
let spents = DuplexTransactionOutputObserver::new(prevout_provider, &noop);
let output_store = DuplexTransactionOutputProvider::new(prevout_provider, &noop);
let tx_acceptor = MemoryPoolTransactionAcceptor::new(
self.store.as_transaction_meta_provider(),
prevouts,
spents,
output_store,
self.network,
canon_tx,
height,

View File

@ -2,16 +2,16 @@
//! require sophisticated (in more than one source) previous transaction lookups
use chain::{OutPoint, TransactionOutput};
use db::{PreviousTransactionOutputProvider, TransactionOutputObserver};
use db::TransactionOutputProvider;
#[derive(Clone, Copy)]
pub struct DuplexTransactionOutputProvider<'a> {
first: &'a PreviousTransactionOutputProvider,
second: &'a PreviousTransactionOutputProvider,
first: &'a TransactionOutputProvider,
second: &'a TransactionOutputProvider,
}
impl<'a> DuplexTransactionOutputProvider<'a> {
pub fn new(first: &'a PreviousTransactionOutputProvider, second: &'a PreviousTransactionOutputProvider) -> Self {
pub fn new(first: &'a TransactionOutputProvider, second: &'a TransactionOutputProvider) -> Self {
DuplexTransactionOutputProvider {
first: first,
second: second,
@ -19,44 +19,25 @@ impl<'a> DuplexTransactionOutputProvider<'a> {
}
}
impl<'a> PreviousTransactionOutputProvider for DuplexTransactionOutputProvider<'a> {
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
self.first.previous_transaction_output(prevout, transaction_index)
.or_else(|| self.second.previous_transaction_output(prevout, transaction_index))
impl<'a> TransactionOutputProvider for DuplexTransactionOutputProvider<'a> {
fn transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
self.first.transaction_output(prevout, transaction_index)
.or_else(|| self.second.transaction_output(prevout, transaction_index))
}
}
#[derive(Clone, Copy)]
pub struct DuplexTransactionOutputObserver<'a> {
first: &'a TransactionOutputObserver,
second: &'a TransactionOutputObserver,
}
impl<'a> DuplexTransactionOutputObserver<'a> {
pub fn new(first: &'a TransactionOutputObserver, second: &'a TransactionOutputObserver) -> Self {
DuplexTransactionOutputObserver {
first: first,
second: second,
}
}
}
impl<'a> TransactionOutputObserver for DuplexTransactionOutputObserver<'a> {
fn is_spent(&self, prevout: &OutPoint) -> bool {
self.first.is_spent(prevout) || self.second.is_spent(prevout)
fn is_double_spent(&self, prevout: &OutPoint) -> bool {
self.first.is_double_spent(prevout) || self.second.is_double_spent(prevout)
}
}
pub struct NoopStore;
impl PreviousTransactionOutputProvider for NoopStore {
fn previous_transaction_output(&self, _prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
impl TransactionOutputProvider for NoopStore {
fn transaction_output(&self, _prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
None
}
}
impl TransactionOutputObserver for NoopStore {
fn is_spent(&self, _prevout: &OutPoint) -> bool {
fn is_double_spent(&self, _prevout: &OutPoint) -> bool {
false
}
}

View File

@ -1,5 +1,5 @@
use chain::Transaction;
use db::PreviousTransactionOutputProvider;
use db::TransactionOutputProvider;
use script::Script;
/// Counts signature operations in given transaction
@ -8,7 +8,7 @@ use script::Script;
/// missing, we simply ignore that fact and just carry on counting
pub fn transaction_sigops(
transaction: &Transaction,
store: &PreviousTransactionOutputProvider,
store: &TransactionOutputProvider,
bip16_active: bool
) -> usize {
let output_sigops: usize = transaction.outputs.iter().map(|output| {
@ -27,7 +27,7 @@ pub fn transaction_sigops(
let input_script: Script = input.script_sig.clone().into();
input_sigops += input_script.sigops_count(false);
if bip16_active {
let previous_output = match store.previous_transaction_output(&input.previous_output, usize::max_value()) {
let previous_output = match store.transaction_output(&input.previous_output, usize::max_value()) {
Some(output) => output,
None => continue,
};