# CDK BDK
CDK onchain payment backend using [BDK (Bitcoin Development Kit)](https://bitcoindevkit.org/), providing on-chain Bitcoin payment functionality for CDK mint operations.
## Features
- On-chain Bitcoin payments (receive and send)
- BIP84 (Native SegWit) key derivation from a BIP39 mnemonic
- SQLite-backed wallet persistence via BDK
- Blockchain sync via Bitcoin Core RPC, Electrum, or Esplora
- Confirmation tracking for incoming and outgoing transactions
- Pending transaction persistence via KV store
## Chain Sources
| Bitcoin Core RPC | Supported |
| Electrum | Supported |
| Esplora | Supported |
Electrum sources accept `tcp://` and `ssl://` server URLs:
```rust,ignore
let chain_source = ChainSource::Electrum(ElectrumConfig {
url: "ssl://electrum.example.com:50002".to_string(),
batch_size: 5,
});
```
## Usage
```rust,ignore
use cdk_bdk::{BatchConfig, BitcoinRpcConfig, CdkBdk, ChainSource, SyncConfig};
let chain_source = ChainSource::BitcoinRpc(BitcoinRpcConfig {
host: "127.0.0.1".to_string(),
port: 18443,
user: "user".to_string(),
password: "password".to_string(),
wallet_rescan_from_height: None,
});
let backend = CdkBdk::new(
mnemonic,
Network::Regtest,
chain_source,
"/path/to/storage".to_string(),
fee_reserve,
kv_store,
Some(BatchConfig::default()),
num_confs,
min_receive_amount_sat,
min_send_amount_sat,
sync_interval_secs,
Some(30), // shutdown_timeout_secs
Some(SyncConfig::default()),
)?;
```
## Shutdown
`stop()` cancels background sync and batch tasks, then awaits their exit up
to a bounded timeout (default 30 seconds; configurable via
`shutdown_timeout_secs`). If the timeout is exceeded the tasks are aborted.
`start()` returns `Error::AlreadyStarted` if called while tasks are already
running.
## Fee Estimation
Fee rates are estimated per-tier from the configured chain source:
| Immediate | 1 |
| Standard | 6 |
| Economy | 144 |
Rates are cached with a configurable TTL (default 60 seconds). On
estimation failure, the configured fallback sat/vB value is used and a
warning is logged -- `get_payment_quote` does not fail due to transient
estimation outages.
## Sync
The blockchain sync loop applies blocks in configurable chunks (default 16
blocks per wallet-lock acquisition) so user-facing operations like address
reveal and batch construction are not blocked during long chain catch-ups.
Chain-source clients are reused across sync iterations and rebuilt only on
error.
Esplora sync, broadcast, and fee-estimation requests have a 10-second timeout.
Esplora's `parallel_requests` must be greater than zero. Missed polling ticks
are skipped so a slow sync does not trigger a burst of catch-up polls.
Fresh Bitcoin Core wallets checkpoint at the current chain tip instead of
scanning from genesis. When restoring from a mnemonic, set
`BitcoinRpcConfig::wallet_rescan_from_height` to the wallet's known birthday
height. This setting only affects wallet creation and never rewinds persisted
wallet state.
On reorgs, Bitcoin Core sync follows the orphaned branch's block headers to
find the actual common ancestor, including ancestors absent from the wallet's
stored checkpoints. It then replays all replacement blocks after that ancestor,
even below the original birthday. The birthday controls wallet creation, not
reorg recovery. Normal catch-up still resumes from the latest checkpoint.
Missing headers or required block data fail the sync tick and are retried; sync
does not skip affected blocks or substitute a genesis rescan for missing history.
A rollback with no replacement block is retried rather than reported as completed
recovery.
## Finality and Confirmation Policy
- Finalization is policy-based: once a send or receive reaches `num_confs`, it is
treated as final and is not reopened after deep reorgs
- Incoming payment tracking is confirmed-only; unconfirmed tracked outputs are
intentionally ignored until they satisfy the configured confirmation threshold
## Known Limitations
- **Tiered batching is scaffolded but not reachable via the standard melt
flow.** `PaymentTier::Standard` and `PaymentTier::Economy` are accepted by
`make_payment` and drive the internal batch processor, but the cdk melt
pipeline currently passes `tier: None` at execute time. All melts are
effectively treated as `Immediate` until the upstream plumbing lands in
cdk-common and cdk. See `TODO(#TBD)` in
`crates/cdk-common/src/payment.rs` (`from_melt_quote_with_fee`) and
`crates/cdk/src/mint/melt/mod.rs` (`get_melt_onchain_quote_impl`).