diff --git a/src/bitcoin/d/mod.rs b/src/bitcoin/d/mod.rs index 9704ff83..2bfe3a5e 100644 --- a/src/bitcoin/d/mod.rs +++ b/src/bitcoin/d/mod.rs @@ -651,6 +651,34 @@ impl BitcoinD { None } + + pub fn get_block_stats(&self, blockhash: bitcoin::BlockHash) -> BlockStats { + let res = self.make_node_request( + "getblockheader", + ¶ms!(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 for GetTxRes { } } } + +#[derive(Debug, Clone)] +pub struct BlockStats { + pub confirmations: i32, + pub previous_blockhash: bitcoin::BlockHash, + pub blockhash: bitcoin::BlockHash, + pub height: i32, +} diff --git a/src/bitcoin/mod.rs b/src/bitcoin/mod.rs index f4c2df81..e9c2e6ba 100644 --- a/src/bitcoin/mod.rs +++ b/src/bitcoin/mod.rs @@ -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> ) -> 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 diff --git a/src/testutils.rs b/src/testutils.rs index 54a9cbb1..bae0f84b 100644 --- a/src/testutils.rs +++ b/src/testutils.rs @@ -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 {