Portal Community

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 }
}
KeyTypeRequiredDescription
DefaultNetworktext✓ YesNetwork used when a read operation doesn't set its own network field. One of mainnet, testnet, signet, regtest.
Networksobject✓ YesMap of network name → EsploraApiUrl + BroadcastEndpoint. Both must be http/https — validated at startup. See Networks.
RequestTimeoutnumber (ms)NoHTTP timeout for Esplora calls. Default 30000.
CoinSelection.DustThresholdSatsnumberNoBelow this, a computed change output is folded into the fee instead of created. Default 546.
CoinSelection.MinUtxoValueSatsnumberNoUTXOs below this value are excluded from candidate selection entirely — hardens against dust/UTXO-pollution attacks. Default 546.
UtxoReservation.LockTimeoutSecondsnumberNoDefault hold window for a UTXO reserved by psbt/build. Default 120. Override per-call with reservationTimeoutSeconds for long human-in-the-loop multisig flows.
Missing or misconfigured network: If an operation requests a 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.

FieldTypeRequiredDescription
networkselectReads: No · Writes/signing: ✓ Yesmainnet | 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:

Host apps must also add an explicit registration call: alongside the standard DI registration, add 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:

ProjectResponsibility
BizFirst.Integration.Bitcoin.DomainPure result records and network configuration model. Zero project references, zero NuGet packages.
BizFirst.Integration.Bitcoin.ServicesNBitcoin-backed business logic: one service per resource, EsploraClient, BitcoinWalletFactory (WIF/mnemonic → signing key), BitcoinPsbtService (the PSBT pipeline), CoinSelectionHelper, UtxoReservationHelper.
BizFirst.Ai.ExecutionNodes.Blockchain.BitcoinThe executor: config parsing, routing, credential resolution, result mapping, DI registration.
Deployment-topology limitation: 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.