diff --git a/zebra-consensus/src/checkpoint.rs b/zebra-consensus/src/checkpoint.rs index 5ed62fa4..43ea141d 100644 --- a/zebra-consensus/src/checkpoint.rs +++ b/zebra-consensus/src/checkpoint.rs @@ -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() { diff --git a/zebrad/src/commands/start/sync.rs b/zebrad/src/commands/start/sync.rs index eed6f8fe..96a541d6 100644 --- a/zebrad/src/commands/start/sync.rs +++ b/zebrad/src/commands/start/sync.rs @@ -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) -> Result<(), Report> { tracing::debug!(hashes.len = hashes.len(), "requesting blocks");