Expand description
CDK onchain backend using BDK
§CDK BDK
CDK onchain payment backend using BDK (Bitcoin Development Kit), 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
| Source | Status |
|---|---|
| Bitcoin Core RPC | Supported |
| Electrum | Supported |
| Esplora | Supported |
Electrum sources accept tcp:// and ssl:// server URLs:
let chain_source = ChainSource::Electrum(ElectrumConfig {
url: "ssl://electrum.example.com:50002".to_string(),
batch_size: 5,
});§Usage
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:
| Tier | Target blocks |
|---|---|
| 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::StandardandPaymentTier::Economyare accepted bymake_paymentand drive the internal batch processor, but the cdk melt pipeline currently passestier: Noneat execute time. All melts are effectively treated asImmediateuntil the upstream plumbing lands in cdk-common and cdk. SeeTODO(#TBD)incrates/cdk-common/src/payment.rs(from_melt_quote_with_fee) andcrates/cdk/src/mint/melt/mod.rs(get_melt_onchain_quote_impl).
Re-exports§
pub use crate::chain::BitcoinRpcConfig;pub use crate::chain::ChainSource;pub use crate::chain::ElectrumConfig;pub use crate::chain::EsploraConfig;pub use crate::error::Error;pub use crate::storage::BdkStorage;pub use crate::storage::FinalizedReceiveIntentRecord;pub use crate::storage::FinalizedSendIntentRecord;pub use crate::types::BatchConfig;pub use crate::types::FeeEstimationConfig;pub use crate::types::PaymentMetadata;pub use crate::types::PaymentTier;pub use crate::types::SyncConfig;pub use crate::types::DEFAULT_TARGET_BLOCK_TIME_SECS;pub use crate::wallet_info::WalletAddress;pub use crate::wallet_info::WalletBalance;pub use crate::wallet_info::WalletKeychain;pub use crate::wallet_info::WalletPage;pub use crate::wallet_info::WalletTransaction;pub use crate::wallet_info::WalletTransactionInput;pub use crate::wallet_info::WalletTransactionOutput;
Modules§
- chain
- error
- CDK BDK onchain backend errors
- receive
- Receive saga types and modules
- send
- On-chain send saga for batched Bitcoin transactions
- storage
- BDK storage operations using KV store
- types
- wallet_
info - Read-only wallet information for operator interfaces.
Structs§
- CdkBdk
- CDK onchain payment backend using BDK (Bitcoin Development Kit)
Functions§
- validate_
existing_ wallet - Verify that an existing BDK wallet is present and belongs to the configured mnemonic and network without modifying its database.