Download genesis (#731)

* feature: Add more CheckpointVerifier tracing

* fix: Download the genesis block
This commit is contained in:
teor 2020-07-24 03:56:52 +10:00 committed by GitHub
parent e99e9653eb
commit 77a1fefa1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 6 deletions

View File

@ -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() {

View File

@ -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");