From d891e80ad7808f4fad948cdaa45a3f4b0ca9f3e9 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Mon, 1 Aug 2016 19:10:13 +0200 Subject: [PATCH] Commit best block after closing transaction (#1791) --- ethcore/src/blockchain/blockchain.rs | 62 ++++++++++++++++++++++++---- ethcore/src/client/client.rs | 1 + ethcore/src/tests/helpers.rs | 2 + 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index bd8488a66..0ae1c138e 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -161,6 +161,10 @@ pub struct BlockChain { db: Arc, cache_man: RwLock>, + + pending_best_block: RwLock>, + pending_block_hashes: RwLock>, + pending_transaction_addresses: RwLock>, } impl BlockProvider for BlockChain { @@ -329,6 +333,9 @@ impl BlockChain { block_receipts: RwLock::new(HashMap::new()), db: db.clone(), cache_man: RwLock::new(cache_man), + pending_best_block: RwLock::new(None), + pending_block_hashes: RwLock::new(HashMap::new()), + pending_transaction_addresses: RwLock::new(HashMap::new()), }; // load best block @@ -540,6 +547,8 @@ impl BlockChain { return ImportRoute::none(); } + assert!(self.pending_best_block.read().is_none()); + let block_rlp = UntrustedRlp::new(bytes); let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks); let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks); @@ -559,7 +568,7 @@ impl BlockChain { ); } - self.apply_update(batch, ExtrasUpdate { + self.prepare_update(batch, ExtrasUpdate { block_hashes: self.prepare_block_hashes_update(bytes, &info), block_details: self.prepare_block_details_update(bytes, &info), block_receipts: self.prepare_block_receipts_update(receipts, &info), @@ -614,8 +623,8 @@ impl BlockChain { } } - /// Applies extras update. - fn apply_update(&self, batch: &DBTransaction, update: ExtrasUpdate) { + /// Prepares extras update. + fn prepare_update(&self, batch: &DBTransaction, update: ExtrasUpdate) { { for hash in update.block_details.keys().cloned() { self.note_used(CacheID::BlockDetails(hash)); @@ -638,29 +647,46 @@ impl BlockChain { // These cached values must be updated last with all three locks taken to avoid // cache decoherence { - let mut best_block = self.best_block.write(); + let mut best_block = self.pending_best_block.write(); // update best block match update.info.location { BlockLocation::Branch => (), _ => { batch.put(DB_COL_EXTRA, b"best", &update.info.hash).unwrap(); - *best_block = BestBlock { + *best_block = Some(BestBlock { hash: update.info.hash, number: update.info.number, total_difficulty: update.info.total_difficulty, block: update.block.to_vec(), - }; + }); } } - let mut write_hashes = self.block_hashes.write(); - let mut write_txs = self.transaction_addresses.write(); + let mut write_hashes = self.pending_block_hashes.write(); + let mut write_txs = self.pending_transaction_addresses.write(); batch.extend_with_cache(DB_COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Remove); batch.extend_with_cache(DB_COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Remove); } } + /// Applt pending insertion updates + pub fn commit(&self) { + let mut best_block = self.best_block.write(); + let mut write_hashes = self.block_hashes.write(); + let mut write_txs = self.transaction_addresses.write(); + let mut pending_best_block = self.pending_best_block.write(); + let mut pending_write_hashes = self.pending_block_hashes.write(); + let mut pending_write_txs = self.pending_transaction_addresses.write(); + // update best block + if let Some(block) = pending_best_block.take() { + *best_block = block; + } + + write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new())); + write_txs.extend(mem::replace(&mut *pending_write_txs, HashMap::new())); + } + /// Iterator that lists `first` and then all of `first`'s ancestors, by hash. pub fn ancestry_iter(&self, first: H256) -> Option { if self.is_known(&first) { @@ -940,6 +966,8 @@ mod tests { // when let batch = db.transaction(); bc.insert_block(&batch, &first, vec![]); + assert_eq!(bc.best_block_number(), 0); + bc.commit(); // NOTE no db.write here (we want to check if best block is cached) // then @@ -969,6 +997,7 @@ mod tests { let batch = db.transaction(); bc.insert_block(&batch, &first, vec![]); db.write(batch).unwrap(); + bc.commit(); assert_eq!(bc.block_hash(0), Some(genesis_hash.clone())); assert_eq!(bc.best_block_number(), 1); @@ -996,6 +1025,7 @@ mod tests { let block = canon_chain.generate(&mut finalizer).unwrap(); block_hashes.push(BlockView::new(&block).header_view().sha3()); bc.insert_block(&batch, &block, vec![]); + bc.commit(); } db.write(batch).unwrap(); @@ -1026,7 +1056,10 @@ mod tests { let bc = BlockChain::new(Config::default(), &genesis, db.clone()); let batch = db.transaction(); - bc.insert_block(&batch, &b1a, vec![]); + for b in [&b1a, &b1b, &b2a, &b2b, &b3a, &b3b, &b4a, &b4b, &b5a, &b5b].iter() { + bc.insert_block(&batch, b, vec![]); + bc.commit(); + } bc.insert_block(&batch, &b1b, vec![]); bc.insert_block(&batch, &b2a, vec![]); bc.insert_block(&batch, &b2b, vec![]); @@ -1072,11 +1105,15 @@ mod tests { let batch = db.transaction(); let ir1 = bc.insert_block(&batch, &b1, vec![]); + bc.commit(); let ir2 = bc.insert_block(&batch, &b2, vec![]); + bc.commit(); let ir3b = bc.insert_block(&batch, &b3b, vec![]); + bc.commit(); db.write(batch).unwrap(); let batch = db.transaction(); let ir3a = bc.insert_block(&batch, &b3a, vec![]); + bc.commit(); db.write(batch).unwrap(); assert_eq!(ir1, ImportRoute { @@ -1184,6 +1221,7 @@ mod tests { let batch = db.transaction(); bc.insert_block(&batch, &first, vec![]); db.write(batch).unwrap(); + bc.commit(); assert_eq!(bc.best_block_hash(), first_hash); } @@ -1248,6 +1286,7 @@ mod tests { let batch = db.transaction(); bc.insert_block(&batch, &b1, vec![]); db.write(batch).unwrap(); + bc.commit(); let transactions = bc.transactions(&b1_hash).unwrap(); assert_eq!(transactions.len(), 7); @@ -1260,6 +1299,7 @@ mod tests { let batch = db.transaction(); let res = bc.insert_block(&batch, bytes, receipts); db.write(batch).unwrap(); + bc.commit(); res } @@ -1350,11 +1390,13 @@ mod tests { for _ in 0..5 { let canon_block = canon_chain.generate(&mut finalizer).unwrap(); bc.insert_block(&batch, &canon_block, vec![]); + bc.commit(); } assert_eq!(bc.best_block_number(), 5); bc.insert_block(&batch, &uncle, vec![]); db.write(batch).unwrap(); + bc.commit(); } // re-loading the blockchain should load the correct best block. @@ -1380,7 +1422,9 @@ mod tests { let batch = db.transaction(); bc.insert_block(&batch, &first, vec![]); + bc.commit(); bc.insert_block(&batch, &second, vec![]); + bc.commit(); db.write(batch).unwrap(); assert_eq!(bc.rewind(), Some(first_hash.clone())); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4fd4e93ed..bb878b834 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -452,6 +452,7 @@ impl Client { }); // Final commit to the DB self.db.write(batch).expect("State DB write failed."); + self.chain.commit(); self.update_last_hashes(&parent, hash); route diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index b88c773c2..c4b015f15 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -261,6 +261,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult GuardedTempRes let batch = db.transaction(); for block_order in 1..block_number { bc.insert_block(&batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]); + bc.commit(); } db.write(batch).unwrap();