[bdk_chain_redesign] Consistent ChainOracle
The problem with the previous `ChainOracle` interface is that it had no guarantee for consistency. For example, a block deemed to be part of the "best chain" can be reorged out. So when `ChainOracle` is called multiple times for an operation (such as getting the UTXO set), the returned result may be inconsistent. This PR changes `ChainOracle::is_block_in_chain` to take in another input `static_block`, ensuring `block` is an ancestor of `static_block`. Thus, if `static_block` is consistent across the operation, the result will be consistent also. `is_block_in_chain` now returns `Option<bool>`. The `None` case means that the oracle implementation cannot determine whether block is an ancestor of static block. `IndexedTxGraph::list_chain_txouts` handles this case by checking child spends that are in chain, and if so, the parent tx must be in chain too.
This commit is contained in:
@@ -22,16 +22,25 @@ pub struct LocalChain {
|
||||
impl ChainOracle for LocalChain {
|
||||
type Error = Infallible;
|
||||
|
||||
fn get_tip_in_best_chain(&self) -> Result<Option<BlockId>, Self::Error> {
|
||||
Ok(self
|
||||
.blocks
|
||||
.iter()
|
||||
.last()
|
||||
.map(|(&height, &hash)| BlockId { height, hash }))
|
||||
}
|
||||
|
||||
fn get_block_in_best_chain(&self, height: u32) -> Result<Option<BlockHash>, Self::Error> {
|
||||
Ok(self.blocks.get(&height).cloned())
|
||||
fn is_block_in_chain(
|
||||
&self,
|
||||
block: BlockId,
|
||||
static_block: BlockId,
|
||||
) -> Result<Option<bool>, Self::Error> {
|
||||
if block.height > static_block.height {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(
|
||||
match (
|
||||
self.blocks.get(&block.height),
|
||||
self.blocks.get(&static_block.height),
|
||||
) {
|
||||
(Some(&hash), Some(&static_hash)) => {
|
||||
Some(hash == block.hash && static_hash == static_block.hash)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user