From 4b35a6cacfde5ea346aecb4ed994e9644cecb04b Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 15 Dec 2016 15:24:02 +0300 Subject: [PATCH] provider is always passed to execute_verification_task --- sync/src/synchronization_verifier.rs | 92 ++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/sync/src/synchronization_verifier.rs b/sync/src/synchronization_verifier.rs index 553f83d5..e02d51f2 100644 --- a/sync/src/synchronization_verifier.rs +++ b/sync/src/synchronization_verifier.rs @@ -60,6 +60,8 @@ pub struct AsyncVerifier { /// Transaction output observer, which looks into storage && into memory pool struct ChainMemoryPoolTransactionOutputProvider { + /// In-storage checker + storage_provider: StorageTransactionOutputProvider, /// Chain reference chain: ChainRef, /// Previous outputs, for which we should return 'Not spent' value. @@ -67,6 +69,12 @@ struct ChainMemoryPoolTransactionOutputProvider { nonfinal_spends: Option, } +/// Transaction output observer, which looks into storage +struct StorageTransactionOutputProvider { + /// Storage reference + storage: SharedStore, +} + impl VerificationTask { /// Returns transaction reference if it is transaction verification task pub fn transaction(&self) -> Option<&Transaction> { @@ -104,12 +112,13 @@ impl AsyncVerifier { sink.on_transaction_verification_error(&format!("{:?}", e), &transaction.hash()); return; }, - Ok(prevout_provider) => Some(prevout_provider), + Ok(prevout_provider) => prevout_provider, } } else { - None + ChainMemoryPoolTransactionOutputProvider::for_block(chain.clone()) + .expect("no error when creating for block") }; - execute_verification_task(&sink, prevout_provider.as_ref(), &verifier, task) + execute_verification_task(&sink, &prevout_provider, &verifier, task) }, } } @@ -145,6 +154,8 @@ impl Verifier for AsyncVerifier { /// Synchronous synchronization verifier pub struct SyncVerifier { + /// Storage reference + storage: SharedStore, /// Verifier verifier: ChainVerifier, /// Verification sink @@ -154,8 +165,9 @@ pub struct SyncVerifier { impl SyncVerifier where T: VerificationSink { /// Create new sync verifier pub fn new(network: Magic, storage: SharedStore, sink: Arc) -> Self { - let verifier = ChainVerifier::new(storage, network); + let verifier = ChainVerifier::new(storage.clone(), network); SyncVerifier { + storage: storage, verifier: verifier, sink: sink, } @@ -165,7 +177,8 @@ impl SyncVerifier where T: VerificationSink { impl Verifier for SyncVerifier where T: VerificationSink { /// Verify block fn verify_block(&self, block: IndexedBlock) { - execute_verification_task::(&self.sink, None, &self.verifier, VerificationTask::VerifyBlock(block)) + let prevout_provider = StorageTransactionOutputProvider::with_storage(self.storage.clone()); + execute_verification_task(&self.sink, &prevout_provider, &self.verifier, VerificationTask::VerifyBlock(block)) } /// Verify transaction @@ -175,7 +188,7 @@ impl Verifier for SyncVerifier where T: VerificationSink { } /// Execute single verification task -fn execute_verification_task(sink: &Arc, tx_output_provider: Option<&U>, verifier: &ChainVerifier, task: VerificationTask) { +fn execute_verification_task(sink: &Arc, tx_output_provider: &U, verifier: &ChainVerifier, task: VerificationTask) { let mut tasks_queue: VecDeque = VecDeque::new(); tasks_queue.push_back(task); @@ -200,7 +213,6 @@ fn execute_verification_task { let time: u32 = get_time().sec as u32; - let tx_output_provider = tx_output_provider.expect("must be provided for transaction checks"); 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()), @@ -211,6 +223,28 @@ fn execute_verification_task Self { + StorageTransactionOutputProvider { + storage: storage, + } + } +} + +impl TransactionOutputObserver for StorageTransactionOutputProvider { + fn is_spent(&self, prevout: &OutPoint) -> Option { + 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) -> Option { + self.storage.as_previous_transaction_output_provider().previous_transaction_output(prevout) + } +} + impl ChainMemoryPoolTransactionOutputProvider { pub fn for_transaction(chain: ChainRef, transaction: &Transaction) -> Result { // we have to check if there are another in-mempool transactions which spent same outputs here @@ -218,14 +252,23 @@ impl ChainMemoryPoolTransactionOutputProvider { ChainMemoryPoolTransactionOutputProvider::for_double_spend_check_result(chain, check_result) } - pub fn for_double_spend_check_result(chain: ChainRef, check_result: DoubleSpendCheckResult) -> Result { + pub fn for_block(chain: ChainRef) -> Result { + // we have to check if there are another in-mempool transactions which spent same outputs here + let check_result = DoubleSpendCheckResult::NoDoubleSpend; + ChainMemoryPoolTransactionOutputProvider::for_double_spend_check_result(chain, check_result) + } + + fn for_double_spend_check_result(chain: ChainRef, check_result: DoubleSpendCheckResult) -> Result { +println!("{:?}", check_result); match check_result { DoubleSpendCheckResult::DoubleSpend(_, hash, index) => Err(verification::TransactionError::UsingSpentOutput(hash, index)), DoubleSpendCheckResult::NoDoubleSpend => Ok(ChainMemoryPoolTransactionOutputProvider { + storage_provider: StorageTransactionOutputProvider::with_storage(chain.read().storage()), chain: chain.clone(), nonfinal_spends: None, }), DoubleSpendCheckResult::NonFinalDoubleSpend(nonfinal_spends) => Ok(ChainMemoryPoolTransactionOutputProvider { + storage_provider: StorageTransactionOutputProvider::with_storage(chain.read().storage()), chain: chain.clone(), nonfinal_spends: Some(nonfinal_spends), }), @@ -235,18 +278,27 @@ impl ChainMemoryPoolTransactionOutputProvider { impl TransactionOutputObserver for ChainMemoryPoolTransactionOutputProvider { fn is_spent(&self, prevout: &OutPoint) -> Option { - // check if this output is 'locked' by mempool transaction if let Some(ref nonfinal_spends) = self.nonfinal_spends { - if nonfinal_spends.double_spends.contains(&prevout.clone().into()) { + let prevout = prevout.clone().into(); + // check if this output is 'locked' by mempool transaction + if nonfinal_spends.double_spends.contains(&prevout) { return Some(false); } - } + // check if this output is output of transaction, which depends on locked mempool transaction + if nonfinal_spends.dependent_spends.contains(&prevout) { + 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.chain.read().storage() - .transaction_meta(&prevout.hash) - .and_then(|tm| tm.is_spent(prevout.index as usize)) + // we can omit memory_pool check here when we're verifying new transactions, because this + // check has already been completed in `for_transaction` method + // BUT if transactions are verifying because of reorganzation, we should check mempool + // because while reorganizing, we can get new transactions to the mempool + let chain = self.chain.read(); + if chain.memory_pool().is_spent(prevout) { + return Some(true); + } + self.storage_provider.is_spent(prevout) } } @@ -352,8 +404,8 @@ pub mod tests { // when inserting t3: // check that is_spent(t0[0]) == Some(false) (as it is spent by nonfinal t1) - // check that is_spent(t1[0]) == None (as t1 is virtually removed) - // check that is_spent(t2[0]) == None (as t2 is virtually removed) + // check that is_spent(t1[0]) == Some(false) (as t1 is virtually removed) + // check that is_spent(t2[0]) == Some(false) (as t2 is virtually removed) // check that previous_transaction_output(t0[0]) = Some(_) // check that previous_transaction_output(t1[0]) = None (as t1 is virtually removed) // check that previous_transaction_output(t2[0]) = None (as t2 is virtually removed) @@ -362,8 +414,8 @@ pub mod tests { let chain = ChainRef::new(RwLock::new(chain)); let provider = ChainMemoryPoolTransactionOutputProvider::for_transaction(chain, &dchain.at(3)).unwrap(); assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(0).hash(), index: 0, }), Some(false)); - assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), None); - assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(2).hash(), index: 0, }), None); + assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), Some(false)); + assert_eq!(provider.is_spent(&OutPoint { hash: dchain.at(2).hash(), index: 0, }), Some(false)); assert_eq!(provider.previous_transaction_output(&OutPoint { hash: dchain.at(0).hash(), index: 0, }), Some(dchain.at(0).outputs[0].clone())); assert_eq!(provider.previous_transaction_output(&OutPoint { hash: dchain.at(1).hash(), index: 0, }), None); assert_eq!(provider.previous_transaction_output(&OutPoint { hash: dchain.at(2).hash(), index: 0, }), None);