Download genesis (#731)
* feature: Add more CheckpointVerifier tracing * fix: Download the genesis block
This commit is contained in:
parent
e99e9653eb
commit
77a1fefa1e
|
@ -376,7 +376,17 @@ impl CheckpointVerifier {
|
|||
qblocks.reserve_exact(1);
|
||||
qblocks.push(new_qblock);
|
||||
|
||||
tracing::debug!("queued block");
|
||||
let is_checkpoint = self.checkpoint_list.contains(height);
|
||||
tracing::debug!(?height, ?hash, ?is_checkpoint, "Queued block");
|
||||
|
||||
// TODO(teor):
|
||||
// - Remove this log once the CheckpointVerifier is working?
|
||||
// - Modify the default filter or add another log, so users see
|
||||
// regular download progress info (vs verification info)
|
||||
if is_checkpoint {
|
||||
tracing::info!(?height, ?hash, ?is_checkpoint, "Queued checkpoint block");
|
||||
}
|
||||
|
||||
rx
|
||||
}
|
||||
|
||||
|
@ -421,12 +431,16 @@ impl CheckpointVerifier {
|
|||
// The first valid block at the current height
|
||||
valid_qblock = Some(qblock);
|
||||
} else {
|
||||
tracing::info!(?height, ?qblock.hash, ?expected_hash,
|
||||
"Duplicate block at height in CheckpointVerifier");
|
||||
// Reject duplicate blocks at the same height
|
||||
let _ = qblock.tx.send(Err(
|
||||
"duplicate valid blocks at this height, only one was chosen".into(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
tracing::info!(?height, ?qblock.hash, ?expected_hash,
|
||||
"Bad block hash at height in CheckpointVerifier");
|
||||
// A bad block, that isn't part of the chain.
|
||||
let _ = qblock.tx.send(Err(
|
||||
"the block hash does not match the chained checkpoint hash".into(),
|
||||
|
@ -509,8 +523,11 @@ impl CheckpointVerifier {
|
|||
} else {
|
||||
// The last block height we processed did not have any blocks
|
||||
// with a matching hash, so chain verification has failed.
|
||||
//
|
||||
// TODO(teor||jlusby): log an error here?
|
||||
tracing::warn!(
|
||||
?current_height,
|
||||
?current_range,
|
||||
"No valid blocks at height in CheckpointVerifier"
|
||||
);
|
||||
|
||||
// We kept all the matching blocks down to this height, in
|
||||
// anticipation of the chain verifying. But the chain is
|
||||
|
@ -554,6 +571,8 @@ impl CheckpointVerifier {
|
|||
"the previous checkpoint should match: bad checkpoint list, zebra bug, or bad chain"
|
||||
);
|
||||
|
||||
tracing::info!(?current_range, "Verified checkpoint range");
|
||||
|
||||
// All the blocks we've kept are valid, so let's verify them
|
||||
// in height order.
|
||||
for qblock in rev_valid_blocks.drain(..).rev() {
|
||||
|
|
|
@ -73,6 +73,10 @@ where
|
|||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn sync(&mut self) -> Result<(), Report> {
|
||||
// We can't download the genesis block using our normal algorithm,
|
||||
// due to protocol limitations
|
||||
self.request_genesis().await?;
|
||||
|
||||
loop {
|
||||
self.obtain_tips().await?;
|
||||
metrics::gauge!(
|
||||
|
@ -215,8 +219,7 @@ where
|
|||
"found index of first unknown hash in response"
|
||||
);
|
||||
if first_unknown == hashes.len() {
|
||||
// XXX until we fix the TODO above to construct the locator correctly,
|
||||
// we might hit this case, but it will be unexpected afterwards.
|
||||
// We should only stop getting hashes once we've finished the initial sync
|
||||
tracing::debug!("no new hashes, even though we gave our tip?");
|
||||
continue;
|
||||
}
|
||||
|
@ -298,7 +301,6 @@ where
|
|||
// response is the genesis block; if so, discard the response.
|
||||
// It indicates that the remote peer does not have any blocks
|
||||
// following the prospective tip.
|
||||
// TODO(jlusby): reject both main and test net genesis blocks
|
||||
match (hashes.first(), hashes.len()) {
|
||||
(_, 0) => {
|
||||
tracing::debug!("skipping empty response");
|
||||
|
@ -355,6 +357,35 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Queue a download for the genesis block, if it isn't currently known to
|
||||
/// our node.
|
||||
async fn request_genesis(&mut self) -> Result<(), Report> {
|
||||
// Due to Bitcoin protocol limitations, we can't request the genesis
|
||||
// block using our standard tip-following algorithm:
|
||||
// - getblocks requires at least one hash
|
||||
// - responses start with the block *after* the requested block, and
|
||||
// - the genesis hash is used as a placeholder for "no matches".
|
||||
//
|
||||
// So we just queue the genesis block here.
|
||||
|
||||
let state_has_genesis = self
|
||||
.state
|
||||
.ready_and()
|
||||
.await
|
||||
.map_err(|e| eyre!(e))?
|
||||
.call(zebra_state::Request::GetBlock {
|
||||
hash: self.genesis_hash,
|
||||
})
|
||||
.await
|
||||
.is_ok();
|
||||
|
||||
if !state_has_genesis {
|
||||
self.request_blocks(vec![self.genesis_hash]).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Queue downloads for each block that isn't currently known to our node
|
||||
async fn request_blocks(&mut self, hashes: Vec<BlockHeaderHash>) -> Result<(), Report> {
|
||||
tracing::debug!(hashes.len = hashes.len(), "requesting blocks");
|
||||
|
|
Loading…
Reference in New Issue