mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 08:51:08 +03:00
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:
parent
2d793639b5
commit
a7089d1975
3 changed files with 49 additions and 10 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
Loading…
Reference in a new issue