Skip to main content

cdk_bdk/chain/
mod.rs

1use bdk_wallet::bitcoin::Transaction;
2use tokio_util::sync::CancellationToken;
3
4use crate::error::Error;
5
6#[cfg(feature = "bitcoin-rpc")]
7pub mod bitcoin_rpc;
8#[cfg(feature = "esplora")]
9pub mod esplora;
10
11/// Configuration for connecting to Bitcoin RPC
12#[derive(Debug, Clone)]
13pub struct BitcoinRpcConfig {
14    /// Bitcoin RPC server hostname or IP address
15    pub host: String,
16    /// Bitcoin RPC server port number
17    pub port: u16,
18    /// Username for Bitcoin RPC authentication
19    pub user: String,
20    /// Password for Bitcoin RPC authentication
21    pub password: String,
22}
23
24/// Configuration for connecting to Esplora
25#[derive(Debug, Clone)]
26pub struct EsploraConfig {
27    /// URL of the Esplora server endpoint
28    pub url: String,
29    /// Number of parallel requests to use during sync
30    pub parallel_requests: usize,
31}
32
33/// Source of blockchain data for the BDK wallet
34#[derive(Debug, Clone)]
35pub enum ChainSource {
36    /// Use an Esplora server for blockchain data
37    #[cfg(feature = "esplora")]
38    Esplora(EsploraConfig),
39    /// Use Bitcoin Core RPC for blockchain data
40    #[cfg(feature = "bitcoin-rpc")]
41    BitcoinRpc(BitcoinRpcConfig),
42}
43
44/// Classified result of submitting a transaction to a chain backend.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub(crate) enum BroadcastOutcome {
47    /// Backend accepted the transaction.
48    Accepted,
49    /// Backend already knows the transaction; this is success-equivalent.
50    AlreadyKnown,
51}
52
53/// Classification for broadcast errors.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub(crate) enum BroadcastErrorKind {
56    /// Deterministic backend rejection.
57    Rejected,
58    /// Network or upstream failure expected to resolve on retry.
59    Transient,
60    /// Ambiguous or unrecognized error; retry conservatively.
61    Unknown,
62}
63
64/// A classified broadcast failure.
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub(crate) struct BroadcastFailure {
67    /// Failure class.
68    pub kind: BroadcastErrorKind,
69    /// Human-readable backend error.
70    pub message: String,
71}
72
73impl BroadcastFailure {
74    pub(crate) fn new(kind: BroadcastErrorKind, message: String) -> Self {
75        Self { kind, message }
76    }
77}
78
79impl ChainSource {
80    pub async fn sync_wallet(
81        &self,
82        cdk_bdk: &crate::CdkBdk,
83        cancel_token: CancellationToken,
84    ) -> Result<(), Error> {
85        match self {
86            #[cfg(feature = "esplora")]
87            ChainSource::Esplora(config) => {
88                esplora::sync_esplora(cdk_bdk, config, cancel_token).await
89            }
90            #[cfg(feature = "bitcoin-rpc")]
91            ChainSource::BitcoinRpc(config) => {
92                bitcoin_rpc::sync_bitcoin_rpc(cdk_bdk, config, cancel_token).await
93            }
94            #[allow(unreachable_patterns)]
95            _ => unreachable!("ChainSource must have at least one feature enabled"),
96        }
97    }
98
99    pub(crate) async fn broadcast(
100        &self,
101        tx: Transaction,
102    ) -> Result<BroadcastOutcome, BroadcastFailure> {
103        match self {
104            #[cfg(feature = "esplora")]
105            ChainSource::Esplora(config) => esplora::broadcast_esplora(config, tx).await,
106            #[cfg(feature = "bitcoin-rpc")]
107            ChainSource::BitcoinRpc(config) => bitcoin_rpc::broadcast_bitcoin_rpc(config, tx).await,
108            #[allow(unreachable_patterns)]
109            _ => unreachable!("ChainSource must have at least one feature enabled"),
110        }
111    }
112
113    pub async fn fetch_fee_rate(&self, target_blocks: u16) -> Result<f64, Error> {
114        match self {
115            #[cfg(feature = "esplora")]
116            ChainSource::Esplora(config) => {
117                esplora::fetch_fee_rate_esplora(config, target_blocks).await
118            }
119            #[cfg(feature = "bitcoin-rpc")]
120            ChainSource::BitcoinRpc(config) => {
121                bitcoin_rpc::fetch_fee_rate_bitcoin_rpc(config, target_blocks).await
122            }
123            #[allow(unreachable_patterns)]
124            _ => unreachable!("ChainSource must have at least one feature enabled"),
125        }
126    }
127}