[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:
志宇
2023-04-10 13:03:51 +08:00
parent bff80ec378
commit 611d2e3ea2
6 changed files with 191 additions and 210 deletions

View File

@@ -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,
},
)
}
}