reorganization result

This commit is contained in:
NikVolf 2016-11-17 17:56:44 +03:00
parent 9b6f24849d
commit 7a0c9f44f6
3 changed files with 56 additions and 15 deletions

View File

@ -34,7 +34,7 @@ pub enum BlockLocation {
} }
pub use best_block::BestBlock; pub use best_block::BestBlock;
pub use storage::{Storage, Store, Error}; pub use storage::{Storage, Store, Error, BlockInsertedChain};
pub use kvdb::Database; pub use kvdb::Database;
#[cfg(feature="dev")] #[cfg(feature="dev")]

View File

@ -65,7 +65,7 @@ pub trait Store : Send + Sync {
} }
/// insert block in the storage /// insert block in the storage
fn insert_block(&self, block: &chain::Block) -> Result<(), Error>; fn insert_block(&self, block: &chain::Block) -> Result<BlockInsertedChain, Error>;
/// get transaction metadata /// get transaction metadata
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>; fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta>;
@ -201,6 +201,35 @@ impl UpdateContext {
} }
} }
#[derive(Debug)]
pub struct Reorganization {
height: u32,
canonized: Vec<H256>,
decanonized: Vec<H256>,
}
impl Reorganization {
fn new(height: u32) -> Reorganization {
Reorganization { height: height, canonized: Vec::new(), decanonized: Vec::new() }
}
fn push_canonized(&mut self, hash: &H256) {
self.canonized.push(hash.clone());
}
fn push_decanonized(&mut self, hash: &H256) {
self.decanonized.push(hash.clone());
}
}
#[derive(Debug)]
pub enum BlockInsertedChain {
Disconnected,
Main,
Side,
Rorganized(Reorganization),
}
impl Storage { impl Storage {
/// new storage at the selected path /// new storage at the selected path
@ -465,7 +494,7 @@ impl Storage {
// maybe reorganize to the _known_ block // maybe reorganize to the _known_ block
// it will actually reorganize only when side chain is at least the same length as main // it will actually reorganize only when side chain is at least the same length as main
fn maybe_reorganize(&self, context: &mut UpdateContext, hash: &H256) -> Result<Option<(u32, H256)>, Error> { fn maybe_reorganize(&self, context: &mut UpdateContext, hash: &H256) -> Result<Option<Reorganization>, Error> {
context.restore_point(); context.restore_point();
match self.maybe_reorganize_fallable(context, hash) { match self.maybe_reorganize_fallable(context, hash) {
@ -479,7 +508,7 @@ impl Storage {
} }
} }
fn maybe_reorganize_fallable(&self, context: &mut UpdateContext, hash: &H256) -> Result<Option<(u32, H256)>, Error> { fn maybe_reorganize_fallable(&self, context: &mut UpdateContext, hash: &H256) -> Result<Option<Reorganization>, Error> {
if self.block_number(hash).is_some() { if self.block_number(hash).is_some() {
return Ok(None); // cannot reorganize to canonical block return Ok(None); // cannot reorganize to canonical block
} }
@ -493,12 +522,14 @@ impl Storage {
return Ok(None); return Ok(None);
} }
let mut reorganization = Reorganization::new(at_height);
let mut now_best = try!(self.best_number().ok_or(Error::Consistency(ConsistencyError::NoBestBlock))); let mut now_best = try!(self.best_number().ok_or(Error::Consistency(ConsistencyError::NoBestBlock)));
// decanonizing main chain to the split point // decanonizing main chain to the split point
loop { loop {
let next_decanonize = try!(self.block_hash(now_best).ok_or(Error::unknown_number(now_best))); let next_decanonize = try!(self.block_hash(now_best).ok_or(Error::unknown_number(now_best)));
try!(self.decanonize_block(context, &next_decanonize)); try!(self.decanonize_block(context, &next_decanonize));
reorganization.push_decanonized(&next_decanonize);
now_best -= 1; now_best -= 1;
@ -509,12 +540,14 @@ impl Storage {
for new_canonical_hash in &route { for new_canonical_hash in &route {
now_best += 1; now_best += 1;
try!(self.canonize_block(context, now_best, &new_canonical_hash)); try!(self.canonize_block(context, now_best, &new_canonical_hash));
reorganization.push_canonized(&new_canonical_hash);
} }
// finaly canonizing the top block we are reorganizing to // finaly canonizing the top block we are reorganizing to
try!(self.canonize_block(context, now_best + 1, hash)); try!(self.canonize_block(context, now_best + 1, hash));
reorganization.push_canonized(&hash);
Ok(Some((now_best+1, hash.clone()))) Ok(Some(reorganization))
} }
} }
@ -570,7 +603,7 @@ impl Store for Storage {
) )
} }
fn insert_block(&self, block: &chain::Block) -> Result<(), Error> { fn insert_block(&self, block: &chain::Block) -> Result<BlockInsertedChain, Error> {
// ! lock will be held during the entire insert routine // ! lock will be held during the entire insert routine
let mut best_block = self.best_block.write(); let mut best_block = self.best_block.write();
@ -612,13 +645,15 @@ impl Store for Storage {
); );
// the block is continuing the main chain // the block is continuing the main chain
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, 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
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), std::ops::Deref::deref(&block_hash)); context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), std::ops::Deref::deref(&block_hash));
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(&block_hash), new_best_number); context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(&block_hash), new_best_number);
BlockInsertedChain::Main
} }
// the block does not continue the main chain // the block does not continue the main chain
@ -626,9 +661,9 @@ impl Store for Storage {
// 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().previous_header_hash) {
Ok(Some((reorg_number, _))) => { 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_number + 1; new_best_number = reorg.height + 1;
new_best_hash = block_hash; new_best_hash = block_hash;
// and we canonize it also by provisioning transactions // and we canonize it also by provisioning transactions
@ -636,6 +671,10 @@ impl Store for Storage {
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), std::ops::Deref::deref(&new_best_hash)); context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), std::ops::Deref::deref(&new_best_hash));
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(&new_best_hash), new_best_number); context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(&new_best_hash), new_best_number);
reorg.push_canonized(&new_best_hash);
BlockInsertedChain::Rorganized(reorg)
}, },
Err(Error::Consistency(consistency_error)) => { Err(Error::Consistency(consistency_error)) => {
match consistency_error { match consistency_error {
@ -653,6 +692,7 @@ impl Store for Storage {
// this is orphan block inserted or disconnected chain head updated, we allow that (by now) // this is orphan block inserted or disconnected chain head updated, we allow that (by now)
// so it is no-op // so it is no-op
warn!(target: "reorg", "Disconnected chain head {} updated with {}", &hash, &block_hash); warn!(target: "reorg", "Disconnected chain head {} updated with {}", &hash, &block_hash);
BlockInsertedChain::Disconnected
}, },
_ => { _ => {
// we don't allow other errors on side chain/orphans // we don't allow other errors on side chain/orphans
@ -665,9 +705,10 @@ impl Store for Storage {
}, },
Ok(None) => { Ok(None) => {
// reorganize didn't happen but the block is ok, no-op here // reorganize didn't happen but the block is ok, no-op here
BlockInsertedChain::Side
} }
} }
} };
// we always update best hash even if it is not changed // we always update best hash even if it is not changed
context.db_transaction.put(Some(COL_META), KEY_BEST_BLOCK_HASH, std::ops::Deref::deref(&new_best_hash)); context.db_transaction.put(Some(COL_META), KEY_BEST_BLOCK_HASH, std::ops::Deref::deref(&new_best_hash));
@ -678,7 +719,7 @@ impl Store for Storage {
// updating locked best block // updating locked best block
*best_block = Some(BestBlock { hash: new_best_hash, number: new_best_number }); *best_block = Some(BestBlock { hash: new_best_hash, number: new_best_number });
Ok(()) Ok(result)
} }
fn transaction(&self, hash: &H256) -> Option<chain::Transaction> { fn transaction(&self, hash: &H256) -> Option<chain::Transaction> {

View File

@ -1,6 +1,6 @@
//! Test storage //! Test storage
use super::{BlockRef, Store, Error, BestBlock, BlockLocation}; use super::{BlockRef, Store, Error, BestBlock, BlockLocation, BlockInsertedChain};
use chain::{self, Block, RepresentH256}; use chain::{self, Block, RepresentH256};
use primitives::hash::H256; use primitives::hash::H256;
use serialization; use serialization;
@ -113,13 +113,13 @@ impl Store for TestStorage {
.cloned() .cloned()
} }
fn insert_block(&self, block: &chain::Block) -> Result<(), Error> { fn insert_block(&self, block: &chain::Block) -> Result<BlockInsertedChain, Error> {
let hash = block.hash(); let hash = block.hash();
let mut data = self.data.write(); let mut data = self.data.write();
match data.blocks.entry(hash.clone()) { match data.blocks.entry(hash.clone()) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
replace(entry.get_mut(), block.clone()); replace(entry.get_mut(), block.clone());
return Ok(()); return Ok(BlockInsertedChain::Main);
}, },
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(block.clone()); entry.insert(block.clone());
@ -144,7 +144,7 @@ impl Store for TestStorage {
}, },
} }
Ok(()) Ok(BlockInsertedChain::Main)
} }
// just spawns new meta so far, use real store for proper tests // just spawns new meta so far, use real store for proper tests