Merge branch 'consolidate' into bip90
This commit is contained in:
commit
8242db0428
|
@ -1,4 +1,4 @@
|
||||||
use std::{cmp, io, borrow, fmt};
|
use std::{cmp, io, fmt};
|
||||||
use hash::H256;
|
use hash::H256;
|
||||||
use ser::{Deserializable, Reader, Error as ReaderError};
|
use ser::{Deserializable, Reader, Error as ReaderError};
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
|
@ -56,15 +56,3 @@ impl Deserializable for IndexedTransaction {
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IndexedTransactionsRef<'a, T> where T: 'a {
|
|
||||||
pub transactions: &'a [T],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> IndexedTransactionsRef<'a, T> where T: borrow::Borrow<IndexedTransaction> {
|
|
||||||
pub fn new(transactions: &'a [T]) -> Self {
|
|
||||||
IndexedTransactionsRef {
|
|
||||||
transactions: transactions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,6 +32,6 @@ pub use transaction::{Transaction, TransactionInput, TransactionOutput, OutPoint
|
||||||
pub use read_and_hash::{ReadAndHash, HashedData};
|
pub use read_and_hash::{ReadAndHash, HashedData};
|
||||||
pub use indexed_block::IndexedBlock;
|
pub use indexed_block::IndexedBlock;
|
||||||
pub use indexed_header::IndexedBlockHeader;
|
pub use indexed_header::IndexedBlockHeader;
|
||||||
pub use indexed_transaction::{IndexedTransaction, IndexedTransactionsRef};
|
pub use indexed_transaction::IndexedTransaction;
|
||||||
|
|
||||||
pub type ShortTransactionID = hash::H48;
|
pub type ShortTransactionID = hash::H48;
|
||||||
|
|
|
@ -19,8 +19,8 @@ use kv::{
|
||||||
use best_block::BestBlock;
|
use best_block::BestBlock;
|
||||||
use {
|
use {
|
||||||
BlockRef, Error, BlockHeaderProvider, BlockProvider, BlockOrigin, TransactionMeta, IndexedBlockProvider,
|
BlockRef, Error, BlockHeaderProvider, BlockProvider, BlockOrigin, TransactionMeta, IndexedBlockProvider,
|
||||||
TransactionMetaProvider, TransactionProvider, PreviousTransactionOutputProvider, BlockChain, Store,
|
TransactionMetaProvider, TransactionProvider, TransactionOutputProvider, BlockChain, Store,
|
||||||
SideChainOrigin, ForkChain, TransactionOutputObserver, Forkable, CanonStore
|
SideChainOrigin, ForkChain, Forkable, CanonStore
|
||||||
};
|
};
|
||||||
|
|
||||||
const COL_COUNT: u32 = 10;
|
const COL_COUNT: u32 = 10;
|
||||||
|
@ -474,19 +474,18 @@ impl<T> TransactionProvider for BlockChainDatabase<T> where T: KeyValueDatabase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PreviousTransactionOutputProvider for BlockChainDatabase<T> where T: KeyValueDatabase {
|
impl<T> TransactionOutputProvider for BlockChainDatabase<T> where T: KeyValueDatabase {
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
|
fn transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
|
||||||
// return previous transaction outputs only for canon chain transactions
|
// return previous transaction outputs only for canon chain transactions
|
||||||
self.transaction_meta(&prevout.hash)
|
self.transaction_meta(&prevout.hash)
|
||||||
.and_then(|_| self.transaction(&prevout.hash))
|
.and_then(|_| self.transaction(&prevout.hash))
|
||||||
.and_then(|tx| tx.outputs.into_iter().nth(prevout.index as usize))
|
.and_then(|tx| tx.outputs.into_iter().nth(prevout.index as usize))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> TransactionOutputObserver for BlockChainDatabase<T> where T: KeyValueDatabase {
|
fn is_double_spent(&self, prevout: &OutPoint) -> bool {
|
||||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool> {
|
|
||||||
self.transaction_meta(&prevout.hash)
|
self.transaction_meta(&prevout.hash)
|
||||||
.and_then(|meta| meta.is_spent(prevout.index as usize))
|
.and_then(|meta| meta.is_spent(prevout.index as usize))
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
use std::cmp;
|
||||||
|
use chain::{OutPoint, TransactionOutput, IndexedBlock, IndexedTransaction};
|
||||||
|
use {TransactionOutputProvider};
|
||||||
|
|
||||||
|
fn transaction_output(transactions: &[IndexedTransaction], prevout: &OutPoint) -> Option<TransactionOutput> {
|
||||||
|
transactions.iter()
|
||||||
|
.find(|tx| tx.hash == prevout.hash)
|
||||||
|
.and_then(|tx| tx.raw.outputs.get(prevout.index as usize))
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// tell that it's spent (double spent)
|
||||||
|
let spends = transactions.iter()
|
||||||
|
.flat_map(|tx| &tx.raw.inputs)
|
||||||
|
.filter(|input| &input.previous_output == prevout)
|
||||||
|
.take(2)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
spends == 2
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_double_spent(&self, outpoint: &OutPoint) -> bool {
|
||||||
|
is_double_spent(&self.transactions, outpoint)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
use std::borrow::Borrow;
|
|
||||||
use chain::{OutPoint, TransactionOutput, IndexedTransactionsRef, IndexedTransaction, IndexedBlock};
|
|
||||||
use transaction_provider::PreviousTransactionOutputProvider;
|
|
||||||
use transaction_meta_provider::TransactionOutputObserver;
|
|
||||||
|
|
||||||
impl<'a, T> PreviousTransactionOutputProvider for IndexedTransactionsRef<'a, T>
|
|
||||||
where T: Borrow<IndexedTransaction> + Send + Sync {
|
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
|
||||||
self.transactions.iter()
|
|
||||||
.take(transaction_index)
|
|
||||||
.map(Borrow::borrow)
|
|
||||||
.find(|tx| tx.hash == prevout.hash)
|
|
||||||
.and_then(|tx| tx.raw.outputs.get(prevout.index as usize))
|
|
||||||
.cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PreviousTransactionOutputProvider for IndexedBlock {
|
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
|
||||||
let txs = IndexedTransactionsRef::new(&self.transactions);
|
|
||||||
txs.previous_transaction_output(prevout, transaction_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransactionOutputObserver for IndexedBlock {
|
|
||||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool> {
|
|
||||||
// the code below is valid, but commented out due it's poor performance
|
|
||||||
// we could optimize it by indexing all outputs once
|
|
||||||
// let tx: IndexedTransaction = { .. }
|
|
||||||
// let indexed_outputs: IndexedOutputs = tx.indexed_outputs();
|
|
||||||
// indexed_outputs.is_spent()
|
|
||||||
//None
|
|
||||||
|
|
||||||
// if previous transaction output appears more than once than we can safely
|
|
||||||
// tell that it's spent (double spent)
|
|
||||||
|
|
||||||
let spends = self.transactions.iter()
|
|
||||||
.flat_map(|tx| &tx.raw.inputs)
|
|
||||||
.filter(|input| &input.previous_output == prevout)
|
|
||||||
.take(2)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
match spends {
|
|
||||||
0 => None,
|
|
||||||
1 => Some(false),
|
|
||||||
2 => Some(true),
|
|
||||||
_ => unreachable!("spends <= 2; self.take(2); qed"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,10 +17,9 @@ mod block_ref;
|
||||||
mod block_chain;
|
mod block_chain;
|
||||||
mod block_chain_db;
|
mod block_chain_db;
|
||||||
mod error;
|
mod error;
|
||||||
mod impls;
|
mod block_impls;
|
||||||
mod store;
|
mod store;
|
||||||
mod transaction_meta;
|
mod transaction_meta;
|
||||||
mod transaction_meta_provider;
|
|
||||||
mod transaction_provider;
|
mod transaction_provider;
|
||||||
|
|
||||||
pub use primitives::{hash, bytes};
|
pub use primitives::{hash, bytes};
|
||||||
|
@ -34,6 +33,5 @@ pub use block_chain_db::{BlockChainDatabase, ForkChainDatabase};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use store::{AsSubstore, Store, SharedStore, CanonStore};
|
pub use store::{AsSubstore, Store, SharedStore, CanonStore};
|
||||||
pub use transaction_meta::TransactionMeta;
|
pub use transaction_meta::TransactionMeta;
|
||||||
pub use transaction_meta_provider::{TransactionMetaProvider, TransactionOutputObserver};
|
pub use transaction_provider::{TransactionProvider, TransactionOutputProvider, TransactionMetaProvider};
|
||||||
pub use transaction_provider::{TransactionProvider, PreviousTransactionOutputProvider};
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@ use std::sync::Arc;
|
||||||
use chain::BlockHeader;
|
use chain::BlockHeader;
|
||||||
use {
|
use {
|
||||||
BestBlock, BlockProvider, BlockHeaderProvider, TransactionProvider, TransactionMetaProvider,
|
BestBlock, BlockProvider, BlockHeaderProvider, TransactionProvider, TransactionMetaProvider,
|
||||||
PreviousTransactionOutputProvider, BlockChain, IndexedBlockProvider, TransactionOutputObserver,
|
TransactionOutputProvider, BlockChain, IndexedBlockProvider, Forkable
|
||||||
Forkable
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait CanonStore: Store + Forkable {
|
pub trait CanonStore: Store + Forkable {
|
||||||
|
@ -23,21 +22,19 @@ pub trait Store: AsSubstore {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows casting Arc<Store> to reference to any substore type
|
/// 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_provider(&self) -> &BlockProvider;
|
||||||
|
|
||||||
fn as_block_header_provider(&self) -> &BlockHeaderProvider;
|
fn as_block_header_provider(&self) -> &BlockHeaderProvider;
|
||||||
|
|
||||||
fn as_transaction_provider(&self) -> &TransactionProvider;
|
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_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 {
|
fn as_block_provider(&self) -> &BlockProvider {
|
||||||
&*self
|
&*self
|
||||||
}
|
}
|
||||||
|
@ -50,17 +47,13 @@ impl<T> AsSubstore for T where T: BlockChain + IndexedBlockProvider + Transactio
|
||||||
&*self
|
&*self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_previous_transaction_output_provider(&self) -> &PreviousTransactionOutputProvider {
|
fn as_transaction_output_provider(&self) -> &TransactionOutputProvider {
|
||||||
&*self
|
&*self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_transaction_meta_provider(&self) -> &TransactionMetaProvider {
|
fn as_transaction_meta_provider(&self) -> &TransactionMetaProvider {
|
||||||
&*self
|
&*self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_transaction_output_observer(&self) -> &TransactionOutputObserver {
|
|
||||||
&*self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SharedStore = Arc<CanonStore + Send + Sync>;
|
pub type SharedStore = Arc<CanonStore + Send + Sync>;
|
||||||
|
|
|
@ -1,18 +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 None if we have no information about previous output
|
|
||||||
/// Returns Some(false) if we know that output hasn't been spent
|
|
||||||
/// Returns Some(true) if we know that output has been spent
|
|
||||||
fn is_spent(&self, prevout: &OutPoint) -> Option<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>;
|
|
||||||
}
|
|
|
@ -1,23 +1,34 @@
|
||||||
use hash::H256;
|
use hash::H256;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use chain::{Transaction, OutPoint, TransactionOutput};
|
use chain::{Transaction, OutPoint, TransactionOutput};
|
||||||
|
use {TransactionMeta};
|
||||||
|
|
||||||
|
/// Should be used to obtain all transactions from canon chain and forks.
|
||||||
pub trait TransactionProvider {
|
pub trait TransactionProvider {
|
||||||
/// returns true if store contains given transaction
|
/// Returns true if store contains given transaction.
|
||||||
fn contains_transaction(&self, hash: &H256) -> bool {
|
fn contains_transaction(&self, hash: &H256) -> bool {
|
||||||
self.transaction(hash).is_some()
|
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>;
|
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>;
|
fn transaction(&self, hash: &H256) -> Option<Transaction>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// During transaction verifiction the only part of old transaction that we need is `TransactionOutput`.
|
/// Should be used to get canon chain transaction outputs.
|
||||||
/// Structures like `IndexedBlock` or `MemoryPool` already have it in memory, so it would be
|
pub trait TransactionOutputProvider: Send + Sync {
|
||||||
/// a shame to clone the whole transaction just to get single output.
|
/// Returns transaction output.
|
||||||
pub trait PreviousTransactionOutputProvider: Send + Sync {
|
fn transaction_output(&self, outpoint: &OutPoint, transaction_index: usize) -> Option<TransactionOutput>;
|
||||||
fn previous_transaction_output(&self, prevout: &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>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashSet;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use primitives::compact::Compact;
|
use primitives::compact::Compact;
|
||||||
use chain::{OutPoint, TransactionOutput, IndexedTransaction};
|
use chain::{OutPoint, TransactionOutput, IndexedTransaction};
|
||||||
use db::{SharedStore, PreviousTransactionOutputProvider};
|
use db::{SharedStore, TransactionOutputProvider};
|
||||||
use network::Magic;
|
use network::Magic;
|
||||||
use memory_pool::{MemoryPool, OrderingStrategy, Entry};
|
use memory_pool::{MemoryPool, OrderingStrategy, Entry};
|
||||||
use verification::{work_required, block_reward_satoshi, transaction_sigops};
|
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
|
/// Iterator iterating over mempool transactions and yielding only those which fit the block
|
||||||
struct FittingTransactionsIterator<'a, T> {
|
struct FittingTransactionsIterator<'a, T> {
|
||||||
/// Shared store is used to query previous transaction outputs from database
|
/// Shared store is used to query previous transaction outputs from database
|
||||||
store: &'a PreviousTransactionOutputProvider,
|
store: &'a TransactionOutputProvider,
|
||||||
/// Memory pool transactions iterator
|
/// Memory pool transactions iterator
|
||||||
iter: T,
|
iter: T,
|
||||||
/// New block height
|
/// New block height
|
||||||
|
@ -153,7 +153,7 @@ struct FittingTransactionsIterator<'a, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> FittingTransactionsIterator<'a, T> where T: Iterator<Item = &'a Entry> {
|
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 {
|
FittingTransactionsIterator {
|
||||||
store: store,
|
store: store,
|
||||||
iter: iter,
|
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 {
|
impl<'a, T> TransactionOutputProvider for FittingTransactionsIterator<'a, T> where T: Send + Sync {
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
fn transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
||||||
self.store.previous_transaction_output(prevout, transaction_index)
|
self.store.transaction_output(prevout, transaction_index)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.previous_entries.iter()
|
self.previous_entries.iter()
|
||||||
.find(|e| e.hash == prevout.hash)
|
.find(|e| e.hash == prevout.hash)
|
||||||
|
@ -179,6 +179,10 @@ impl<'a, T> PreviousTransactionOutputProvider for FittingTransactionsIterator<'a
|
||||||
.cloned()
|
.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 {
|
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 mut transactions = Vec::new();
|
||||||
|
|
||||||
let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore);
|
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 {
|
for entry in tx_iter {
|
||||||
// miner_fee is i64, but we can safely cast it to u64
|
// miner_fee is i64, but we can safely cast it to u64
|
||||||
// memory pool should restrict miner fee to be positive
|
// memory pool should restrict miner fee to be positive
|
||||||
|
@ -277,7 +281,7 @@ impl BlockAssembler {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use chain::{IndexedTransaction, IndexedTransactionsRef};
|
use chain::{IndexedTransaction};
|
||||||
use verification::constants::{MAX_BLOCK_SIZE, MAX_BLOCK_SIGOPS};
|
use verification::constants::{MAX_BLOCK_SIZE, MAX_BLOCK_SIGOPS};
|
||||||
use memory_pool::Entry;
|
use memory_pool::Entry;
|
||||||
use super::{SizePolicy, NextStep, FittingTransactionsIterator};
|
use super::{SizePolicy, NextStep, FittingTransactionsIterator};
|
||||||
|
@ -313,16 +317,6 @@ mod tests {
|
||||||
assert_eq!(NextStep::FinishAndAppend.and(NextStep::Append), NextStep::FinishAndAppend);
|
assert_eq!(NextStep::FinishAndAppend.and(NextStep::Append), NextStep::FinishAndAppend);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_fitting_transactions_iterator_no_transactions() {
|
|
||||||
let store: Vec<IndexedTransaction> = Vec::new();
|
|
||||||
let store_ref = IndexedTransactionsRef::new(&store);
|
|
||||||
let entries: Vec<Entry> = Vec::new();
|
|
||||||
|
|
||||||
let iter = FittingTransactionsIterator::new(&store_ref, entries.iter(), MAX_BLOCK_SIZE as u32, MAX_BLOCK_SIGOPS as u32, 0, 0);
|
|
||||||
assert!(iter.collect::<Vec<_>>().is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fitting_transactions_iterator_max_block_size_reached() {
|
fn test_fitting_transactions_iterator_max_block_size_reached() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! transactions.
|
//! transactions.
|
||||||
//! It also guarantees that ancestor-descendant relation won't break during ordered removal (ancestors always removed
|
//! 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.
|
//! 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::bytes::Bytes;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use chain::{IndexedTransaction, Transaction, OutPoint, TransactionOutput};
|
use chain::{IndexedTransaction, Transaction, OutPoint, TransactionOutput};
|
||||||
|
@ -812,12 +812,16 @@ impl TransactionProvider for MemoryPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PreviousTransactionOutputProvider for MemoryPool {
|
impl TransactionOutputProvider for MemoryPool {
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
|
fn transaction_output(&self, prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
|
||||||
self.get(&prevout.hash)
|
self.get(&prevout.hash)
|
||||||
.and_then(|tx| tx.outputs.get(prevout.index as usize))
|
.and_then(|tx| tx.outputs.get(prevout.index as usize))
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_double_spent(&self, outpoint: &OutPoint) -> bool {
|
||||||
|
self.is_spent(outpoint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeapSizeOf for MemoryPool {
|
impl HeapSizeOf for MemoryPool {
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use chain::{Transaction, TransactionOutput, OutPoint};
|
use chain::{Transaction, TransactionOutput, OutPoint};
|
||||||
use db::{PreviousTransactionOutputProvider, TransactionOutputObserver};
|
use db::TransactionOutputProvider;
|
||||||
use miner::{DoubleSpendCheckResult, HashedOutPoint, NonFinalDoubleSpendSet};
|
use miner::{DoubleSpendCheckResult, HashedOutPoint, NonFinalDoubleSpendSet};
|
||||||
use verification::TransactionError;
|
use verification::TransactionError;
|
||||||
use super::super::types::{MemoryPoolRef, StorageRef};
|
use super::super::types::{MemoryPoolRef, StorageRef};
|
||||||
use super::StorageTransactionOutputProvider;
|
|
||||||
|
|
||||||
/// Transaction output observer, which looks into both storage && into memory pool.
|
/// Transaction output observer, which looks into both storage && into memory pool.
|
||||||
/// It also allows to replace non-final transactions in the memory pool.
|
/// It also allows to replace non-final transactions in the memory pool.
|
||||||
pub struct MemoryPoolTransactionOutputProvider {
|
pub struct MemoryPoolTransactionOutputProvider {
|
||||||
/// Storage provider
|
/// Storage provider
|
||||||
storage_provider: StorageTransactionOutputProvider,
|
storage_provider: StorageRef,
|
||||||
/// Transaction inputs from memory pool transactions
|
/// Transaction inputs from memory pool transactions
|
||||||
mempool_inputs: HashMap<HashedOutPoint, Option<TransactionOutput>>,
|
mempool_inputs: HashMap<HashedOutPoint, Option<TransactionOutput>>,
|
||||||
/// Previous outputs, for which we should return 'Not spent' value.
|
/// Previous outputs, for which we should return 'Not spent' value.
|
||||||
|
@ -29,21 +28,21 @@ impl MemoryPoolTransactionOutputProvider {
|
||||||
DoubleSpendCheckResult::DoubleSpend(_, hash, index) => Err(TransactionError::UsingSpentOutput(hash, index)),
|
DoubleSpendCheckResult::DoubleSpend(_, hash, index) => Err(TransactionError::UsingSpentOutput(hash, index)),
|
||||||
// there are no transactions, which are spending same inputs in memory pool
|
// there are no transactions, which are spending same inputs in memory pool
|
||||||
DoubleSpendCheckResult::NoDoubleSpend => Ok(MemoryPoolTransactionOutputProvider {
|
DoubleSpendCheckResult::NoDoubleSpend => Ok(MemoryPoolTransactionOutputProvider {
|
||||||
storage_provider: StorageTransactionOutputProvider::with_storage(storage),
|
storage_provider: storage,
|
||||||
mempool_inputs: transaction.inputs.iter()
|
mempool_inputs: transaction.inputs.iter()
|
||||||
.map(|input| (
|
.map(|input| (
|
||||||
input.previous_output.clone().into(),
|
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(),
|
)).collect(),
|
||||||
nonfinal_spends: None,
|
nonfinal_spends: None,
|
||||||
}),
|
}),
|
||||||
// there are non-final transactions, which are spending same inputs in memory pool
|
// there are non-final transactions, which are spending same inputs in memory pool
|
||||||
DoubleSpendCheckResult::NonFinalDoubleSpend(nonfinal_spends) => Ok(MemoryPoolTransactionOutputProvider {
|
DoubleSpendCheckResult::NonFinalDoubleSpend(nonfinal_spends) => Ok(MemoryPoolTransactionOutputProvider {
|
||||||
storage_provider: StorageTransactionOutputProvider::with_storage(storage),
|
storage_provider: storage,
|
||||||
mempool_inputs: transaction.inputs.iter()
|
mempool_inputs: transaction.inputs.iter()
|
||||||
.map(|input| (
|
.map(|input| (
|
||||||
input.previous_output.clone().into(),
|
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(),
|
)).collect(),
|
||||||
nonfinal_spends: Some(nonfinal_spends),
|
nonfinal_spends: Some(nonfinal_spends),
|
||||||
}),
|
}),
|
||||||
|
@ -51,23 +50,8 @@ impl MemoryPoolTransactionOutputProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionOutputObserver for MemoryPoolTransactionOutputProvider {
|
impl TransactionOutputProvider for MemoryPoolTransactionOutputProvider {
|
||||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool> {
|
fn transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
||||||
// 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 Some(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> {
|
|
||||||
let hashed_prevout: HashedOutPoint = prevout.clone().into();
|
let hashed_prevout: HashedOutPoint = prevout.clone().into();
|
||||||
|
|
||||||
// check if that is output of some transaction, which is vitually removed from memory pool
|
// check if that is output of some transaction, which is vitually removed from memory pool
|
||||||
|
@ -88,7 +72,20 @@ impl PreviousTransactionOutputProvider for MemoryPoolTransactionOutputProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check in storage
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +96,7 @@ mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use chain::OutPoint;
|
use chain::OutPoint;
|
||||||
use db::{TransactionOutputObserver, PreviousTransactionOutputProvider, BlockChainDatabase};
|
use db::{TransactionOutputProvider, BlockChainDatabase};
|
||||||
use miner::MemoryPool;
|
use miner::MemoryPool;
|
||||||
use super::MemoryPoolTransactionOutputProvider;
|
use super::MemoryPoolTransactionOutputProvider;
|
||||||
|
|
||||||
|
@ -130,11 +127,11 @@ mod tests {
|
||||||
// =>
|
// =>
|
||||||
// if t3 is also depending on t1[0] || t2[0], it will be rejected by verification as missing inputs
|
// 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();
|
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, }), Some(false));
|
assert_eq!(provider.is_double_spent(&OutPoint { hash: dchain.at(0).hash(), index: 0, }), false);
|
||||||
assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), None);
|
assert_eq!(provider.is_double_spent(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), false);
|
||||||
assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(2).hash(), index: 0, }), None);
|
assert_eq!(provider.is_double_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.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.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.transaction_output(&OutPoint { hash: dchain.at(2).hash(), index: 0, }, 0), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ mod message_block_headers_provider;
|
||||||
mod orphan_blocks_pool;
|
mod orphan_blocks_pool;
|
||||||
mod orphan_transactions_pool;
|
mod orphan_transactions_pool;
|
||||||
mod partial_merkle_tree;
|
mod partial_merkle_tree;
|
||||||
mod storage_transaction_provider;
|
|
||||||
mod synchronization_state;
|
mod synchronization_state;
|
||||||
|
|
||||||
pub use self::average_speed_meter::AverageSpeedMeter;
|
pub use self::average_speed_meter::AverageSpeedMeter;
|
||||||
|
@ -27,7 +26,6 @@ pub use self::message_block_headers_provider::MessageBlockHeadersProvider;
|
||||||
pub use self::orphan_blocks_pool::OrphanBlocksPool;
|
pub use self::orphan_blocks_pool::OrphanBlocksPool;
|
||||||
pub use self::orphan_transactions_pool::{OrphanTransactionsPool, OrphanTransaction};
|
pub use self::orphan_transactions_pool::{OrphanTransactionsPool, OrphanTransaction};
|
||||||
pub use self::partial_merkle_tree::{PartialMerkleTree, build_partial_merkle_tree};
|
pub use self::partial_merkle_tree::{PartialMerkleTree, build_partial_merkle_tree};
|
||||||
pub use self::storage_transaction_provider::StorageTransactionOutputProvider;
|
|
||||||
pub use self::synchronization_state::SynchronizationState;
|
pub use self::synchronization_state::SynchronizationState;
|
||||||
|
|
||||||
/// Block height type
|
/// Block height type
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
use chain::{TransactionOutput, OutPoint};
|
|
||||||
use db::{PreviousTransactionOutputProvider, TransactionOutputObserver};
|
|
||||||
use super::super::types::StorageRef;
|
|
||||||
|
|
||||||
/// Transaction output observer, which looks into storage
|
|
||||||
pub struct StorageTransactionOutputProvider {
|
|
||||||
/// Storage reference
|
|
||||||
storage: StorageRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StorageTransactionOutputProvider {
|
|
||||||
pub fn with_storage(storage: StorageRef) -> Self {
|
|
||||||
StorageTransactionOutputProvider {
|
|
||||||
storage: storage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransactionOutputObserver for StorageTransactionOutputProvider {
|
|
||||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool> {
|
|
||||||
self.storage
|
|
||||||
.transaction_meta(&prevout.hash)
|
|
||||||
.and_then(|tm| tm.is_spent(prevout.index as usize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PreviousTransactionOutputProvider for StorageTransactionOutputProvider {
|
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
|
||||||
self.storage.as_previous_transaction_output_provider().previous_transaction_output(prevout, transaction_index)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
use network::{Magic, ConsensusParams};
|
use network::{Magic, ConsensusParams};
|
||||||
use db::PreviousTransactionOutputProvider;
|
use db::TransactionOutputProvider;
|
||||||
use sigops::transaction_sigops;
|
use sigops::transaction_sigops;
|
||||||
use work::block_reward_satoshi;
|
use work::block_reward_satoshi;
|
||||||
use duplex_store::DuplexTransactionOutputProvider;
|
use duplex_store::DuplexTransactionOutputProvider;
|
||||||
|
@ -15,7 +15,7 @@ pub struct BlockAcceptor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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();
|
let params = network.consensus_params();
|
||||||
BlockAcceptor {
|
BlockAcceptor {
|
||||||
finality: BlockFinality::new(block, height),
|
finality: BlockFinality::new(block, height),
|
||||||
|
@ -32,11 +32,6 @@ impl<'a> BlockAcceptor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait BlockRule {
|
|
||||||
/// If verification fails returns an error
|
|
||||||
fn check(&self) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BlockFinality<'a> {
|
pub struct BlockFinality<'a> {
|
||||||
block: CanonBlock<'a>,
|
block: CanonBlock<'a>,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
@ -49,9 +44,7 @@ impl<'a> BlockFinality<'a> {
|
||||||
height: height,
|
height: height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockFinality<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if self.block.is_final(self.height) {
|
if self.block.is_final(self.height) {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -63,13 +56,13 @@ impl<'a> BlockRule for BlockFinality<'a> {
|
||||||
|
|
||||||
pub struct BlockSigops<'a> {
|
pub struct BlockSigops<'a> {
|
||||||
block: CanonBlock<'a>,
|
block: CanonBlock<'a>,
|
||||||
store: &'a PreviousTransactionOutputProvider,
|
store: &'a TransactionOutputProvider,
|
||||||
consensus_params: ConsensusParams,
|
consensus_params: ConsensusParams,
|
||||||
max_sigops: usize,
|
max_sigops: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockSigops<'a> {
|
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 {
|
BlockSigops {
|
||||||
block: block,
|
block: block,
|
||||||
store: store,
|
store: store,
|
||||||
|
@ -77,9 +70,7 @@ impl<'a> BlockSigops<'a> {
|
||||||
max_sigops: max_sigops,
|
max_sigops: max_sigops,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockSigops<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let store = DuplexTransactionOutputProvider::new(self.store, &*self.block);
|
let store = DuplexTransactionOutputProvider::new(self.store, &*self.block);
|
||||||
let bip16_active = self.block.header.raw.time >= self.consensus_params.bip16_time;
|
let bip16_active = self.block.header.raw.time >= self.consensus_params.bip16_time;
|
||||||
|
@ -97,21 +88,19 @@ impl<'a> BlockRule for BlockSigops<'a> {
|
||||||
|
|
||||||
pub struct BlockCoinbaseClaim<'a> {
|
pub struct BlockCoinbaseClaim<'a> {
|
||||||
block: CanonBlock<'a>,
|
block: CanonBlock<'a>,
|
||||||
store: &'a PreviousTransactionOutputProvider,
|
store: &'a TransactionOutputProvider,
|
||||||
height: u32,
|
height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockCoinbaseClaim<'a> {
|
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 {
|
BlockCoinbaseClaim {
|
||||||
block: block,
|
block: block,
|
||||||
store: store,
|
store: store,
|
||||||
height: height,
|
height: height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockCoinbaseClaim<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let store = DuplexTransactionOutputProvider::new(self.store, &*self.block);
|
let store = DuplexTransactionOutputProvider::new(self.store, &*self.block);
|
||||||
|
|
||||||
|
@ -122,7 +111,7 @@ impl<'a> BlockRule for BlockCoinbaseClaim<'a> {
|
||||||
let mut incoming: u64 = 0;
|
let mut incoming: u64 = 0;
|
||||||
for input in tx.raw.inputs.iter() {
|
for input in tx.raw.inputs.iter() {
|
||||||
let (sum, overflow) = incoming.overflowing_add(
|
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 {
|
if overflow {
|
||||||
return Err(Error::ReferencedInputsSumOverflow);
|
return Err(Error::ReferencedInputsSumOverflow);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use canon::CanonBlock;
|
||||||
use accept_block::BlockAcceptor;
|
use accept_block::BlockAcceptor;
|
||||||
use accept_header::HeaderAcceptor;
|
use accept_header::HeaderAcceptor;
|
||||||
use accept_transaction::TransactionAcceptor;
|
use accept_transaction::TransactionAcceptor;
|
||||||
use duplex_store::{DuplexTransactionOutputProvider, DuplexTransactionOutputObserver};
|
use duplex_store::DuplexTransactionOutputProvider;
|
||||||
|
|
||||||
pub struct ChainAcceptor<'a> {
|
pub struct ChainAcceptor<'a> {
|
||||||
pub block: BlockAcceptor<'a>,
|
pub block: BlockAcceptor<'a>,
|
||||||
|
@ -17,24 +17,22 @@ pub struct ChainAcceptor<'a> {
|
||||||
impl<'a> ChainAcceptor<'a> {
|
impl<'a> ChainAcceptor<'a> {
|
||||||
pub fn new(store: &'a Store, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
|
pub fn new(store: &'a Store, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
|
||||||
trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
|
trace!(target: "verification", "Block verification {}", block.hash().to_reversed_str());
|
||||||
let prevouts = DuplexTransactionOutputProvider::new(store.as_previous_transaction_output_provider(), block.raw());
|
let output_store = DuplexTransactionOutputProvider::new(store.as_transaction_output_provider(), block.raw());
|
||||||
let spents = DuplexTransactionOutputObserver::new(store.as_transaction_output_observer(), block.raw());
|
|
||||||
ChainAcceptor {
|
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),
|
header: HeaderAcceptor::new(store.as_block_header_provider(), network, block.header(), height),
|
||||||
transactions: block.transactions()
|
transactions: block.transactions()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, tx)| TransactionAcceptor::new(
|
.map(|(tx_index, tx)| TransactionAcceptor::new(
|
||||||
store.as_transaction_meta_provider(),
|
store.as_transaction_meta_provider(),
|
||||||
prevouts,
|
output_store,
|
||||||
spents,
|
|
||||||
network,
|
network,
|
||||||
tx,
|
tx,
|
||||||
block.hash(),
|
block.hash(),
|
||||||
height,
|
height,
|
||||||
block.header.raw.time,
|
block.header.raw.time,
|
||||||
index
|
tx_index
|
||||||
))
|
))
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,6 @@ impl<'a> HeaderAcceptor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HeaderRule {
|
|
||||||
fn check(&self) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HeaderVersion<'a> {
|
pub struct HeaderVersion<'a> {
|
||||||
header: CanonHeader<'a>,
|
header: CanonHeader<'a>,
|
||||||
min_version: u32,
|
min_version: u32,
|
||||||
|
@ -46,9 +42,7 @@ impl<'a> HeaderVersion<'a> {
|
||||||
min_version: min_version,
|
min_version: min_version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HeaderRule for HeaderVersion<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if self.header.raw.version < self.min_version {
|
if self.header.raw.version < self.min_version {
|
||||||
Err(Error::OldVersionBlock)
|
Err(Error::OldVersionBlock)
|
||||||
|
@ -74,9 +68,7 @@ impl<'a> HeaderWork<'a> {
|
||||||
network: network,
|
network: network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HeaderRule for HeaderWork<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let previous_header_hash = self.header.raw.previous_header_hash.clone();
|
let previous_header_hash = self.header.raw.previous_header_hash.clone();
|
||||||
let time = self.header.raw.time;
|
let time = self.header.raw.time;
|
||||||
|
@ -103,9 +95,7 @@ impl<'a> HeaderMedianTimestamp<'a> {
|
||||||
network: network,
|
network: network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HeaderRule for HeaderMedianTimestamp<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let median = median_timestamp(&self.header.raw, self.store, self.network);
|
let median = median_timestamp(&self.header.raw, self.store, self.network);
|
||||||
if self.header.raw.time <= median {
|
if self.header.raw.time <= median {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use db::{TransactionMetaProvider, PreviousTransactionOutputProvider, TransactionOutputObserver};
|
use db::{TransactionMetaProvider, TransactionOutputProvider};
|
||||||
use network::{Magic, ConsensusParams};
|
use network::{Magic, ConsensusParams};
|
||||||
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
|
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
|
||||||
use duplex_store::{DuplexTransactionOutputProvider, DuplexTransactionOutputObserver};
|
use duplex_store::DuplexTransactionOutputProvider;
|
||||||
use sigops::transaction_sigops;
|
use sigops::transaction_sigops;
|
||||||
use canon::CanonTransaction;
|
use canon::CanonTransaction;
|
||||||
use constants::{COINBASE_MATURITY, MAX_BLOCK_SIGOPS};
|
use constants::{COINBASE_MATURITY, MAX_BLOCK_SIGOPS};
|
||||||
|
@ -23,10 +23,7 @@ impl<'a> TransactionAcceptor<'a> {
|
||||||
meta_store: &'a TransactionMetaProvider,
|
meta_store: &'a TransactionMetaProvider,
|
||||||
// previous transaction outputs
|
// previous transaction outputs
|
||||||
// in case of block validation, that's database and currently processed block
|
// in case of block validation, that's database and currently processed block
|
||||||
prevout_store: DuplexTransactionOutputProvider<'a>,
|
output_store: DuplexTransactionOutputProvider<'a>,
|
||||||
// in case of block validation, that's database and currently processed block
|
|
||||||
//spent_store: &'a TransactionOutputObserver,
|
|
||||||
spent_store: DuplexTransactionOutputObserver<'a>,
|
|
||||||
network: Magic,
|
network: Magic,
|
||||||
transaction: CanonTransaction<'a>,
|
transaction: CanonTransaction<'a>,
|
||||||
block_hash: &'a H256,
|
block_hash: &'a H256,
|
||||||
|
@ -38,11 +35,11 @@ impl<'a> TransactionAcceptor<'a> {
|
||||||
let params = network.consensus_params();
|
let params = network.consensus_params();
|
||||||
TransactionAcceptor {
|
TransactionAcceptor {
|
||||||
bip30: TransactionBip30::new_for_sync(transaction, meta_store, params.clone(), block_hash, height),
|
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),
|
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||||
overspent: TransactionOverspent::new(transaction, prevout_store),
|
overspent: TransactionOverspent::new(transaction, output_store),
|
||||||
double_spent: TransactionDoubleSpend::new(transaction, spent_store),
|
double_spent: TransactionDoubleSpend::new(transaction, output_store),
|
||||||
eval: TransactionEval::new(transaction, prevout_store, params, height, time),
|
eval: TransactionEval::new(transaction, output_store, params, height, time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +55,6 @@ impl<'a> TransactionAcceptor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MemoryPoolTransactionAcceptor<'a> {
|
pub struct MemoryPoolTransactionAcceptor<'a> {
|
||||||
pub bip30: TransactionBip30<'a>,
|
|
||||||
pub missing_inputs: TransactionMissingInputs<'a>,
|
pub missing_inputs: TransactionMissingInputs<'a>,
|
||||||
pub maturity: TransactionMaturity<'a>,
|
pub maturity: TransactionMaturity<'a>,
|
||||||
pub overspent: TransactionOverspent<'a>,
|
pub overspent: TransactionOverspent<'a>,
|
||||||
|
@ -72,10 +68,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
||||||
// TODO: in case of memory pool it should be db and memory pool
|
// TODO: in case of memory pool it should be db and memory pool
|
||||||
meta_store: &'a TransactionMetaProvider,
|
meta_store: &'a TransactionMetaProvider,
|
||||||
// in case of memory pool it should be db and memory pool
|
// in case of memory pool it should be db and memory pool
|
||||||
prevout_store: DuplexTransactionOutputProvider<'a>,
|
output_store: DuplexTransactionOutputProvider<'a>,
|
||||||
// in case of memory pool it should be db and memory pool
|
|
||||||
//spent_store: &'a TransactionOutputObserver,
|
|
||||||
spent_store: DuplexTransactionOutputObserver<'a>,
|
|
||||||
network: Magic,
|
network: Magic,
|
||||||
transaction: CanonTransaction<'a>,
|
transaction: CanonTransaction<'a>,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
@ -85,19 +78,18 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
||||||
let params = network.consensus_params();
|
let params = network.consensus_params();
|
||||||
let transaction_index = 0;
|
let transaction_index = 0;
|
||||||
MemoryPoolTransactionAcceptor {
|
MemoryPoolTransactionAcceptor {
|
||||||
bip30: TransactionBip30::new_for_mempool(transaction, meta_store),
|
missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index),
|
||||||
missing_inputs: TransactionMissingInputs::new(transaction, prevout_store, transaction_index),
|
|
||||||
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||||
overspent: TransactionOverspent::new(transaction, prevout_store),
|
overspent: TransactionOverspent::new(transaction, output_store),
|
||||||
sigops: TransactionSigops::new(transaction, prevout_store, params.clone(), MAX_BLOCK_SIGOPS, time),
|
sigops: TransactionSigops::new(transaction, output_store, params.clone(), MAX_BLOCK_SIGOPS, time),
|
||||||
double_spent: TransactionDoubleSpend::new(transaction, spent_store),
|
double_spent: TransactionDoubleSpend::new(transaction, output_store),
|
||||||
eval: TransactionEval::new(transaction, prevout_store, params, height, time),
|
eval: TransactionEval::new(transaction, output_store, params, height, time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self) -> Result<(), TransactionError> {
|
pub fn check(&self) -> Result<(), TransactionError> {
|
||||||
// TODO: b82 fails, when this is enabled, fix this
|
// Bip30 is not checked because we don't need to allow tx pool acceptance of an unspent duplicate.
|
||||||
//try!(self.bip30.check());
|
// Tx pool validation is not strinctly a matter of consensus.
|
||||||
try!(self.missing_inputs.check());
|
try!(self.missing_inputs.check());
|
||||||
try!(self.maturity.check());
|
try!(self.maturity.check());
|
||||||
try!(self.overspent.check());
|
try!(self.overspent.check());
|
||||||
|
@ -108,10 +100,15 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TransactionRule {
|
/// Bip30 validation
|
||||||
fn check(&self) -> Result<(), TransactionError>;
|
///
|
||||||
}
|
/// A transaction hash that exists in the chain is not acceptable even if
|
||||||
|
/// the original is spent in the new block. This is not necessary nor is it
|
||||||
|
/// described by BIP30, but it is in the code referenced by BIP30. As such
|
||||||
|
/// the tx pool need only test against the chain, skipping the pool.
|
||||||
|
///
|
||||||
|
/// source:
|
||||||
|
/// https://github.com/libbitcoin/libbitcoin/blob/61759b2fd66041bcdbc124b2f04ed5ddc20c7312/src/chain/transaction.cpp#L780-L785
|
||||||
pub struct TransactionBip30<'a> {
|
pub struct TransactionBip30<'a> {
|
||||||
transaction: CanonTransaction<'a>,
|
transaction: CanonTransaction<'a>,
|
||||||
store: &'a TransactionMetaProvider,
|
store: &'a TransactionMetaProvider,
|
||||||
|
@ -135,26 +132,7 @@ impl<'a> TransactionBip30<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_for_mempool(transaction: CanonTransaction<'a>, store: &'a TransactionMetaProvider) -> Self {
|
|
||||||
TransactionBip30 {
|
|
||||||
transaction: transaction,
|
|
||||||
store: store,
|
|
||||||
exception: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionBip30<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
// we allow optionals here, cause previous output may be a part of current block
|
|
||||||
// yet, we do not need to check current block, cause duplicated transactions
|
|
||||||
// in the same block are also forbidden
|
|
||||||
//
|
|
||||||
// update*
|
|
||||||
// TODO:
|
|
||||||
// There is a potential consensus failure here, cause transaction before this one
|
|
||||||
// may have fully spent the output, and we, by checking only storage, have no knowladge
|
|
||||||
// of it
|
|
||||||
match self.store.transaction_meta(&self.transaction.hash) {
|
match self.store.transaction_meta(&self.transaction.hash) {
|
||||||
Some(ref meta) if !meta.is_fully_spent() && !self.exception => {
|
Some(ref meta) if !meta.is_fully_spent() && !self.exception => {
|
||||||
Err(TransactionError::UnspentTransactionWithTheSameHash)
|
Err(TransactionError::UnspentTransactionWithTheSameHash)
|
||||||
|
@ -178,14 +156,12 @@ impl<'a> TransactionMissingInputs<'a> {
|
||||||
transaction_index: transaction_index,
|
transaction_index: transaction_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionMissingInputs<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
let missing_index = self.transaction.raw.inputs.iter()
|
let missing_index = self.transaction.raw.inputs.iter()
|
||||||
.position(|input| {
|
.position(|input| {
|
||||||
let is_not_null = !input.previous_output.is_null();
|
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
|
is_not_null && is_missing
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -210,9 +186,7 @@ impl<'a> TransactionMaturity<'a> {
|
||||||
height: height,
|
height: height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionMaturity<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
// TODO: this is should also fail when we are trying to spend current block coinbase
|
// TODO: this is should also fail when we are trying to spend current block coinbase
|
||||||
let immature_spend = self.transaction.raw.inputs.iter()
|
let immature_spend = self.transaction.raw.inputs.iter()
|
||||||
|
@ -241,16 +215,14 @@ impl<'a> TransactionOverspent<'a> {
|
||||||
store: store,
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionOverspent<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if self.transaction.raw.is_coinbase() {
|
if self.transaction.raw.is_coinbase() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let available = self.transaction.raw.inputs.iter()
|
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>();
|
.sum::<u64>();
|
||||||
|
|
||||||
let spends = self.transaction.raw.total_spends();
|
let spends = self.transaction.raw.total_spends();
|
||||||
|
@ -281,9 +253,7 @@ impl<'a> TransactionSigops<'a> {
|
||||||
time: time,
|
time: time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionSigops<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
let bip16_active = self.time >= self.consensus_params.bip16_time;
|
let bip16_active = self.time >= self.consensus_params.bip16_time;
|
||||||
let sigops = transaction_sigops(&self.transaction.raw, &self.store, bip16_active);
|
let sigops = transaction_sigops(&self.transaction.raw, &self.store, bip16_active);
|
||||||
|
@ -323,9 +293,7 @@ impl<'a> TransactionEval<'a> {
|
||||||
verify_dersig: verify_dersig,
|
verify_dersig: verify_dersig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionEval<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if self.transaction.raw.is_coinbase() {
|
if self.transaction.raw.is_coinbase() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -339,7 +307,7 @@ impl<'a> TransactionRule for TransactionEval<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (index, input) in self.transaction.raw.inputs.iter().enumerate() {
|
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()))?;
|
.ok_or_else(|| TransactionError::UnknownReference(input.previous_output.hash.clone()))?;
|
||||||
|
|
||||||
checker.input_index = index;
|
checker.input_index = index;
|
||||||
|
@ -361,24 +329,20 @@ impl<'a> TransactionRule for TransactionEval<'a> {
|
||||||
|
|
||||||
pub struct TransactionDoubleSpend<'a> {
|
pub struct TransactionDoubleSpend<'a> {
|
||||||
transaction: CanonTransaction<'a>,
|
transaction: CanonTransaction<'a>,
|
||||||
//store: &'a TransactionOutputObserver,
|
store: DuplexTransactionOutputProvider<'a>,
|
||||||
store: DuplexTransactionOutputObserver<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TransactionDoubleSpend<'a> {
|
impl<'a> TransactionDoubleSpend<'a> {
|
||||||
//fn new(transaction: CanonTransaction<'a>, store: &'a TransactionOutputObserver) -> Self {
|
fn new(transaction: CanonTransaction<'a>, store: DuplexTransactionOutputProvider<'a>) -> Self {
|
||||||
fn new(transaction: CanonTransaction<'a>, store: DuplexTransactionOutputObserver<'a>) -> Self {
|
|
||||||
TransactionDoubleSpend {
|
TransactionDoubleSpend {
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
store: store,
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionDoubleSpend<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
for input in &self.transaction.raw.inputs {
|
for input in &self.transaction.raw.inputs {
|
||||||
if self.store.is_spent(&input.previous_output).unwrap_or(false) {
|
if self.store.is_double_spent(&input.previous_output) {
|
||||||
return Err(TransactionError::UsingSpentOutput(
|
return Err(TransactionError::UsingSpentOutput(
|
||||||
input.previous_output.hash.clone(),
|
input.previous_output.hash.clone(),
|
||||||
input.previous_output.index
|
input.previous_output.index
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
|
|
||||||
use hash::H256;
|
use hash::H256;
|
||||||
use chain::{IndexedBlock, IndexedBlockHeader, BlockHeader, Transaction};
|
use chain::{IndexedBlock, IndexedBlockHeader, BlockHeader, Transaction};
|
||||||
use db::{
|
use db::{SharedStore, TransactionOutputProvider, BlockHeaderProvider, BlockOrigin};
|
||||||
SharedStore, PreviousTransactionOutputProvider, BlockHeaderProvider, TransactionOutputObserver,
|
|
||||||
BlockOrigin
|
|
||||||
};
|
|
||||||
use network::Magic;
|
use network::Magic;
|
||||||
use error::{Error, TransactionError};
|
use error::{Error, TransactionError};
|
||||||
use canon::{CanonBlock, CanonTransaction};
|
use canon::{CanonBlock, CanonTransaction};
|
||||||
use duplex_store::{DuplexTransactionOutputObserver, DuplexTransactionOutputProvider, NoopStore};
|
use duplex_store::{DuplexTransactionOutputProvider, NoopStore};
|
||||||
use verify_chain::ChainVerifier;
|
use verify_chain::ChainVerifier;
|
||||||
use verify_header::HeaderVerifier;
|
use verify_header::HeaderVerifier;
|
||||||
use verify_transaction::MemoryPoolTransactionVerifier;
|
use verify_transaction::MemoryPoolTransactionVerifier;
|
||||||
|
@ -89,7 +86,7 @@ impl BackwardsCompatibleChainVerifier {
|
||||||
height: u32,
|
height: u32,
|
||||||
time: u32,
|
time: u32,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
) -> Result<(), TransactionError> where T: PreviousTransactionOutputProvider + TransactionOutputObserver {
|
) -> Result<(), TransactionError> where T: TransactionOutputProvider {
|
||||||
let indexed_tx = transaction.clone().into();
|
let indexed_tx = transaction.clone().into();
|
||||||
// let's do preverification first
|
// let's do preverification first
|
||||||
let tx_verifier = MemoryPoolTransactionVerifier::new(&indexed_tx);
|
let tx_verifier = MemoryPoolTransactionVerifier::new(&indexed_tx);
|
||||||
|
@ -98,12 +95,10 @@ impl BackwardsCompatibleChainVerifier {
|
||||||
let canon_tx = CanonTransaction::new(&indexed_tx);
|
let canon_tx = CanonTransaction::new(&indexed_tx);
|
||||||
// now let's do full verification
|
// now let's do full verification
|
||||||
let noop = NoopStore;
|
let noop = NoopStore;
|
||||||
let prevouts = DuplexTransactionOutputProvider::new(prevout_provider, &noop);
|
let output_store = DuplexTransactionOutputProvider::new(prevout_provider, &noop);
|
||||||
let spents = DuplexTransactionOutputObserver::new(prevout_provider, &noop);
|
|
||||||
let tx_acceptor = MemoryPoolTransactionAcceptor::new(
|
let tx_acceptor = MemoryPoolTransactionAcceptor::new(
|
||||||
self.store.as_transaction_meta_provider(),
|
self.store.as_transaction_meta_provider(),
|
||||||
prevouts,
|
output_store,
|
||||||
spents,
|
|
||||||
self.network,
|
self.network,
|
||||||
canon_tx,
|
canon_tx,
|
||||||
height,
|
height,
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
//! require sophisticated (in more than one source) previous transaction lookups
|
//! require sophisticated (in more than one source) previous transaction lookups
|
||||||
|
|
||||||
use chain::{OutPoint, TransactionOutput};
|
use chain::{OutPoint, TransactionOutput};
|
||||||
use db::{PreviousTransactionOutputProvider, TransactionOutputObserver};
|
use db::TransactionOutputProvider;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct DuplexTransactionOutputProvider<'a> {
|
pub struct DuplexTransactionOutputProvider<'a> {
|
||||||
first: &'a PreviousTransactionOutputProvider,
|
first: &'a TransactionOutputProvider,
|
||||||
second: &'a PreviousTransactionOutputProvider,
|
second: &'a TransactionOutputProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DuplexTransactionOutputProvider<'a> {
|
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 {
|
DuplexTransactionOutputProvider {
|
||||||
first: first,
|
first: first,
|
||||||
second: second,
|
second: second,
|
||||||
|
@ -19,48 +19,25 @@ impl<'a> DuplexTransactionOutputProvider<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PreviousTransactionOutputProvider for DuplexTransactionOutputProvider<'a> {
|
impl<'a> TransactionOutputProvider for DuplexTransactionOutputProvider<'a> {
|
||||||
fn previous_transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
fn transaction_output(&self, prevout: &OutPoint, transaction_index: usize) -> Option<TransactionOutput> {
|
||||||
self.first.previous_transaction_output(prevout, transaction_index)
|
self.first.transaction_output(prevout, transaction_index)
|
||||||
.or_else(|| self.second.previous_transaction_output(prevout, transaction_index))
|
.or_else(|| self.second.transaction_output(prevout, transaction_index))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
fn is_double_spent(&self, prevout: &OutPoint) -> bool {
|
||||||
pub struct DuplexTransactionOutputObserver<'a> {
|
self.first.is_double_spent(prevout) || self.second.is_double_spent(prevout)
|
||||||
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) -> Option<bool> {
|
|
||||||
if self.first.is_spent(prevout).unwrap_or(false) {
|
|
||||||
Some(true)
|
|
||||||
} else {
|
|
||||||
self.second.is_spent(prevout)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoopStore;
|
pub struct NoopStore;
|
||||||
|
|
||||||
impl PreviousTransactionOutputProvider for NoopStore {
|
impl TransactionOutputProvider for NoopStore {
|
||||||
fn previous_transaction_output(&self, _prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
|
fn transaction_output(&self, _prevout: &OutPoint, _transaction_index: usize) -> Option<TransactionOutput> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TransactionOutputObserver for NoopStore {
|
fn is_double_spent(&self, _prevout: &OutPoint) -> bool {
|
||||||
fn is_spent(&self, _prevout: &OutPoint) -> Option<bool> {
|
false
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use chain::Transaction;
|
use chain::Transaction;
|
||||||
use db::PreviousTransactionOutputProvider;
|
use db::TransactionOutputProvider;
|
||||||
use script::Script;
|
use script::Script;
|
||||||
|
|
||||||
/// Counts signature operations in given transaction
|
/// Counts signature operations in given transaction
|
||||||
|
@ -8,7 +8,7 @@ use script::Script;
|
||||||
/// missing, we simply ignore that fact and just carry on counting
|
/// missing, we simply ignore that fact and just carry on counting
|
||||||
pub fn transaction_sigops(
|
pub fn transaction_sigops(
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
store: &PreviousTransactionOutputProvider,
|
store: &TransactionOutputProvider,
|
||||||
bip16_active: bool
|
bip16_active: bool
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let output_sigops: usize = transaction.outputs.iter().map(|output| {
|
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();
|
let input_script: Script = input.script_sig.clone().into();
|
||||||
input_sigops += input_script.sigops_count(false);
|
input_sigops += input_script.sigops_count(false);
|
||||||
if bip16_active {
|
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,
|
Some(output) => output,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,10 +40,6 @@ impl<'a> BlockVerifier<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait BlockRule {
|
|
||||||
fn check(&self) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BlockEmpty<'a> {
|
pub struct BlockEmpty<'a> {
|
||||||
block: &'a IndexedBlock,
|
block: &'a IndexedBlock,
|
||||||
}
|
}
|
||||||
|
@ -54,9 +50,7 @@ impl<'a> BlockEmpty<'a> {
|
||||||
block: block,
|
block: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockEmpty<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if self.block.transactions.is_empty() {
|
if self.block.transactions.is_empty() {
|
||||||
Err(Error::Empty)
|
Err(Error::Empty)
|
||||||
|
@ -78,9 +72,7 @@ impl<'a> BlockSerializedSize<'a> {
|
||||||
max_size: max_size,
|
max_size: max_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockSerializedSize<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let size = self.block.size();
|
let size = self.block.size();
|
||||||
if size > self.max_size {
|
if size > self.max_size {
|
||||||
|
@ -101,9 +93,7 @@ impl<'a> BlockCoinbase<'a> {
|
||||||
block: block,
|
block: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockCoinbase<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if self.block.transactions.first().map(|tx| tx.raw.is_coinbase()).unwrap_or(false) {
|
if self.block.transactions.first().map(|tx| tx.raw.is_coinbase()).unwrap_or(false) {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -123,9 +113,7 @@ impl<'a> BlockExtraCoinbases<'a> {
|
||||||
block: block,
|
block: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockExtraCoinbases<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let misplaced = self.block.transactions.iter()
|
let misplaced = self.block.transactions.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
@ -148,9 +136,7 @@ impl<'a> BlockTransactionsUniqueness<'a> {
|
||||||
block: block,
|
block: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockTransactionsUniqueness<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let hashes = self.block.transactions.iter().map(|tx| tx.hash.clone()).collect::<HashSet<_>>();
|
let hashes = self.block.transactions.iter().map(|tx| tx.hash.clone()).collect::<HashSet<_>>();
|
||||||
if hashes.len() == self.block.transactions.len() {
|
if hashes.len() == self.block.transactions.len() {
|
||||||
|
@ -173,9 +159,7 @@ impl<'a> BlockSigops<'a> {
|
||||||
max_sigops: max_sigops,
|
max_sigops: max_sigops,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockSigops<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
// We cannot know if bip16 is enabled at this point so we disable it.
|
// We cannot know if bip16 is enabled at this point so we disable it.
|
||||||
let sigops = self.block.transactions.iter()
|
let sigops = self.block.transactions.iter()
|
||||||
|
@ -200,9 +184,7 @@ impl<'a> BlockMerkleRoot<'a> {
|
||||||
block: block,
|
block: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockMerkleRoot<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if self.block.merkle_root() == self.block.header.raw.merkle_root_hash {
|
if self.block.merkle_root() == self.block.header.raw.merkle_root_hash {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -25,10 +25,6 @@ impl<'a> HeaderVerifier<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HeaderRule {
|
|
||||||
fn check(&self) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HeaderProofOfWork<'a> {
|
pub struct HeaderProofOfWork<'a> {
|
||||||
header: &'a IndexedBlockHeader,
|
header: &'a IndexedBlockHeader,
|
||||||
max_work_bits: Compact,
|
max_work_bits: Compact,
|
||||||
|
@ -41,9 +37,7 @@ impl<'a> HeaderProofOfWork<'a> {
|
||||||
max_work_bits: network.max_bits(),
|
max_work_bits: network.max_bits(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HeaderRule for HeaderProofOfWork<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if is_valid_proof_of_work(self.max_work_bits, self.header.raw.bits, &self.header.hash) {
|
if is_valid_proof_of_work(self.max_work_bits, self.header.raw.bits, &self.header.hash) {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -67,9 +61,7 @@ impl<'a> HeaderTimestamp<'a> {
|
||||||
max_future: max_future,
|
max_future: max_future,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HeaderRule for HeaderTimestamp<'a> {
|
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
if self.header.raw.time > self.current_time + self.max_future {
|
if self.header.raw.time > self.current_time + self.max_future {
|
||||||
Err(Error::FuturisticTimestamp)
|
Err(Error::FuturisticTimestamp)
|
||||||
|
|
|
@ -60,10 +60,6 @@ impl<'a> MemoryPoolTransactionVerifier<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TransactionRule {
|
|
||||||
fn check(&self) -> Result<(), TransactionError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TransactionEmpty<'a> {
|
pub struct TransactionEmpty<'a> {
|
||||||
transaction: &'a IndexedTransaction,
|
transaction: &'a IndexedTransaction,
|
||||||
}
|
}
|
||||||
|
@ -74,9 +70,7 @@ impl<'a> TransactionEmpty<'a> {
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionEmpty<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if self.transaction.raw.is_empty() {
|
if self.transaction.raw.is_empty() {
|
||||||
Err(TransactionError::Empty)
|
Err(TransactionError::Empty)
|
||||||
|
@ -96,9 +90,7 @@ impl<'a> TransactionNullNonCoinbase<'a> {
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionNullNonCoinbase<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if !self.transaction.raw.is_coinbase() && self.transaction.raw.is_null() {
|
if !self.transaction.raw.is_coinbase() && self.transaction.raw.is_null() {
|
||||||
Err(TransactionError::NullNonCoinbase)
|
Err(TransactionError::NullNonCoinbase)
|
||||||
|
@ -120,9 +112,7 @@ impl<'a> TransactionOversizedCoinbase<'a> {
|
||||||
size_range: size_range,
|
size_range: size_range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionOversizedCoinbase<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if self.transaction.raw.is_coinbase() {
|
if self.transaction.raw.is_coinbase() {
|
||||||
let script_len = self.transaction.raw.inputs[0].script_sig.len();
|
let script_len = self.transaction.raw.inputs[0].script_sig.len();
|
||||||
|
@ -144,9 +134,7 @@ impl<'a> TransactionMemoryPoolCoinbase<'a> {
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionMemoryPoolCoinbase<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if self.transaction.raw.is_coinbase() {
|
if self.transaction.raw.is_coinbase() {
|
||||||
Err(TransactionError::MemoryPoolCoinbase)
|
Err(TransactionError::MemoryPoolCoinbase)
|
||||||
|
@ -168,9 +156,7 @@ impl<'a> TransactionSize<'a> {
|
||||||
max_size: max_size,
|
max_size: max_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionSize<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
if self.transaction.raw.serialized_size() > self.max_size {
|
if self.transaction.raw.serialized_size() > self.max_size {
|
||||||
Err(TransactionError::MaxSize)
|
Err(TransactionError::MaxSize)
|
||||||
|
@ -192,9 +178,7 @@ impl<'a> TransactionSigops<'a> {
|
||||||
max_sigops: max_sigops,
|
max_sigops: max_sigops,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TransactionRule for TransactionSigops<'a> {
|
|
||||||
fn check(&self) -> Result<(), TransactionError> {
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
let sigops = transaction_sigops(&self.transaction.raw, &NoopStore, false);
|
let sigops = transaction_sigops(&self.transaction.raw, &NoopStore, false);
|
||||||
if sigops > self.max_sigops {
|
if sigops > self.max_sigops {
|
||||||
|
|
Loading…
Reference in New Issue