Skip to content

feat(bip322-multisig): new package for BIP-322 PSBTs from P2WSH descriptors#165

Merged
TaprootFreak merged 9 commits into
developfrom
feat/bip322-multisig
Apr 29, 2026
Merged

feat(bip322-multisig): new package for BIP-322 PSBTs from P2WSH descriptors#165
TaprootFreak merged 9 commits into
developfrom
feat/bip322-multisig

Conversation

@TaprootFreak
Copy link
Copy Markdown
Contributor

Summary

New monorepo package `@dfx.swiss/bip322-multisig` providing the deterministic core logic to:

  • parse standard `wsh(sortedmulti(t, [fp/path]xpub.../<0;1>/*, …))` descriptors with key origin info
  • derive the matching child index for a given P2WSH address
  • build a BIP-322 `to_sign` PSBT (base64) with all PSBT fields hardware wallets need (witnessUtxo, witnessScript, sighashType=ALL, bip32Derivation per cosigner, nonWitnessUtxo)
  • extract the BIP-322 simple signature (base64-encoded witness) from a finalized PSBT

The package is consumed by both the CLI tool (`DFXswiss/bip322-multisig-cli`) and the upcoming services frontend integration so they share one tested implementation.

Public API

```ts
import { parseDescriptor, deriveAddress, findAddress, buildBip322Psbt, extractBip322Signature } from '@dfx.swiss/bip322-multisig';
```

Test plan

  • 15 unit tests across descriptor parsing, child derivation, PSBT construction, witness extraction (all passing)
  • `tsc --build` produces clean dist
  • prettier ✓, eslint ✓ (only standard test-file warnings)
  • Integration: CLI repo migrated to consume this package
  • Integration: services frontend uses it for the multisig connector

@TaprootFreak TaprootFreak force-pushed the feat/bip322-multisig branch from 6406164 to 97ea30e Compare April 28, 2026 23:47
TaprootFreak and others added 4 commits April 29, 2026 01:52
…alidate pubkeys

- parseDescriptor: strip whitespace/newlines (Sparrow exports often line-wrapped)
- parseKey regex: accept both `h` and `'` as hardened markers (BIP-380), normalise to `h`
- parseDescriptor: throw on inconsistent receive/change branches across cosigners
- buildSortedMultisigScript: validate each pubkey is 33 bytes and starts with 0x02/0x03
- centralise bitcoin.initEccLib(ecc) in ecc-init.ts (was duplicated in psbt.ts and witness.ts)
- add 5 tests covering apostrophe paths, whitespace, branch inconsistency, uncompressed and malformed pubkeys
…lize)

CLI scripts moved into the package as thin TS wrappers that import from the
package's own lib. Single source of truth for both the JS API (used by
services frontend) and the CLI (npm install -g @dfx.swiss/bip322-multisig
provides bip322-sign and bip322-finalize commands).

- src/bin/sign.ts: fetch DFX challenge, build PSBT, write file
- src/bin/finalize.ts: read signed PSBT, extract witness, POST to /v1/auth
- package.json: add bin field + engines >=18 (uses native fetch)
- tsconfig.build.json: emit CommonJS so CLI runs in Node without ESM resolver

Replaces the standalone DFXswiss/bip322-multisig-cli repo (to be archived).
…mitives

Split the package into two layers so btc-wallet (React Native) can
import the pure BIP-322 math without triggering ecc side-effects:

- core.ts: bip322MessageHash, buildToSpendTx, buildToSignPsbt,
  extractBip322Signature, buildSortedMultisigScript, p2wshScriptPubKey,
  p2wshAddress — no ecc-init import, no descriptor/bip32 dependency
- psbt.ts: high-level buildBip322Psbt (descriptor-based, imports ecc)
- descriptor.ts: imports helpers from core instead of duplicating
- witness.ts: removed (absorbed into core.ts)
- New core.test.ts with 10 tests for the low-level API
@TaprootFreak TaprootFreak merged commit 2d48fb4 into develop Apr 29, 2026
1 check passed
@TaprootFreak TaprootFreak deleted the feat/bip322-multisig branch April 29, 2026 16:48
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.

1 participant