Skip to content

Commit 4d1fdaf

Browse files
authored
Merge pull request #118 from mempool/junderw/fix-rollback
Fix rollback tip handling
2 parents 7daee2c + c6367b9 commit 4d1fdaf

2 files changed

Lines changed: 31 additions & 4 deletions

File tree

src/new_index/schema.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,12 +326,27 @@ impl Indexer {
326326
// Must rollback blocks before rolling forward
327327
let headers_len = {
328328
let mut headers = self.store.indexed_headers.write().unwrap();
329-
let reorged = headers.apply(new_headers.clone());
329+
let (reorged, rollback_tip) = headers.apply(new_headers.clone());
330330
assert_eq!(tip, *headers.tip());
331331
let headers_len = headers.len();
332332
drop(headers);
333333

334334
if !reorged.is_empty() {
335+
// We should rollback the tip blockhash first in case something crashes during rollback
336+
// or before the next block appears and sets the new tip.
337+
match rollback_tip {
338+
Some(rb_tip) => {
339+
debug!("updating reorged tip to {:?}", rb_tip);
340+
self.store.txstore_db.put_sync(b"t", &serialize(&rb_tip));
341+
}
342+
None => {
343+
// This should only happen on regtest or some weird networks.
344+
error!("Rollback to genesis block detected!!! (rollback to height 0)");
345+
// There is no tip anymore.
346+
self.store.txstore_db.delete(vec![b"t".into()]);
347+
}
348+
}
349+
335350
self.reorg(reorged, &daemon)?;
336351
}
337352

src/util/block.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ impl HeaderList {
156156
}
157157

158158
/// Returns any rolled back blocks in order from old tip first and first block in the fork is last
159-
pub fn apply(&mut self, new_headers: Vec<HeaderEntry>) -> Vec<HeaderEntry> {
159+
/// It also returns the blockhash of the post-rollback tip.
160+
pub fn apply(
161+
&mut self,
162+
new_headers: Vec<HeaderEntry>,
163+
) -> (Vec<HeaderEntry>, Option<BlockHash>) {
160164
// new_headers[i] -> new_headers[i - 1] (i.e. new_headers.last() is the tip)
161165
for i in 1..new_headers.len() {
162166
assert_eq!(new_headers[i - 1].height() + 1, new_headers[i].height());
@@ -176,14 +180,22 @@ impl HeaderList {
176180
assert_eq!(entry.header().prev_blockhash, expected_prev_blockhash);
177181
height
178182
}
179-
None => return vec![],
183+
None => return (vec![], None),
180184
};
181185
debug!(
182186
"applying {} new headers from height {}",
183187
new_headers.len(),
184188
new_height
185189
);
186190
let mut removed = self.headers.split_off(new_height); // keep [0..new_height) entries
191+
192+
// If we reorged, we should return the last blockhash before adding the new chain's blockheaders.
193+
let reorged_tip = if !removed.is_empty() {
194+
self.headers.last().map(|be| be.hash()).cloned()
195+
} else {
196+
None
197+
};
198+
187199
for new_header in new_headers {
188200
let height = new_header.height();
189201
assert_eq!(height, self.headers.len());
@@ -192,7 +204,7 @@ impl HeaderList {
192204
self.heights.insert(self.tip, height);
193205
}
194206
removed.reverse();
195-
removed
207+
(removed, reorged_tip)
196208
}
197209

198210
pub fn header_by_blockhash(&self, blockhash: &BlockHash) -> Option<&HeaderEntry> {

0 commit comments

Comments
 (0)