TransactionAcceptor
This commit is contained in:
parent
95c2fa7d8d
commit
1d2e0ce495
|
@ -2,7 +2,6 @@
|
||||||
name = "verification"
|
name = "verification"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"db 0.1.0",
|
"db 0.1.0",
|
||||||
"ethcore-devtools 1.3.0",
|
"ethcore-devtools 1.3.0",
|
||||||
|
|
|
@ -72,6 +72,7 @@ pub use error::{Error, ConsistencyError};
|
||||||
pub use kvdb::Database;
|
pub use kvdb::Database;
|
||||||
pub use transaction_provider::{TransactionProvider, PreviousTransactionOutputProvider};
|
pub use transaction_provider::{TransactionProvider, PreviousTransactionOutputProvider};
|
||||||
pub use transaction_meta_provider::{TransactionMetaProvider, TransactionOutputObserver};
|
pub use transaction_meta_provider::{TransactionMetaProvider, TransactionOutputObserver};
|
||||||
|
pub use transaction_meta::TransactionMeta;
|
||||||
pub use block_stapler::{BlockStapler, BlockInsertedChain};
|
pub use block_stapler::{BlockStapler, BlockInsertedChain};
|
||||||
pub use block_provider::{BlockProvider, BlockHeaderProvider};
|
pub use block_provider::{BlockProvider, BlockHeaderProvider};
|
||||||
pub use indexed_block::IndexedBlock;
|
pub use indexed_block::IndexedBlock;
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub trait TransactionOutputObserver {
|
||||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool>;
|
fn is_spent(&self, prevout: &OutPoint) -> Option<bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TransactionMetaProvider {
|
pub trait TransactionMetaProvider: Send + Sync {
|
||||||
/// get transaction metadata
|
/// get transaction metadata
|
||||||
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
|
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,6 @@ pub trait TransactionProvider {
|
||||||
/// During transaction the only part of old transaction that we need is `TransactionOutput`.
|
/// During transaction 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
|
/// 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.
|
/// a shame to clone the whole transaction just to get single output.
|
||||||
pub trait PreviousTransactionOutputProvider {
|
pub trait PreviousTransactionOutputProvider: Send + Sync {
|
||||||
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>;
|
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ version = "0.1.0"
|
||||||
authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
authors = ["Nikolay Volf <nikvolf@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "0.5"
|
|
||||||
parking_lot = "0.3"
|
parking_lot = "0.3"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use network::{Magic, ConsensusParams};
|
use network::{Magic, ConsensusParams};
|
||||||
use db::{SharedStore, PreviousTransactionOutputProvider};
|
use db::PreviousTransactionOutputProvider;
|
||||||
use sigops::{StoreWithUnretainedOutputs, transaction_sigops};
|
use sigops::transaction_sigops;
|
||||||
use utils::block_reward_satoshi;
|
use utils::block_reward_satoshi;
|
||||||
|
use duplex_store::DuplexTransactionOutputProvider;
|
||||||
use canon::CanonBlock;
|
use canon::CanonBlock;
|
||||||
use constants::MAX_BLOCK_SIGOPS;
|
use constants::MAX_BLOCK_SIGOPS;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
|
|
||||||
const EXPECT_CANON: &'static str = "Block ancestors expected to be found in canon chain";
|
|
||||||
|
|
||||||
/// Flexible verification of ordered block
|
/// Flexible verification of ordered block
|
||||||
pub struct BlockAcceptor<'a> {
|
pub struct BlockAcceptor<'a> {
|
||||||
pub finality: BlockFinality<'a>,
|
pub finality: BlockFinality<'a>,
|
||||||
|
@ -16,12 +15,12 @@ pub struct BlockAcceptor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockAcceptor<'a> {
|
impl<'a> BlockAcceptor<'a> {
|
||||||
pub fn new(store: &'a SharedStore, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
|
pub fn new(store: &'a PreviousTransactionOutputProvider, 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),
|
||||||
sigops: BlockSigops::new(block, store.as_previous_transaction_output_provider(), params, MAX_BLOCK_SIGOPS),
|
sigops: BlockSigops::new(block, store, params, MAX_BLOCK_SIGOPS),
|
||||||
coinbase_claim: BlockCoinbaseClaim::new(block, store.as_previous_transaction_output_provider(), height),
|
coinbase_claim: BlockCoinbaseClaim::new(block, store, height),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,10 +81,13 @@ impl<'a> BlockSigops<'a> {
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockSigops<'a> {
|
impl<'a> BlockRule for BlockSigops<'a> {
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let store = StoreWithUnretainedOutputs::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;
|
||||||
let sigops = self.block.transactions.iter()
|
let sigops = self.block.transactions.iter()
|
||||||
.map(|tx| transaction_sigops(&tx.raw, &store, bip16_active).expect(EXPECT_CANON))
|
.map(|tx| transaction_sigops(&tx.raw, &store, bip16_active))
|
||||||
|
.collect::<Option<Vec<usize>>>()
|
||||||
|
.ok_or_else(|| Error::MaximumSigops)?
|
||||||
|
.into_iter()
|
||||||
.sum::<usize>();
|
.sum::<usize>();
|
||||||
|
|
||||||
if sigops > self.max_sigops {
|
if sigops > self.max_sigops {
|
||||||
|
@ -114,23 +116,22 @@ impl<'a> BlockCoinbaseClaim<'a> {
|
||||||
|
|
||||||
impl<'a> BlockRule for BlockCoinbaseClaim<'a> {
|
impl<'a> BlockRule for BlockCoinbaseClaim<'a> {
|
||||||
fn check(&self) -> Result<(), Error> {
|
fn check(&self) -> Result<(), Error> {
|
||||||
let store = StoreWithUnretainedOutputs::new(self.store, &*self.block);
|
let store = DuplexTransactionOutputProvider::new(self.store, &*self.block);
|
||||||
let total_outputs = self.block.transactions.iter()
|
let available = self.block.transactions.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.flat_map(|tx| tx.raw.inputs.iter())
|
.flat_map(|tx| tx.raw.inputs.iter())
|
||||||
.map(|input| store.previous_transaction_output(&input.previous_output).expect(EXPECT_CANON))
|
.map(|input| store.previous_transaction_output(&input.previous_output).map(|o| o.value).unwrap_or(0))
|
||||||
.map(|output| output.value)
|
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
|
|
||||||
let total_inputs = self.block.transactions.iter()
|
let spends = self.block.transactions.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|tx| tx.raw.total_spends())
|
.map(|tx| tx.raw.total_spends())
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
|
|
||||||
let claim = self.block.transactions[0].raw.total_spends();
|
let claim = self.block.transactions[0].raw.total_spends();
|
||||||
let (fees, overflow) = total_outputs.overflowing_sub(total_inputs);
|
let (fees, overflow) = available.overflowing_sub(spends);
|
||||||
let reward = fees + block_reward_satoshi(self.height);
|
let (reward, overflow2) = fees.overflowing_add(block_reward_satoshi(self.height));
|
||||||
if overflow || claim > reward {
|
if overflow || overflow2 || claim > reward {
|
||||||
Err(Error::CoinbaseOverspend { expected_max: reward, actual: claim })
|
Err(Error::CoinbaseOverspend { expected_max: reward, actual: claim })
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -6,6 +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;
|
||||||
|
|
||||||
pub struct ChainAcceptor<'a> {
|
pub struct ChainAcceptor<'a> {
|
||||||
pub block: BlockAcceptor<'a>,
|
pub block: BlockAcceptor<'a>,
|
||||||
|
@ -15,10 +16,14 @@ pub struct ChainAcceptor<'a> {
|
||||||
|
|
||||||
impl<'a> ChainAcceptor<'a> {
|
impl<'a> ChainAcceptor<'a> {
|
||||||
pub fn new(store: &'a SharedStore, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
|
pub fn new(store: &'a SharedStore, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
|
||||||
|
let prevouts = DuplexTransactionOutputProvider::new(store.as_previous_transaction_output_provider(), block.raw());
|
||||||
ChainAcceptor {
|
ChainAcceptor {
|
||||||
block: BlockAcceptor::new(store, network, block, height),
|
block: BlockAcceptor::new(store.as_previous_transaction_output_provider(), network, block, height),
|
||||||
header: HeaderAcceptor::new(store, network, block.header(), height),
|
header: HeaderAcceptor::new(store.as_block_header_provider(), network, block.header(), height),
|
||||||
transactions: block.transactions().into_iter().map(TransactionAcceptor::new).collect(),
|
transactions: block.transactions()
|
||||||
|
.into_iter()
|
||||||
|
.map(|tx| TransactionAcceptor::new(store.as_transaction_meta_provider(), prevouts, network, tx, block.hash(), height))
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use network::Magic;
|
use network::Magic;
|
||||||
use db::{SharedStore, BlockHeaderProvider};
|
use db::BlockHeaderProvider;
|
||||||
use canon::CanonHeader;
|
use canon::{CanonHeader, EXPECT_CANON};
|
||||||
use constants::MIN_BLOCK_VERSION;
|
use constants::MIN_BLOCK_VERSION;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use utils::work_required;
|
use utils::work_required;
|
||||||
|
|
||||||
const EXPECT_CANON: &'static str = "Block ancestors expected to be found in canon chain";
|
|
||||||
|
|
||||||
pub struct HeaderAcceptor<'a> {
|
pub struct HeaderAcceptor<'a> {
|
||||||
pub version: HeaderVersion<'a>,
|
pub version: HeaderVersion<'a>,
|
||||||
pub work: HeaderWork<'a>,
|
pub work: HeaderWork<'a>,
|
||||||
|
@ -16,12 +14,12 @@ pub struct HeaderAcceptor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HeaderAcceptor<'a> {
|
impl<'a> HeaderAcceptor<'a> {
|
||||||
pub fn new(store: &'a SharedStore, network: Magic, header: CanonHeader<'a>, height: u32) -> Self {
|
pub fn new(store: &'a BlockHeaderProvider, network: Magic, header: CanonHeader<'a>, height: u32) -> Self {
|
||||||
HeaderAcceptor {
|
HeaderAcceptor {
|
||||||
// TODO: check last 1000 blocks instead of hardcoding the value
|
// TODO: check last 1000 blocks instead of hardcoding the value
|
||||||
version: HeaderVersion::new(header, MIN_BLOCK_VERSION),
|
version: HeaderVersion::new(header, MIN_BLOCK_VERSION),
|
||||||
work: HeaderWork::new(header, store.as_block_header_provider(), height, network),
|
work: HeaderWork::new(header, store, height, network),
|
||||||
median_timestamp: HeaderMedianTimestamp::new(header, store.as_block_header_provider(), height, network),
|
median_timestamp: HeaderMedianTimestamp::new(header, store, height, network),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,188 @@
|
||||||
|
use primitives::hash::H256;
|
||||||
|
use db::{TransactionMetaProvider, PreviousTransactionOutputProvider};
|
||||||
|
use network::{Magic, ConsensusParams};
|
||||||
|
use duplex_store::{DuplexTransactionOutputProvider};
|
||||||
use canon::CanonTransaction;
|
use canon::CanonTransaction;
|
||||||
|
use constants::COINBASE_MATURITY;
|
||||||
use error::TransactionError;
|
use error::TransactionError;
|
||||||
|
|
||||||
pub struct TransactionAcceptor<'a> {
|
pub struct TransactionAcceptor<'a> {
|
||||||
_tmp: CanonTransaction<'a>,
|
pub bip30: TransactionBip30<'a>,
|
||||||
|
pub missing_inputs: TransactionMissingInputs<'a>,
|
||||||
|
pub maturity: TransactionMaturity<'a>,
|
||||||
|
pub overspent: TransactionOverspent<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TransactionAcceptor<'a> {
|
impl<'a> TransactionAcceptor<'a> {
|
||||||
pub fn new(transaction: CanonTransaction<'a>) -> Self {
|
pub fn new(
|
||||||
|
// transactions meta
|
||||||
|
// in case of block validation, it's only current block,
|
||||||
|
// TODO: in case of memory pool it should be db and memory pool
|
||||||
|
meta_store: &'a TransactionMetaProvider,
|
||||||
|
// previous transaction outputs
|
||||||
|
// in case of block validation, that's database and currently processed block
|
||||||
|
// in case of memory pool it should be db and memory pool
|
||||||
|
prevout_store: DuplexTransactionOutputProvider<'a>,
|
||||||
|
network: Magic,
|
||||||
|
transaction: CanonTransaction<'a>,
|
||||||
|
block_hash: &'a H256,
|
||||||
|
height: u32
|
||||||
|
) -> Self {
|
||||||
TransactionAcceptor {
|
TransactionAcceptor {
|
||||||
_tmp: transaction,
|
bip30: TransactionBip30::new(transaction, meta_store, network.consensus_params(), block_hash, height),
|
||||||
|
missing_inputs: TransactionMissingInputs::new(transaction, prevout_store),
|
||||||
|
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||||
|
overspent: TransactionOverspent::new(transaction, prevout_store),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self) -> Result<(), TransactionError> {
|
pub fn check(&self) -> Result<(), TransactionError> {
|
||||||
|
try!(self.bip30.check());
|
||||||
|
try!(self.missing_inputs.check());
|
||||||
|
// TODO: double spends
|
||||||
|
try!(self.maturity.check());
|
||||||
|
try!(self.overspent.check());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TransactionRule {
|
||||||
|
fn check(&self) -> Result<(), TransactionError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransactionBip30<'a> {
|
||||||
|
transaction: CanonTransaction<'a>,
|
||||||
|
store: &'a TransactionMetaProvider,
|
||||||
|
consensus_params: ConsensusParams,
|
||||||
|
block_hash: &'a H256,
|
||||||
|
height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionBip30<'a> {
|
||||||
|
fn new(transaction: CanonTransaction<'a>, store: &'a TransactionMetaProvider, consensus_params: ConsensusParams, block_hash: &'a H256, height: u32) -> Self {
|
||||||
|
TransactionBip30 {
|
||||||
|
transaction: transaction,
|
||||||
|
store: store,
|
||||||
|
consensus_params: consensus_params,
|
||||||
|
block_hash: block_hash,
|
||||||
|
height: height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionRule for TransactionBip30<'a> {
|
||||||
|
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) {
|
||||||
|
Some(ref meta) if !meta.is_fully_spent() && !self.consensus_params.is_bip30_exception(self.block_hash, self.height) => {
|
||||||
|
Err(TransactionError::UnspentTransactionWithTheSameHash)
|
||||||
|
},
|
||||||
|
_ => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransactionMissingInputs<'a> {
|
||||||
|
transaction: CanonTransaction<'a>,
|
||||||
|
store: DuplexTransactionOutputProvider<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionMissingInputs<'a> {
|
||||||
|
fn new(transaction: CanonTransaction<'a>, store: DuplexTransactionOutputProvider<'a>) -> Self {
|
||||||
|
TransactionMissingInputs {
|
||||||
|
transaction: transaction,
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionRule for TransactionMissingInputs<'a> {
|
||||||
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
|
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).is_none();
|
||||||
|
is_not_null && is_missing
|
||||||
|
});
|
||||||
|
|
||||||
|
match missing_index {
|
||||||
|
Some(index) => Err(TransactionError::Input(index)),
|
||||||
|
None => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransactionMaturity<'a> {
|
||||||
|
transaction: CanonTransaction<'a>,
|
||||||
|
store: &'a TransactionMetaProvider,
|
||||||
|
height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionMaturity<'a> {
|
||||||
|
fn new(transaction: CanonTransaction<'a>, store: &'a TransactionMetaProvider, height: u32) -> Self {
|
||||||
|
TransactionMaturity {
|
||||||
|
transaction: transaction,
|
||||||
|
store: store,
|
||||||
|
height: height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionRule for TransactionMaturity<'a> {
|
||||||
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
|
// TODO: this is should also fail when we are trying to spend current block coinbase
|
||||||
|
let immature_spend = self.transaction.raw.inputs.iter()
|
||||||
|
.any(|input| match self.store.transaction_meta(&input.previous_output.hash) {
|
||||||
|
Some(ref meta) if meta.is_coinbase() && self.height < meta.height() + COINBASE_MATURITY => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if immature_spend {
|
||||||
|
Err(TransactionError::Maturity)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransactionOverspent<'a> {
|
||||||
|
transaction: CanonTransaction<'a>,
|
||||||
|
store: DuplexTransactionOutputProvider<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionOverspent<'a> {
|
||||||
|
fn new(transaction: CanonTransaction<'a>, store: DuplexTransactionOutputProvider<'a>) -> Self {
|
||||||
|
TransactionOverspent {
|
||||||
|
transaction: transaction,
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionRule for TransactionOverspent<'a> {
|
||||||
|
fn check(&self) -> Result<(), TransactionError> {
|
||||||
|
if self.transaction.raw.is_coinbase() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let available = self.transaction.raw.inputs.iter()
|
||||||
|
.map(|input| self.store.previous_transaction_output(&input.previous_output).map(|o| o.value).unwrap_or(0))
|
||||||
|
.sum::<u64>();
|
||||||
|
|
||||||
|
let spends = self.transaction.raw.total_spends();
|
||||||
|
|
||||||
|
if spends > available {
|
||||||
|
Err(TransactionError::Overspend)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use primitives::hash::H256;
|
||||||
use db::{IndexedBlock, IndexedTransaction, IndexedBlockHeader};
|
use db::{IndexedBlock, IndexedTransaction, IndexedBlockHeader};
|
||||||
|
|
||||||
|
pub const EXPECT_CANON: &'static str = "Block ancestors expected to be found in canon chain";
|
||||||
|
|
||||||
/// Blocks whose parents are known to be in the chain
|
/// Blocks whose parents are known to be in the chain
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct CanonBlock<'a> {
|
pub struct CanonBlock<'a> {
|
||||||
|
@ -14,6 +17,14 @@ impl<'a> CanonBlock<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hash<'b>(&'b self) -> &'a H256 where 'a: 'b {
|
||||||
|
&self.block.header.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw<'b>(&'b self) -> &'a IndexedBlock where 'a: 'b {
|
||||||
|
self.block
|
||||||
|
}
|
||||||
|
|
||||||
pub fn header<'b>(&'b self) -> CanonHeader<'a> where 'a: 'b {
|
pub fn header<'b>(&'b self) -> CanonHeader<'a> where 'a: 'b {
|
||||||
CanonHeader::new(&self.block.header)
|
CanonHeader::new(&self.block.header)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
//! Some transaction validation rules,
|
||||||
|
//! require sophisticated (in more than one source) previous transaction lookups
|
||||||
|
|
||||||
|
use primitives::hash::H256;
|
||||||
|
use chain::{OutPoint, TransactionOutput};
|
||||||
|
use db::{PreviousTransactionOutputProvider, TransactionMetaProvider, TransactionMeta};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct DuplexTransactionOutputProvider<'a> {
|
||||||
|
first: &'a PreviousTransactionOutputProvider,
|
||||||
|
second: &'a PreviousTransactionOutputProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DuplexTransactionOutputProvider<'a> {
|
||||||
|
pub fn new(first: &'a PreviousTransactionOutputProvider, second: &'a PreviousTransactionOutputProvider) -> Self {
|
||||||
|
DuplexTransactionOutputProvider {
|
||||||
|
first: first,
|
||||||
|
second: second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PreviousTransactionOutputProvider for DuplexTransactionOutputProvider<'a> {
|
||||||
|
fn previous_transaction_output(&self, prevout: &OutPoint) -> Option<TransactionOutput> {
|
||||||
|
self.first.previous_transaction_output(prevout)
|
||||||
|
.or_else(|| self.second.previous_transaction_output(prevout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct DuplexTransactionMetaProvider<'a> {
|
||||||
|
first: &'a TransactionMetaProvider,
|
||||||
|
second: &'a TransactionMetaProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DuplexTransactionMetaProvider<'a> {
|
||||||
|
pub fn new(first: &'a TransactionMetaProvider, second: &'a TransactionMetaProvider) -> Self {
|
||||||
|
DuplexTransactionMetaProvider {
|
||||||
|
first: first,
|
||||||
|
second: second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionMetaProvider for DuplexTransactionMetaProvider<'a> {
|
||||||
|
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> {
|
||||||
|
self.first.transaction_meta(hash)
|
||||||
|
.or_else(|| self.second.transaction_meta(hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,8 +33,15 @@
|
||||||
//!
|
//!
|
||||||
//! C.1 VerifyHeader
|
//! C.1 VerifyHeader
|
||||||
//! C.2 AcceptHeader (?)
|
//! C.2 AcceptHeader (?)
|
||||||
|
//!
|
||||||
|
//! --> D. after successfull chain_reorganization
|
||||||
|
//!
|
||||||
|
//! D.1 AcceptMemoryPoolTransaction on each tx in memory pool
|
||||||
|
//!
|
||||||
|
//! --> E. D might be super inefficient when memory pool is large
|
||||||
|
//! so instead we might want to call AcceptMemoryPoolTransaction on each tx
|
||||||
|
//! that is inserted into assembled block
|
||||||
|
|
||||||
extern crate byteorder;
|
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -61,6 +68,7 @@ mod task;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
mod duplex_store;
|
||||||
mod canon;
|
mod canon;
|
||||||
mod accept_block;
|
mod accept_block;
|
||||||
mod accept_chain;
|
mod accept_chain;
|
||||||
|
|
Loading…
Reference in New Issue