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.reserve_exact(1);
|
||||||
qblocks.push(new_qblock);
|
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
|
rx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,12 +431,16 @@ impl CheckpointVerifier {
|
||||||
// The first valid block at the current height
|
// The first valid block at the current height
|
||||||
valid_qblock = Some(qblock);
|
valid_qblock = Some(qblock);
|
||||||
} else {
|
} else {
|
||||||
|
tracing::info!(?height, ?qblock.hash, ?expected_hash,
|
||||||
|
"Duplicate block at height in CheckpointVerifier");
|
||||||
// Reject duplicate blocks at the same height
|
// Reject duplicate blocks at the same height
|
||||||
let _ = qblock.tx.send(Err(
|
let _ = qblock.tx.send(Err(
|
||||||
"duplicate valid blocks at this height, only one was chosen".into(),
|
"duplicate valid blocks at this height, only one was chosen".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
// A bad block, that isn't part of the chain.
|
||||||
let _ = qblock.tx.send(Err(
|
let _ = qblock.tx.send(Err(
|
||||||
"the block hash does not match the chained checkpoint hash".into(),
|
"the block hash does not match the chained checkpoint hash".into(),
|
||||||
|
@ -509,8 +523,11 @@ impl CheckpointVerifier {
|
||||||
} else {
|
} else {
|
||||||
// The last block height we processed did not have any blocks
|
// The last block height we processed did not have any blocks
|
||||||
// with a matching hash, so chain verification has failed.
|
// with a matching hash, so chain verification has failed.
|
||||||
//
|
tracing::warn!(
|
||||||
// TODO(teor||jlusby): log an error here?
|
?current_height,
|
||||||
|
?current_range,
|
||||||
|
"No valid blocks at height in CheckpointVerifier"
|
||||||
|
);
|
||||||
|
|
||||||
// We kept all the matching blocks down to this height, in
|
// We kept all the matching blocks down to this height, in
|
||||||
// anticipation of the chain verifying. But the chain is
|
// 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"
|
"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
|
// All the blocks we've kept are valid, so let's verify them
|
||||||
// in height order.
|
// in height order.
|
||||||
for qblock in rev_valid_blocks.drain(..).rev() {
|
for qblock in rev_valid_blocks.drain(..).rev() {
|
||||||
|
|
|
@ -73,6 +73,10 @@ where
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn sync(&mut self) -> Result<(), Report> {
|
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 {
|
loop {
|
||||||
self.obtain_tips().await?;
|
self.obtain_tips().await?;
|
||||||
metrics::gauge!(
|
metrics::gauge!(
|
||||||
|
@ -215,8 +219,7 @@ where
|
||||||
"found index of first unknown hash in response"
|
"found index of first unknown hash in response"
|
||||||
);
|
);
|
||||||
if first_unknown == hashes.len() {
|
if first_unknown == hashes.len() {
|
||||||
// XXX until we fix the TODO above to construct the locator correctly,
|
// We should only stop getting hashes once we've finished the initial sync
|
||||||
// we might hit this case, but it will be unexpected afterwards.
|
|
||||||
tracing::debug!("no new hashes, even though we gave our tip?");
|
tracing::debug!("no new hashes, even though we gave our tip?");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +301,6 @@ where
|
||||||
// response is the genesis block; if so, discard the response.
|
// response is the genesis block; if so, discard the response.
|
||||||
// It indicates that the remote peer does not have any blocks
|
// It indicates that the remote peer does not have any blocks
|
||||||
// following the prospective tip.
|
// following the prospective tip.
|
||||||
// TODO(jlusby): reject both main and test net genesis blocks
|
|
||||||
match (hashes.first(), hashes.len()) {
|
match (hashes.first(), hashes.len()) {
|
||||||
(_, 0) => {
|
(_, 0) => {
|
||||||
tracing::debug!("skipping empty response");
|
tracing::debug!("skipping empty response");
|
||||||
|
@ -355,6 +357,35 @@ where
|
||||||
Ok(())
|
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
|
/// Queue downloads for each block that isn't currently known to our node
|
||||||
async fn request_blocks(&mut self, hashes: Vec<BlockHeaderHash>) -> Result<(), Report> {
|
async fn request_blocks(&mut self, hashes: Vec<BlockHeaderHash>) -> Result<(), Report> {
|
||||||
tracing::debug!(hashes.len = hashes.len(), "requesting blocks");
|
tracing::debug!(hashes.len = hashes.len(), "requesting blocks");
|
||||||
|
|
Loading…
Reference in New Issue