Skip to content

fix(ts-sdk): harden payout PSBT signature extraction#1405

Open
jeremy-babylonlabs wants to merge 1 commit intomainfrom
fix/payout-psbt-sig-extraction
Open

fix(ts-sdk): harden payout PSBT signature extraction#1405
jeremy-babylonlabs wants to merge 1 commit intomainfrom
fix/payout-psbt-sig-extraction

Conversation

@jeremy-babylonlabs
Copy link
Copy Markdown
Contributor

@jeremy-babylonlabs jeremy-babylonlabs commented Apr 15, 2026

Summary

  • Enforce exact Taproot single-sig witness stack size (3 items: signature, script, controlBlock) in extractPayoutSignature so unexpected wallet finalizations fail loudly instead of silently returning a potentially-wrong signature.
  • Make parseWitnessStack reject malformed witness data: require enough bytes before reads, fix a missing >>> 0 on the 4-byte varint path, throw on unsupported 8-byte varints, and reject trailing bytes.
  • Add tests covering the new invariants.

Test plan

  • pnpm --filter @babylonlabs-io/ts-sdk run test
  • pnpm run lint

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 15, 2026

🔐 Commit Signature Verification

All 1 commit(s) passed verification

Commit Author Signature Key Type Key Check
ac7896286f34 jeremy-babylonlabs sk-ssh-ed25519

Summary

  • Commits verified: 1
  • Signature check: ✅ All passed
  • Key type enforcement: ✅ All sk-ssh-ed25519

Required key type: sk-ssh-ed25519 (FIDO2 hardware key)

Last verified: 2026-04-15 09:23 UTC

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 15, 2026

Greptile Summary

This PR hardens the payout PSBT signature extraction path in two ways: (1) extractPayoutSignature now enforces an exact witness stack size of 3 for Taproot single-sig script-path spends instead of accepting any non-empty stack, and (2) parseWitnessStack gains proper bounds checking, a >>> 0 fix for the 4-byte varint path's sign-bit issue, rejection of unsupported 8-byte varints, and trailing-bytes detection. New tests cover all added invariants.

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 style suggestions with no correctness impact.

The hardening changes are logically sound: the >>> 0 fix on the 4-byte varint path is correct, bounds checking is properly placed, the 0xff rejection is appropriate, trailing-bytes detection is correct, and the strict stack-size enforcement is a deliberate and well-documented security improvement. Tests cover every new code path. No P0/P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
packages/babylon-ts-sdk/src/tbv/core/primitives/psbt/payout.ts Adds exact witness stack size enforcement and hardens parseWitnessStack with bounds checking, unsigned arithmetic fix for 4-byte varints, 8-byte varint rejection, and trailing-bytes detection. Logic is correct; minor P2 style note on subarray (view vs. copy) and missing >>> 0 on the 2-byte varint path for consistency.
packages/babylon-ts-sdk/src/tbv/core/primitives/psbt/tests/payout.test.ts Adds seven well-structured tests for the new finalized-witness path, including happy-path extraction, sighash stripping, wrong stack sizes, invalid sig length, truncated buffer, 8-byte varint, and trailing bytes. Helper functions are correct and self-contained.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[extractPayoutSignature] --> B{tapScriptSig present?}
    B -- Yes --> C[Find entry matching depositorPubkey]
    C --> D{Found?}
    D -- No --> E[Throw: no sig for depositor pubkey]
    D -- Yes --> F[extractSchnorrSig]

    B -- No --> G{finalScriptWitness present?}
    G -- No --> H[Throw: no tapScriptSig or finalScriptWitness]
    G -- Yes --> I[parseWitnessStack]

    I --> I1{requireBytes check at each read}
    I1 -- fail --> I2[Throw: Malformed witness data]
    I1 -- pass --> I3{varint prefix}
    I3 -- 0x00-0xFC --> I4[1-byte count/len]
    I3 -- 0xFD --> I5[2-byte LE count/len]
    I3 -- 0xFE --> I6[4-byte LE count/len with >>> 0]
    I3 -- 0xFF --> I7[Throw: 8-byte varint not supported]
    I4 & I5 & I6 --> I8[Read items into Buffer views]
    I8 --> I9{Trailing bytes?}
    I9 -- Yes --> I10[Throw: trailing bytes]
    I9 -- No --> J[witnessStack]

    J --> K{stack.length === 3?}
    K -- No --> L[Throw: unexpected stack size]
    K -- Yes --> F

    F --> M{sig.length}
    M -- 64 --> N[Return hex]
    M -- 65 --> O{sighash === SIGHASH_ALL?}
    O -- Yes --> P[Strip byte, return 64-byte hex]
    O -- No --> Q[Throw: unexpected sighash type]
    M -- other --> R[Throw: unexpected sig length]
Loading

Reviews (1): Last reviewed commit: "fix(ts-sdk): harden payout PSBT signatur..." | Re-trigger Greptile

Comment thread packages/babylon-ts-sdk/src/tbv/core/primitives/psbt/payout.ts
Comment thread packages/babylon-ts-sdk/src/tbv/core/primitives/psbt/payout.ts Outdated
@jeremy-babylonlabs jeremy-babylonlabs force-pushed the fix/payout-psbt-sig-extraction branch from dc39a8d to 8f3a0e0 Compare April 15, 2026 09:20
Enforce exact Taproot single-sig witness stack size and fail loudly on
malformed witness data instead of silently returning a possibly-wrong
signature or zero-valued varint.
@jeremy-babylonlabs jeremy-babylonlabs force-pushed the fix/payout-psbt-sig-extraction branch from 8f3a0e0 to ac78962 Compare April 15, 2026 09:23
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