DoubleSpends verification
This commit is contained in:
parent
64a3d4d080
commit
2f7b940794
|
@ -20,7 +20,22 @@ impl PreviousTransactionOutputProvider for IndexedBlock {
|
|||
|
||||
impl TransactionOutputObserver for IndexedBlock {
|
||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool> {
|
||||
self.previous_transaction_output(prevout).map(|_output| false)
|
||||
// if previous transaction output appears more than once than we can safely
|
||||
// tell that it's spent (double spent)
|
||||
// TODO: optimize it
|
||||
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"),
|
||||
}
|
||||
//self.previous_transaction_output(prevout).map(|_output| false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use primitives::hash::H256;
|
|||
use chain::OutPoint;
|
||||
use transaction_meta::TransactionMeta;
|
||||
|
||||
pub trait TransactionOutputObserver {
|
||||
pub trait TransactionOutputObserver: Send + Sync {
|
||||
fn is_spent(&self, prevout: &OutPoint) -> Option<bool>;
|
||||
}
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ fn execute_verification_task<T: VerificationSink, U: PreviousTransactionOutputPr
|
|||
},
|
||||
VerificationTask::VerifyTransaction(height, transaction) => {
|
||||
let time: u32 = get_time().sec as u32;
|
||||
match verifier.verify_transaction(tx_output_provider, height, time, &transaction, 1) {
|
||||
match verifier.verify_mempool_transaction(tx_output_provider, height, time, &transaction) {
|
||||
Ok(_) => sink.on_transaction_verification_success(transaction),
|
||||
Err(e) => sink.on_transaction_verification_error(&format!("{:?}", e), &transaction.hash()),
|
||||
}
|
||||
|
|
|
@ -22,7 +22,16 @@ impl<'a> ChainAcceptor<'a> {
|
|||
header: HeaderAcceptor::new(store.as_block_header_provider(), network, block.header(), height),
|
||||
transactions: block.transactions()
|
||||
.into_iter()
|
||||
.map(|tx| TransactionAcceptor::new(store.as_transaction_meta_provider(), prevouts, network, tx, block.hash(), height, block.header.raw.time))
|
||||
.map(|tx| TransactionAcceptor::new(
|
||||
store.as_transaction_meta_provider(),
|
||||
prevouts,
|
||||
block.raw(),
|
||||
network,
|
||||
tx,
|
||||
block.hash(),
|
||||
height,
|
||||
block.header.raw.time
|
||||
))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use primitives::hash::H256;
|
||||
use db::{TransactionMetaProvider, PreviousTransactionOutputProvider};
|
||||
use db::{TransactionMetaProvider, PreviousTransactionOutputProvider, TransactionOutputObserver};
|
||||
use network::{Magic, ConsensusParams};
|
||||
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
|
||||
use duplex_store::{DuplexTransactionOutputProvider};
|
||||
|
@ -13,6 +13,7 @@ pub struct TransactionAcceptor<'a> {
|
|||
pub missing_inputs: TransactionMissingInputs<'a>,
|
||||
pub maturity: TransactionMaturity<'a>,
|
||||
pub overspent: TransactionOverspent<'a>,
|
||||
pub double_spent: TransactionDoubleSpend<'a>,
|
||||
pub eval: TransactionEval<'a>,
|
||||
}
|
||||
|
||||
|
@ -23,6 +24,8 @@ impl<'a> TransactionAcceptor<'a> {
|
|||
// previous transaction outputs
|
||||
// in case of block validation, that's database and currently processed block
|
||||
prevout_store: DuplexTransactionOutputProvider<'a>,
|
||||
// in case of block validation, that's database and currently processed block
|
||||
spent_store: &'a TransactionOutputObserver,
|
||||
network: Magic,
|
||||
transaction: CanonTransaction<'a>,
|
||||
block_hash: &'a H256,
|
||||
|
@ -35,6 +38,7 @@ impl<'a> TransactionAcceptor<'a> {
|
|||
missing_inputs: TransactionMissingInputs::new(transaction, prevout_store),
|
||||
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||
overspent: TransactionOverspent::new(transaction, prevout_store),
|
||||
double_spent: TransactionDoubleSpend::new(transaction, spent_store),
|
||||
eval: TransactionEval::new(transaction, prevout_store, params, height, time),
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +46,9 @@ impl<'a> TransactionAcceptor<'a> {
|
|||
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());
|
||||
try!(self.double_spent.check());
|
||||
try!(self.eval.check());
|
||||
Ok(())
|
||||
}
|
||||
|
@ -54,9 +58,9 @@ impl<'a> TransactionAcceptor<'a> {
|
|||
pub fn check_with_eval(&self, eval: bool) -> Result<(), TransactionError> {
|
||||
try!(self.bip30.check());
|
||||
try!(self.missing_inputs.check());
|
||||
// TODO: double spends
|
||||
try!(self.maturity.check());
|
||||
try!(self.overspent.check());
|
||||
try!(self.double_spent.check());
|
||||
if eval {
|
||||
try!(self.eval.check());
|
||||
}
|
||||
|
@ -70,6 +74,7 @@ pub struct MemoryPoolTransactionAcceptor<'a> {
|
|||
pub maturity: TransactionMaturity<'a>,
|
||||
pub overspent: TransactionOverspent<'a>,
|
||||
pub sigops: TransactionSigops<'a>,
|
||||
pub double_spent: TransactionDoubleSpend<'a>,
|
||||
pub eval: TransactionEval<'a>,
|
||||
}
|
||||
|
||||
|
@ -79,6 +84,8 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
|||
meta_store: &'a TransactionMetaProvider,
|
||||
// in case of memory pool it should be db and memory pool
|
||||
prevout_store: DuplexTransactionOutputProvider<'a>,
|
||||
// in case of memory pool it should be db and memory pool
|
||||
spent_store: &'a TransactionOutputObserver,
|
||||
network: Magic,
|
||||
transaction: CanonTransaction<'a>,
|
||||
height: u32,
|
||||
|
@ -91,6 +98,7 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
|||
maturity: TransactionMaturity::new(transaction, meta_store, height),
|
||||
overspent: TransactionOverspent::new(transaction, prevout_store),
|
||||
sigops: TransactionSigops::new(transaction, prevout_store, params.clone(), MAX_BLOCK_SIGOPS, time),
|
||||
double_spent: TransactionDoubleSpend::new(transaction, spent_store),
|
||||
eval: TransactionEval::new(transaction, prevout_store, params, height, time),
|
||||
}
|
||||
}
|
||||
|
@ -98,10 +106,10 @@ impl<'a> MemoryPoolTransactionAcceptor<'a> {
|
|||
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());
|
||||
try!(self.sigops.check());
|
||||
try!(self.double_spent.check());
|
||||
try!(self.eval.check());
|
||||
Ok(())
|
||||
}
|
||||
|
@ -353,3 +361,34 @@ impl<'a> TransactionRule for TransactionEval<'a> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TransactionDoubleSpend<'a> {
|
||||
transaction: CanonTransaction<'a>,
|
||||
store: &'a TransactionOutputObserver,
|
||||
}
|
||||
|
||||
impl<'a> TransactionDoubleSpend<'a> {
|
||||
fn new(transaction: CanonTransaction<'a>, store: &'a TransactionOutputObserver) -> Self {
|
||||
TransactionDoubleSpend {
|
||||
transaction: transaction,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TransactionRule for TransactionDoubleSpend<'a> {
|
||||
fn check(&self) -> Result<(), TransactionError> {
|
||||
let double_spent_input = self.transaction.raw.inputs.iter()
|
||||
.find(|input| self.store.is_spent(&input.previous_output).unwrap_or(false));
|
||||
|
||||
match double_spent_input {
|
||||
Some(input) => {
|
||||
Err(TransactionError::UsingSpentOutput(
|
||||
input.previous_output.hash.clone(),
|
||||
input.previous_output.index
|
||||
))
|
||||
},
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use network::Magic;
|
|||
use error::{Error, TransactionError};
|
||||
use {Verify, chain};
|
||||
use canon::{CanonBlock, CanonTransaction};
|
||||
use duplex_store::DuplexTransactionOutputProvider;
|
||||
use duplex_store::{DuplexTransactionOutputProvider, NoopStore};
|
||||
use verify_chain::ChainVerifier;
|
||||
use verify_header::HeaderVerifier;
|
||||
use verify_transaction::MemoryPoolTransactionVerifier;
|
||||
|
@ -95,13 +95,12 @@ impl BackwardsCompatibleChainVerifier {
|
|||
header_verifier.check_with_pow(!self.skip_pow)
|
||||
}
|
||||
|
||||
pub fn verify_transaction<T>(
|
||||
pub fn verify_mempool_transaction<T>(
|
||||
&self,
|
||||
prevout_provider: &T,
|
||||
height: u32,
|
||||
time: u32,
|
||||
transaction: &chain::Transaction,
|
||||
_sequence: usize
|
||||
) -> Result<(), TransactionError> where T: PreviousTransactionOutputProvider + TransactionOutputObserver {
|
||||
let indexed_tx = transaction.clone().into();
|
||||
// let's do preverification first
|
||||
|
@ -110,10 +109,12 @@ impl BackwardsCompatibleChainVerifier {
|
|||
|
||||
let canon_tx = CanonTransaction::new(&indexed_tx);
|
||||
// now let's do full verification
|
||||
let prevouts = DuplexTransactionOutputProvider::new(self.store.as_previous_transaction_output_provider(), prevout_provider);
|
||||
let noop = NoopStore;
|
||||
let prevouts = DuplexTransactionOutputProvider::new(prevout_provider, &noop);
|
||||
let tx_acceptor = MemoryPoolTransactionAcceptor::new(
|
||||
self.store.as_transaction_meta_provider(),
|
||||
prevouts,
|
||||
prevout_provider,
|
||||
self.network,
|
||||
canon_tx,
|
||||
height,
|
||||
|
|
|
@ -25,3 +25,11 @@ impl<'a> PreviousTransactionOutputProvider for DuplexTransactionOutputProvider<'
|
|||
.or_else(|| self.second.previous_transaction_output(prevout))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoopStore;
|
||||
|
||||
impl PreviousTransactionOutputProvider for NoopStore {
|
||||
fn previous_transaction_output(&self, _prevout: &OutPoint) -> Option<TransactionOutput> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue