Merge bitcoindevkit/bdk#634: Get block hash by its height
2af678aa846a82657d13bcda2ac0b4a9db4e2b80 Get block hash by its height (Vladimir Fomene) Pull request description: ### Description This PR create a new trait `blockchain::GetBlockHash` with a `get_block_hash` method which returns a block hash given the block height. This has been implemented for all blockchain backends. Fixes #603 ### Notes to the reviewers I haven't updated the `CHANGELOG.md` and docs. Am I suppose to update it for this change? ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added tests for the new feature * [ ] I've added docs for the new feature * [ ] I've updated `CHANGELOG.md` ACKs for top commit: notmandatory: ACK 2af678aa846a82657d13bcda2ac0b4a9db4e2b80 Tree-SHA512: 9c084a6665ecbf27ee8170fdb06e0dc8373d6a901ce29e5f5a1bec111d1507cb3bee6b03a653a55fd20e0fabe7a5eada3353e24a1e21f3a11f01bb9881ae99e5
This commit is contained in:
commit
73d4f6d3b1
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fee sniping discouraging through nLockTime - if the user specifies a `current_height`, we use that as a nlocktime, otherwise we use the last sync height (or 0 if we never synced)
|
||||
- Fix hang when `ElectrumBlockchainConfig::stop_gap` is zero.
|
||||
- Set coin type in BIP44, BIP49, and BIP84 templates
|
||||
- Get block hash given a block height - A `get_block_hash` method is now defined on the `GetBlockHash` trait and implemented on every blockchain backend. This method expects a block height and returns the corresponding block hash.
|
||||
|
||||
## [v0.19.0] - [v0.18.0]
|
||||
|
||||
|
@ -120,6 +120,13 @@ impl GetTx for AnyBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl GetBlockHash for AnyBlockchain {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
maybe_await!(impl_inner_method!(self, get_block_hash, height))
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl WalletSync for AnyBlockchain {
|
||||
fn wallet_sync<D: BatchDatabase>(
|
||||
|
@ -260,6 +260,16 @@ impl GetTx for CompactFiltersBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetBlockHash for CompactFiltersBlockchain {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
self.headers
|
||||
.get_block_hash(height as usize)?
|
||||
.ok_or(Error::CompactFilters(
|
||||
CompactFiltersError::BlockHashNotFound,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletSync for CompactFiltersBlockchain {
|
||||
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
@ -536,6 +546,8 @@ pub enum CompactFiltersError {
|
||||
InvalidFilter,
|
||||
/// The peer is missing a block in the valid chain
|
||||
MissingBlock,
|
||||
/// Block hash at specified height not found
|
||||
BlockHashNotFound,
|
||||
/// The data stored in the block filters storage are corrupted
|
||||
DataCorruption,
|
||||
|
||||
|
@ -98,6 +98,13 @@ impl GetTx for ElectrumBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetBlockHash for ElectrumBlockchain {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
let block_header = self.client.block_header(height as usize)?;
|
||||
Ok(block_header.block_hash())
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletSync for ElectrumBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
|
@ -117,6 +117,14 @@ impl GetTx for EsploraBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl GetBlockHash for EsploraBlockchain {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
let block_header = await_or_block!(self.url_client._get_header(height as u32))?;
|
||||
Ok(block_header.block_hash())
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl WalletSync for EsploraBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
|
@ -112,6 +112,13 @@ impl GetTx for EsploraBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetBlockHash for EsploraBlockchain {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
let block_header = self.url_client._get_header(height as u32)?;
|
||||
Ok(block_header.block_hash())
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletSync for EsploraBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
|
@ -21,7 +21,7 @@ use std::ops::Deref;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::sync::Arc;
|
||||
|
||||
use bitcoin::{Transaction, Txid};
|
||||
use bitcoin::{BlockHash, Transaction, Txid};
|
||||
|
||||
use crate::database::BatchDatabase;
|
||||
use crate::error::Error;
|
||||
@ -87,7 +87,7 @@ pub enum Capability {
|
||||
|
||||
/// Trait that defines the actions that must be supported by a blockchain backend
|
||||
#[maybe_async]
|
||||
pub trait Blockchain: WalletSync + GetHeight + GetTx {
|
||||
pub trait Blockchain: WalletSync + GetHeight + GetTx + GetBlockHash {
|
||||
/// Return the set of [`Capability`] supported by this backend
|
||||
fn get_capabilities(&self) -> HashSet<Capability>;
|
||||
/// Broadcast a transaction
|
||||
@ -110,6 +110,13 @@ pub trait GetTx {
|
||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
/// Trait for getting block hash by block height
|
||||
pub trait GetBlockHash {
|
||||
/// fetch block hash given its height
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error>;
|
||||
}
|
||||
|
||||
/// Trait for blockchains that can sync by updating the database directly.
|
||||
#[maybe_async]
|
||||
pub trait WalletSync {
|
||||
@ -359,6 +366,13 @@ impl<T: GetHeight> GetHeight for Arc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl<T: GetBlockHash> GetBlockHash for Arc<T> {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
maybe_await!(self.deref().get_block_hash(height))
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl<T: WalletSync> WalletSync for Arc<T> {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
|
@ -169,6 +169,12 @@ impl GetHeight for RpcBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
impl GetBlockHash for RpcBlockchain {
|
||||
fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
|
||||
Ok(self.client.get_block_hash(height)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl WalletSync for RpcBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
|
@ -1361,6 +1361,35 @@ macro_rules! bdk_blockchain_tests {
|
||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||
assert_eq!(finalized, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_block_hash() {
|
||||
use bitcoincore_rpc::{ RpcApi };
|
||||
use crate::blockchain::GetBlockHash;
|
||||
|
||||
// create wallet with init_wallet
|
||||
let (_, blockchain, _descriptors, mut test_client) = init_single_sig();
|
||||
|
||||
let height = test_client.bitcoind.client.get_blockchain_info().unwrap().blocks as u64;
|
||||
let best_hash = test_client.bitcoind.client.get_best_block_hash().unwrap();
|
||||
|
||||
// use get_block_hash to get best block hash and compare with best_hash above
|
||||
let block_hash = blockchain.get_block_hash(height).unwrap();
|
||||
assert_eq!(best_hash, block_hash);
|
||||
|
||||
// generate blocks to address
|
||||
let node_addr = test_client.get_node_address(None);
|
||||
test_client.generate(10, Some(node_addr));
|
||||
|
||||
let height = test_client.bitcoind.client.get_blockchain_info().unwrap().blocks as u64;
|
||||
let best_hash = test_client.bitcoind.client.get_best_block_hash().unwrap();
|
||||
|
||||
let block_hash = blockchain.get_block_hash(height).unwrap();
|
||||
assert_eq!(best_hash, block_hash);
|
||||
|
||||
// try to get hash for block that has not yet been created.
|
||||
assert!(blockchain.get_block_hash(height + 1).is_err());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user