Bitcoin Configuration
appsettings.json, coin-selection policy, UTXO reservation, and how the node registers itself
appsettings.json
The node reads its Esplora endpoints and policy defaults from a Bitcoin section in
application settings, bound to IOptions<BitcoinNetworkOptions> and validated at startup:
"Bitcoin": {
"DefaultNetwork": "mainnet",
"Networks": {
"mainnet": { "EsploraApiUrl": "https://blockstream.info/api", "BroadcastEndpoint": "https://blockstream.info/api/tx" },
"testnet": { "EsploraApiUrl": "https://blockstream.info/testnet/api", "BroadcastEndpoint": "https://blockstream.info/testnet/api/tx" }
},
"RequestTimeout": 30000,
"CoinSelection": { "DustThresholdSats": 546, "MinUtxoValueSats": 546 },
"UtxoReservation": { "LockTimeoutSeconds": 120 }
}
| Key | Type | Required | Description |
|---|---|---|---|
DefaultNetwork | text | ✓ Yes | Network used when a read operation doesn't set its own network field. One of mainnet, testnet, signet, regtest. |
Networks | object | ✓ Yes | Map of network name → EsploraApiUrl + BroadcastEndpoint. Both must be http/https — validated at startup. See Networks. |
RequestTimeout | number (ms) | No | HTTP timeout for Esplora calls. Default 30000. |
CoinSelection.DustThresholdSats | number | No | Below this, a computed change output is folded into the fee instead of created. Default 546. |
CoinSelection.MinUtxoValueSats | number | No | UTXOs below this value are excluded from candidate selection entirely — hardens against dust/UTXO-pollution attacks. Default 546. |
UtxoReservation.LockTimeoutSeconds | number | No | Default hold window for a UTXO reserved by psbt/build. Default 120. Override per-call with reservationTimeoutSeconds for long human-in-the-loop multisig flows. |
network
value with no matching entry under Bitcoin:Networks, the node throws
BitcoinInvalidNetworkException before any HTTP call is made. See
Troubleshooting.
Per-Operation network Field
Every operation accepts a network config key. Read operations fall back to
Bitcoin:DefaultNetwork when it's omitted; every write/signing operation
(psbt.*, wallet.signMessage/deriveAddress, transaction.broadcast)
requires it explicitly — a misconfigured workflow must never silently fall back to a default network for
an operation that moves real funds or signs with real key material.
| Field | Type | Required | Description |
|---|---|---|---|
network | select | Reads: No · Writes/signing: ✓ Yes | mainnet | testnet | signet | regtest. |
Credential Resolution
WIF or BIP-39 mnemonic key material is never a config field. It's resolved from the
vault via the standard credentialID config key and read as a raw CryptoWalletRecord
so both WIF (.PrivateKey) and mnemonic (.SeedPhrase) credentials resolve correctly.
This mirrors the Ethereum and Solana nodes' credential pattern in this codebase exactly. Only
psbt/sign, wallet/deriveAddress (non-watch-only), and wallet/signMessage
need a credential — see each operation's page for the exact walletType/credentialID
fields.
Registration
BitcoinDependency.RegisterDefaults(services) registers everything the node needs:
- The Esplora REST client (
EsploraClient, a rate-limit-aware typedHttpClient) - Per-resource services (address, transaction, block, mempool, fee, psbt, wallet)
CoinSelectionHelperandUtxoReservationHelper- The executor itself, scoped
- The
ExecutorRegistryentry forbitcoin
new BitcoinDependency().RegisterDefaults(services); to your
node-plugin bootstrap (Plugins_RegisterAllNodes(...) in
ServiceCollectionExtensionsForAI.cs) so the assembly is force-loaded and discoverable
at runtime. Without this line the node type won't appear in the workflow designer even though the
package is referenced.
Project Layout
The node ships as three .NET 9 projects:
| Project | Responsibility |
|---|---|
BizFirst.Integration.Bitcoin.Domain | Pure result records and network configuration model. Zero project references, zero NuGet packages. |
BizFirst.Integration.Bitcoin.Services | NBitcoin-backed business logic: one service per resource, EsploraClient, BitcoinWalletFactory (WIF/mnemonic → signing key), BitcoinPsbtService (the PSBT pipeline), CoinSelectionHelper, UtxoReservationHelper. |
BizFirst.Ai.ExecutionNodes.Blockchain.Bitcoin | The executor: config parsing, routing, credential resolution, result mapping, DI registration. |
UtxoReservationHelper is an in-memory,
single-process store — it does not coordinate UTXO reservations across multiple horizontally-scaled
instances. psbt/finalize re-validates every input UTXO is still unspent immediately before
finalizing regardless, so this is an operational-efficiency gap, not a fund-safety one — but back this
helper with a distributed lock (Redis or SQL Server) before scaling this node horizontally in production.