Skip to content

feat: modular Bitcoin backend with adapter pattern#62

Merged
landabaso merged 35 commits intobitcoinerlab:mainfrom
Kukks:feat/modular-backend
Mar 25, 2026
Merged

feat: modular Bitcoin backend with adapter pattern#62
landabaso merged 35 commits intobitcoinerlab:mainfrom
Kukks:feat/modular-backend

Conversation

@Kukks
Copy link
Copy Markdown
Contributor

@Kukks Kukks commented Mar 15, 2026

Summary

  • Introduces a BitcoinLib interface (src/bitcoinLib.ts) that abstracts all Bitcoin operations (payments, script, crypto, PSBT, transaction parsing) behind a pluggable adapter layer
  • Ships a bitcoinjs-lib adapter (src/adapters/bitcoinjs.ts) as the default backend
  • DescriptorsFactory accepts either TinySecp256k1Interface (backward compatible) or a full BitcoinLib adapter (new modular path)
  • All leaf modules (miniscript, tapMiniscript, ledger, signers, etc.) receive operations via parameter injection rather than importing from bitcoinjs-lib directly
  • Exports PsbtLike, BitcoinLib, Network, Payment, and other adapter types from the package index

This is a non-breaking change: existing code that passes ecc to DescriptorsFactory(ecc) continues to work identically (bitcoinjs-lib adapter is loaded lazily). New code can pass a full BitcoinLib adapter to use an alternative backend.

Test plan

  • All 2274 existing unit tests pass (verified locally)
  • CI passes on all platforms
  • Verify backward compatibility: DescriptorsFactory(ecc) works identically to before
  • Verify new path: DescriptorsFactory(createBitcoinjsLib(ecc)) produces same results

Kukks added 2 commits March 15, 2026 18:45
Introduce BitcoinLib interface that abstracts all Bitcoin operations
(payments, script, crypto, PSBT, transactions) behind a pluggable
adapter layer. This enables users to choose between bitcoinjs-lib
and alternative backends (e.g. @scure/btc-signer) without changing
application code.

Key changes:
- Add src/bitcoinLib.ts with BitcoinLib, PsbtLike, and Payment interfaces
- Add src/adapters/bitcoinjs.ts wrapping bitcoinjs-lib as the default adapter
- Refactor DescriptorsFactory to accept either TinySecp256k1Interface
  (backward compatible) or BitcoinLib (new modular path)
- Thread operation dependencies (scriptOps, cryptoOps, taggedHash, etc.)
  through all leaf modules via parameter injection
- Export PsbtLike, BitcoinLib, Network, and other types from index
- Update all tests to use PsbtLike interface

All 2274 existing tests pass.
Add a full adapter for @scure/btc-signer alongside the existing
bitcoinjs-lib adapter, letting users choose their preferred Bitcoin
library. Both are declared as optional peer dependencies so only the
installed backend gets bundled.

- src/adapters/scure.ts: ScurePsbtAdapter wrapping btc.Transaction,
  payment wrappers (p2pkh, p2wpkh, p2wsh, p2sh, p2tr), script utils,
  HD key adapter, taproot BIP32 signing, network definitions
- src/scure.ts: separate entry point re-exporting createScureLib
- package.json: bitcoinjs-lib moved to optional peerDependency,
  added @scure/btc-signer + @noble/* as optional peers, added
  ./scure subpath export, integrated test:scure script
- src/index.ts: exported createBitcoinjsLib for symmetry
- test/scureAdapter.cjs: 201-test standalone suite covering adapter
  basics, custom fixtures and Bitcoin Core fixtures
- README.md: added "Choosing a Bitcoin Backend" section with install
  and usage instructions for both backends
@landabaso
Copy link
Copy Markdown
Member

Hey @Kukks , thanks for the PR!!

It's quite large and I still do manual close checks for BitcoinerLab. Before I dig into it properly, let me know if you consider the PR finished on your side.

I get the idea and agree with the general direction, though I might adjust a few things or even take a slightly different approach.

Rather than dragging you through a long review cycle here, I think it's better if I take it from here once you're done. I might add more comments after I look into it further.

@Kukks
Copy link
Copy Markdown
Contributor Author

Kukks commented Mar 16, 2026

I'm going to finish wiring up a test matrix and some accompanying fixes but yeah, feel free to take over then.

The way I've done it is to include everything in one package, but allow libs to install which peer dependency they want and treeshaking does the rest as needed

@Kukks
Copy link
Copy Markdown
Contributor Author

Kukks commented Mar 17, 2026

Hey @Kukks , thanks for the PR!!

It's quite large and I still do manual close checks for BitcoinerLab. Before I dig into it properly, let me know if you consider the PR finished on your side.

I get the idea and agree with the general direction, though I might adjust a few things or even take a slightly different approach.

Rather than dragging you through a long review cycle here, I think it's better if I take it from here once you're done. I might add more comments after I look into it further.

It's all yours now

@landabaso
Copy link
Copy Markdown
Member

yeah, i'm on it. btw, which repo is importing your fork so i can take a look?

@landabaso
Copy link
Copy Markdown
Member

landabaso commented Mar 19, 2026

PR continuation notes

  • I reduced differences wrt main where possible. A lot of the previous refactor pushed new code too far into the core and tests. I moved that complexity toward the adapters and kept the original descriptor / PSBT flow as intact as possible. I prefer to keep that original code as unchanged as possible.
  • I fixed several typing/lint issues:
    • the any keyword should not be used in this codebase,
    • ts-ignore / linter suppressions should not be used to hide real typing problems.
  • I created a small internal src/crypto.ts module instead of passing crypto functions around or depending on backend-specific wrappers.
  • I restored bitcoinjs compatibility for the PSBT surface (to fix a breaking change):
    • the internal Psbt type is again an actual subset of bitcoinjs-lib Psbt,
    • native bitcoinjs Psbt instances can be passed directly to updatePsbtAsInput / updatePsbtAsOutput and the signer APIs,
    • wrapper-only methods like getInput / getTxInput were removed from the public contract and the core now uses data.inputs / txInputs again.
  • I simplified the public adapter surface:
    • centralized shared network definitions/defaults,
    • removed some adapter return values that were only really there for tests,
    • restored old defaults that had changed unnecessarily (for example optional network behavior in helper paths). This fixed a breaking change.
  • For Ledger, I removed TransactionOps from the public signer API. Ledger now loads bitcoinjs-lib internally for the transaction parsing it needs. Since Ledger support is optional and Ledger itself already depends on bitcoinjs-lib, this felt like the least harmful simplification. This also restored the previous signer API and fixed a breaking change introduced by the refactor.
  • I internalized the bip174 types/helpers into a local src/bip174.ts, so the library no longer imports bip174.
  • I fixed a number of real scure adapter bugs/parity issues:
    • txid byte-order conversion,
    • taproot PSBT metadata conversion,
    • p2tr payment parity,
    • p2sh / p2wsh input / witness parity,
    • and some subtle wrapping / interop issues.
  • I modified the scure-specific PSBT interop path:
    • createScureLib(ecc).Psbt
    • createScureLib(ecc).Psbt.fromTransaction(nativeTx)
    • psbt.raw to access the native scure btc.Transaction
  • I also updated the tests heavily:
    • converted the old scure test to TypeScript,
    • fixed the Jest ESM issue for noble/scure deps,
    • routed backend-agnostic tests through the backend helper,
    • and added a dedicated scure integration flow/example test.

@landabaso
Copy link
Copy Markdown
Member

That said, I'm still not fully happy with the abstraction. The current scure flow still depends on wrapping the native scure transaction. I'd like to push that further so the scure/noble path feels more native/equivalent instead of just "bitcoinjs-shaped through wrappers".

So I do not want to merge this yet. I'd rather continue from a follow-up PR and then merge the work from both PRs together.

For now, though, this PR makes @scure/btc-signer usable and integrates it properly into the library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants