Merge pull request #218 from ethcore/previous_transaction_output_provider
initial work on b82, abstraction for verify_transaction, use Previous…
This commit is contained in:
commit
c3b87773dd
|
@ -63,10 +63,6 @@ impl Block {
|
||||||
&self.block_header
|
&self.block_header
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain(self) -> (BlockHeader, Vec<Transaction>) {
|
|
||||||
(self.block_header, self.transactions)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash(&self) -> H256 {
|
pub fn hash(&self) -> H256 {
|
||||||
self.block_header.hash()
|
self.block_header.hash()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use chain;
|
use chain;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use serialization::Serializable;
|
use serialization::Serializable;
|
||||||
|
use PreviousTransactionOutputProvider;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IndexedBlock {
|
pub struct IndexedBlock {
|
||||||
|
@ -11,19 +12,24 @@ pub struct IndexedBlock {
|
||||||
transaction_hashes: Vec<H256>,
|
transaction_hashes: Vec<H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PreviousTransactionOutputProvider for IndexedBlock {
|
||||||
|
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput> {
|
||||||
|
self.transaction(&prevout.hash)
|
||||||
|
.and_then(|tx| tx.outputs.get(prevout.index as usize))
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<chain::Block> for IndexedBlock {
|
impl From<chain::Block> for IndexedBlock {
|
||||||
fn from(block: chain::Block) -> Self {
|
fn from(block: chain::Block) -> Self {
|
||||||
let (org_header, org_txs) = block.drain();
|
let chain::Block { block_header, transactions } = block;
|
||||||
let mut hashes = Vec::with_capacity(org_txs.len());
|
let header_hash = block_header.hash();
|
||||||
for tx in org_txs.iter() {
|
let hashes = transactions.iter().map(chain::Transaction::hash).collect();
|
||||||
hashes.push(tx.hash())
|
|
||||||
}
|
|
||||||
let header_hash = org_header.hash();
|
|
||||||
|
|
||||||
IndexedBlock {
|
IndexedBlock {
|
||||||
header: org_header,
|
header: block_header,
|
||||||
header_hash: header_hash,
|
header_hash: header_hash,
|
||||||
transactions: org_txs,
|
transactions: transactions,
|
||||||
transaction_hashes: hashes,
|
transaction_hashes: hashes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +52,12 @@ impl IndexedBlock {
|
||||||
block
|
block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transaction(&self, hash: &H256) -> Option<&chain::Transaction> {
|
||||||
|
self.transaction_hashes.iter()
|
||||||
|
.position(|x| x == hash)
|
||||||
|
.map(|position| &self.transactions[position])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transactions(&self) -> IndexedTransactions {
|
pub fn transactions(&self) -> IndexedTransactions {
|
||||||
IndexedTransactions {
|
IndexedTransactions {
|
||||||
position: 0,
|
position: 0,
|
||||||
|
|
|
@ -58,7 +58,7 @@ pub use best_block::BestBlock;
|
||||||
pub use storage::{Storage, Store};
|
pub use storage::{Storage, Store};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use kvdb::Database;
|
pub use kvdb::Database;
|
||||||
pub use transaction_provider::{TransactionProvider, AsTransactionProvider};
|
pub use transaction_provider::{TransactionProvider, AsTransactionProvider, PreviousTransactionOutputProvider};
|
||||||
pub use transaction_meta_provider::TransactionMetaProvider;
|
pub use transaction_meta_provider::TransactionMetaProvider;
|
||||||
pub use block_stapler::{BlockStapler, BlockInsertedChain};
|
pub use block_stapler::{BlockStapler, BlockInsertedChain};
|
||||||
pub use block_provider::BlockProvider;
|
pub use block_provider::BlockProvider;
|
||||||
|
|
|
@ -151,14 +151,8 @@ impl Storage {
|
||||||
|
|
||||||
fn block_transactions_by_hash(&self, h: &H256) -> Vec<chain::Transaction> {
|
fn block_transactions_by_hash(&self, h: &H256) -> Vec<chain::Transaction> {
|
||||||
self.block_transaction_hashes_by_hash(h)
|
self.block_transaction_hashes_by_hash(h)
|
||||||
.into_iter()
|
.iter()
|
||||||
.filter_map(|tx_hash| {
|
.map(|tx_hash| self.transaction(tx_hash).expect("Missing transaction, possible db corruption"))
|
||||||
self.transaction_bytes(&tx_hash)
|
|
||||||
.map(|tx_bytes| {
|
|
||||||
deserialize::<_, chain::Transaction>(tx_bytes.as_ref())
|
|
||||||
.expect("Error deserializing transaction, possible db corruption")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,22 +163,12 @@ impl Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_by_hash(&self, h: &H256) -> Option<IndexedBlock> {
|
fn block_by_hash(&self, h: &H256) -> Option<IndexedBlock> {
|
||||||
|
|
||||||
self.block_header_by_hash(h).map(|header| {
|
self.block_header_by_hash(h).map(|header| {
|
||||||
let tx_index =
|
let tx_hashes = self.block_transaction_hashes_by_hash(h);
|
||||||
self.block_transaction_hashes_by_hash(h)
|
let txs = tx_hashes.iter()
|
||||||
.into_iter()
|
.map(|tx_hash| self.transaction(tx_hash).expect("Missing transaction, possible db corruption"))
|
||||||
.filter_map(|tx_hash| {
|
.collect::<Vec<_>>();
|
||||||
self.transaction_bytes(&tx_hash)
|
let tx_index = tx_hashes.into_iter().zip(txs.into_iter()).collect();
|
||||||
.map(|tx_bytes| {
|
|
||||||
(
|
|
||||||
tx_hash,
|
|
||||||
deserialize::<_, chain::Transaction>(tx_bytes.as_ref())
|
|
||||||
.expect("Error deserializing transaction, possible db corruption"),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
IndexedBlock::new(header, tx_index)
|
IndexedBlock::new(header, tx_index)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,3 +21,10 @@ pub trait AsTransactionProvider {
|
||||||
/// returns `TransactionProvider`
|
/// returns `TransactionProvider`
|
||||||
fn as_transaction_provider(&self) -> &TransactionProvider;
|
fn as_transaction_provider(&self) -> &TransactionProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// a shame to clone the whole transaction just to get single output.
|
||||||
|
pub trait PreviousTransactionOutputProvider {
|
||||||
|
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>;
|
||||||
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
//! 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;
|
use db::{TransactionProvider, PreviousTransactionOutputProvider};
|
||||||
use primitives::bytes::Bytes;
|
use primitives::bytes::Bytes;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use chain::Transaction;
|
use chain::{Transaction, OutPoint, TransactionOutput};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -705,6 +705,14 @@ impl TransactionProvider for MemoryPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PreviousTransactionOutputProvider for MemoryPool {
|
||||||
|
fn previous_transaction_output(&self, prevout: &OutPoint) -> Option<TransactionOutput> {
|
||||||
|
self.get(&prevout.hash)
|
||||||
|
.and_then(|tx| tx.outputs.get(prevout.index as usize))
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HeapSizeOf for MemoryPool {
|
impl HeapSizeOf for MemoryPool {
|
||||||
fn heap_size_of_children(&self) -> usize {
|
fn heap_size_of_children(&self) -> usize {
|
||||||
self.storage.heap_size_of_children()
|
self.storage.heap_size_of_children()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Bitcoin chain verifier
|
//! Bitcoin chain verifier
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use db::{self, BlockLocation};
|
use db::{self, BlockLocation, PreviousTransactionOutputProvider};
|
||||||
use network::Magic;
|
use network::Magic;
|
||||||
use script::Script;
|
use script::Script;
|
||||||
use super::{Verify, VerificationResult, Chain, Error, TransactionError};
|
use super::{Verify, VerificationResult, Chain, Error, TransactionError};
|
||||||
|
@ -63,11 +63,11 @@ impl ChainVerifier {
|
||||||
|
|
||||||
/// Returns previous transaction output.
|
/// Returns previous transaction output.
|
||||||
/// NOTE: This function expects all previous blocks to be already in database.
|
/// NOTE: This function expects all previous blocks to be already in database.
|
||||||
fn previous_transaction_output(&self, block: &db::IndexedBlock, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput> {
|
fn previous_transaction_output<T>(&self, prevout_provider: &T, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput>
|
||||||
|
where T: PreviousTransactionOutputProvider {
|
||||||
self.store.transaction(&prevout.hash)
|
self.store.transaction(&prevout.hash)
|
||||||
.as_ref()
|
.and_then(|tx| tx.outputs.into_iter().nth(prevout.index as usize))
|
||||||
.or_else(|| block.transactions().find(|&(hash, _)| hash == &prevout.hash).and_then(|(_, tx)| Some(tx)))
|
.or_else(|| prevout_provider.previous_transaction_output(prevout))
|
||||||
.and_then(|tx| tx.outputs.iter().nth(prevout.index as usize).cloned())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of transaction signature operations.
|
/// Returns number of transaction signature operations.
|
||||||
|
@ -181,11 +181,14 @@ impl ChainVerifier {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_transaction(&self,
|
//block: &db::IndexedBlock,
|
||||||
block: &db::IndexedBlock,
|
pub fn verify_transaction<T>(
|
||||||
|
&self,
|
||||||
|
prevout_provider: &T,
|
||||||
transaction: &chain::Transaction,
|
transaction: &chain::Transaction,
|
||||||
sequence: usize,
|
sequence: usize
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> where T: PreviousTransactionOutputProvider {
|
||||||
|
|
||||||
use script::{
|
use script::{
|
||||||
TransactionInputSigner,
|
TransactionInputSigner,
|
||||||
TransactionSignatureChecker,
|
TransactionSignatureChecker,
|
||||||
|
@ -204,7 +207,7 @@ impl ChainVerifier {
|
||||||
for (input_index, input) in transaction.inputs().iter().enumerate() {
|
for (input_index, input) in transaction.inputs().iter().enumerate() {
|
||||||
// signature verification
|
// signature verification
|
||||||
let signer: TransactionInputSigner = transaction.clone().into();
|
let signer: TransactionInputSigner = transaction.clone().into();
|
||||||
let paired_output = match self.previous_transaction_output(block, &input.previous_output) {
|
let paired_output = match self.previous_transaction_output(prevout_provider, &input.previous_output) {
|
||||||
Some(output) => output,
|
Some(output) => output,
|
||||||
_ => return Err(TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
_ => return Err(TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue