mirror of
https://github.com/romanz/electrs.git
synced 2025-02-23 22:56:55 +01:00
Allow reorgs to shorten valid headers' chain
This commit is contained in:
parent
bb62df8793
commit
bf68461e8f
3 changed files with 50 additions and 34 deletions
|
@ -149,7 +149,7 @@ fn load_headers(daemon: &Daemon) -> Result<HeaderList> {
|
|||
let tip = daemon.getbestblockhash()?;
|
||||
let mut headers = HeaderList::empty();
|
||||
let new_headers = headers.order(daemon.get_new_headers(&headers, &tip)?);
|
||||
headers.apply(new_headers);
|
||||
headers.apply(new_headers, tip);
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
|
|
|
@ -227,6 +227,7 @@ fn read_indexed_headers(store: &ReadStore) -> HeaderList {
|
|||
Some(row) => deserialize(&row).unwrap(),
|
||||
None => Sha256dHash::default(),
|
||||
};
|
||||
trace!("lastest indexed blockhash: {}", latest_blockhash);
|
||||
let mut map = HeaderMap::new();
|
||||
for row in store.scan(b"B") {
|
||||
let key: BlockKey = bincode::deserialize(&row.key).unwrap();
|
||||
|
@ -260,7 +261,7 @@ fn read_indexed_headers(store: &ReadStore) -> HeaderList {
|
|||
);
|
||||
let mut result = HeaderList::empty();
|
||||
let entries = result.order(headers);
|
||||
result.apply(entries);
|
||||
result.apply(entries, latest_blockhash);
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -345,7 +346,7 @@ impl Index {
|
|||
|
||||
pub fn best_header(&self) -> Option<HeaderEntry> {
|
||||
let headers = self.headers.read().unwrap();
|
||||
headers.header_by_blockhash(headers.tip()).cloned()
|
||||
headers.header_by_blockhash(&headers.tip()).cloned()
|
||||
}
|
||||
|
||||
pub fn get_header(&self, height: usize) -> Option<HeaderEntry> {
|
||||
|
@ -419,8 +420,8 @@ impl Index {
|
|||
timer.observe_duration();
|
||||
|
||||
fetcher.join().expect("block fetcher failed");
|
||||
self.headers.write().unwrap().apply(new_headers);
|
||||
assert_eq!(tip, *self.headers.read().unwrap().tip());
|
||||
self.headers.write().unwrap().apply(new_headers, tip);
|
||||
assert_eq!(tip, self.headers.read().unwrap().tip());
|
||||
Ok(tip)
|
||||
}
|
||||
}
|
||||
|
|
73
src/util.rs
73
src/util.rs
|
@ -62,10 +62,30 @@ impl fmt::Debug for HeaderEntry {
|
|||
}
|
||||
}
|
||||
|
||||
struct HashedHeader {
|
||||
blockhash: Sha256dHash,
|
||||
header: BlockHeader,
|
||||
}
|
||||
|
||||
fn hash_headers(headers: Vec<BlockHeader>) -> Vec<HashedHeader> {
|
||||
// header[i] -> header[i-1] (i.e. header.last() is the tip)
|
||||
let hashed_headers =
|
||||
Vec::<HashedHeader>::from_iter(headers.into_iter().map(|header| HashedHeader {
|
||||
blockhash: header.bitcoin_hash(),
|
||||
header,
|
||||
}));
|
||||
for i in 1..hashed_headers.len() {
|
||||
assert_eq!(
|
||||
hashed_headers[i].header.prev_blockhash,
|
||||
hashed_headers[i - 1].blockhash
|
||||
);
|
||||
}
|
||||
hashed_headers
|
||||
}
|
||||
|
||||
pub struct HeaderList {
|
||||
headers: Vec<HeaderEntry>,
|
||||
heights: HashMap<Sha256dHash, usize>,
|
||||
tip: Sha256dHash,
|
||||
}
|
||||
|
||||
impl HeaderList {
|
||||
|
@ -73,27 +93,12 @@ impl HeaderList {
|
|||
HeaderList {
|
||||
headers: vec![],
|
||||
heights: HashMap::new(),
|
||||
tip: Sha256dHash::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn order(&self, new_headers: Vec<BlockHeader>) -> Vec<HeaderEntry> {
|
||||
// header[i] -> header[i-1] (i.e. header.last() is the tip)
|
||||
struct HashedHeader {
|
||||
blockhash: Sha256dHash,
|
||||
header: BlockHeader,
|
||||
}
|
||||
let hashed_headers =
|
||||
Vec::<HashedHeader>::from_iter(new_headers.into_iter().map(|header| HashedHeader {
|
||||
blockhash: header.bitcoin_hash(),
|
||||
header,
|
||||
}));
|
||||
for i in 1..hashed_headers.len() {
|
||||
assert_eq!(
|
||||
hashed_headers[i].header.prev_blockhash,
|
||||
hashed_headers[i - 1].blockhash
|
||||
);
|
||||
}
|
||||
let hashed_headers = hash_headers(new_headers);
|
||||
let prev_blockhash = match hashed_headers.first() {
|
||||
Some(h) => h.header.prev_blockhash,
|
||||
None => return vec![], // hashed_headers is empty
|
||||
|
@ -117,7 +122,7 @@ impl HeaderList {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn apply(&mut self, new_headers: Vec<HeaderEntry>) {
|
||||
pub fn apply(&mut self, new_headers: Vec<HeaderEntry>, tip: Sha256dHash) {
|
||||
// new_headers[i] -> new_headers[i - 1] (i.e. new_headers.last() is the tip)
|
||||
for i in 1..new_headers.len() {
|
||||
assert_eq!(new_headers[i - 1].height() + 1, new_headers[i].height());
|
||||
|
@ -128,6 +133,10 @@ impl HeaderList {
|
|||
}
|
||||
let new_height = match new_headers.first() {
|
||||
Some(entry) => {
|
||||
// Make sure tip is consistent (if there are new headers)
|
||||
let expected_tip = new_headers.last().unwrap().hash();
|
||||
assert_eq!(tip, *expected_tip);
|
||||
// Make sure first header connects correctly to existing chain
|
||||
let height = entry.height();
|
||||
let expected_prev_blockhash = if height > 0 {
|
||||
*self.headers[height - 1].hash()
|
||||
|
@ -135,9 +144,17 @@ impl HeaderList {
|
|||
Sha256dHash::default()
|
||||
};
|
||||
assert_eq!(entry.header().prev_blockhash, expected_prev_blockhash);
|
||||
// First new header's height (may override existing headers)
|
||||
height
|
||||
}
|
||||
None => return,
|
||||
// No new headers - chain's "tail" may be removed
|
||||
None => {
|
||||
let tip_height = *self
|
||||
.heights
|
||||
.get(&tip)
|
||||
.unwrap_or_else(|| panic!("missing tip: {}", tip));
|
||||
tip_height + 1 // keep the tip, drop the rest
|
||||
}
|
||||
};
|
||||
debug!(
|
||||
"applying {} new headers from height {}",
|
||||
|
@ -145,13 +162,15 @@ impl HeaderList {
|
|||
new_height
|
||||
);
|
||||
self.headers.split_off(new_height); // keep [0..new_height) entries
|
||||
assert_eq!(new_height, self.headers.len());
|
||||
for new_header in new_headers {
|
||||
let height = new_header.height();
|
||||
assert_eq!(height, self.headers.len());
|
||||
self.tip = *new_header.hash();
|
||||
assert_eq!(new_header.height(), self.headers.len());
|
||||
assert_eq!(new_header.header().prev_blockhash, self.tip());
|
||||
self.heights.insert(*new_header.hash(), new_header.height());
|
||||
self.headers.push(new_header);
|
||||
self.heights.insert(self.tip, height);
|
||||
}
|
||||
assert_eq!(tip, self.tip());
|
||||
assert!(self.heights.contains_key(&tip));
|
||||
}
|
||||
|
||||
pub fn header_by_blockhash(&self, blockhash: &Sha256dHash) -> Option<&HeaderEntry> {
|
||||
|
@ -175,12 +194,8 @@ impl HeaderList {
|
|||
self.headers.last() == other.headers.last()
|
||||
}
|
||||
|
||||
pub fn tip(&self) -> &Sha256dHash {
|
||||
assert_eq!(
|
||||
self.tip,
|
||||
self.headers.last().map(|h| *h.hash()).unwrap_or_default()
|
||||
);
|
||||
&self.tip
|
||||
pub fn tip(&self) -> Sha256dHash {
|
||||
self.headers.last().map(|h| *h.hash()).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
|
Loading…
Add table
Reference in a new issue