From 05ec6fda38015a08918f27aa6e380e3c7283405f Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 31 Oct 2023 12:18:17 +0000 Subject: [PATCH] [PIBD] Fix to restart of PIBD download on corrupted data or mismatched roots (#3775) * process restart * remove trace statements * remove further debug statement * remove further debug statement --- chain/src/chain.rs | 63 ++++++++++++++++++++++++----- servers/src/grin/sync/state_sync.rs | 6 +-- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 6d2382cc0..273a44153 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -158,7 +158,7 @@ pub struct Chain { pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>, denylist: Arc>>, archive_mode: bool, - genesis: BlockHeader, + genesis: Block, } impl Chain { @@ -184,7 +184,7 @@ impl Chain { None, )?; - setup_head(&genesis, &store, &mut header_pmmr, &mut txhashset)?; + setup_head(&genesis, &store, &mut header_pmmr, &mut txhashset, false)?; // Initialize the output_pos index based on UTXO set // and NRD kernel_pos index based recent kernel history. @@ -207,7 +207,7 @@ impl Chain { pow_verifier, denylist: Arc::new(RwLock::new(vec![])), archive_mode, - genesis: genesis.header, + genesis: genesis, }; chain.log_heads()?; @@ -269,6 +269,33 @@ impl Chain { Ok(()) } + /// wipes the chain head down to genesis, without attempting to rewind + /// Used upon PIBD failure, where we want to keep the header chain but + /// restart the output PMMRs from scratch + pub fn reset_chain_head_to_genesis(&self) -> Result<(), Error> { + let mut header_pmmr = self.header_pmmr.write(); + let mut txhashset = self.txhashset.write(); + let batch = self.store.batch()?; + + // Change head back to genesis + { + let head = Tip::from_header(&self.genesis.header); + batch.save_body_head(&head)?; + batch.commit()?; + } + + // Reinit + setup_head( + &self.genesis, + &self.store, + &mut header_pmmr, + &mut txhashset, + true, + )?; + + Ok(()) + } + /// Reset prune lists (when PIBD resets and rolls back the /// entire chain, the prune list needs to be manually wiped /// as it's currently not included as part of rewind) @@ -309,7 +336,7 @@ impl Chain { /// return genesis header pub fn genesis(&self) -> BlockHeader { - self.genesis.clone() + self.genesis.header.clone() } /// Shared store instance. @@ -703,7 +730,7 @@ impl Chain { txhashset::extending_readonly(&mut header_pmmr, &mut txhashset, |ext, batch| { self.rewind_and_apply_fork(&header, ext, batch)?; ext.extension.validate( - &self.genesis, + &self.genesis.header, fast_validation, &NoStatus, None, @@ -943,7 +970,7 @@ impl Chain { self.txhashset(), self.header_pmmr.clone(), header.clone(), - self.genesis.clone(), + self.genesis.header.clone(), self.store.clone(), )) } @@ -1125,7 +1152,13 @@ impl Chain { let header_pmmr = self.header_pmmr.read(); let batch = self.store.batch()?; - txhashset.verify_kernel_pos_index(&self.genesis, &header_pmmr, &batch, None, None)?; + txhashset.verify_kernel_pos_index( + &self.genesis.header, + &header_pmmr, + &batch, + None, + None, + )?; } // all good, prepare a new batch and update all the required records @@ -1143,8 +1176,15 @@ impl Chain { // Validate the extension, generating the utxo_sum and kernel_sum. // Full validation, including rangeproofs and kernel signature verification. - let (utxo_sum, kernel_sum) = - extension.validate(&self.genesis, false, status, None, None, &header, None)?; + let (utxo_sum, kernel_sum) = extension.validate( + &self.genesis.header, + false, + status, + None, + None, + &header, + None, + )?; // Save the block_sums (utxo_sum, kernel_sum) to the db for use later. batch.save_block_sums( @@ -1231,7 +1271,7 @@ impl Chain { let tail = match batch.tail() { Ok(tail) => tail, - Err(_) => Tip::from_header(&self.genesis), + Err(_) => Tip::from_header(&self.genesis.header), }; let mut cutoff = head.height.saturating_sub(horizon); @@ -1643,6 +1683,7 @@ fn setup_head( store: &store::ChainStore, header_pmmr: &mut txhashset::PMMRHandle, txhashset: &mut txhashset::TxHashSet, + resetting_pibd: bool, ) -> Result<(), Error> { let mut batch = store.batch()?; @@ -1689,7 +1730,7 @@ fn setup_head( let head = batch.get_block_header(&head.last_block_h)?; let pibd_tip = store.pibd_head()?; let pibd_head = batch.get_block_header(&pibd_tip.last_block_h)?; - if pibd_head.height > head.height { + if pibd_head.height > head.height && !resetting_pibd { pibd_in_progress = true; pibd_head } else { diff --git a/servers/src/grin/sync/state_sync.rs b/servers/src/grin/sync/state_sync.rs index 60f733550..bdd8d1fe4 100644 --- a/servers/src/grin/sync/state_sync.rs +++ b/servers/src/grin/sync/state_sync.rs @@ -111,12 +111,12 @@ impl StateSync { if let Some(d) = desegmenter.write().as_mut() { d.reset(); }; - if let Err(e) = self.chain.reset_chain_head(self.chain.genesis(), false) { - error!("pibd_sync restart: chain reset error = {}", e); - } if let Err(e) = self.chain.reset_pibd_head() { error!("pibd_sync restart: reset pibd_head error = {}", e); } + if let Err(e) = self.chain.reset_chain_head_to_genesis() { + error!("pibd_sync restart: chain reset to genesis error = {}", e); + } if let Err(e) = self.chain.reset_prune_lists() { error!("pibd_sync restart: reset prune lists error = {}", e); }