reorganization on insert
This commit is contained in:
parent
4bb0bc47c8
commit
dac7978e8f
|
@ -99,6 +99,8 @@ pub enum Error {
|
||||||
ForkTooLong,
|
ForkTooLong,
|
||||||
/// Main chain block transaction attempts to double-spend
|
/// Main chain block transaction attempts to double-spend
|
||||||
DoubleSpend(H256),
|
DoubleSpend(H256),
|
||||||
|
/// Main chain block transaction attempts to double-spend
|
||||||
|
NoBestBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for Error {
|
impl From<String> for Error {
|
||||||
|
@ -376,23 +378,55 @@ impl Storage {
|
||||||
Err(Error::ForkTooLong)
|
Err(Error::ForkTooLong)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_best_number(&self) -> Option<u32> {
|
fn best_number(&self) -> Option<u32> {
|
||||||
self.read_meta_u32(KEY_BEST_BLOCK_NUMBER)
|
self.read_meta_u32(KEY_BEST_BLOCK_NUMBER)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn best_hash(&self) -> Option<H256> {
|
||||||
|
self.get(COL_META, KEY_BEST_BLOCK_HASH).map(|val| H256::from(&**val))
|
||||||
|
}
|
||||||
|
|
||||||
fn canonize_block(&self, context: &mut UpdateContext, at_height: u32, hash: &H256) -> Result<(), Error> {
|
fn canonize_block(&self, context: &mut UpdateContext, at_height: u32, hash: &H256) -> Result<(), Error> {
|
||||||
let transactions = self.block_transactions_by_hash(hash);
|
let transactions = self.block_transactions_by_hash(hash);
|
||||||
try!(self.update_transactions_meta(context, at_height, &transactions));
|
try!(self.update_transactions_meta(context, at_height, &transactions));
|
||||||
|
|
||||||
|
// only canonical blocks are allowed to wield a number
|
||||||
|
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(at_height), std::ops::Deref::deref(hash));
|
||||||
|
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(hash), at_height);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_reorganize(&self, context: &mut UpdateContext, hash: &H256) -> Result<(), Error> {
|
// maybe reorganize to the _known_ block
|
||||||
|
// 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> {
|
||||||
let (at_height, route) = try!(self.fork_route(MAX_FORK_ROUTE_PRESET, hash));
|
let (at_height, route) = try!(self.fork_route(MAX_FORK_ROUTE_PRESET, hash));
|
||||||
if (route.len() as i32) < (self.read_best_number().unwrap_or(0) as i32 - at_height as i32) {
|
if (route.len() as i32) <= (self.best_number().unwrap_or(0) as i32 - at_height as i32) {
|
||||||
return Ok(());
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
let mut now_best = try!(self.best_number().ok_or(Error::NoBestBlock));
|
||||||
|
|
||||||
|
// decanonizing main chain to the split point
|
||||||
|
loop {
|
||||||
|
let next_to_decanonize = try!(self.best_hash().ok_or(Error::NoBestBlock));
|
||||||
|
self.decanonize_block(context, &next_to_decanonize);
|
||||||
|
|
||||||
|
now_best -= 1;
|
||||||
|
|
||||||
|
if now_best == at_height { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// canonizing all route from the split point
|
||||||
|
for new_canonical_hash in route.iter() {
|
||||||
|
now_best += 1;
|
||||||
|
try!(self.canonize_block(context, now_best, &new_canonical_hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
// finaly canonizing the top block
|
||||||
|
try!(self.canonize_block(context, now_best + 1, hash));
|
||||||
|
|
||||||
|
Ok(Some((now_best+1, hash.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,12 +489,12 @@ impl Store for Storage {
|
||||||
|
|
||||||
let block_hash = block.hash();
|
let block_hash = block.hash();
|
||||||
|
|
||||||
let 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().previous_header_hash != best_hash => best_hash.clone(),
|
||||||
_ => block_hash.clone(),
|
_ => block_hash.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_best_number = match best_block.as_ref().map(|b| b.number) {
|
let mut new_best_number = match best_block.as_ref().map(|b| b.number) {
|
||||||
Some(best_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 }
|
else { best_number }
|
||||||
|
@ -495,6 +529,19 @@ impl Store for Storage {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
// this can canonize the block parent if block parent + this block is longer than the main chain
|
||||||
|
else if let Some((reorg_number, reorg_hash)) = self.maybe_reorganize(&mut context, &block.header().previous_header_hash).unwrap_or(None) {
|
||||||
|
// if so, we have new best main chain block
|
||||||
|
new_best_number = reorg_number;
|
||||||
|
new_best_hash = new_best_hash;
|
||||||
|
|
||||||
|
// and we canonize it also by provisioning 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.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.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));
|
||||||
|
|
||||||
context.apply(&self.database);
|
context.apply(&self.database);
|
||||||
|
|
Loading…
Reference in New Issue