- 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]
}
}
+#[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>(
}
}
+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>(
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,
}
}
+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,
}
}
+#[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>(
}
}
+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,
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;
/// 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
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 {
}
}
+#[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>(
}
}
+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,
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());
+ }
}
};