bitcoin: interface to get the common block in our and the backend's chains

This will be used to get the common ancestor in the chain upon detecting
a block chain reorganization.
This commit is contained in:
Antoine Poinsot 2022-10-13 16:37:31 +02:00
parent efe2cf634d
commit cce227f80f
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
3 changed files with 62 additions and 0 deletions

View File

@ -651,6 +651,34 @@ impl BitcoinD {
None
}
pub fn get_block_stats(&self, blockhash: bitcoin::BlockHash) -> BlockStats {
let res = self.make_node_request(
"getblockheader",
&params!(Json::String(blockhash.to_string()),),
);
let confirmations = res
.get("confirmations")
.and_then(Json::as_i64)
.expect("Invalid confirmations in `getblockheader` response: not an i64")
as i32;
let previous_blockhash = res
.get("previousblockhash")
.and_then(Json::as_str)
.and_then(|s| bitcoin::BlockHash::from_str(s).ok())
.expect("Invalid previousblockhash in `getblockheader` response");
let height = res
.get("height")
.and_then(Json::as_i64)
.expect("Invalid height in `getblockheader` response: not an u32")
as i32;
BlockStats {
confirmations,
previous_blockhash,
height,
blockhash,
}
}
}
// Bitcoind uses a guess for the value of verificationprogress. It will eventually get to
// be 1, and we want to be less conservative.
@ -779,3 +807,11 @@ impl From<Json> for GetTxRes {
}
}
}
#[derive(Debug, Clone)]
pub struct BlockStats {
pub confirmations: i32,
pub previous_blockhash: bitcoin::BlockHash,
pub blockhash: bitcoin::BlockHash,
pub height: i32,
}

View File

@ -52,6 +52,9 @@ pub trait BitcoinInterface: Send {
&self,
outpoints: &[(bitcoin::OutPoint, bitcoin::Txid)],
) -> Vec<(bitcoin::OutPoint, bitcoin::Txid, u32)>;
/// Get the common ancestor between the Bitcoin backend's tip and the given tip.
fn common_ancestor(&self, tip: &BlockChainTip) -> BlockChainTip;
}
impl BitcoinInterface for d::BitcoinD {
@ -207,6 +210,21 @@ impl BitcoinInterface for d::BitcoinD {
spent
}
fn common_ancestor(&self, tip: &BlockChainTip) -> BlockChainTip {
let mut stats = self.get_block_stats(tip.hash);
let mut ancestor = *tip;
while stats.confirmations == -1 {
stats = self.get_block_stats(stats.previous_blockhash);
ancestor = BlockChainTip {
hash: stats.blockhash,
height: stats.height,
};
}
ancestor
}
}
// FIXME: do we need to repeat the entire trait implemenation? Isn't there a nicer way?
@ -251,6 +269,10 @@ impl BitcoinInterface for sync::Arc<sync::Mutex<dyn BitcoinInterface + 'static>>
) -> Vec<(bitcoin::OutPoint, bitcoin::Txid, u32)> {
self.lock().unwrap().spent_coins(outpoints)
}
fn common_ancestor(&self, tip: &BlockChainTip) -> BlockChainTip {
self.lock().unwrap().common_ancestor(tip)
}
}
// FIXME: We could avoid this type (and all the conversions entailing allocations) if bitcoind

View File

@ -62,6 +62,10 @@ impl BitcoinInterface for DummyBitcoind {
) -> Vec<(bitcoin::OutPoint, bitcoin::Txid, u32)> {
Vec::new()
}
fn common_ancestor(&self, _: &BlockChainTip) -> BlockChainTip {
todo!()
}
}
pub struct DummyDb {