# Bitcoin Armory Crypto Test Gap Analysis

Date: 2026-06-30

Purpose: map the current automated evidence to the derivation and signing matrix, and identify the highest-value missing tests for the audit.

Related docs:

- [crypto-audit-plan.md](/Users/juthicamallela/bitcoinarmory/docs/security/crypto-audit-plan.md:1)
- [crypto-derivation-matrix.md](/Users/juthicamallela/bitcoinarmory/docs/security/crypto-derivation-matrix.md:1)

## 1. Current coverage summary

### Good existing coverage

| Area | Current evidence |
|---|---|
| BIP39 mnemonic generation and seed derivation | [tests/test_keys.py](/Users/juthicamallela/bitcoinarmory/tests/test_keys.py:1) |
| BIP32 master and child derivation vectors | [tests/test_keys.py](/Users/juthicamallela/bitcoinarmory/tests/test_keys.py:1) |
| BIP84/BIP86 path helper strings | [tests/test_keys.py](/Users/juthicamallela/bitcoinarmory/tests/test_keys.py:109) |
| wallet address derivation basics | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:38) |
| watch-only wallet deriving same public addresses | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:97) |
| extended script lookup past pre-derived window | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:81) |
| save/load and watch-only export basics | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:129) |
| P2WPKH signing basics | [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:13) |
| wrong-wallet fast failure for standard derivation hints | [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:92) |
| Silent Payment internal send/detect/spend round trips | [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:145) and [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:159) |
| watch-only export includes SP scan private key and excludes SP spend private key | [tests/test_wallet_api.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet_api.py:377), [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:301) |

### Partial coverage

| Area | Current evidence | Gap |
|---|---|---|
| BIP86 address derivation | basic receive-address tests exist | no independent vector or script/tweak cross-check |
| online wallet derivation-window extension | covered for BIP84 | no equivalent BIP86 window-extension proof |
| SP wallet persistence | basic save/load coverage exists | no recovery proof from backup-only plus rescan assumptions |
| online PSBT creation | basic happy-path tests exist | no inspection of per-input and per-output derivation metadata |

## 2. Highest-priority missing evidence

### Gap 1: no official BIP-352 compatibility vectors

Current status:

1. [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:1) validates the implementation mostly against itself.
2. That is useful for regression prevention, but not for proving network compatibility.

Why it matters:

1. send and scan code can agree with each other and still both be wrong
2. that failure mode can strand real funds

Required next test:

1. import and execute official BIP-352 test vectors

### Gap 2: no end-to-end BIP86 signing proof

Current status:

1. there are tests for BIP86 receive addresses
2. there are Silent Payment Taproot spend tests
3. there is no focused test proving standard BIP86 PSBT construction and offline signing end to end

Why it matters:

1. BIP86 receive derivation and BIP86 spend tweaking are different steps
2. a bug here can make Taproot funds visible but unspendable

Required next tests:

1. create BIP86 UTXO
2. attach Taproot BIP32 derivation metadata
3. sign offline
4. verify witness exists and derived tweaked key matches output key

### Gap 3: no explicit descriptor-origin correctness tests

Current status:

1. descriptor-building code exists in [online/setup.py](/Users/juthicamallela/bitcoinarmory/online/setup.py:532)
2. current tests do not prove the exact descriptor strings, origin fingerprints, or origin paths for fixed seeds

Why it matters:

1. incorrect descriptor origins can make the online side derive or import the wrong tree
2. that can lead to addresses being handed out that the offline side cannot spend

Required next tests:

1. fixed-seed snapshot tests for BIP84 and BIP86 descriptors
2. explicit assertions for fingerprint and origin path

### Gap 4: no tests that inspect change-output derivation metadata

Current status:

1. online code writes output derivation metadata for change
2. signer comments rely on this to avoid address-pool lag issues
3. current tests do not inspect the PSBT output maps directly

Why it matters:

1. missing or malformed output metadata can cause change misclassification
2. fallback scanning may hide the bug instead of surfacing it

Required next tests:

1. verify BIP84 change output gets `PSBT_OUT_BIP32_DERIVATION`
2. verify BIP86 change output gets `PSBT_OUT_TAP_BIP32_DERIVATION`
3. verify offline logic still recognizes change when wallet state lags

### Gap 5: no negative tests for malformed Taproot derivation metadata

Current status:

1. standard wrong-wallet fast-fail exists for fingerprint mismatch
2. there is not enough coverage for malformed or conflicting Taproot derivation metadata

Why it matters:

1. Taproot inputs carry different metadata shape than P2WPKH inputs
2. bugs here can lead to unsafe fallback scanning or wrong-key acceptance

Required next tests:

1. mismatched x-only pubkey versus path
2. mismatched fingerprint with Taproot derivation present
3. missing internal key with tap derivation present

## 3. Matrix by area

### BIP84

| Assertion | Status | Notes |
|---|---|---|
| path helper correct | covered | [tests/test_keys.py](/Users/juthicamallela/bitcoinarmory/tests/test_keys.py:115) |
| receive and change addresses derive | covered | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:42) |
| watch-only derives same addresses | covered | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:97) |
| script lookup past pool works | covered | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:81) |
| PSBT input metadata stamped | not directly covered | needs inspection test |
| PSBT change metadata stamped | not directly covered | needs inspection test |
| offline signing end to end | covered for P2WPKH | [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:13) |

### BIP86

| Assertion | Status | Notes |
|---|---|---|
| path helper correct | covered | [tests/test_keys.py](/Users/juthicamallela/bitcoinarmory/tests/test_keys.py:119) |
| receive address shape | partially covered | [tests/test_wallet.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet.py:53) |
| tweaked output-key correctness | not independently covered | needs explicit test |
| watch-only derivation parity | not explicitly covered | BIP84 parity test exists, BIP86 equivalent should be added |
| descriptor string correctness | not covered | needs fixed-seed snapshot |
| PSBT Taproot input metadata stamped | not directly covered | needs inspection test |
| PSBT Taproot change metadata stamped | not covered | needs inspection test |
| offline BIP86 signing end to end | not covered | highest priority |

### Silent Payments

| Assertion | Status | Notes |
|---|---|---|
| path strings and key derivation basics | covered | [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:35) |
| address encode/decode round-trip | covered | [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:68) |
| internal send/detect/spend agreement | covered | [tests/test_silent_payment.py](/Users/juthicamallela/bitcoinarmory/tests/test_silent_payment.py:145) |
| watch-only export boundary | covered | [tests/test_wallet_api.py](/Users/juthicamallela/bitcoinarmory/tests/test_wallet_api.py:382) |
| online PSBT placeholder plus offline resolution | covered | [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:159) |
| spend detected SP UTXO | covered | [tests/test_signer.py](/Users/juthicamallela/bitcoinarmory/tests/test_signer.py:249) |
| official external compatibility vectors | not covered | highest priority |
| recovery assumptions from backup-only state | not covered | important |

## 4. Recommended implementation order for new tests

### Batch 1

1. BIP86 end-to-end signing test
2. descriptor snapshot tests for BIP84 and BIP86
3. PSBT output derivation metadata inspection tests

### Batch 2

1. official BIP-352 vectors
2. malformed Taproot derivation negative tests
3. BIP86 watch-only parity and derivation-window tests

### Batch 3

1. SP recovery from persisted state and rescan assumptions
2. mixed-input transaction tests combining BIP84, BIP86, and SP

## 5. Immediate audit conclusion

The current test suite is strong on internal deterministic derivation and on standard P2WPKH behavior. It is materially weaker where the money-critical edge cases live:

1. external compatibility proof for BIP-352
2. standard BIP86 Taproot spend correctness
3. exact descriptor-origin correctness
4. explicit PSBT metadata inspection

Those should be treated as the next proof obligations before relying on the new Silent Payment feature and before assuming the derivation model is fully safe against coin-stranding bugs.

