Fixes for chain processing during bootstrap

Adds checks to ignore a block that's already known (can happen
if the block was saved but the chain update failed). Also saves
saves the chain slightly differently during bootstrap as the
header chain should only be updated when a header is received
(and not when we got a full block). Finally, do not broadcast
during bootstrap.
This commit is contained in:
Ignotus Peverell 2017-04-27 21:59:53 -07:00
parent 2d793639b5
commit a7089d1975
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
3 changed files with 49 additions and 10 deletions

View file

@ -115,9 +115,9 @@ pub fn process_block(b: &Block,
try!(validate_header(&b.header, &mut ctx));
}
try!(validate_block(b, &mut ctx));
info!("Block at {} with hash {} is valid, going to save and append.",
b.header.height,
b.hash());
debug!("Block at {} with hash {} is valid, going to save and append.",
b.header.height,
b.hash());
try!(add_block(b, &mut ctx));
// TODO a global lock should be set before that step or even earlier
update_head(b, &mut ctx)
@ -155,6 +155,13 @@ fn check_known(bh: Hash, ctx: &mut BlockContext) -> Result<(), Error> {
if bh == ctx.head.last_block_h || bh == ctx.head.prev_block_h {
return Err(Error::Unfit("already known".to_string()));
}
if let Ok(b) = ctx.store.get_block(&bh) {
// there is a window where a block can be saved but the chain head not
// updated yet, we plug that window here by re-accepting the block
if b.header.total_difficulty <= ctx.head.total_difficulty {
return Err(Error::Unfit("already in store".to_string()));
}
}
Ok(())
}
@ -212,12 +219,18 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
/// Fully validate the block content.
fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
if b.header.height > ctx.head.height + 1 {
// check orphan again, an orphan coming out of order from sync will have
// bypassed header checks
// TODO actually handle orphans and add them to a size-limited set
return Err(Error::Unfit("orphan".to_string()));
}
let curve = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
try!(b.validate(&curve).map_err(&Error::InvalidBlockProof));
if !ctx.opts.intersects(SYNC) {
// TODO check every input exists as a UTXO using the UXTO index
}
// TODO check every input exists as a UTXO using the UTXO index
Ok(())
}
@ -225,9 +238,11 @@ fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
fn add_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
ctx.store.save_block(b).map_err(&Error::StoreErr)?;
// broadcast the block
let adapter = ctx.adapter.clone();
adapter.block_accepted(b);
if !ctx.opts.intersects(SYNC) {
// broadcast the block
let adapter = ctx.adapter.clone();
adapter.block_accepted(b);
}
Ok(())
}
@ -244,8 +259,17 @@ fn update_head(b: &Block, ctx: &mut BlockContext) -> Result<Option<Tip>, Error>
// when extending the head), update it
let tip = Tip::from_block(&b.header);
if tip.total_difficulty > ctx.head.total_difficulty {
// update the block height index
ctx.store.setup_height(&b.header).map_err(&Error::StoreErr)?;
ctx.store.save_head(&tip).map_err(&Error::StoreErr)?;
// in sync mode, only update the "body chain", otherwise update both the
// "header chain" and "body chain"
if ctx.opts.intersects(SYNC) {
ctx.store.save_body_head(&tip).map_err(&Error::StoreErr)?;
} else {
ctx.store.save_head(&tip).map_err(&Error::StoreErr)?;
}
ctx.head = tip.clone();
info!("Updated head to {} at {}.", b.hash(), b.header.height);

View file

@ -62,6 +62,10 @@ impl ChainStore for ChainKVStore {
.write()
}
fn save_body_head(&self, t: &Tip) -> Result<(), Error> {
self.db.put_ser(&vec![HEAD_PREFIX], t)
}
fn get_header_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]))
}
@ -78,6 +82,10 @@ impl ChainStore for ChainKVStore {
option_to_not_found(self.db.get_ser(&to_key(BLOCK_HEADER_PREFIX, &mut h.to_vec())))
}
fn check_block_exists(&self, h: &Hash) -> Result<bool, Error> {
self.db.exists(&to_key(BLOCK_PREFIX, &mut h.to_vec()))
}
fn save_block(&self, b: &Block) -> Result<(), Error> {
// saving the block and its header
let mut batch = self.db

View file

@ -97,12 +97,19 @@ pub trait ChainStore: Send + Sync {
/// Save the provided tip as the current head of our chain
fn save_head(&self, t: &Tip) -> Result<(), Error>;
/// Save the provided tip as the current head of the body chain, leaving the
/// header chain alone.
fn save_body_head(&self, t: &Tip) -> Result<(), Error>;
/// Gets a block header by hash
fn get_block(&self, h: &Hash) -> Result<Block, Error>;
/// Gets a block header by hash
fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, Error>;
/// Checks whether a block has been been processed and saved
fn check_block_exists(&self, h: &Hash) -> Result<bool, Error>;
/// Save the provided block in store
fn save_block(&self, b: &Block) -> Result<(), Error>;