bitcoin: make sure we are *completely* synced before starting up

When not in IBD but catching up to the latest tip, our rounding up of
verificationprogress makes us start while bitcoind is still kind of far
from being caught up. Make sure it doesn't happen by not returning until
bitcoind validated all the blocks we've fetched the headers for.
This commit is contained in:
Antoine Poinsot 2023-10-18 19:04:23 +02:00
parent 03fe06fd1f
commit f687145c1e
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
6 changed files with 52 additions and 17 deletions

View File

@ -748,14 +748,28 @@ impl BitcoinD {
self.make_node_request("getblockchaininfo", &[])
}
pub fn sync_progress(&self) -> f64 {
pub fn sync_progress(&self) -> SyncProgress {
// TODO: don't harass lianad, be smarter like in revaultd.
roundup_progress(
self.block_chain_info()
let chain_info = self.block_chain_info();
let percentage = roundup_progress(
chain_info
.get("verificationprogress")
.and_then(Json::as_f64)
.expect("No valid 'verificationprogress' in getblockchaininfo response?"),
)
);
let headers = chain_info
.get("headers")
.and_then(Json::as_u64)
.expect("No valid 'verificationprogress' in getblockchaininfo response?");
let blocks = chain_info
.get("blocks")
.and_then(Json::as_u64)
.expect("No valid 'blocks' in getblockchaininfo response?");
SyncProgress {
percentage,
headers,
blocks,
}
}
pub fn chain_tip(&self) -> BlockChainTip {
@ -1122,6 +1136,17 @@ impl BitcoinD {
}
}
/// Information about the block chain verification progress.
#[derive(Debug, Clone, Copy)]
pub struct SyncProgress {
/// Chain verification progress as a percentage between 0 and 1.
pub percentage: f64,
/// Headers count for the best known tip.
pub headers: u64,
/// Number of blocks validated toward the best known tip.
pub blocks: u64,
}
/// An entry in the 'listdescriptors' result.
#[derive(Debug, Clone)]
pub struct ListDescEntry {

View File

@ -9,6 +9,7 @@ use crate::{
bitcoin::d::{BitcoindError, CachedTxGetter, LSBlockEntry},
descriptors,
};
pub use d::SyncProgress;
use std::{fmt, sync};
@ -42,8 +43,9 @@ pub trait BitcoinInterface: Send {
fn genesis_block(&self) -> BlockChainTip;
/// Get the progress of the block chain synchronization.
/// Returns a percentage between 0 and 1.
fn sync_progress(&self) -> f64;
/// Returns a rounded up percentage between 0 and 1. Use the `is_synced` method to be sure the
/// backend is completely synced to the best known tip.
fn sync_progress(&self) -> SyncProgress;
/// Get the best block info.
fn chain_tip(&self) -> BlockChainTip;
@ -117,7 +119,7 @@ impl BitcoinInterface for d::BitcoinD {
BlockChainTip { hash, height }
}
fn sync_progress(&self) -> f64 {
fn sync_progress(&self) -> SyncProgress {
self.sync_progress()
}
@ -333,7 +335,7 @@ impl BitcoinInterface for sync::Arc<sync::Mutex<dyn BitcoinInterface + 'static>>
self.lock().unwrap().genesis_block()
}
fn sync_progress(&self) -> f64 {
fn sync_progress(&self) -> SyncProgress {
self.lock().unwrap().sync_progress()
}

View File

@ -1,5 +1,5 @@
use crate::{
bitcoin::{BitcoinInterface, BlockChainTip, UTxO},
bitcoin::{BitcoinInterface, BlockChainTip, SyncProgress, UTxO},
database::{Coin, DatabaseConnection, DatabaseInterface},
descriptors,
};
@ -356,12 +356,16 @@ pub fn looper(
// Don't poll until the Bitcoin backend is fully synced.
if !synced {
let sync_progress = bit.sync_progress();
let SyncProgress {
percentage,
headers,
blocks,
} = bit.sync_progress();
log::info!(
"Block chain synchronization progress: {:.2}%",
sync_progress * 100.0
percentage * 100.0
);
synced = sync_progress == 1.0;
synced = headers == blocks;
if !synced {
continue;
}

View File

@ -263,7 +263,7 @@ impl DaemonControl {
version: VERSION.to_string(),
network: self.config.bitcoin_config.network,
block_height,
sync: self.bitcoin.sync_progress(),
sync: self.bitcoin.sync_progress().percentage,
descriptors: GetInfoDescriptors {
main: self.config.main_descriptor.clone(),
},

View File

@ -671,7 +671,7 @@ mod tests {
// Send them a response to 'getblockchaininfo' saying we are far from being synced
fn complete_sync_check(server: &net::TcpListener) {
let net_resp = [
"HTTP/1.1 200\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"verificationprogress\":0.1}}\n".as_bytes(),
"HTTP/1.1 200\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"verificationprogress\":0.1,\"headers\":1000,\"blocks\":100}}\n".as_bytes(),
]
.concat();
let (mut stream, _) = server.accept().unwrap();

View File

@ -1,5 +1,5 @@
use crate::{
bitcoin::{BitcoinInterface, Block, BlockChainTip, UTxO},
bitcoin::{BitcoinInterface, Block, BlockChainTip, SyncProgress, UTxO},
config::{BitcoinConfig, Config},
database::{BlockInfo, Coin, CoinStatus, DatabaseConnection, DatabaseInterface, LabelItem},
descriptors, DaemonHandle,
@ -42,8 +42,12 @@ impl BitcoinInterface for DummyBitcoind {
BlockChainTip { hash, height: 0 }
}
fn sync_progress(&self) -> f64 {
1.0
fn sync_progress(&self) -> SyncProgress {
SyncProgress {
percentage: 1.0,
headers: 1_000,
blocks: 1_000,
}
}
fn chain_tip(&self) -> BlockChainTip {