rewind to header as part of txhashset validation (#808)

* rewind to header as part of txhashset validation
otherwise we risk including a new block and the roots do not match

* fix bug in rm_log rewind (wants to be inclusive of provided index)

* put block marker in the index so we can rewind correctly
during validation of the new txhashset

* rustfmt
This commit is contained in:
Antioch Peverell 2018-03-19 21:31:57 -04:00 committed by GitHub
parent cd72be893e
commit 7816f35238
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 24 additions and 13 deletions

View file

@ -515,10 +515,14 @@ impl Chain {
let header = self.store.get_block_header(&h)?; let header = self.store.get_block_header(&h)?;
txhashset::zip_write(self.db_root.clone(), txhashset_data)?; txhashset::zip_write(self.db_root.clone(), txhashset_data)?;
// write the block marker so we can safely rewind to
// the pos for that block when we validate the extension below
self.store
.save_block_marker(&h, &(rewind_to_output, rewind_to_kernel))?;
let mut txhashset = let mut txhashset =
txhashset::TxHashSet::open(self.db_root.clone(), self.store.clone(), None)?; txhashset::TxHashSet::open(self.db_root.clone(), self.store.clone(), None)?;
txhashset::extending(&mut txhashset, |extension| { txhashset::extending(&mut txhashset, |extension| {
extension.rewind_pos(header.height, rewind_to_output, rewind_to_kernel)?;
extension.validate(&header)?; extension.validate(&header)?;
// TODO validate kernels and their sums with Outputs // TODO validate kernels and their sums with Outputs
extension.rebuild_index()?; extension.rebuild_index()?;

View file

@ -550,8 +550,13 @@ impl<'a> Extension<'a> {
} }
} }
/// Validate the current txhashset state against a block header /// Validate the txhashset state against the provided block header.
pub fn validate(&self, header: &BlockHeader) -> Result<(), Error> { /// Rewinds to that pos for the header first so we see a consistent
/// view of the world.
pub fn validate(&mut self, header: &BlockHeader) -> Result<(), Error> {
// first rewind to the provided header
&self.rewind(header)?;
// validate all hashes and sums within the trees // validate all hashes and sums within the trees
if let Err(e) = self.output_pmmr.validate() { if let Err(e) = self.output_pmmr.validate() {
return Err(Error::InvalidTxHashSet(e)); return Err(Error::InvalidTxHashSet(e));

View file

@ -44,6 +44,7 @@ bitflags! {
/// A helper to hold the roots of the txhashset in order to keep them /// A helper to hold the roots of the txhashset in order to keep them
/// readable /// readable
#[derive(Debug)]
pub struct TxHashSetRoots { pub struct TxHashSetRoots {
/// Output root /// Output root
pub output_root: Hash, pub output_root: Hash,

View file

@ -215,10 +215,11 @@ where
fn dump_stats(&self) { fn dump_stats(&self) {
debug!( debug!(
LOGGER, LOGGER,
"pmmr backend: unpruned - {}, hashes - {}, data - {}", "pmmr backend: unpruned - {}, hashes - {}, data - {}, rm_log - {:?}",
self.unpruned_size().unwrap_or(0), self.unpruned_size().unwrap_or(0),
self.hash_size().unwrap_or(0), self.hash_size().unwrap_or(0),
self.data_size().unwrap_or(0) self.data_size().unwrap_or(0),
self.rm_log.removed
); );
} }
} }

View file

@ -250,21 +250,21 @@ impl RemoveLog {
Ok(rl) Ok(rl)
} }
/// Truncate and empties the remove log. /// Rewinds the remove log back to the provided index.
pub fn rewind(&mut self, last_offs: u32) -> io::Result<()> { /// We keep everything in the rm_log from that index and earlier.
/// In practice the index is a block height, so we rewind back to that block
/// keeping everything in the rm_log up to and including that block.
pub fn rewind(&mut self, idx: u32) -> io::Result<()> {
// simplifying assumption: we always remove older than what's in tmp // simplifying assumption: we always remove older than what's in tmp
self.removed_tmp = vec![]; self.removed_tmp = vec![];
// backing it up before truncating // backing it up before truncating
self.removed_bak = self.removed.clone(); self.removed_bak = self.removed.clone();
if last_offs == 0 { if idx == 0 {
self.removed = vec![]; self.removed = vec![];
} else { } else {
self.removed = self.removed // retain rm_log entries up to and including those at the provided index
.iter() self.removed.retain(|&(_, x)| x <= idx);
.filter(|&&(_, idx)| idx < last_offs)
.map(|x| *x)
.collect();
} }
Ok(()) Ok(())
} }