diff --git a/db/src/block_stapler.rs b/db/src/block_stapler.rs index c82fdedd..088814f8 100644 --- a/db/src/block_stapler.rs +++ b/db/src/block_stapler.rs @@ -1,5 +1,5 @@ use primitives::hash::H256; -use super::BlockLocation; +use super::{BlockLocation, IndexedBlock}; use chain; use error::Error; @@ -46,4 +46,7 @@ pub trait BlockStapler { /// insert block in the storage fn insert_block(&self, block: &chain::Block) -> Result; + + /// insert pre-processed block in the storage + fn insert_indexed_block(&self, block: &IndexedBlock) -> Result; } diff --git a/db/src/indexed_block.rs b/db/src/indexed_block.rs index 6eed113f..8d20d45f 100644 --- a/db/src/indexed_block.rs +++ b/db/src/indexed_block.rs @@ -28,6 +28,22 @@ impl From for IndexedBlock { } impl IndexedBlock { + pub fn new(header: chain::BlockHeader, transaction_index: Vec<(H256, chain::Transaction)>) -> Self { + let mut block = IndexedBlock { + header_hash: header.hash(), + header: header, + transactions: Vec::new(), + transaction_hashes: Vec::new(), + }; + + for (h256, tx) in transaction_index { + block.transactions.push(tx); + block.transaction_hashes.push(h256); + } + + block + } + pub fn transactions(&self) -> IndexedTransactions { IndexedTransactions { position: 0, @@ -35,6 +51,10 @@ impl IndexedBlock { } } + pub fn transaction_hashes(&self) -> &[H256] { + &self.transaction_hashes + } + pub fn header(&self) -> &chain::BlockHeader { &self.header } @@ -42,6 +62,17 @@ impl IndexedBlock { pub fn hash(&self) -> &H256 { &self.header_hash } + + pub fn transaction_count(&self) -> usize { + self.transaction_hashes.len() + } + + pub fn to_block(&self) -> chain::Block { + chain::Block::new( + self.header.clone(), + self.transactions.clone(), + ) + } } pub struct IndexedTransactions<'a> { diff --git a/db/src/storage.rs b/db/src/storage.rs index 1adf709d..9d6c0bb8 100644 --- a/db/src/storage.rs +++ b/db/src/storage.rs @@ -6,7 +6,7 @@ use kvdb::{Database, DatabaseConfig}; use byteorder::{LittleEndian, ByteOrder}; use primitives::hash::H256; use primitives::bytes::Bytes; -use super::{BlockRef, BestBlock, BlockLocation}; +use super::{BlockRef, BestBlock, BlockLocation, IndexedBlock, IndexedTransactions}; use serialization::{serialize, deserialize}; use chain; use parking_lot::RwLock; @@ -168,20 +168,41 @@ impl Storage { }) } + fn block_by_hash(&self, h: &H256) -> Option { + + self.block_header_by_hash(h).map(|header| { + let tx_index = + self.block_transaction_hashes_by_hash(h) + .into_iter() + .filter_map(|tx_hash| { + self.transaction_bytes(&tx_hash) + .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) + }) + } + /// update transactions metadata in the specified database transaction - fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction]) + fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &mut IndexedTransactions) -> Result<(), Error> { - if let Some(accepted_tx) = accepted_txs.iter().next() { + if let Some((accepted_hash, accepted_tx)) = accepted_txs.next() { context.meta.insert( - accepted_tx.hash(), + accepted_hash.clone(), TransactionMeta::new_coinbase(number, accepted_tx.outputs.len()) ); } - for accepted_tx in accepted_txs.iter().skip(1) { + for (accepted_hash, accepted_tx) in accepted_txs { context.meta.insert( - accepted_tx.hash(), + accepted_hash.clone(), TransactionMeta::new(number, accepted_tx.outputs.len()) ); @@ -294,8 +315,8 @@ impl Storage { } fn canonize_block(&self, context: &mut UpdateContext, at_height: u32, hash: &H256) -> Result<(), Error> { - let transactions = self.block_transactions_by_hash(hash); - try!(self.update_transactions_meta(context, at_height, &transactions)); + 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())); // only canonical blocks are allowed to wield a number context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(at_height), &**hash); @@ -412,8 +433,12 @@ impl BlockProvider for Storage { impl BlockStapler for Storage { + /// insert pre-processed block in the storage fn insert_block(&self, block: &chain::Block) -> Result { + self.insert_indexed_block(&block.clone().into()) + } + fn insert_indexed_block(&self, block: &IndexedBlock) -> Result { // ! lock will be held during the entire insert routine let mut best_block = self.best_block.write(); @@ -428,39 +453,38 @@ impl BlockStapler for Storage { let mut new_best_number = match best_block.as_ref().map(|b| b.number) { Some(best_number) => { - if block.hash() == new_best_hash { best_number + 1 } + if block.hash() == &new_best_hash { best_number + 1 } else { best_number } }, None => 0, }; - let tx_space = block.transactions().len() * 32; + let tx_space = block.transaction_count() * 32; let mut tx_refs = Vec::with_capacity(tx_space); - for tx in block.transactions() { - let tx_hash = tx.hash(); - tx_refs.extend(&*tx_hash); + for (tx_hash, tx) in block.transactions() { + tx_refs.extend(&**tx_hash); context.db_transaction.put( Some(COL_TRANSACTIONS), - &*tx_hash, + &**tx_hash, &serialize(tx), ); } - context.db_transaction.put(Some(COL_BLOCK_TRANSACTIONS), &*block_hash, &tx_refs); + context.db_transaction.put(Some(COL_BLOCK_TRANSACTIONS), &**block_hash, &tx_refs); context.db_transaction.put( Some(COL_BLOCK_HEADERS), - &*block_hash, + &**block_hash, &serialize(block.header()) ); // the block is continuing the main chain 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, block.transactions())); + try!(self.update_transactions_meta(&mut context, new_best_number, &mut block.transactions())); context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number); // updating main chain height reference - context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), &*block_hash); - context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &*block_hash, new_best_number); + context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), &**block_hash); + context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &**block_hash, new_best_number); BlockInsertedChain::Main } @@ -473,10 +497,10 @@ impl BlockStapler for Storage { Ok(Some(mut reorg)) => { // if so, we have new best main chain block new_best_number = reorg.height + 1; - new_best_hash = block_hash; + new_best_hash = block_hash.clone(); // and we canonize it also by provisioning transactions - try!(self.update_transactions_meta(&mut context, new_best_number, block.transactions())); + try!(self.update_transactions_meta(&mut context, new_best_number, &mut block.transactions())); 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.write_u32(Some(COL_BLOCK_NUMBERS), &*new_best_hash, new_best_number); diff --git a/db/src/test_storage.rs b/db/src/test_storage.rs index 891259a5..22750aad 100644 --- a/db/src/test_storage.rs +++ b/db/src/test_storage.rs @@ -2,7 +2,8 @@ use super::{ BlockRef, Store, Error, BestBlock, BlockLocation, BlockInsertedChain, BlockProvider, - BlockStapler, TransactionMetaProvider, TransactionProvider, AsTransactionProvider + BlockStapler, TransactionMetaProvider, TransactionProvider, AsTransactionProvider, + IndexedBlock, }; use chain::{self, Block}; use primitives::hash::H256; @@ -111,6 +112,10 @@ impl BlockProvider for TestStorage { } impl BlockStapler for TestStorage { + /// insert pre-processed block in the storage + fn insert_indexed_block(&self, block: &IndexedBlock) -> Result { + self.insert_block(&block.to_block()) + } fn insert_block(&self, block: &chain::Block) -> Result { let hash = block.hash();