refactor indexed_block, avoid unnecessery copying

This commit is contained in:
debris 2016-12-09 15:43:00 +01:00
parent da80c69a80
commit 4204f8b1b9
12 changed files with 146 additions and 202 deletions

View File

@ -1,158 +1,68 @@
use chain;
use primitives::hash::H256; use primitives::hash::H256;
use chain::{Block, BlockHeader, OutPoint, TransactionOutput, merkle_root};
use serialization::Serializable; use serialization::Serializable;
use indexed_header::IndexedBlockHeader;
use indexed_transaction::IndexedTransaction;
use PreviousTransactionOutputProvider; use PreviousTransactionOutputProvider;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct IndexedBlock { pub struct IndexedBlock {
header: chain::BlockHeader, pub header: IndexedBlockHeader,
header_hash: H256, pub transactions: Vec<IndexedTransaction>,
transactions: Vec<chain::Transaction>,
// guaranteed to be the same length as transactions
transaction_hashes: Vec<H256>,
} }
impl PreviousTransactionOutputProvider for IndexedBlock { impl PreviousTransactionOutputProvider for IndexedBlock {
fn previous_transaction_output(&self, prevout: &chain::OutPoint) -> Option<chain::TransactionOutput> { fn previous_transaction_output(&self, prevout: &OutPoint) -> Option<TransactionOutput> {
self.transaction(&prevout.hash) let txs: &[_] = &self.transactions;
.and_then(|tx| tx.outputs.get(prevout.index as usize)) txs.previous_transaction_output(prevout)
.cloned()
} }
fn is_spent(&self, _prevout: &chain::OutPoint) -> bool { fn is_spent(&self, _prevout: &OutPoint) -> bool {
// block does not need it to be checked unimplemented!();
false
} }
} }
impl From<chain::Block> for IndexedBlock { impl From<Block> for IndexedBlock {
fn from(block: chain::Block) -> Self { fn from(block: Block) -> Self {
let chain::Block { block_header, transactions } = block; let Block { block_header, transactions } = block;
let header_hash = block_header.hash();
let hashes = transactions.iter().map(chain::Transaction::hash).collect();
IndexedBlock { IndexedBlock {
header: block_header, header: block_header.into(),
header_hash: header_hash, transactions: transactions.into_iter().map(Into::into).collect(),
transactions: transactions,
transaction_hashes: hashes,
} }
} }
} }
impl IndexedBlock { impl IndexedBlock {
pub fn new(header: chain::BlockHeader, transaction_index: Vec<(H256, chain::Transaction)>) -> Self { pub fn new(header: IndexedBlockHeader, transactions: Vec<IndexedTransaction>) -> Self {
let mut block = IndexedBlock { IndexedBlock {
header_hash: header.hash(),
header: header, header: header,
transactions: Vec::with_capacity(transaction_index.len()), transactions: transactions,
transaction_hashes: Vec::with_capacity(transaction_index.len()),
};
for (h256, tx) in transaction_index {
block.transactions.push(tx);
block.transaction_hashes.push(h256);
} }
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 {
IndexedTransactions {
position: 0,
block: self,
}
}
pub fn transaction_hashes(&self) -> &[H256] {
&self.transaction_hashes
}
pub fn header(&self) -> &chain::BlockHeader {
&self.header
} }
pub fn hash(&self) -> &H256 { pub fn hash(&self) -> &H256 {
&self.header_hash &self.header.hash
} }
pub fn transaction_count(&self) -> usize { pub fn header(&self) -> &BlockHeader {
self.transaction_hashes.len() &self.header.raw
} }
pub fn to_block(&self) -> chain::Block { pub fn to_raw_block(self) -> Block {
chain::Block::new( Block::new(self.header.raw, self.transactions.into_iter().map(|tx| tx.raw).collect())
self.header.clone(),
self.transactions.clone(),
)
} }
pub fn size(&self) -> usize { pub fn size(&self) -> usize {
// todo: optimize let txs_size = self.transactions.iter().map(|tx| tx.raw.serialized_size()).sum::<usize>();
self.to_block().serialized_size() self.header.raw.serialized_size() + txs_size
} }
pub fn merkle_root(&self) -> H256 { pub fn merkle_root(&self) -> H256 {
chain::merkle_root(&self.transaction_hashes) merkle_root(&self.transactions.iter().map(|tx| tx.hash.clone()).collect::<Vec<_>>())
} }
pub fn is_final(&self, height: u32) -> bool { pub fn is_final(&self, height: u32) -> bool {
self.transactions.iter().all(|t| t.is_final(height, self.header.time)) self.transactions.iter().all(|tx| tx.raw.is_final(height, self.header.raw.time))
}
pub fn transaction_at(&self, index: usize) -> (&H256, &chain::Transaction) {
(&self.transaction_hashes[index], &self.transactions[index])
}
}
pub struct IndexedTransactions<'a> {
position: usize,
block: &'a IndexedBlock,
}
impl<'a> Iterator for IndexedTransactions<'a> {
type Item = (&'a H256, &'a chain::Transaction);
fn next(&mut self) -> Option<(&'a H256, &'a chain::Transaction)> {
if self.position >= self.block.transactions.len() {
None
}
else {
let result = Some((&self.block.transaction_hashes[self.position], &self.block.transactions[self.position]));
self.position += 1;
result
}
}
}
#[cfg(test)]
mod tests {
use test_data;
use super::IndexedBlock;
#[test]
fn index() {
let block = test_data::block_h1();
let indexed_block: IndexedBlock = block.clone().into();
assert_eq!(*indexed_block.transactions().nth(0).unwrap().0, block.transactions()[0].hash());
}
#[test]
fn iter() {
let block = test_data::block_builder()
.header().build()
.transaction().coinbase().output().value(3).build().build()
.transaction().coinbase().output().value(5).build().build()
.build();
let indexed_block: IndexedBlock = block.clone().into();
assert_eq!(*indexed_block.transactions().nth(1).unwrap().0, block.transactions()[1].hash());
} }
} }

26
db/src/indexed_header.rs Normal file
View File

@ -0,0 +1,26 @@
use primitives::hash::H256;
use chain::BlockHeader;
#[derive(Debug, Clone)]
pub struct IndexedBlockHeader {
pub hash: H256,
pub raw: BlockHeader,
}
impl From<BlockHeader> for IndexedBlockHeader {
fn from(header: BlockHeader) -> Self {
IndexedBlockHeader {
hash: header.hash(),
raw: header,
}
}
}
impl IndexedBlockHeader {
pub fn new(hash: H256, header: BlockHeader) -> Self {
IndexedBlockHeader {
hash: hash,
raw: header,
}
}
}

View File

@ -3,18 +3,26 @@ use primitives::hash::H256;
use chain::{Transaction, OutPoint, TransactionOutput}; use chain::{Transaction, OutPoint, TransactionOutput};
use PreviousTransactionOutputProvider; use PreviousTransactionOutputProvider;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct IndexedTransaction { pub struct IndexedTransaction {
pub transaction: Transaction,
pub hash: H256, pub hash: H256,
pub raw: Transaction,
} }
impl From<Transaction> for IndexedTransaction { impl From<Transaction> for IndexedTransaction {
fn from(t: Transaction) -> Self { fn from(tx: Transaction) -> Self {
let hash = t.hash(); IndexedTransaction {
hash: tx.hash(),
raw: tx,
}
}
}
impl IndexedTransaction {
pub fn new(hash: H256, transaction: Transaction) -> Self {
IndexedTransaction { IndexedTransaction {
transaction: t,
hash: hash, hash: hash,
raw: transaction,
} }
} }
} }
@ -29,7 +37,7 @@ impl<'a> PreviousTransactionOutputProvider for &'a [IndexedTransaction] {
fn previous_transaction_output(&self, prevout: &OutPoint) -> Option<TransactionOutput> { fn previous_transaction_output(&self, prevout: &OutPoint) -> Option<TransactionOutput> {
self.iter() self.iter()
.find(|tx| tx.hash == prevout.hash) .find(|tx| tx.hash == prevout.hash)
.map(|tx| tx.transaction.outputs[prevout.index as usize].clone()) .map(|tx| tx.raw.outputs[prevout.index as usize].clone())
} }
fn is_spent(&self, _prevout: &OutPoint) -> bool { fn is_spent(&self, _prevout: &OutPoint) -> bool {

View File

@ -28,6 +28,7 @@ mod transaction_meta_provider;
mod error; mod error;
mod update_context; mod update_context;
mod indexed_block; mod indexed_block;
mod indexed_header;
mod indexed_transaction; mod indexed_transaction;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -72,8 +73,9 @@ pub use transaction_provider::{TransactionProvider, AsTransactionProvider, Previ
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, BlockHeaderProvider, AsBlockHeaderProvider}; pub use block_provider::{BlockProvider, BlockHeaderProvider, AsBlockHeaderProvider};
pub use indexed_block::{IndexedBlock, IndexedTransactions}; pub use indexed_block::IndexedBlock;
pub use indexed_transaction::{IndexedTransaction}; pub use indexed_header::IndexedBlockHeader;
pub use indexed_transaction::IndexedTransaction;
#[cfg(feature="dev")] #[cfg(feature="dev")]
pub use test_storage::TestStorage; pub use test_storage::TestStorage;

View File

@ -6,7 +6,7 @@ use kvdb::{Database, DatabaseConfig};
use byteorder::{LittleEndian, ByteOrder}; use byteorder::{LittleEndian, ByteOrder};
use primitives::hash::H256; use primitives::hash::H256;
use primitives::bytes::Bytes; use primitives::bytes::Bytes;
use super::{BlockRef, BestBlock, BlockLocation, IndexedBlock, IndexedTransactions}; use super::{BlockRef, BestBlock, BlockLocation, IndexedBlock};
use serialization::{serialize, deserialize}; use serialization::{serialize, deserialize};
use chain; use chain;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -19,6 +19,9 @@ use transaction_provider::TransactionProvider;
use transaction_meta_provider::TransactionMetaProvider; use transaction_meta_provider::TransactionMetaProvider;
use block_stapler::{BlockStapler, BlockInsertedChain, Reorganization}; use block_stapler::{BlockStapler, BlockInsertedChain, Reorganization};
use indexed_header::IndexedBlockHeader;
use indexed_transaction::IndexedTransaction;
pub const COL_COUNT: u32 = 10; pub const COL_COUNT: u32 = 10;
pub const COL_META: u32 = 0; pub const COL_META: u32 = 0;
pub const COL_BLOCK_HASHES: u32 = 1; pub const COL_BLOCK_HASHES: u32 = 1;
@ -166,32 +169,34 @@ 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_hashes = self.block_transaction_hashes_by_hash(h); let tx_hashes = self.block_transaction_hashes_by_hash(h);
let txs = tx_hashes.iter() let txs = tx_hashes.into_iter()
.map(|tx_hash| self.transaction(tx_hash).expect("Missing transaction, possible db corruption")) .map(|tx_hash| {
let tx = self.transaction(&tx_hash).expect("Missing transaction, possible db corruption");
IndexedTransaction::new(tx_hash, tx)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let tx_index = tx_hashes.into_iter().zip(txs.into_iter()).collect(); IndexedBlock::new(IndexedBlockHeader::new(h.clone(), header), txs)
IndexedBlock::new(header, tx_index)
}) })
} }
/// update transactions metadata in the specified database transaction /// update transactions metadata in the specified database transaction
fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &mut IndexedTransactions) fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[IndexedTransaction])
-> Result<(), Error> -> Result<(), Error>
{ {
if let Some((accepted_hash, accepted_tx)) = accepted_txs.next() { if let Some(tx) = accepted_txs.first() {
context.meta.insert( context.meta.insert(
accepted_hash.clone(), tx.hash.clone(),
TransactionMeta::new_coinbase(number, accepted_tx.outputs.len()) TransactionMeta::new_coinbase(number, tx.raw.outputs.len())
); );
} }
for (accepted_hash, accepted_tx) in accepted_txs { for tx in accepted_txs {
context.meta.insert( context.meta.insert(
accepted_hash.clone(), tx.hash.clone(),
TransactionMeta::new(number, accepted_tx.outputs.len()) TransactionMeta::new(number, tx.raw.outputs.len())
); );
for input in &accepted_tx.inputs { for input in &tx.raw.inputs {
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
match context.meta.entry(input.previous_output.hash.clone()) { match context.meta.entry(input.previous_output.hash.clone()) {
@ -303,7 +308,7 @@ impl Storage {
trace!(target: "db", "Canonizing block {}", hash.to_reversed_str()); trace!(target: "db", "Canonizing block {}", hash.to_reversed_str());
let block = try!(self.block_by_hash(hash).ok_or(Error::unknown_hash(hash))); let block = try!(self.block_by_hash(hash).ok_or(Error::unknown_hash(hash)));
try!(self.update_transactions_meta(context, at_height, &mut block.transactions())); try!(self.update_transactions_meta(context, at_height, &block.transactions));
// only canonical blocks are allowed to wield a number // only canonical blocks are allowed to wield a number
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(at_height), &**hash); context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(at_height), &**hash);
@ -470,7 +475,7 @@ impl BlockStapler for Storage {
let block_hash = block.hash(); let block_hash = block.hash();
let mut new_best_hash = match best_block.as_ref().map(|bb| &bb.hash) { let mut new_best_hash = match best_block.as_ref().map(|bb| &bb.hash) {
Some(best_hash) if &block.header().previous_header_hash != best_hash => best_hash.clone(), Some(best_hash) if &block.header.raw.previous_header_hash != best_hash => best_hash.clone(),
_ => block_hash.clone(), _ => block_hash.clone(),
}; };
@ -482,14 +487,14 @@ impl BlockStapler for Storage {
None => 0, None => 0,
}; };
let tx_space = block.transaction_count() * 32; let tx_space = block.transactions.len() * 32;
let mut tx_refs = Vec::with_capacity(tx_space); let mut tx_refs = Vec::with_capacity(tx_space);
for (tx_hash, tx) in block.transactions() { for tx in &block.transactions {
tx_refs.extend(&**tx_hash); tx_refs.extend(&*tx.hash);
context.db_transaction.put( context.db_transaction.put(
Some(COL_TRANSACTIONS), Some(COL_TRANSACTIONS),
&**tx_hash, &*tx.hash,
&serialize(tx), &serialize(&tx.raw),
); );
} }
context.db_transaction.put(Some(COL_BLOCK_TRANSACTIONS), &**block_hash, &tx_refs); context.db_transaction.put(Some(COL_BLOCK_TRANSACTIONS), &**block_hash, &tx_refs);
@ -497,12 +502,12 @@ impl BlockStapler for Storage {
context.db_transaction.put( context.db_transaction.put(
Some(COL_BLOCK_HEADERS), Some(COL_BLOCK_HEADERS),
&**block_hash, &**block_hash,
&serialize(block.header()) &serialize(&block.header.raw)
); );
// the block is continuing the main chain // the block is continuing the main chain
let result = if best_block.as_ref().map(|b| b.number) != Some(new_best_number) { let result = if best_block.as_ref().map(|b| b.number) != Some(new_best_number) {
try!(self.update_transactions_meta(&mut context, new_best_number, &mut block.transactions())); try!(self.update_transactions_meta(&mut context, new_best_number, &block.transactions));
context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number); context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number);
// updating main chain height reference // updating main chain height reference
@ -516,14 +521,14 @@ impl BlockStapler for Storage {
// but can cause reorganization here // but can cause reorganization here
// this can canonize the block parent if block parent + this block is longer than the main chain // this can canonize the block parent if block parent + this block is longer than the main chain
else { else {
match self.maybe_reorganize(&mut context, &block.header().previous_header_hash) { match self.maybe_reorganize(&mut context, &block.header.raw.previous_header_hash) {
Ok(Some(mut reorg)) => { Ok(Some(mut reorg)) => {
// if so, we have new best main chain block // if so, we have new best main chain block
new_best_number = reorg.height + 1; new_best_number = reorg.height + 1;
new_best_hash = block_hash.clone(); new_best_hash = block_hash.clone();
// and we canonize it also by provisioning transactions // and we canonize it also by provisioning transactions
try!(self.update_transactions_meta(&mut context, new_best_number, &mut block.transactions())); try!(self.update_transactions_meta(&mut context, new_best_number, &block.transactions));
context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number); context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number);
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), &*new_best_hash); context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), &*new_best_hash);
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &*new_best_hash, new_best_number); context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &*new_best_hash, new_best_number);

View File

@ -126,7 +126,7 @@ impl BlockProvider for TestStorage {
impl BlockStapler for TestStorage { impl BlockStapler for TestStorage {
/// insert pre-processed block in the storage /// insert pre-processed block in the storage
fn insert_indexed_block(&self, block: &IndexedBlock) -> Result<BlockInsertedChain, Error> { fn insert_indexed_block(&self, block: &IndexedBlock) -> Result<BlockInsertedChain, Error> {
self.insert_block(&block.to_block()) self.insert_block(&block.clone().to_raw_block())
} }
fn insert_block(&self, block: &chain::Block) -> Result<BlockInsertedChain, Error> { fn insert_block(&self, block: &chain::Block) -> Result<BlockInsertedChain, Error> {

View File

@ -183,20 +183,17 @@ impl BlockAssembler {
let size_step = block_size.decide(transaction_size); let size_step = block_size.decide(transaction_size);
let sigops_step = sigops.decide(sigops_count); let sigops_step = sigops.decide(sigops_count);
let transaction = IndexedTransaction {
transaction: entry.transaction.clone(),
hash: entry.hash.clone(),
};
match size_step.and(sigops_step) { match size_step.and(sigops_step) {
NextStep::Append => { NextStep::Append => {
let tx = IndexedTransaction::new(entry.hash.clone(), entry.transaction.clone());
// 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
*coinbase_value += entry.miner_fee as u64; *coinbase_value += entry.miner_fee as u64;
transactions.push(transaction); transactions.push(tx);
}, },
NextStep::FinishAndAppend => { NextStep::FinishAndAppend => {
transactions.push(transaction); let tx = IndexedTransaction::new(entry.hash.clone(), entry.transaction.clone());
transactions.push(tx);
break; break;
}, },
NextStep::Ignore => (), NextStep::Ignore => (),

View File

@ -15,27 +15,27 @@ pub fn build_compact_block(block: IndexedBlock, prefilled_transactions_indexes:
let nonce: u64 = thread_rng().gen(); let nonce: u64 = thread_rng().gen();
let prefilled_transactions_len = prefilled_transactions_indexes.len(); let prefilled_transactions_len = prefilled_transactions_indexes.len();
let mut short_ids: Vec<ShortTransactionID> = Vec::with_capacity(block.transaction_count() - prefilled_transactions_len); let mut short_ids: Vec<ShortTransactionID> = Vec::with_capacity(block.transactions.len() - prefilled_transactions_len);
let mut prefilled_transactions: Vec<PrefilledTransaction> = Vec::with_capacity(prefilled_transactions_len); let mut prefilled_transactions: Vec<PrefilledTransaction> = Vec::with_capacity(prefilled_transactions_len);
let mut prefilled_transactions_size: usize = 0; let mut prefilled_transactions_size: usize = 0;
let (key0, key1) = short_transaction_id_keys(nonce, block.header()); let (key0, key1) = short_transaction_id_keys(nonce, &block.header.raw);
for (transaction_index, (transaction_hash, transaction)) in block.transactions().enumerate() { for (transaction_index, transaction) in block.transactions.into_iter().enumerate() {
let transaction_size = transaction.serialized_size(); let transaction_size = transaction.raw.serialized_size();
if prefilled_transactions_size + transaction_size < MAX_COMPACT_BLOCK_PREFILLED_SIZE if prefilled_transactions_size + transaction_size < MAX_COMPACT_BLOCK_PREFILLED_SIZE
&& prefilled_transactions_indexes.contains(&transaction_index) { && prefilled_transactions_indexes.contains(&transaction_index) {
prefilled_transactions_size += transaction_size; prefilled_transactions_size += transaction_size;
prefilled_transactions.push(PrefilledTransaction { prefilled_transactions.push(PrefilledTransaction {
index: transaction_index, index: transaction_index,
transaction: transaction.clone(), transaction: transaction.raw,
}) })
} else { } else {
short_ids.push(short_transaction_id(key0, key1, transaction_hash)); short_ids.push(short_transaction_id(key0, key1, &transaction.hash));
} }
} }
BlockHeaderAndIDs { BlockHeaderAndIDs {
header: block.header().clone(), header: block.header.raw,
nonce: nonce, nonce: nonce,
short_ids: short_ids, short_ids: short_ids,
prefilled_transactions: prefilled_transactions, prefilled_transactions: prefilled_transactions,

View File

@ -371,10 +371,10 @@ impl Chain {
// all transactions from this block were accepted // all transactions from this block were accepted
// => delete accepted transactions from verification queue and from the memory pool // => delete accepted transactions from verification queue and from the memory pool
// + also remove transactions which spent outputs which have been spent by transactions from the block // + also remove transactions which spent outputs which have been spent by transactions from the block
for (tx_hash, tx) in block.transactions() { for tx in &block.transactions {
self.memory_pool.remove_by_hash(tx_hash); self.memory_pool.remove_by_hash(&tx.hash);
self.verifying_transactions.remove(tx_hash); self.verifying_transactions.remove(&tx.hash);
for tx_input in &tx.inputs { for tx_input in &tx.raw.inputs {
self.memory_pool.remove_by_prevout(&tx_input.previous_output); self.memory_pool.remove_by_prevout(&tx_input.previous_output);
} }
} }
@ -398,7 +398,7 @@ impl Chain {
// all transactions from this block were accepted // all transactions from this block were accepted
// + all transactions from previous blocks of this fork were accepted // + all transactions from previous blocks of this fork were accepted
// => delete accepted transactions from verification queue and from the memory pool // => delete accepted transactions from verification queue and from the memory pool
let this_block_transactions_hashes: Vec<H256> = block.transaction_hashes().iter().cloned().collect(); let this_block_transactions_hashes = block.transactions.iter().map(|tx| tx.hash.clone()).collect::<Vec<_>>();
let mut canonized_blocks_hashes: Vec<H256> = Vec::new(); let mut canonized_blocks_hashes: Vec<H256> = Vec::new();
let mut new_main_blocks_transactions_hashes: Vec<H256> = Vec::new(); let mut new_main_blocks_transactions_hashes: Vec<H256> = Vec::new();
while let Some(canonized_block_hash) = reorganization.pop_canonized() { while let Some(canonized_block_hash) = reorganization.pop_canonized() {

View File

@ -583,9 +583,9 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
None => notfound.push(item), None => notfound.push(item),
Some(block) => { Some(block) => {
let indexed_block: IndexedBlock = block.into(); let indexed_block: IndexedBlock = block.into();
let prefilled_transactions_indexes = indexed_block.transactions().enumerate() let prefilled_transactions_indexes = indexed_block.transactions.iter().enumerate()
// we do not filter by fee rate here, because it only reasonable for non-mined transactions // we do not filter by fee rate here, because it only reasonable for non-mined transactions
.filter(|&(_, (h, t))| filter.filter_transaction(h, t, None)) .filter(|&(_, tx)| filter.filter_transaction(&tx.hash, &tx.raw, None))
.map(|(idx, _)| idx) .map(|(idx, _)| idx)
.collect(); .collect();
let compact_block = types::CompactBlock { let compact_block = types::CompactBlock {
@ -1296,9 +1296,9 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
let block_header_and_ids: Vec<_> = indexed_blocks.into_iter() let block_header_and_ids: Vec<_> = indexed_blocks.into_iter()
.filter_map(|b| if self.peers.filter(peer_index).filter_block(&b.hash()) { .filter_map(|b| if self.peers.filter(peer_index).filter_block(&b.hash()) {
let prefilled_transactions_indexes = b.transactions().enumerate() let prefilled_transactions_indexes = b.transactions.iter().enumerate()
// we do not filter by fee rate here, because it only reasonable for non-mined transactions // we do not filter by fee rate here, because it only reasonable for non-mined transactions
.filter(|&(_, (h, t))| self.peers.filter_mut(peer_index).filter_transaction(h, t, None)) .filter(|&(_, tx)| self.peers.filter_mut(peer_index).filter_transaction(&tx.hash, &tx.raw, None))
.map(|(idx, _)| idx) .map(|(idx, _)| idx)
.collect(); .collect();
Some(build_compact_block(b, prefilled_transactions_indexes)) Some(build_compact_block(b, prefilled_transactions_indexes))

View File

@ -78,8 +78,8 @@ impl ChainVerifier {
// strict pay-to-script-hash signature operations count toward block // strict pay-to-script-hash signature operations count toward block
// signature operations limit is enforced with BIP16 // signature operations limit is enforced with BIP16
let store = StoreWithUnretainedOutputs::new(&self.store, block); let store = StoreWithUnretainedOutputs::new(&self.store, block);
let bip16_active = self.verify_p2sh(block.header().time); let bip16_active = self.verify_p2sh(block.header.raw.time);
block.transactions().map(|(_, tx)| transaction_sigops(tx, &store, bip16_active)).sum() block.transactions.iter().map(|tx| transaction_sigops(&tx.raw, &store, bip16_active)).sum()
} }
fn ordered_verify(&self, block: &db::IndexedBlock, at_height: u32) -> Result<(), Error> { fn ordered_verify(&self, block: &db::IndexedBlock, at_height: u32) -> Result<(), Error> {
@ -98,28 +98,24 @@ impl ChainVerifier {
//if let Some(work) = self.work_required(block, at_height) { //if let Some(work) = self.work_required(block, at_height) {
if at_height != 0 && !self.skip_pow { if at_height != 0 && !self.skip_pow {
let work = utils::work_required( let work = utils::work_required(
block.header().previous_header_hash.clone(), block.header.raw.previous_header_hash.clone(),
block.header().time, block.header.raw.time,
at_height, at_height,
self.store.as_block_header_provider(), self.store.as_block_header_provider(),
self.network self.network
); );
if !self.skip_pow && work != block.header().bits { if !self.skip_pow && work != block.header.raw.bits {
trace!(target: "verification", "pow verification error at height: {}", at_height); trace!(target: "verification", "pow verification error at height: {}", at_height);
trace!(target: "verification", "expected work: {:?}, got {:?}", work, block.header().bits); trace!(target: "verification", "expected work: {:?}, got {:?}", work, block.header.raw.bits);
return Err(Error::Difficulty); return Err(Error::Difficulty);
} }
} }
let coinbase_spends = block.transactions() let coinbase_spends = block.transactions[0].raw.total_spends();
.nth(0)
.expect("block emptyness should be checked at this point")
.1
.total_spends();
// bip30 // bip30
for (tx_index, (tx_hash, _)) in block.transactions().enumerate() { for (tx_index, tx) in block.transactions.iter().enumerate() {
if let Some(meta) = self.store.transaction_meta(tx_hash) { if let Some(meta) = self.store.transaction_meta(&tx.hash) {
if !meta.is_fully_spent() && !self.consensus_params.is_bip30_exception(&block_hash, at_height) { if !meta.is_fully_spent() && !self.consensus_params.is_bip30_exception(&block_hash, at_height) {
return Err(Error::Transaction(tx_index, TransactionError::UnspentTransactionWithTheSameHash)); return Err(Error::Transaction(tx_index, TransactionError::UnspentTransactionWithTheSameHash));
} }
@ -128,9 +124,9 @@ impl ChainVerifier {
let unretained_store = StoreWithUnretainedOutputs::new(&self.store, block); let unretained_store = StoreWithUnretainedOutputs::new(&self.store, block);
let mut total_unspent = 0u64; let mut total_unspent = 0u64;
for (tx_index, (_, tx)) in block.transactions().enumerate().skip(1) { for (tx_index, tx) in block.transactions.iter().enumerate().skip(1) {
let mut total_claimed: u64 = 0; let mut total_claimed: u64 = 0;
for input in &tx.inputs { for input in &tx.raw.inputs {
// Coinbase maturity check // Coinbase maturity check
if let Some(previous_meta) = self.store.transaction_meta(&input.previous_output.hash) { if let Some(previous_meta) = self.store.transaction_meta(&input.previous_output.hash) {
// check if it exists only // check if it exists only
@ -147,7 +143,7 @@ impl ChainVerifier {
total_claimed += previous_output.value; total_claimed += previous_output.value;
} }
let total_spends = tx.total_spends(); let total_spends = tx.raw.total_spends();
if total_claimed < total_spends { if total_claimed < total_spends {
return Err(Error::Transaction(tx_index, TransactionError::Overspend)); return Err(Error::Transaction(tx_index, TransactionError::Overspend));
@ -265,12 +261,12 @@ impl ChainVerifier {
let hash = block.hash(); let hash = block.hash();
// There should be at least 1 transaction // There should be at least 1 transaction
if block.transaction_count() == 0 { if block.transactions.is_empty() {
return Err(Error::Empty); return Err(Error::Empty);
} }
// block header checks // block header checks
try!(self.verify_block_header(self.store.as_block_header_provider(), &hash, block.header())); try!(self.verify_block_header(self.store.as_block_header_provider(), &hash, &block.header.raw));
// todo: serialized_size function is at least suboptimal // todo: serialized_size function is at least suboptimal
let size = block.size(); let size = block.size();
@ -279,11 +275,11 @@ impl ChainVerifier {
} }
// verify merkle root // verify merkle root
if block.merkle_root() != block.header().merkle_root_hash { if block.merkle_root() != block.header.raw.merkle_root_hash {
return Err(Error::MerkleRoot); return Err(Error::MerkleRoot);
} }
let first_tx = block.transactions().nth(0).expect("transaction count is checked above to be greater than 0").1; let first_tx = &block.transactions[0].raw;
// check first transaction is a coinbase transaction // check first transaction is a coinbase transaction
if !first_tx.is_coinbase() { if !first_tx.is_coinbase() {
return Err(Error::Coinbase) return Err(Error::Coinbase)
@ -295,19 +291,19 @@ impl ChainVerifier {
return Err(Error::CoinbaseSignatureLength(coinbase_script_len)); return Err(Error::CoinbaseSignatureLength(coinbase_script_len));
} }
let location = match self.store.accepted_location(block.header()) { let location = match self.store.accepted_location(&block.header.raw) {
Some(location) => location, Some(location) => location,
None => return Ok(Chain::Orphan), None => return Ok(Chain::Orphan),
}; };
if block.transaction_count() > TRANSACTIONS_VERIFY_PARALLEL_THRESHOLD { if block.transactions.len() > TRANSACTIONS_VERIFY_PARALLEL_THRESHOLD {
// todo: might use on-stack vector (smallvec/elastic array) // todo: might use on-stack vector (smallvec/elastic array)
let mut transaction_tasks: Vec<Task> = Vec::with_capacity(TRANSACTIONS_VERIFY_THREADS); let mut transaction_tasks: Vec<Task> = Vec::with_capacity(TRANSACTIONS_VERIFY_THREADS);
let mut last = 0; let mut last = 0;
for num_task in 0..TRANSACTIONS_VERIFY_THREADS { for num_task in 0..TRANSACTIONS_VERIFY_THREADS {
let from = last; let from = last;
last = ::std::cmp::max(1, block.transaction_count() / TRANSACTIONS_VERIFY_THREADS); last = ::std::cmp::max(1, block.transactions.len() / TRANSACTIONS_VERIFY_THREADS);
if num_task == TRANSACTIONS_VERIFY_THREADS - 1 { last = block.transaction_count(); }; if num_task == TRANSACTIONS_VERIFY_THREADS - 1 { last = block.transactions.len(); };
transaction_tasks.push(Task::new(block, location.height(), from, last)); transaction_tasks.push(Task::new(block, location.height(), from, last));
} }
@ -325,8 +321,8 @@ impl ChainVerifier {
} }
} }
else { else {
for (index, (_, tx)) in block.transactions().enumerate() { for (index, tx) in block.transactions.iter().enumerate() {
if let Err(tx_err) = self.verify_transaction(block, location.height(), block.header().time, tx, index) { if let Err(tx_err) = self.verify_transaction(block, location.height(), block.header.raw.time, &tx.raw, index) {
return Err(Error::Transaction(index, tx_err)); return Err(Error::Transaction(index, tx_err));
} }
} }
@ -372,7 +368,7 @@ impl Verify for ChainVerifier {
trace!( trace!(
target: "verification", "Block {} (transactions: {}) verification finished. Result {:?}", target: "verification", "Block {} (transactions: {}) verification finished. Result {:?}",
block.hash().to_reversed_str(), block.hash().to_reversed_str(),
block.transaction_count(), block.transactions.len(),
result, result,
); );
result result

View File

@ -25,7 +25,7 @@ impl<'a> Task<'a> {
pub fn progress(&mut self, verifier: &ChainVerifier) { pub fn progress(&mut self, verifier: &ChainVerifier) {
for index in self.from..self.to { for index in self.from..self.to {
if let Err(e) = verifier.verify_transaction(self.block, self.block_height, self.block.header().time, self.block.transaction_at(index).1, index) { if let Err(e) = verifier.verify_transaction(self.block, self.block_height, self.block.header.raw.time, &self.block.transactions[index].raw, index) {
self.result = Err((index, e)) self.result = Err((index, e))
} }
} }