{"version":3,"file":"verifyScriptPathSchnorrSignature-la8XOAFm.cjs","sources":["../src/tbv/core/primitives/challengers.ts","../src/tbv/core/primitives/psbt/assertWasmPeginSizing.ts","../src/tbv/core/primitives/psbt/pegin.ts","../src/tbv/core/primitives/scripts/payout.ts","../src/tbv/core/primitives/utils/taproot.ts","../src/tbv/core/primitives/psbt/assertPayoutFeeBand.ts","../src/tbv/core/primitives/psbt/standardPayoutScript.ts","../src/tbv/core/primitives/psbt/payout.ts","../src/tbv/core/primitives/psbt/assertPsbtUnsignedTxMatches.ts","../src/tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature.ts"],"sourcesContent":["/**\n * Challenger derivation utilities — counting local challengers for UI-level\n * validation (e.g. minimum deposit amounts) and deriving the exact\n * local-challenger set a claimer's tx graph is built from.\n */\n\nimport { processPublicKeyToXOnly } from \"./utils/bitcoin\";\n\n/**\n * Normalize a public key to lowercase x-only hex for reliable comparison.\n *\n * Handles `0x` prefixes, compressed (33-byte), and uncompressed (65-byte) keys.\n */\nfunction normalizeKey(key: string): string {\n  return processPublicKeyToXOnly(key).toLowerCase();\n}\n\n/**\n * Compute the number of local challengers for a vault.\n *\n * Mirrors the VP's `compute_num_challengers()` logic:\n * local challengers = {vault_provider} ∪ {vault_keepers} − {depositor}\n *\n * Keys are normalized to x-only lowercase hex before comparison, so\n * `0x`-prefixed, compressed, or mixed-case keys are handled correctly.\n *\n * @param vaultProviderPubkey - Vault provider BTC public key\n * @param vaultKeeperPubkeys - Vault keeper BTC public keys\n * @param depositorPubkey - Depositor (claimer) BTC public key\n * @returns Number of local challengers\n */\nexport function computeNumLocalChallengers(\n  vaultProviderPubkey: string,\n  vaultKeeperPubkeys: string[],\n  depositorPubkey: string,\n): number {\n  const localSet = new Set<string>();\n  localSet.add(normalizeKey(vaultProviderPubkey));\n  for (const vk of vaultKeeperPubkeys) {\n    localSet.add(normalizeKey(vk));\n  }\n  localSet.delete(normalizeKey(depositorPubkey));\n  return localSet.size;\n}\n\n/** Roles a claimer can hold; determines which local-challenger rule applies. */\nexport interface DeriveLocalChallengersParams {\n  /** The graph's claimer (VP, a vault keeper, or the depositor). */\n  claimerBtcPubkey: string;\n  /** Depositor registered on-chain for the vault. */\n  depositorBtcPubkey: string;\n  /** Vault provider registered on-chain for the vault. */\n  vaultProviderBtcPubkey: string;\n  /** Vault keepers registered on-chain for the vault. */\n  vaultKeeperBtcPubkeys: string[];\n}\n\n/**\n * Derive the local-challenger set for a claimer's graph.\n *\n * Byte-for-byte mirror of btc-vault `derive_challengers_for`\n * (`crates/vault/src/tx_graph/graph.rs:257-284`):\n * - depositor-as-claimer → vault keepers only (VP excluded)\n * - VP- or VK-claimer → `({VP} ∪ VKs) − {claimer}`, sorted and deduplicated\n *\n * The protocol guarantees the depositor is not a vault keeper\n * (`TxGraphParams::validate`), so the depositor filter in the first branch is\n * defense-in-depth; it surfaces a clear error if a misconfigured context ever\n * violates the invariant.\n *\n * @throws If no local challenger remains, or (depositor path) the keeper set\n *   contains duplicates — both mean the signing context is misconfigured.\n */\nexport function deriveLocalChallengers(\n  params: DeriveLocalChallengersParams,\n): string[] {\n  const claimer = normalizeKey(params.claimerBtcPubkey);\n  const depositor = normalizeKey(params.depositorBtcPubkey);\n  const vaultKeepers = params.vaultKeeperBtcPubkeys.map(normalizeKey);\n\n  if (claimer === depositor) {\n    const filtered = vaultKeepers.filter((k) => k !== depositor);\n    if (filtered.length === 0) {\n      throw new Error(\n        \"Cannot derive localChallengers: vault keeper set is empty (or contains only the depositor)\",\n      );\n    }\n    if (new Set(filtered).size !== filtered.length) {\n      throw new Error(\n        \"Cannot derive localChallengers: duplicate vaultKeeper key — signing context is misconfigured\",\n      );\n    }\n    return filtered;\n  }\n\n  // Rust sorts then dedups before filtering; a Set keeps insertion order, so\n  // sort after deduping for the same result.\n  const deduped = [\n    ...new Set([normalizeKey(params.vaultProviderBtcPubkey), ...vaultKeepers]),\n  ].sort();\n  const filtered = deduped.filter((k) => k !== claimer);\n  if (filtered.length === 0) {\n    throw new Error(\n      `Cannot derive localChallengers: no vault provider or vault keeper remains after excluding claimer ${claimer}`,\n    );\n  }\n  return filtered;\n}\n","/**\n * Cross-check the values WASM returns from `createPrePeginTransaction`\n * against independently-known expectations before they feed a signed\n * Bitcoin transaction or the on-chain PegIn registration.\n *\n * CLAUDE.md critical path #1: the Rust/WASM layer computes\n * `htlcValue = peginAmount + depositorClaimValue + p2aAnchorValue +\n * minPeginFee` internally (the anchor term is 0 for graph versions without a\n * P2A anchor, 240 sats for v2/v3) and JS receives the outputs with no runtime\n * validation. A doctored or buggy binary that returns a different\n * `peginAmount`, an out-of-formula `htlcValue`, or a wrong\n * `depositorClaimValue` would otherwise be committed verbatim — taxing the\n * depositor or starving the downstream tx graph of fees.\n *\n * @module primitives/psbt/assertWasmPeginSizing\n */\n\nimport {\n  computeMinClaimValue,\n  computeMinPeginFee,\n  peginP2aAnchorOutput,\n  type PrePeginResult,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport { MAX_REASONABLE_PEGIN_VBYTES } from \"../../utils/fee/constants\";\nimport type { ParsedOutput } from \"../../utils/transaction/fundPeginTransaction\";\n\nimport type { PrePeginParams } from \"./pegin\";\n\n/**\n * Assert the WASM Pre-PegIn sizing result is internally consistent and\n * matches what the caller requested.\n *\n * Two layers of checks. Pure-JS, binary-INDEPENDENT: the per-HTLC\n * `peginAmount` must echo the requested amount, array lengths must match,\n * every value must be positive, and the implied per-HTLC reserve\n * (`htlcValue - peginAmount - depositorClaimValue`) must be strictly\n * positive and under the plausibility cap\n * ({@link MAX_REASONABLE_PEGIN_VBYTES}). WASM-vs-WASM consistency: the\n * reserve must also EXACTLY equal `computeMinPeginFee(version, …) +\n * p2aAnchorValue(version)` (like the `computeMinClaimValue` check) —\n * exact, but both sides come from the same binary, which is why the\n * independent bounds above are kept alongside it.\n *\n * The resume rebuild (`deposit-terms/rebuildDepositTermsCore.ts`) mirrors these\n * bounds (positivity + reserve band) — keep the two in sync when editing.\n *\n * @param result - The result returned by `createPrePeginTransaction`.\n * @param params - The parameters that were passed to build it.\n * @returns The independently computed `minPeginFee` this function already\n *   asserted against `result`'s implied reserve — callers that need the\n *   value should reuse this instead of recomputing it.\n * @throws If any value is missing, non-positive, mismatched against the\n *   request, or outside the protocol formula.\n */\nexport async function assertWasmPeginSizing(\n  result: PrePeginResult,\n  params: PrePeginParams,\n): Promise<bigint> {\n  const expectedCount = params.pegInAmounts.length;\n\n  // Count: every parallel array must carry exactly one entry per requested\n  // deposit, otherwise the per-HTLC indexing downstream is meaningless.\n  if (result.htlcValues.length !== expectedCount) {\n    throw new Error(\n      `WASM Pre-PegIn returned ${result.htlcValues.length} HTLC value(s), ` +\n        `expected ${expectedCount} (one per requested deposit).`,\n    );\n  }\n  if (\n    result.peginAmounts.length !== expectedCount ||\n    result.htlcScriptPubKeys.length !== expectedCount ||\n    result.htlcAddresses.length !== expectedCount\n  ) {\n    throw new Error(\n      `WASM Pre-PegIn returned mismatched array lengths ` +\n        `(htlcValues=${result.htlcValues.length}, ` +\n        `peginAmounts=${result.peginAmounts.length}, ` +\n        `htlcScriptPubKeys=${result.htlcScriptPubKeys.length}, ` +\n        `htlcAddresses=${result.htlcAddresses.length}); ` +\n        `expected ${expectedCount} each.`,\n    );\n  }\n\n  // depositorClaimValue: positivity + WASM-vs-WASM consistency. Sized by the\n  // tx-graph `feeRate` (see PrePeginParams.feeRate), so the standalone\n  // `computeMinClaimValue` must reproduce the constructor's internal value.\n  if (result.depositorClaimValue <= 0n) {\n    throw new Error(\n      `WASM Pre-PegIn returned non-positive depositorClaimValue ` +\n        `${result.depositorClaimValue}; expected > 0.`,\n    );\n  }\n  const expectedClaimValue = await computeMinClaimValue(\n    // Must price the same graph version the builder constructed.\n    params.vaultCoreVersion,\n    params.numLocalChallengers,\n    params.universalChallengerPubkeys.length,\n    params.councilQuorum,\n    params.councilSize,\n    params.feeRate,\n  );\n  if (result.depositorClaimValue !== expectedClaimValue) {\n    throw new Error(\n      `WASM Pre-PegIn depositorClaimValue ${result.depositorClaimValue} does ` +\n        `not match the independently computed minimum claim value ` +\n        `${expectedClaimValue} (vaultCoreVersion=${params.vaultCoreVersion}, ` +\n        `numLocalChallengers=${params.numLocalChallengers}, ` +\n        `numUniversalChallengers=${params.universalChallengerPubkeys.length}, ` +\n        `councilQuorum=${params.councilQuorum}, councilSize=${params.councilSize}, ` +\n        `feeRate=${params.feeRate}).`,\n    );\n  }\n\n  // The per-HTLC reserve above pegin+claim decomposes into the version's\n  // P2A anchor value (0 when the version has no anchor) plus the exact\n  // minimum PegIn fee. Both terms are Rust-model values fetched through\n  // independent WASM entry points; the builder must reproduce their sum.\n  // The Rust fee model sizes the PegIn input witness from the vault keeper\n  // and universal challenger counts (btc-vault `PegInTx::estimate_vsize`).\n  const anchor = await peginP2aAnchorOutput(params.vaultCoreVersion);\n  const anchorValue = anchor?.value ?? 0n;\n  const expectedPeginFee = await computeMinPeginFee(\n    params.vaultCoreVersion,\n    params.vaultKeeperPubkeys.length,\n    params.universalChallengerPubkeys.length,\n    params.minPeginFeeRate,\n  );\n  const expectedReserve = expectedPeginFee + anchorValue;\n  // Binary-INDEPENDENT plausibility cap: the exact identity below compares\n  // WASM against WASM, so a consistently doctored binary could satisfy it\n  // with an inflated reserve. This pure-JS bound (max standard-relay tx\n  // vbytes × the caller's fee rate) caps how much a compromised binary can\n  // burn.\n  const maxImpliedReserve =\n    params.minPeginFeeRate * MAX_REASONABLE_PEGIN_VBYTES;\n\n  for (let i = 0; i < expectedCount; i++) {\n    const requested = params.pegInAmounts[i];\n    const peginAmount = result.peginAmounts[i];\n    const htlcValue = result.htlcValues[i];\n\n    // Amount echo (strongest, fully independent): the recorded pegin amount\n    // must equal exactly what the caller requested. A mismatch is the\n    // WASM-tax attack — the contract would record a doctored amount while the\n    // depositor's wallet funds the original, and the difference is a\n    // WASM-controlled tax.\n    if (peginAmount !== requested) {\n      throw new Error(\n        `WASM Pre-PegIn peginAmount[${i}] ${peginAmount} does not match the ` +\n          `requested amount ${requested}; refusing to build a tx whose ` +\n          `recorded amount differs from the depositor's request.`,\n      );\n    }\n    if (peginAmount <= 0n) {\n      throw new Error(\n        `WASM Pre-PegIn peginAmount[${i}] is non-positive (${peginAmount}); ` +\n          `expected > 0.`,\n      );\n    }\n    if (htlcValue <= 0n) {\n      throw new Error(\n        `WASM Pre-PegIn htlcValue[${i}] is non-positive (${htlcValue}); ` +\n          `expected > 0.`,\n      );\n    }\n\n    // Formula: htlcValue = peginAmount + depositorClaimValue +\n    // p2aAnchorValue + minPeginFee. The reserve must match exactly — a\n    // shortfall starves the PegIn's fee/anchor, an excess locks sats\n    // irrecoverably in the HTLC.\n    const impliedReserve = htlcValue - peginAmount - result.depositorClaimValue;\n    // Independent JS-side bounds first (see maxImpliedReserve above): the\n    // reserve must be strictly positive (a zero reserve starves the PegIn\n    // of its fee) and plausibly sized, regardless of what the binary's own\n    // reference entry points claim.\n    if (impliedReserve <= 0n) {\n      throw new Error(\n        `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} does not strictly ` +\n          `cover peginAmount ${peginAmount} + depositorClaimValue ` +\n          `${result.depositorClaimValue} + a PegIn reserve (implied ` +\n          `reserve ${impliedReserve}).`,\n      );\n    }\n    if (impliedReserve > maxImpliedReserve) {\n      throw new Error(\n        `WASM Pre-PegIn implied reserve for HTLC[${i}] (${impliedReserve} ` +\n          `sat) exceeds the plausibility cap ${maxImpliedReserve} sat ` +\n          `(minPeginFeeRate=${params.minPeginFeeRate} × ` +\n          `${MAX_REASONABLE_PEGIN_VBYTES} vbytes); htlcValue ${htlcValue} ` +\n          `appears grossly inflated.`,\n      );\n    }\n    if (impliedReserve !== expectedReserve) {\n      throw new Error(\n        `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} implies a PegIn ` +\n          `fee+anchor reserve of ${impliedReserve} sat, expected exactly ` +\n          `${expectedReserve} sat (minPeginFee ${expectedPeginFee} + ` +\n          `p2aAnchor ${anchorValue} for vaultCoreVersion ` +\n          `${params.vaultCoreVersion}, vaultKeepers=` +\n          `${params.vaultKeeperPubkeys.length}, universalChallengers=` +\n          `${params.universalChallengerPubkeys.length}, minPeginFeeRate=` +\n          `${params.minPeginFeeRate}).`,\n      );\n    }\n  }\n\n  return expectedPeginFee;\n}\n\n/**\n * A funded Pre-PegIn's HTLC outputs disagree with the expected value or\n * scriptPubKey.\n *\n * Typed so a caller trialling several parameter sets can tell \"the transaction\n * definitively does not match these parameters\" — a genuine, informative\n * rejection — from \"evaluating this parameter set failed\", which says nothing\n * about the transaction and must not be counted as a rejection. Without the\n * distinction an incidental failure on the true parameter set silently removes\n * it from consideration and a look-alike wins.\n */\nexport class HtlcOutputMismatchError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"HtlcOutputMismatchError\";\n  }\n}\n\n/**\n * Bind the validated metadata to the bytes that actually get funded and\n * signed.\n *\n * `assertWasmPeginSizing` proves the WASM *metadata* (`htlcValues`,\n * `htlcScriptPubKeys`) matches the request and the protocol formula — but the\n * transaction the depositor funds and signs is `result.txHex`. If the encoded\n * tx carried a different HTLC output value or script than the metadata, the\n * depositor would fund a transaction whose real outputs differ from the values\n * that were cross-checked. This closes that final link: the encoded HTLC\n * outputs must equal the validated metadata.\n *\n * The WASM lays out HTLC outputs first (vouts `0..N-1`), then the optional\n * auth-anchor OP_RETURN, then the CPFP anchor — so we only compare the first\n * `htlcValues.length` outputs.\n *\n * @param outputs - Outputs parsed from the unfunded Pre-PegIn tx hex.\n * @param htlcValues - The (already value-validated) per-HTLC values.\n * @param htlcScriptPubKeys - The per-HTLC scriptPubKeys (hex).\n * @throws {HtlcOutputMismatchError} If the encoded outputs are too few, or any\n *   HTLC output's value or scriptPubKey disagrees with the validated metadata.\n */\nexport function assertEncodedHtlcOutputsMatch(\n  outputs: readonly ParsedOutput[],\n  htlcValues: readonly bigint[],\n  htlcScriptPubKeys: readonly string[],\n): void {\n  if (outputs.length < htlcValues.length) {\n    throw new HtlcOutputMismatchError(\n      `Encoded Pre-PegIn tx has ${outputs.length} output(s), fewer than the ` +\n        `${htlcValues.length} HTLC output(s) the cross-check validated.`,\n    );\n  }\n\n  for (let i = 0; i < htlcValues.length; i++) {\n    const encodedValue = BigInt(outputs[i].value);\n    if (encodedValue !== htlcValues[i]) {\n      throw new HtlcOutputMismatchError(\n        `Encoded Pre-PegIn HTLC output[${i}] value ${encodedValue} does not ` +\n          `match the cross-checked htlcValue ${htlcValues[i]}; the funded/signed ` +\n          `tx would not pay the validated amount.`,\n      );\n    }\n\n    const encodedScript = outputs[i].script.toString(\"hex\").toLowerCase();\n    const expectedScript = htlcScriptPubKeys[i].toLowerCase();\n    if (encodedScript !== expectedScript) {\n      throw new HtlcOutputMismatchError(\n        `Encoded Pre-PegIn HTLC output[${i}] scriptPubKey ${encodedScript} does ` +\n          `not match the cross-checked htlcScriptPubKey ${expectedScript}.`,\n      );\n    }\n  }\n}\n","/**\n * Pre-PegIn PSBT Builder Primitive\n *\n * This module provides pure functions for building unfunded Pre-PegIn transactions\n * and deriving PegIn transactions from them, using the WASM implementation from\n * @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * Pre-PegIn Flow:\n * 1. buildPrePeginPsbt()     — creates unfunded Pre-PegIn tx (HTLC output)\n * 2. [caller funds Pre-PegIn tx and computes txid]\n * 3. buildPeginTxFromFundedPrePegin() — derives PegIn tx spending the HTLC\n * 4. buildPeginInputPsbt()   — PSBT for depositor to sign PegIn HTLC leaf 0 input\n *\n * @module primitives/psbt/pegin\n */\n\nimport {\n  buildPeginTxFromPrePegin,\n  computeMinClaimValue,\n  createPrePeginTransaction,\n  peginP2aAnchorOutput,\n  tapInternalPubkey,\n  validatePeginP2aAnchor,\n  type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Buffer } from \"buffer\";\nimport { payments, script as bscript, Transaction, opcodes } from \"bitcoinjs-lib\";\n\nimport { parseUnfundedWasmTransaction } from \"../../utils/transaction/fundPeginTransaction\";\nimport {\n  hexToUint8Array,\n  stripHexPrefix,\n  uint8ArrayToHex,\n} from \"../utils/bitcoin\";\n\nimport {\n  assertEncodedHtlcOutputsMatch,\n  assertWasmPeginSizing,\n} from \"./assertWasmPeginSizing\";\n\n/**\n * Parameters for building an unfunded Pre-PegIn PSBT\n */\nexport interface PrePeginParams {\n  /**\n   * Vault core (tx-graph) version to build. Fresh deposits use the contract's\n   * `ProtocolParams.activeVaultCoreVersion()`; resumed vaults use their\n   * stamped on-chain `vaultCoreVersion`. The WASM facade fails closed on\n   * versions it wasn't compiled with.\n   */\n  vaultCoreVersion: number;\n  /** Depositor's BTC public key (x-only, 64-char hex without 0x prefix) */\n  depositorPubkey: string;\n  /** Vault provider's BTC public key (x-only, 64-char hex) */\n  vaultProviderPubkey: string;\n  /** Array of vault keeper BTC public keys (x-only, 64-char hex) */\n  vaultKeeperPubkeys: string[];\n  /** Array of universal challenger BTC public keys (x-only, 64-char hex) */\n  universalChallengerPubkeys: string[];\n  /** SHA256 hash commitment(s) (64 hex chars = 32 bytes each) */\n  hashlocks: readonly string[];\n  /** CSV timelock in blocks for the HTLC refund path */\n  timelockRefund: number;\n  /** Amounts to peg in (satoshis), one per deposit */\n  pegInAmounts: readonly bigint[];\n  /** TX-graph fee rate in sat/vB from contract offchain params; sizes the depositor claim value */\n  feeRate: bigint;\n  /** Minimum PegIn fee rate in sat/vB from contract offchain params; sizes the PegIn tx fee */\n  minPeginFeeRate: bigint;\n  /** Number of local challengers (from contract params) */\n  numLocalChallengers: number;\n  /** M in M-of-N council multisig (from contract params) */\n  councilQuorum: number;\n  /** N in M-of-N council multisig (from contract params) */\n  councilSize: number;\n  /** Bitcoin network */\n  network: Network;\n  /**\n   * Optional 32-byte `SHA256(auth_anchor)` commitment (64-char hex, no\n   * `0x` prefix). If provided, the Pre-PegIn tx will include an\n   * `OP_RETURN <PUSH32 authAnchorHash>` output at vout =\n   * `hashlocks.length`, binding the depositor's bearer-token\n   * `auth_anchor` preimage to this Pre-PegIn.\n   */\n  authAnchorHash?: string;\n}\n\n/**\n * Byte length of an `auth_anchor_hash` commitment when encoded as a\n * lowercase hex string (32 bytes → 64 hex chars).\n */\nconst AUTH_ANCHOR_HASH_HEX_LEN = 64;\n\nconst HEX_PATTERN = /^[0-9a-fA-F]+$/;\n\n/**\n * Result of building an unfunded Pre-PegIn transaction\n */\nexport interface PrePeginPsbtResult {\n  /**\n   * Unfunded transaction hex (no inputs, HTLC outputs + optional\n   * auth-anchor OP_RETURN + CPFP anchor).\n   *\n   * The caller is responsible for:\n   * - Selecting UTXOs covering totalOutputValue + network fees\n   * - Funding the transaction (add inputs and change output)\n   * - Calling buildPeginTxFromFundedPrePegin() with the funded tx hex\n   */\n  psbtHex: string;\n  /** Sum of all unfunded outputs — use this for UTXO selection */\n  totalOutputValue: bigint;\n  /**\n   * HTLC output values in satoshis, one per deposit. Each includes\n   * peginAmount + depositorClaimValue + p2aAnchorValue + minPeginFee (the\n   * anchor term is 0 for graph versions without a P2A anchor, 240 for v2/v3).\n   */\n  htlcValues: readonly bigint[];\n  /** HTLC output scriptPubKeys (hex encoded), one per deposit */\n  htlcScriptPubKeys: readonly string[];\n  /** HTLC Taproot addresses, one per deposit */\n  htlcAddresses: readonly string[];\n  /** Pegin amounts in satoshis, one per deposit */\n  peginAmounts: readonly bigint[];\n  /** Depositor claim value computed by WASM from contract parameters */\n  depositorClaimValue: bigint;\n  /**\n   * Vout index of the auth-anchor `OP_RETURN` output if one was\n   * included (i.e. `authAnchorHash` was provided), or `null` if not.\n   * Always equals `htlcValues.length` when present.\n   */\n  authAnchorVout: number | null;\n  /**\n   * Minimum PegIn fee (sats), independently computed and asserted against\n   * `htlcValues`' implied reserve by {@link assertWasmPeginSizing}. Reuse\n   * this instead of recomputing — it is already the cross-checked value.\n   */\n  minPeginFee: bigint;\n}\n\n/**\n * Parameters for building the PegIn transaction from a funded Pre-PegIn tx\n */\nexport interface BuildPeginTxParams {\n  /** Same PrePeginParams used to create the Pre-PegIn transaction */\n  prePeginParams: PrePeginParams;\n  /** CSV timelock in blocks for the PegIn vault output */\n  timelockPegin: number;\n  /** Hex-encoded funded Pre-PegIn transaction */\n  fundedPrePeginTxHex: string;\n  /** Index of the HTLC output to spend */\n  htlcVout: number;\n}\n\n/**\n * Result of building the PegIn transaction\n */\nexport interface PeginTxResult {\n  /**\n   * PegIn transaction hex. 1 input spending the HTLC; outputs are\n   * version-shaped: v1 = vault + depositor claim, v2/v3 = vault + depositor\n   * claim + P2A anchor at vout 2 (nVersion 3 / TRUC).\n   */\n  txHex: string;\n  /** PegIn transaction ID */\n  txid: string;\n  /** Vault output scriptPubKey (hex encoded) */\n  vaultScriptPubKey: string;\n  /** Vault output value in satoshis */\n  vaultValue: bigint;\n}\n\n/**\n * Build unfunded Pre-PegIn transaction using WASM.\n *\n * Creates a Bitcoin transaction template with no inputs, an HTLC output, and a\n * CPFP anchor output. The HTLC value is computed internally from the contract\n * parameters — the caller does not need to compute depositorClaimValue separately.\n *\n * @param params - Pre-PegIn parameters\n * @returns Unfunded Pre-PegIn transaction details with HTLC output information\n * @throws If WASM initialization fails or parameters are invalid\n */\nexport async function buildPrePeginPsbt(\n  params: PrePeginParams,\n): Promise<PrePeginPsbtResult> {\n  const authAnchorHash = normalizeAuthAnchorHash(params.authAnchorHash);\n\n  const result = await createPrePeginTransaction({\n    txGraphVersion: params.vaultCoreVersion,\n    depositorPubkey: params.depositorPubkey,\n    vaultProviderPubkey: params.vaultProviderPubkey,\n    vaultKeeperPubkeys: params.vaultKeeperPubkeys,\n    universalChallengerPubkeys: params.universalChallengerPubkeys,\n    hashlocks: [...params.hashlocks],\n    timelockRefund: params.timelockRefund,\n    pegInAmounts: [...params.pegInAmounts],\n    feeRate: params.feeRate,\n    minPeginFeeRate: params.minPeginFeeRate,\n    numLocalChallengers: params.numLocalChallengers,\n    councilQuorum: params.councilQuorum,\n    councilSize: params.councilSize,\n    network: params.network,\n    authAnchorHash,\n  });\n\n  // CLAUDE.md critical path #1: the WASM outputs reach JS with no runtime\n  // validation. Cross-check every value-bearing field against the request\n  // and the protocol formula before it can feed a signed tx or the on-chain\n  // PegIn registration. Both the sizing and commit passes route through here.\n  const minPeginFee = await assertWasmPeginSizing(result, params);\n\n  // Parse the unfunded tx to sum all output values\n  // (HTLCs + optional OP_RETURN + CPFP anchor). This is the amount\n  // UTXOs must cover before adding network fees.\n  const parsed = parseUnfundedWasmTransaction(result.txHex);\n\n  // Bind the validated metadata to the bytes that get funded and signed:\n  // the encoded HTLC outputs must carry exactly the values/scripts the\n  // cross-check above validated. Otherwise a tx whose real outputs differ\n  // from the checked metadata could still be funded and signed.\n  assertEncodedHtlcOutputsMatch(\n    parsed.outputs,\n    result.htlcValues,\n    result.htlcScriptPubKeys,\n  );\n\n  const totalOutputValue = parsed.outputs.reduce(\n    (sum, o) => sum + BigInt(o.value),\n    0n,\n  );\n\n  // The WASM places the OP_RETURN commitment immediately after the\n  // HTLC outputs when authAnchorHash is provided.\n  const authAnchorVout =\n    authAnchorHash !== undefined ? result.htlcValues.length : null;\n\n  return {\n    psbtHex: result.txHex,\n    totalOutputValue,\n    htlcValues: result.htlcValues,\n    htlcScriptPubKeys: result.htlcScriptPubKeys,\n    htlcAddresses: result.htlcAddresses,\n    peginAmounts: result.peginAmounts,\n    depositorClaimValue: result.depositorClaimValue,\n    authAnchorVout,\n    minPeginFee,\n  };\n}\n\n/**\n * Validate and normalize an `authAnchorHash` hex string before passing\n * it to the WASM boundary. WASM expects exactly 64 lowercase hex chars.\n */\nexport function normalizeAuthAnchorHash(\n  value: string | undefined,\n): string | undefined {\n  if (value === undefined) return undefined;\n  const cleaned =\n    value.startsWith(\"0x\") || value.startsWith(\"0X\") ? value.slice(2) : value;\n  if (\n    cleaned.length !== AUTH_ANCHOR_HASH_HEX_LEN ||\n    !HEX_PATTERN.test(cleaned)\n  ) {\n    throw new Error(\n      `authAnchorHash must be 32-byte hex (${AUTH_ANCHOR_HASH_HEX_LEN} chars, no 0x prefix); got length ${cleaned.length}`,\n    );\n  }\n  return cleaned.toLowerCase();\n}\n\n/**\n * Build the PegIn transaction from a funded Pre-PegIn transaction.\n *\n * The PegIn transaction spends the Pre-PegIn HTLC output at htlcVout via the\n * hashlock + all-party script (leaf 0).\n *\n * @param params - Build parameters including Pre-PegIn params and funded tx hex\n * @returns PegIn transaction details\n * @throws If WASM initialization fails or parameters are invalid\n */\nexport async function buildPeginTxFromFundedPrePegin(\n  params: BuildPeginTxParams,\n): Promise<PeginTxResult> {\n  // WASM reconstructs the Pre-PegIn template from these params to\n  // decode the funded tx. Must pass `authAnchorHash` (normalized\n  // identically to buildPrePeginPsbt) so the reconstruction matches\n  // the original outputs, including the OP_RETURN at vout =\n  // hashlocks.length.\n  const result = await buildPeginTxFromPrePegin(\n    {\n      txGraphVersion: params.prePeginParams.vaultCoreVersion,\n      depositorPubkey: params.prePeginParams.depositorPubkey,\n      vaultProviderPubkey: params.prePeginParams.vaultProviderPubkey,\n      vaultKeeperPubkeys: params.prePeginParams.vaultKeeperPubkeys,\n      universalChallengerPubkeys:\n        params.prePeginParams.universalChallengerPubkeys,\n      hashlocks: [...params.prePeginParams.hashlocks],\n      timelockRefund: params.prePeginParams.timelockRefund,\n      pegInAmounts: [...params.prePeginParams.pegInAmounts],\n      feeRate: params.prePeginParams.feeRate,\n      minPeginFeeRate: params.prePeginParams.minPeginFeeRate,\n      numLocalChallengers: params.prePeginParams.numLocalChallengers,\n      councilQuorum: params.prePeginParams.councilQuorum,\n      councilSize: params.prePeginParams.councilSize,\n      network: params.prePeginParams.network,\n      authAnchorHash: normalizeAuthAnchorHash(\n        params.prePeginParams.authAnchorHash,\n      ),\n    },\n    params.timelockPegin,\n    params.fundedPrePeginTxHex,\n    params.htlcVout,\n  );\n\n  await assertPeginTxShape(result, params);\n\n  return {\n    txHex: result.txHex,\n    txid: result.txid,\n    vaultScriptPubKey: result.vaultScriptPubKey,\n    vaultValue: result.vaultValue,\n  };\n}\n\n/**\n * PegIn outputs common to every graph version: the vault output (vout 0)\n * and the depositor claim output (vout 1). Versions with a P2A anchor (v2)\n * append it after these.\n */\nconst PEGIN_BASE_OUTPUT_COUNT = 2;\n\n/**\n * Vout of the depositor-claim output in every PegIn version (btc-vault:\n * vault at 0, depositor claim at 1, optional P2A anchor appended after).\n */\nconst PEGIN_DEPOSITOR_CLAIM_VOUT = 1;\n\n/**\n * Cross-check the WASM-built PegIn transaction's bytes against the request\n * and the version's expected output layout before the depositor signs it.\n *\n * CLAUDE.md critical path #1: the metadata (`vaultValue`, `txid`,\n * `vaultScriptPubKey`) and the tx bytes both come from WASM — bind them to\n * each other and to the caller's requested amount so a doctored binary\n * can't commit one thing and encode another. The vault output value is the\n * exact on-chain vault amount, so it must equal the requested peg-in amount\n * (btc-vault: PegIn vout 0 carries `pegin_amount` verbatim). The P2A anchor\n * (exact value/vout/script for v2; complete absence for v1) is enforced by\n * the version-dispatched `validatePeginP2aAnchor`.\n *\n * @throws If the encoded outputs disagree with the metadata, the requested\n *   amount, or the version's anchor rules.\n */\nasync function assertPeginTxShape(\n  result: {\n    txHex: string;\n    txid: string;\n    vaultScriptPubKey: string;\n    vaultValue: bigint;\n  },\n  params: BuildPeginTxParams,\n): Promise<void> {\n  const version = params.prePeginParams.vaultCoreVersion;\n\n  await validatePeginP2aAnchor(version, result.txHex);\n\n  const anchor = await peginP2aAnchorOutput(version);\n  const expectedOutputCount = PEGIN_BASE_OUTPUT_COUNT + (anchor ? 1 : 0);\n\n  const peginTx = Transaction.fromHex(stripHexPrefix(result.txHex));\n\n  // Input bind: the PegIn must spend EXACTLY the requested HTLC outpoint\n  // (fundedPrePeginTxid, htlcVout). Without this, a doctored tx could spend\n  // a sibling HTLC while the caller registers it under `htlcVout` — in a\n  // multi-HTLC batch that both double-spends the sibling and strands the\n  // registered vault. The downstream PSBT builder follows the tx's own\n  // input index, so this is the only place the requested vout is enforced.\n  if (peginTx.ins.length !== 1) {\n    throw new Error(\n      `PegIn tx has ${peginTx.ins.length} input(s), expected exactly 1 ` +\n        `(the Pre-PegIn HTLC outpoint).`,\n    );\n  }\n  const fundedPrePeginTxid = Transaction.fromHex(\n    stripHexPrefix(params.fundedPrePeginTxHex),\n  ).getId();\n  const inputTxid = uint8ArrayToHex(\n    new Uint8Array(peginTx.ins[0].hash).slice().reverse(),\n  );\n  if (inputTxid !== fundedPrePeginTxid) {\n    throw new Error(\n      `PegIn input spends txid ${inputTxid}, expected the funded Pre-PegIn ` +\n        `${fundedPrePeginTxid}.`,\n    );\n  }\n  if (peginTx.ins[0].index !== params.htlcVout) {\n    throw new Error(\n      `PegIn input spends Pre-PegIn output ${peginTx.ins[0].index}, ` +\n        `expected the requested HTLC vout ${params.htlcVout}.`,\n    );\n  }\n\n  if (peginTx.outs.length !== expectedOutputCount) {\n    throw new Error(\n      `PegIn tx has ${peginTx.outs.length} output(s), expected exactly ` +\n        `${expectedOutputCount} for vaultCoreVersion ${version} (vault + ` +\n        `depositor claim${anchor ? \" + P2A anchor\" : \"\"}).`,\n    );\n  }\n\n  const requestedAmount =\n    params.prePeginParams.pegInAmounts[params.htlcVout];\n  if (result.vaultValue !== requestedAmount) {\n    throw new Error(\n      `PegIn vault output value ${result.vaultValue} does not match the ` +\n        `requested peg-in amount ${requestedAmount} for HTLC ` +\n        `${params.htlcVout}; refusing to sign a vault that locks a ` +\n        `different amount than requested.`,\n    );\n  }\n\n  const encodedVaultOut = peginTx.outs[0];\n  if (BigInt(encodedVaultOut.value) !== result.vaultValue) {\n    throw new Error(\n      `Encoded PegIn vault output value ${encodedVaultOut.value} does not ` +\n        `match the WASM-reported vaultValue ${result.vaultValue}.`,\n    );\n  }\n  const encodedVaultScript = encodedVaultOut.script.toString(\"hex\");\n  const expectedVaultScript = stripHexPrefix(\n    result.vaultScriptPubKey,\n  ).toLowerCase();\n  if (encodedVaultScript.toLowerCase() !== expectedVaultScript) {\n    throw new Error(\n      `Encoded PegIn vault output scriptPubKey ${encodedVaultScript} does ` +\n        `not match the WASM-reported vaultScriptPubKey ` +\n        `${result.vaultScriptPubKey}.`,\n    );\n  }\n\n  const encodedTxid = peginTx.getId();\n  if (encodedTxid !== stripHexPrefix(result.txid).toLowerCase()) {\n    throw new Error(\n      `Encoded PegIn txid ${encodedTxid} does not match the WASM-reported ` +\n        `txid ${result.txid}.`,\n    );\n  }\n\n  // Depositor-claim output (vout 1): a doctored binary could burn the claim\n  // into miner fee or redirect it while the vault, anchor, and txid binds\n  // all pass. Bind its value to the independently recomputed minimum claim\n  // value (the same WASM-vs-WASM identity `assertWasmPeginSizing` uses) and\n  // its script to a fully JS-derived expectation. With the vault bind, the\n  // output count, and value conservation against the validated HTLC input,\n  // the implied fee is bound too.\n  const expectedClaimValue = await computeMinClaimValue(\n    version,\n    params.prePeginParams.numLocalChallengers,\n    params.prePeginParams.universalChallengerPubkeys.length,\n    params.prePeginParams.councilQuorum,\n    params.prePeginParams.councilSize,\n    params.prePeginParams.feeRate,\n  );\n  const encodedClaimOut = peginTx.outs[PEGIN_DEPOSITOR_CLAIM_VOUT];\n  if (BigInt(encodedClaimOut.value) !== expectedClaimValue) {\n    throw new Error(\n      `Encoded PegIn depositor-claim output value ${encodedClaimOut.value} ` +\n        `does not match the independently computed claim value ` +\n        `${expectedClaimValue} for vaultCoreVersion ${version}.`,\n    );\n  }\n\n  const expectedClaimScript = deriveDepositorClaimScriptPubKey(\n    params.prePeginParams.depositorPubkey,\n  );\n  if (!encodedClaimOut.script.equals(expectedClaimScript)) {\n    throw new Error(\n      `Encoded PegIn depositor-claim output scriptPubKey ` +\n        `${encodedClaimOut.script.toString(\"hex\")} does not pay to the ` +\n        `depositor's claim script (expected ` +\n        `${expectedClaimScript.toString(\"hex\")}).`,\n    );\n  }\n}\n\n/**\n * Independently derive the PegIn depositor-claim output's scriptPubKey in\n * JS: a Taproot output with the NUMS internal key and a single\n * `<depositor> OP_CHECKSIG` leaf — btc-vault's `SingleKeyConnector`\n * (`crates/vault/src/connectors/mod.rs`, identical across graph versions).\n * Uses the same `tapInternalPubkey` NUMS constant the HTLC connector pins.\n */\nfunction deriveDepositorClaimScriptPubKey(depositorPubkey: string): Buffer {\n  const claimLeafScript = bscript.compile([\n    Buffer.from(hexToUint8Array(depositorPubkey)),\n    opcodes.OP_CHECKSIG,\n  ]);\n  const { output } = payments.p2tr({\n    internalPubkey: Buffer.from(tapInternalPubkey),\n    scriptTree: { output: claimLeafScript },\n  });\n  if (!output) {\n    throw new Error(\n      \"Failed to derive the depositor-claim P2TR scriptPubKey for PegIn output validation\",\n    );\n  }\n  return output;\n}\n","/**\n * Payout Script Generator Primitive\n *\n * This module provides pure functions for generating payout scripts and taproot information\n * by wrapping the WASM implementation from @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * The payout script is used for signing payout transactions in the vault system.\n * It defines the spending conditions for the vault output, enabling the depositor\n * to authorize payouts during the peg-in flow (Step 3).\n *\n * @remarks\n * This is a low-level primitive. For most use cases, prefer using {@link buildPayoutPsbt}\n * which handles script creation internally. For high-level wallet orchestration, use\n * PayoutManager from the managers module.\n *\n * @see {@link buildPayoutPsbt} - Higher-level function that uses this internally\n *\n * @module primitives/scripts/payout\n */\n\nimport {\n  createPayoutConnector,\n  type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\n/**\n * Parameters for creating a payout script.\n *\n * These parameters define the participants in a vault and are used to generate\n * the taproot script that controls how funds can be spent from the vault.\n */\nexport interface PayoutScriptParams {\n  /**\n   * Vault core (tx-graph) version the vault was registered under — the\n   * vault's stamped on-chain `vaultCoreVersion`. Selects which graph's\n   * payout connector the WASM derives.\n   */\n  vaultCoreVersion: number;\n\n  /**\n   * Depositor's BTC public key (x-only, 64-char hex without 0x prefix).\n   *\n   * This is the user depositing BTC into the vault. The depositor must sign\n   * payout transactions to authorize fund distribution.\n   */\n  depositor: string;\n\n  /**\n   * Vault provider's BTC public key (x-only, 64-char hex without 0x prefix).\n   *\n   * The service provider managing vault operations. Also referred to as\n   * \"claimer\" in the WASM layer.\n   */\n  vaultProvider: string;\n\n  /**\n   * Array of vault keeper BTC public keys (x-only, 64-char hex without 0x prefix).\n   *\n   * Vault keepers participate in vault operations and script spending conditions.\n   */\n  vaultKeepers: string[];\n\n  /**\n   * Array of universal challenger BTC public keys (x-only, 64-char hex without 0x prefix).\n   *\n   * These parties can challenge the vault under certain conditions.\n   */\n  universalChallengers: string[];\n\n  /**\n   * CSV timelock in blocks for the PegIn output.\n   */\n  timelockPegin: number;\n\n  /**\n   * Bitcoin network for script generation.\n   *\n   * Must match the network used for all other vault operations to ensure\n   * address encoding compatibility.\n   */\n  network: Network;\n}\n\n/**\n * Result of creating a payout script.\n *\n * Contains all the taproot-related data needed for constructing and signing\n * payout transactions from the vault.\n */\nexport interface PayoutScriptResult {\n  /**\n   * The payout script hex used in taproot script path spending.\n   *\n   * This is the raw script bytes that define the spending conditions,\n   * encoded as a hexadecimal string. Used when constructing the\n   * tapLeafScript for PSBT signing.\n   */\n  payoutScript: string;\n\n  /**\n   * The taproot script hash (leaf hash) for the payout script.\n   *\n   * This is the tagged hash of the script used in taproot tree construction.\n   * Required for computing the control block during script path spending.\n   */\n  taprootScriptHash: string;\n\n  /**\n   * The full scriptPubKey for the vault output address.\n   *\n   * This is the complete output script (OP_1 <32-byte-key>) that should be\n   * used when creating the vault output in a peg-in transaction.\n   */\n  scriptPubKey: string;\n\n  /**\n   * The vault Bitcoin address derived from the script.\n   *\n   * A human-readable bech32m address (bc1p... for mainnet, tb1p... for testnet/signet)\n   * that can be used to receive funds into the vault.\n   */\n  address: string;\n\n  /**\n   * Serialized control block for Taproot script path spend (hex encoded).\n   *\n   * Computed by the Rust WASM PeginPayoutConnector. Used directly in\n   * tapLeafScript when building payout PSBTs.\n   */\n  payoutControlBlock: string;\n}\n\n/**\n * Create payout script and taproot information using WASM.\n *\n * This is a pure function that wraps the Rust WASM implementation.\n * The payout connector generates the necessary taproot scripts and information\n * required for signing payout transactions.\n *\n * @remarks\n * The generated script encodes spending conditions that require signatures from\n * the depositor and vault provider (or liquidators in challenge scenarios).\n * This script is used internally by {@link buildPayoutPsbt}.\n *\n * @param params - Payout script parameters defining vault participants and network\n * @returns Payout script and taproot information for PSBT construction\n *\n * @see {@link buildPayoutPsbt} - Use this for building complete payout PSBTs\n */\nexport async function createPayoutScript(\n  params: PayoutScriptParams,\n): Promise<PayoutScriptResult> {\n  // Call the WASM wrapper with the correct parameter structure\n  const connector = await createPayoutConnector(\n    {\n      txGraphVersion: params.vaultCoreVersion,\n      depositor: params.depositor,\n      vaultProvider: params.vaultProvider,\n      vaultKeepers: params.vaultKeepers,\n      universalChallengers: params.universalChallengers,\n      timelockPegin: params.timelockPegin,\n    },\n    params.network,\n  );\n\n  return {\n    payoutScript: connector.payoutScript,\n    taprootScriptHash: connector.taprootScriptHash,\n    scriptPubKey: connector.scriptPubKey,\n    address: connector.address,\n    payoutControlBlock: connector.payoutControlBlock,\n  };\n}\n","/**\n * BIP-341 Taproot primitives shared by the PSBT builders and verifiers.\n *\n * Everything here is a byte-level restatement of BIP-341; the spec is the\n * source of truth for each constant and each hash preimage.\n *\n * @see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki\n * @module tbv/core/primitives/utils/taproot\n */\n\nimport * as ecc from \"@bitcoin-js/tiny-secp256k1-asmjs\";\nimport { crypto as bcrypto } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\n// Bitcoin CompactSize (varint) prefix markers — values fixed by the protocol.\n// https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers\nconst COMPACT_SIZE_UINT16_PREFIX = 0xfd; // value in [0xfd, 0xffff] → 0xfd + uint16 LE\nconst COMPACT_SIZE_UINT32_PREFIX = 0xfe; // value in [0x10000, 0xffffffff] → 0xfe + uint32 LE\nconst COMPACT_SIZE_UINT16_MAX = 0xffff;\nconst COMPACT_SIZE_UINT32_MAX = 0xffffffff;\n\n/** BIP-341 tag for the TapLeaf hash. */\nconst TAPLEAF_TAG = \"TapLeaf\";\n/** BIP-341 tag for an internal merkle node of the taptree. */\nconst TAPBRANCH_TAG = \"TapBranch\";\n/** BIP-341 tag for the output-key tweak. */\nconst TAPTWEAK_TAG = \"TapTweak\";\n\n/** BIP-341 control block prefix: 1 leaf-version/parity byte + 32-byte internal key. */\nconst CONTROL_BLOCK_PREFIX_LEN = 33;\n/** Each merkle-path element in a control block is a 32-byte node hash. */\nconst CONTROL_BLOCK_NODE_LEN = 32;\n/** BIP-341 caps the taptree at 128 levels, so the path holds at most 128 nodes. */\nconst CONTROL_BLOCK_MAX_NODES = 128;\n/** Low bit of control-block byte 0 carries the output key's y-parity. */\nconst CONTROL_BLOCK_PARITY_MASK = 0x01;\n/** Remaining bits of control-block byte 0 carry the leaf version. */\nconst CONTROL_BLOCK_LEAF_VERSION_MASK = 0xfe;\n\n/** A P2TR scriptPubKey is `OP_1 <32-byte push>` followed by the output key. */\nconst P2TR_SCRIPT_PUBKEY_PREFIX = Buffer.from([0x51, 0x20]);\n\n/**\n * Encode a length as a Bitcoin CompactSize (varint). Tapscript leaf scripts can\n * exceed 252 bytes (WOTS scripts), so the multi-byte forms are required, not\n * just the single-byte fast path.\n */\nfunction encodeCompactSize(n: number): Buffer {\n  if (n < COMPACT_SIZE_UINT16_PREFIX) {\n    return Buffer.from([n]);\n  }\n  if (n <= COMPACT_SIZE_UINT16_MAX) {\n    const value = Buffer.alloc(2); // uint16, little-endian\n    value.writeUInt16LE(n);\n    return Buffer.concat([Buffer.from([COMPACT_SIZE_UINT16_PREFIX]), value]);\n  }\n  if (n <= COMPACT_SIZE_UINT32_MAX) {\n    const value = Buffer.alloc(4); // uint32, little-endian\n    value.writeUInt32LE(n);\n    return Buffer.concat([Buffer.from([COMPACT_SIZE_UINT32_PREFIX]), value]);\n  }\n  throw new Error(`Script too large to encode as CompactSize: ${n} bytes`);\n}\n\n/**\n * Compute the BIP-341 TapLeaf hash for a tapscript leaf:\n * `tagged_hash(\"TapLeaf\", leaf_version || compact_size(script) || script)`.\n */\nexport function computeTapLeafHash(\n  leafVersion: number,\n  script: Uint8Array,\n): Buffer {\n  const preimage = Buffer.concat([\n    Buffer.from([leafVersion]),\n    encodeCompactSize(script.length),\n    Buffer.from(script),\n  ]);\n  return bcrypto.taggedHash(TAPLEAF_TAG, preimage);\n}\n\nexport interface TaprootScriptPathBinding {\n  /** Tapscript leaf version, e.g. `0xc0`. */\n  leafVersion: number;\n  /** The tapscript leaf being spent. */\n  script: Uint8Array;\n  /** BIP-341 control block: version/parity byte, internal key, merkle path. */\n  controlBlock: Uint8Array;\n}\n\n/**\n * Recompute the P2TR scriptPubKey that a `(leafVersion, script, controlBlock)`\n * triple can spend, by walking the control block's merkle path to the taptree\n * root and tweaking the control block's internal key with it (BIP-341\n * \"Script validation rules\").\n *\n * Comparing the result against a real previous output is what proves the triple\n * belongs to that output rather than to some other taptree.\n *\n * @throws If the control block is malformed, the leaf version disagrees with\n *   the control block, the tweak is off-curve, or the recovered output key's\n *   parity contradicts the control block.\n */\nexport function computeTaprootScriptPubKey(\n  binding: TaprootScriptPathBinding,\n): Buffer {\n  const { leafVersion, script, controlBlock } = binding;\n\n  const pathLen = controlBlock.length - CONTROL_BLOCK_PREFIX_LEN;\n  if (\n    pathLen < 0 ||\n    pathLen % CONTROL_BLOCK_NODE_LEN !== 0 ||\n    pathLen / CONTROL_BLOCK_NODE_LEN > CONTROL_BLOCK_MAX_NODES\n  ) {\n    throw new Error(\n      `Malformed Taproot control block: length ${controlBlock.length} must be ` +\n        `${CONTROL_BLOCK_PREFIX_LEN} + 32*m with 0 <= m <= ${CONTROL_BLOCK_MAX_NODES}`,\n    );\n  }\n\n  const controlLeafVersion = controlBlock[0] & CONTROL_BLOCK_LEAF_VERSION_MASK;\n  if (controlLeafVersion !== leafVersion) {\n    throw new Error(\n      `Taproot control block leaf version 0x${controlLeafVersion.toString(16)} ` +\n        `does not match the tapLeafScript leaf version 0x${leafVersion.toString(16)}`,\n    );\n  }\n\n  const internalKey = Buffer.from(\n    controlBlock.subarray(1, CONTROL_BLOCK_PREFIX_LEN),\n  );\n\n  // Fold the merkle path into the taptree root; siblings are hashed in\n  // lexicographic order (BIP-341).\n  let node = computeTapLeafHash(leafVersion, script);\n  for (\n    let offset = CONTROL_BLOCK_PREFIX_LEN;\n    offset < controlBlock.length;\n    offset += CONTROL_BLOCK_NODE_LEN\n  ) {\n    const sibling = Buffer.from(\n      controlBlock.subarray(offset, offset + CONTROL_BLOCK_NODE_LEN),\n    );\n    node = bcrypto.taggedHash(\n      TAPBRANCH_TAG,\n      Buffer.compare(node, sibling) <= 0\n        ? Buffer.concat([node, sibling])\n        : Buffer.concat([sibling, node]),\n    );\n  }\n\n  const tweak = bcrypto.taggedHash(\n    TAPTWEAK_TAG,\n    Buffer.concat([internalKey, node]),\n  );\n  const tweaked = ecc.xOnlyPointAddTweak(internalKey, tweak);\n  if (tweaked === null) {\n    throw new Error(\n      \"Taproot control block does not yield a valid output key (tweak is off-curve)\",\n    );\n  }\n  const expectedParity = controlBlock[0] & CONTROL_BLOCK_PARITY_MASK;\n  if (tweaked.parity !== expectedParity) {\n    throw new Error(\n      `Taproot output key parity ${tweaked.parity} contradicts the control ` +\n        `block's parity bit ${expectedParity}`,\n    );\n  }\n\n  return Buffer.concat([\n    P2TR_SCRIPT_PUBKEY_PREFIX,\n    Buffer.from(tweaked.xOnlyPubkey),\n  ]);\n}\n","/**\n * Bands a VP-built payout's implicit fee before the depositor pre-signs it.\n * Ceiling (ours, #2105) blocks a VP deflating outputs and burning the\n * difference as miner fee; floor (vault-wasm `computePayoutFeeFloor`) rejects\n * a fee no legitimate VP model produces and that may not relay at redemption.\n *\n * Caller (`buildPayoutPsbt`) must have run `assertPayoutFeeBandDomain` first,\n * and `implicitFeeSats` must be `inputs − outputs` over verified prevouts.\n * Band non-emptiness is proven by the corner sweep in the tests, not by a\n * closed-form argument — slack is not monotone at small rosters.\n *\n * @module primitives/psbt/assertPayoutFeeBand\n */\n\nimport { computePayoutFeeFloor } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\n/**\n * Floor-model limit, NOT a protocol bound: vault-wasm seeds dummy keys from a\n * u8, so 257 keepers trap in `VaultKeepers::new` (measured). The contract sets\n * no keeper maximum — a larger roster is an upstream vault-wasm fix.\n */\nconst MAX_FLOOR_MODEL_KEEPERS = 256;\n\n/**\n * Mirrors `vault-contracts-aave-v4 ProtocolParams.sol:22`\n * MAX_UNIVERSAL_CHALLENGERS_SIZE. Safe to copy\n * rather than read: it is a bytecode `constant`, not a governance param, so it\n * cannot move without a redeploy — re-verify it on the next one.\n */\nconst MAX_UNIVERSAL_CHALLENGERS = 1500;\n\n/** Floor-model limit, same u8 dummy-seeding cause as the keeper cap (measured). */\nconst MAX_FLOOR_MODEL_COUNCIL_SIZE = 256;\n\n/**\n * Coarse garbage-input guard, far above the contract's own cap\n * (`vault-contracts-aave-v4 ProtocolParams.sol:44` MAX_FEE_RATE_SAT_VB = 1000).\n */\nconst MAX_SANE_FEE_RATE_SAT_PER_VB = 0xffffffffn;\n\n/**\n * Ceiling vsize model `BASE + PER_PARTICIPANT * (N + M)` — ~13%+ headroom over\n * the exact vsize the VP pays. Values adopted per #2105; kept at parity with\n * the Ledger app's bound so device-signable and SDK-acceptable stay aligned.\n */\nconst MAX_PAYOUT_VSIZE_BASE = 500;\nconst MAX_PAYOUT_VSIZE_PER_PARTICIPANT = 55;\n\n/** P2TR length the model assumes; a longer pinned outs[0] extends the ceiling linearly. */\nconst PAYOUT_BOUND_ASSUMED_SCRIPT_LEN = 34;\n\n/** Shape/rate inputs the fee band is evaluated over. */\nexport interface PayoutFeeBandParams {\n  /** Vault core (tx-graph) version — selects the floor's pinned model set. */\n  vaultCoreVersion: number;\n  /** Vault keeper count (N). */\n  numVaultKeepers: number;\n  /** Universal challenger count (M). */\n  numUniversalChallengers: number;\n  /** Security council size from the locked offchain params version. */\n  councilSize: number;\n  /** Version-locked tx-graph fee rate (sat/vB); anchors both band ends. */\n  protocolFeeRate: bigint;\n}\n\n/**\n * Validate the fee-band inputs lie in the accepted domain. Bounds differ in\n * KIND per role — see each constant.\n *\n * @throws If the rate, a participant count, or `councilSize` is out of range\n */\nexport function assertPayoutFeeBandDomain(params: PayoutFeeBandParams): void {\n  if (\n    typeof params.protocolFeeRate !== \"bigint\" ||\n    params.protocolFeeRate <= 0n ||\n    params.protocolFeeRate > MAX_SANE_FEE_RATE_SAT_PER_VB\n  ) {\n    throw new Error(\n      `protocolFeeRate must be in [1, ${MAX_SANE_FEE_RATE_SAT_PER_VB}] sat/vB, ` +\n        `got ${params.protocolFeeRate}`,\n    );\n  }\n  // Lower bound 1: btc-vault's VaultKeepers::new / UniversalChallengers::new\n  // reject an empty set (crates/vault/src/lib.rs).\n  for (const [role, count, max, reason] of [\n    [\n      \"keepers\",\n      params.numVaultKeepers,\n      MAX_FLOOR_MODEL_KEEPERS,\n      \"fee-floor model limit\",\n    ],\n    [\n      \"challengers\",\n      params.numUniversalChallengers,\n      MAX_UNIVERSAL_CHALLENGERS,\n      \"protocol maximum\",\n    ],\n  ] as const) {\n    if (!Number.isInteger(count) || count < 1 || count > max) {\n      // Only an over-max count is attributable to the bound's reason.\n      throw new Error(\n        `Participant count for ${role} (${count}) is outside the supported ` +\n          `range [1, ${max}]${count > max ? ` (${reason})` : \"\"}.`,\n      );\n    }\n  }\n  // Unvalidated at the WASM boundary: a non-integer truncates at the u32 ABI\n  // and 0 is silently promoted to a 1-member council by the estimator.\n  if (\n    !Number.isInteger(params.councilSize) ||\n    params.councilSize < 1 ||\n    params.councilSize > MAX_FLOOR_MODEL_COUNCIL_SIZE\n  ) {\n    throw new Error(\n      `councilSize must be an integer in ` +\n        `[1, ${MAX_FLOOR_MODEL_COUNCIL_SIZE}], got ${params.councilSize}`,\n    );\n  }\n}\n\n/**\n * Assert `floor <= implicitFeeSats <= ceiling`. `out1Len` is `undefined` for\n * the 2-output (no-commission) layouts.\n *\n * @throws If the fee is below the floor or above the ceiling\n */\nexport async function assertPayoutFeeInBand(\n  params: PayoutFeeBandParams,\n  measured: {\n    implicitFeeSats: number;\n    out0Len: number;\n    out1Len: number | undefined;\n  },\n): Promise<void> {\n  const { implicitFeeSats, out0Len, out1Len } = measured;\n  const numParticipants =\n    params.numVaultKeepers + params.numUniversalChallengers;\n  const implicitFee = BigInt(implicitFeeSats);\n\n  // Ceiling first: synchronous, no WASM round-trip. Only outs[0] widens it —\n  // including the VP-controlled out1Len would hand a padded commission script\n  // up to 94 vB x rate of extra burnable band.\n  const scriptExcess = Math.max(0, out0Len - PAYOUT_BOUND_ASSUMED_SCRIPT_LEN);\n  const maxPayoutVsize =\n    MAX_PAYOUT_VSIZE_BASE +\n    MAX_PAYOUT_VSIZE_PER_PARTICIPANT * numParticipants +\n    scriptExcess;\n  const maxFeeSats = params.protocolFeeRate * BigInt(maxPayoutVsize);\n  if (implicitFee > maxFeeSats) {\n    throw new Error(\n      `Payout implicit fee ${implicitFeeSats} sats exceeds the safety cap ` +\n        `of ${maxFeeSats} sats (${params.protocolFeeRate} sat/vB x ` +\n        `${maxPayoutVsize} vB for ${numParticipants} ` +\n        `participants); refusing to sign payout.`,\n    );\n  }\n\n  // Local challengers are exactly the keeper count for every claimer role\n  // (btc-vault graph.rs derive_challengers).\n  const minFeeSats = await computePayoutFeeFloor(\n    params.vaultCoreVersion,\n    params.numVaultKeepers,\n    params.numUniversalChallengers,\n    params.numVaultKeepers,\n    params.councilSize,\n    out0Len,\n    out1Len,\n    params.protocolFeeRate,\n  );\n  if (implicitFee < minFeeSats) {\n    throw new Error(\n      `Payout implicit fee ${implicitFeeSats} sats is below the floor of ` +\n        `${minFeeSats} sats (the smallest fee any known vault-provider ` +\n        `build produces for this shape); refusing to sign payout.`,\n    );\n  }\n}\n","/**\n * Standard-scriptPubKey validation for registry-supplied payout destinations.\n *\n * RFC-006 lets an operator register an arbitrary payout scriptPubKey, so the\n * bytes we pin a `PayoutTx` output against are now chosen by that operator\n * rather than derived by us. Pinning an output to them without checking the\n * shape would let a keeper that registered a garbage or unspendable script get\n * the depositor to pre-sign a payout nobody can ever claim.\n *\n * This bound is ours alone — it is deliberately NOT parity with `vaultd`.\n * `vaultd` uses registry scripts verbatim at graph-build time and documents\n * why: applying a predicate the contract does not apply would block every\n * peg-in for an operator the instant the two disagreed. The registry itself\n * only bounds length to 1..128 bytes, and the registration CLI has an\n * `--allow-non-standard` escape hatch.\n *\n * We hold the stricter line because we occupy a different position: we are the\n * party being asked to *pre-sign* against these bytes, and a depositor\n * signature pinned to a provably unspendable output is not recoverable. The\n * bound is also load-bearing for the payout fee band — capping a standard\n * output at 34 bytes is what lets `assertPayoutFeeBand` reason about `outs[1]`\n * without a separate length cap.\n *\n * The deliberate consequence: an operator that registers a non-standard script\n * via `--allow-non-standard` can have its graphs built by `vaultd` and still be\n * refused here, blocking deposits against that operator in this dApp. That is\n * fail-closed by choice — the alternative is pre-signing into a destination we\n * cannot verify is spendable.\n *\n * @module primitives/psbt/standardPayoutScript\n */\n\nimport { stripHexPrefix } from \"../utils/bitcoin\";\n\n/** `OP_0 <20>` — P2WPKH. */\nconst P2WPKH_LEN = 22;\n/** `OP_0 <32>` — P2WSH. */\nconst P2WSH_LEN = 34;\n/** `OP_1 <32>` — P2TR. */\nconst P2TR_LEN = 34;\n/** `OP_DUP OP_HASH160 <20> OP_EQUALVERIFY OP_CHECKSIG` — P2PKH. */\nconst P2PKH_LEN = 25;\n/** `OP_HASH160 <20> OP_EQUAL` — P2SH. */\nconst P2SH_LEN = 23;\n\nfunction isP2wpkh(s: string): boolean {\n  return s.length === P2WPKH_LEN * 2 && s.startsWith(\"0014\");\n}\nfunction isP2wsh(s: string): boolean {\n  return s.length === P2WSH_LEN * 2 && s.startsWith(\"0020\");\n}\nfunction isP2tr(s: string): boolean {\n  return s.length === P2TR_LEN * 2 && s.startsWith(\"5120\");\n}\nfunction isP2pkh(s: string): boolean {\n  return (\n    s.length === P2PKH_LEN * 2 && s.startsWith(\"76a914\") && s.endsWith(\"88ac\")\n  );\n}\nfunction isP2sh(s: string): boolean {\n  return s.length === P2SH_LEN * 2 && s.startsWith(\"a914\") && s.endsWith(\"87\");\n}\n\n/**\n * Assert a registry-supplied scriptPubKey is a standard, spendable output type\n * (P2TR, P2WPKH, P2WSH, P2PKH, or P2SH).\n *\n * Rejects empty scripts, `OP_RETURN` (provably unspendable), and anything else\n * that is not one of the five standard forms. `label` names the source in the\n * error, e.g. `vault keeper payout script (admin=0x…)`.\n */\nexport function assertStandardPayoutScript(\n  scriptHex: string,\n  label: string,\n): void {\n  const script = stripHexPrefix(scriptHex).toLowerCase();\n\n  if (script.length === 0) {\n    throw new Error(`${label} is empty`);\n  }\n  if (!/^[0-9a-f]+$/.test(script) || script.length % 2 !== 0) {\n    throw new Error(`${label} is not valid hex`);\n  }\n  if (script.startsWith(\"6a\")) {\n    throw new Error(\n      `${label} is an OP_RETURN output, which is provably unspendable`,\n    );\n  }\n\n  if (\n    !isP2tr(script) &&\n    !isP2wpkh(script) &&\n    !isP2wsh(script) &&\n    !isP2pkh(script) &&\n    !isP2sh(script)\n  ) {\n    throw new Error(\n      `${label} is not a standard scriptPubKey ` +\n        `(expected P2TR, P2WPKH, P2WSH, P2PKH, or P2SH; got ${script.length / 2} bytes: ${script})`,\n    );\n  }\n}\n","/**\n * Payout PSBT Builder Primitives\n *\n * This module provides pure functions for building unsigned payout PSBTs and extracting\n * Schnorr signatures from signed PSBTs. It uses WASM-generated scripts from the payout\n * connector and bitcoinjs-lib for PSBT construction.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @module primitives/psbt/payout\n */\n\nimport {\n  getAssertPayoutScriptInfo,\n  tapInternalPubkey,\n  type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Psbt, Transaction, type TxInput, type TxOutput } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\nimport { deriveLocalChallengers } from \"../challengers\";\nimport { createPayoutScript } from \"../scripts/payout\";\nimport {\n  TAPSCRIPT_LEAF_VERSION,\n  deriveBip86ScriptPubKeyHex,\n  hexToUint8Array,\n  isValidHex,\n  stripHexPrefix,\n  uint8ArrayToHex,\n} from \"../utils/bitcoin\";\nimport { computeTaprootScriptPubKey } from \"../utils/taproot\";\nimport {\n  assertPayoutFeeBandDomain,\n  assertPayoutFeeInBand,\n} from \"./assertPayoutFeeBand\";\nimport {\n  ASSERT_PAYOUT_OUTPUT_INDEX,\n  BPS_DENOMINATOR,\n  DEPOSITOR_PAYOUT_INPUT_COUNT,\n  MAX_PAYOUT_SCRIPT_LEN,\n  MAX_VP_COMMISSION_BPS_EXCLUSIVE,\n  NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n  PAYOUT_ANCHOR_DUST_SATS,\n  PAYOUT_TX_LOCKTIME,\n  PAYOUT_TX_VERSION,\n  PEGIN_VAULT_OUTPUT_INDEX,\n  VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n} from \"./constants\";\nimport { assertStandardPayoutScript } from \"./standardPayoutScript\";\n\n/**\n * Number of items in a Taproot script-path spend witness stack for a\n * single-signature script: [signature, script, controlBlock].\n *\n * The current payout script requires exactly one depositor signature. If the\n * protocol evolves to require multiple signatures in the payout script, this\n * invariant and the finalized-PSBT extraction path must be revisited because\n * the first witness item would no longer necessarily be the depositor's.\n */\nconst TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE = 3;\n\n/**\n * Parameters for building an unsigned Payout PSBT\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface PayoutParams {\n  /**\n   * Vault core (tx-graph) version the vault was registered under — the\n   * vault's stamped on-chain `vaultCoreVersion`. Selects which graph's\n   * payout connector scripts are derived.\n   */\n  vaultCoreVersion: number;\n\n  /**\n   * Payout transaction hex (unsigned)\n   * This is the transaction that needs to be signed by the depositor\n   */\n  payoutTxHex: string;\n\n  /**\n   * Assert transaction hex\n   * Payout input 1 references Assert output 0\n   */\n  assertTxHex: string;\n\n  /**\n   * Peg-in transaction hex\n   * This transaction created the vault output that we're spending\n   */\n  peginTxHex: string;\n\n  /**\n   * Depositor's BTC public key (x-only, 64-char hex without 0x prefix)\n   */\n  depositorBtcPubkey: string;\n\n  /**\n   * Vault provider's BTC public key (x-only, 64-char hex)\n   */\n  vaultProviderBtcPubkey: string;\n\n  /**\n   * Vault keeper BTC public keys (x-only, 64-char hex)\n   */\n  vaultKeeperBtcPubkeys: string[];\n\n  /**\n   * Universal challenger BTC public keys (x-only, 64-char hex)\n   */\n  universalChallengerBtcPubkeys: string[];\n\n  /**\n   * CSV timelock in blocks for the PegIn output (btc-vault `timelock_pegin`);\n   * payout input 0's sequence.\n   */\n  timelockPegin: number;\n\n  /**\n   * CSV timelock in blocks on the Assert:0 payout leaf (btc-vault\n   * `timelock_assert`); payout input 1's sequence.\n   */\n  timelockAssert: number;\n\n  /**\n   * Bitcoin network\n   */\n  network: Network;\n\n  /**\n   * Claimer's x-only BTC public key (64-char hex, no prefix). Drives role\n   * inference (VP / depositor-as-claimer / VK-claimer) inside `buildPayoutPsbt`.\n   */\n  claimerBtcPubkey: string;\n\n  /**\n   * On-chain registered depositor payout scriptPubKey (hex, 0x optional).\n   * Expected outs[0].script for VP- and depositor-claimer roles; unused for\n   * VK-claimer (its outs[0].script is derived from `claimerBtcPubkey`).\n   */\n  registeredPayoutScriptPubKey: string;\n\n  /**\n   * VP commission in basis points (`BTCVaultRegistry.vaultProviderCommissionBps`).\n   * Caps the VP-claimer outs[1].value. The protocol minimum is enforced\n   * upstream; here only `0 <= bps < 10_000` is checked, for safe cap math.\n   */\n  commissionBps: number;\n\n  /**\n   * Tx-graph fee rate (sat/vB) the graph was built with — the version-locked\n   * `offchainParams.feeRate` at the vault's stamped `offchainParamsVersion`,\n   * NOT a live read. Anchors both ends of the fee band.\n   */\n  protocolFeeRate: bigint;\n\n  /**\n   * Security council member x-only public keys (hex) from the locked offchain\n   * params version — `getOffchainParamsByVersion(...).securityCouncilKeys`.\n   * The council occupies the last leaf of the Assert:0 taptree\n   * (btc-vault `crates/vault/src/connectors/assert_payout_nopayout_council.rs`),\n   * so the keys are needed to rebuild input 1's payout leaf, and the count\n   * feeds the fee-band domain.\n   */\n  councilMembers: string[];\n\n  /**\n   * M-of-N council quorum from the locked offchain params version —\n   * `getOffchainParamsByVersion(...).councilQuorum`. Shapes the council leaf's\n   * multisig script, and with it the Assert:0 taptree root.\n   */\n  councilQuorum: number;\n\n  /**\n   * RFC-006. Expected `outs[0].script` per vault-keeper claimer, keyed by\n   * lowercased x-only **operation** pubkey (no `0x`), resolved from\n   * `ApplicationRegistry.getPayoutScriptAtEpoch` at the vault's frozen\n   * `appKeeperKeyEpoch`.\n   *\n   * Every VK claimer must be present: a claimer missing from the map is an\n   * error rather than a cue to derive BIP-86, because a gap means resolution\n   * was incomplete and we do not know what that keeper registered.\n   *\n   * Each entry accepts either that registered script or the BIP-86 default of\n   * the same bonded key, so graphs built before btc-vault#2440 remain signable\n   * — see {@link acceptedPayoutScriptHexes} for why that is required and when\n   * it can be dropped.\n   */\n  vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n\n  /**\n   * RFC-006. Expected `outs[1].script` for the VP-claimer commission output,\n   * from `BTCVaultRegistry.getPayoutScriptAtEpoch` at the vault's frozen\n   * `vpKeyEpoch`. The BIP-86 default of the bonded VP key is accepted alongside\n   * it — see {@link acceptedPayoutScriptHexes}.\n   */\n  vpCommissionScriptPubKey: string;\n}\n\n/**\n * Result of building an unsigned payout PSBT\n */\nexport interface PayoutPsbtResult {\n  /**\n   * Unsigned PSBT hex ready for signing\n   */\n  psbtHex: string;\n}\n\n/**\n * Build unsigned Payout PSBT for depositor to sign.\n *\n * Payout is used in the **challenge path** when the claimer proves validity:\n * 1. Vault provider submits Claim transaction\n * 2. Challenge is raised during challenge period\n * 3. Claimer submits Assert transaction to prove validity\n * 4. Payout can be executed (references Assert tx)\n *\n * Payout transactions have the following structure:\n * - Input 0: from PeginTx output0 (signed by depositor)\n * - Input 1: from Assert output0 (NOT signed by depositor)\n *\n * Both inputs carry their taproot script-path leaf. Input 1's is not signed\n * here — it is what a hardware signer reads to display the payout terms.\n *\n * @param params - Payout parameters\n * @returns Unsigned PSBT ready for depositor to sign\n *\n * @throws If payout transaction does not have exactly 2 inputs\n * @throws If input 0 does not spend PegIn:0 (vault UTXO)\n * @throws If input 1 does not spend Assert:0 (proof output)\n * @throws If previous output is not found for either input\n * @throws If sum of output values exceeds sum of input values (invalid tx)\n * @throws If the implicit fee (inputs − outputs) is outside the fee band —\n *   below the floor or above the fee-band ceiling (see\n *   {@link assertPayoutFeeInBand})\n * @throws If `protocolFeeRate`, a participant count, or the council size is\n *   outside the accepted input domain (see {@link assertPayoutFeeBandDomain})\n * @throws If a non-anchor scriptPubKey length is outside `[1,\n *   {@link MAX_PAYOUT_SCRIPT_LEN}]`\n * @throws If `claimerBtcPubkey` is not VP, depositor, or a registered VK\n * @throws If payout output count, outs[0] script, outs[last] anchor value, or\n *   (VP-claimer) outs[1] commission cap do not match the protocol layout\n * @throws If `commissionBps` is not a non-negative integer below 10_000\n * @throws If the locally rebuilt Assert:0 payout leaf does not bind to the\n *   Assert output input 1 spends\n */\nexport async function buildPayoutPsbt(\n  params: PayoutParams,\n): Promise<PayoutPsbtResult> {\n  const feeBandParams = {\n    vaultCoreVersion: params.vaultCoreVersion,\n    numVaultKeepers: params.vaultKeeperBtcPubkeys.length,\n    numUniversalChallengers: params.universalChallengerBtcPubkeys.length,\n    councilSize: params.councilMembers.length,\n    protocolFeeRate: params.protocolFeeRate,\n  };\n  assertPayoutFeeBandDomain(feeBandParams);\n\n  const payoutTx = Transaction.fromHex(stripHexPrefix(params.payoutTxHex));\n  const peginTx = Transaction.fromHex(stripHexPrefix(params.peginTxHex));\n  const assertTx = Transaction.fromHex(stripHexPrefix(params.assertTxHex));\n\n  if (payoutTx.ins.length !== DEPOSITOR_PAYOUT_INPUT_COUNT) {\n    throw new Error(\n      `Payout transaction must have exactly ${DEPOSITOR_PAYOUT_INPUT_COUNT} ` +\n        `inputs, got ${payoutTx.ins.length}`,\n    );\n  }\n  // btc-vault builds every payout with these literals (payout.rs:154-155);\n  // the sighash commits to them, so refuse rather than sign a foreign shape.\n  if (payoutTx.version !== PAYOUT_TX_VERSION) {\n    throw new Error(\n      `Payout transaction version ${payoutTx.version} must be ` +\n        `${PAYOUT_TX_VERSION}; refusing to sign payout.`,\n    );\n  }\n  if (payoutTx.locktime !== PAYOUT_TX_LOCKTIME) {\n    throw new Error(\n      `Payout transaction locktime ${payoutTx.locktime} must be ` +\n        `${PAYOUT_TX_LOCKTIME}; refusing to sign payout.`,\n    );\n  }\n  // Input sequences carry the CSV timelocks (payout.rs:108,119). Each is\n  // checked after its own outpoint resolves so a wrong prevout reports itself.\n  const peginPrevOut = requirePrevOut(\n    payoutTx.ins[0],\n    0,\n    peginTx,\n    \"PegIn\",\n    PEGIN_VAULT_OUTPUT_INDEX,\n  );\n  if (payoutTx.ins[0].sequence !== params.timelockPegin) {\n    throw new Error(\n      `Payout input 0 sequence ${payoutTx.ins[0].sequence} must equal the ` +\n        `PegIn CSV timelock ${params.timelockPegin}; refusing to sign payout.`,\n    );\n  }\n\n  const assertPrevOut = requirePrevOut(\n    payoutTx.ins[1],\n    1,\n    assertTx,\n    \"Assert\",\n    ASSERT_PAYOUT_OUTPUT_INDEX,\n  );\n  if (payoutTx.ins[1].sequence !== params.timelockAssert) {\n    throw new Error(\n      `Payout input 1 sequence ${payoutTx.ins[1].sequence} must equal the ` +\n        `Assert CSV timelock ${params.timelockAssert}; refusing to sign payout.`,\n    );\n  }\n  // Assert:0's value is deliberately NOT validated: the taproot sighash\n  // commits to input 1's amount and outpoint, so a misstated value yields a\n  // signature invalid against the real Assert tx — nothing to protect. (The\n  // device pins the band [546, 546 + base_fee_rate*500] here, Q15.)\n\n  // Per-role output validation — blocks an extra attacker output or value\n  // routed into a non-payout slot. Returns the layout-trusted script lengths\n  // the fee band consumes.\n  const { out0Len, out1Len } = assertPayoutOutputLayout(\n    payoutTx,\n    peginPrevOut.value,\n    params,\n  );\n\n  const inputValueSats = peginPrevOut.value + assertPrevOut.value;\n  let outputValueSats = 0;\n  for (const out of payoutTx.outs) outputValueSats += out.value;\n  if (outputValueSats > inputValueSats) {\n    throw new Error(\n      `Payout outputs (${outputValueSats} sats) exceed inputs ` +\n        `(${inputValueSats} sats); invalid transaction.`,\n    );\n  }\n  const implicitFeeSats = inputValueSats - outputValueSats;\n  await assertPayoutFeeInBand(feeBandParams, {\n    implicitFeeSats,\n    out0Len,\n    out1Len,\n  });\n\n  // Only assembly needs the WASM-derived scripts — derive them after the\n  // transaction has passed every validation gate.\n  const payoutConnector = await createPayoutScript({\n    vaultCoreVersion: params.vaultCoreVersion,\n    depositor: params.depositorBtcPubkey,\n    vaultProvider: params.vaultProviderBtcPubkey,\n    vaultKeepers: params.vaultKeeperBtcPubkeys,\n    universalChallengers: params.universalChallengerBtcPubkeys,\n    timelockPegin: params.timelockPegin,\n    network: params.network,\n  });\n\n  const assertPayoutLeaf = await resolveAssertPayoutLeaf(params, assertPrevOut);\n\n  return {\n    psbtHex: assemblePayoutPsbt(\n      payoutTx,\n      peginPrevOut,\n      assertPrevOut,\n      payoutConnector,\n      assertPayoutLeaf,\n    ),\n  };\n}\n\n/** The Assert:0 payout tapscript leaf, as attached to payout input 1. */\ninterface AssertPayoutLeaf {\n  script: Uint8Array;\n  controlBlock: Uint8Array;\n}\n\n/**\n * Rebuild the Assert:0 payout leaf from the vault's on-chain participant set\n * and assert it spends `assertPrevOut`.\n *\n * The depositor does not sign input 1, but the Ledger vault app reads this leaf\n * off the PSBT to display the payout terms\n * (`sign_psbt_validate.c::vault_read_payout_leaf_script`), so a leaf that\n * belonged to a different taptree would show the depositor terms the\n * transaction cannot actually enforce. Critical Path #3: derive, then bind to\n * the real previous output — never forward VP-supplied bytes.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nasync function resolveAssertPayoutLeaf(\n  params: PayoutParams,\n  assertPrevOut: TxOutput,\n): Promise<AssertPayoutLeaf> {\n  const { payoutScript, payoutControlBlock } = await getAssertPayoutScriptInfo({\n    txGraphVersion: params.vaultCoreVersion,\n    claimer: stripHexPrefix(params.claimerBtcPubkey).toLowerCase(),\n    localChallengers: deriveLocalChallengers({\n      claimerBtcPubkey: params.claimerBtcPubkey,\n      depositorBtcPubkey: params.depositorBtcPubkey,\n      vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n      vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n    }),\n    universalChallengers: params.universalChallengerBtcPubkeys.map((k) =>\n      stripHexPrefix(k).toLowerCase(),\n    ),\n    timelockAssert: params.timelockAssert,\n    councilMembers: params.councilMembers.map((k) =>\n      stripHexPrefix(k).toLowerCase(),\n    ),\n    councilQuorum: params.councilQuorum,\n  });\n\n  const script = hexToUint8Array(payoutScript);\n  const controlBlock = hexToUint8Array(payoutControlBlock);\n  const boundScriptPubKey = computeTaprootScriptPubKey({\n    leafVersion: TAPSCRIPT_LEAF_VERSION,\n    script,\n    controlBlock,\n  });\n  if (!assertPrevOut.script.equals(boundScriptPubKey)) {\n    throw new Error(\n      `Rebuilt Assert:0 payout leaf does not spend the Assert output being ` +\n        `referenced: leaf binds to ${boundScriptPubKey.toString(\"hex\")}, ` +\n        `Assert:${ASSERT_PAYOUT_OUTPUT_INDEX} pays ` +\n        `${assertPrevOut.script.toString(\"hex\")}; refusing to sign payout.`,\n    );\n  }\n\n  return { script, controlBlock };\n}\n\n/**\n * Verify `payoutTx.ins[inputIndex]` spends `parentTx:expectedVout` (both txid\n * AND vout — the vout is the input-side anchor that prevents a malicious VP\n * from binding the depositor's signature to a different output of the same\n * parent) and return the referenced previous output.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction requirePrevOut(\n  input: TxInput,\n  inputIndex: number,\n  parentTx: Transaction,\n  parentLabel: string,\n  expectedVout: number,\n): TxOutput {\n  const inputTxid = uint8ArrayToHex(\n    new Uint8Array(input.hash).slice().reverse(),\n  );\n  const parentTxid = parentTx.getId();\n  if (inputTxid !== parentTxid || input.index !== expectedVout) {\n    throw new Error(\n      `Input ${inputIndex} must spend ${parentLabel}:${expectedVout}. ` +\n        `Expected ${parentTxid}:${expectedVout}, got ${inputTxid}:${input.index}`,\n    );\n  }\n  const prevOut = parentTx.outs[input.index];\n  if (!prevOut) {\n    throw new Error(\n      `Previous output not found for input ${inputIndex} ` +\n        `(txid: ${inputTxid}, index: ${input.index})`,\n    );\n  }\n  return prevOut;\n}\n\n/**\n * Assemble the unsigned PSBT from the validated payout transaction.\n *\n * IMPORTANT: For Taproot SIGHASH_DEFAULT (0x00), the sighash commits to ALL\n * inputs' prevouts, not just the one being signed — both inputs must be in\n * the PSBT so the wallet computes the sighash the VP expects.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assemblePayoutPsbt(\n  payoutTx: Transaction,\n  peginPrevOut: TxOutput,\n  assertPrevOut: TxOutput,\n  payoutConnector: { payoutScript: string; payoutControlBlock: string },\n  assertPayoutLeaf: AssertPayoutLeaf,\n): string {\n  const psbt = new Psbt();\n  psbt.setVersion(payoutTx.version);\n  psbt.setLocktime(payoutTx.locktime);\n\n  const [input0, input1] = payoutTx.ins;\n\n  // Input 0: depositor signs via Taproot script path — carries tapLeafScript.\n  psbt.addInput({\n    hash: input0.hash,\n    index: input0.index,\n    sequence: input0.sequence,\n    witnessUtxo: {\n      script: peginPrevOut.script,\n      value: peginPrevOut.value,\n    },\n    tapLeafScript: [\n      {\n        leafVersion: TAPSCRIPT_LEAF_VERSION,\n        script: Buffer.from(hexToUint8Array(payoutConnector.payoutScript)),\n        controlBlock: Buffer.from(\n          hexToUint8Array(payoutConnector.payoutControlBlock),\n        ),\n      },\n    ],\n    tapInternalKey: Buffer.from(tapInternalPubkey),\n    // sighashType omitted - defaults to SIGHASH_DEFAULT (0x00) for Taproot\n  });\n\n  // Input 1: from Assert — not signed by the depositor, but the Ledger vault\n  // app reads the payout leaf here to display the terms, so it must be present.\n  psbt.addInput({\n    hash: input1.hash,\n    index: input1.index,\n    sequence: input1.sequence,\n    witnessUtxo: {\n      script: assertPrevOut.script,\n      value: assertPrevOut.value,\n    },\n    tapLeafScript: [\n      {\n        leafVersion: TAPSCRIPT_LEAF_VERSION,\n        script: Buffer.from(assertPayoutLeaf.script),\n        controlBlock: Buffer.from(assertPayoutLeaf.controlBlock),\n      },\n    ],\n    tapInternalKey: Buffer.from(tapInternalPubkey),\n  });\n\n  for (const output of payoutTx.outs) {\n    psbt.addOutput({\n      script: output.script,\n      value: output.value,\n    });\n  }\n\n  return psbt.toHex();\n}\n\n/**\n * The scriptPubKeys a payout output may legitimately pay for `bondedPubkey`.\n *\n * ## What this is\n *\n * A deliberate dual-accept: both the operator's RFC-006 **registered** payout\n * script and the **BIP-86 default** of its bonded operation key are treated as\n * valid destinations for the same output.\n *\n * ## Why both forms exist\n *\n * A vault provider running a daemon that predates btc-vault#2440 computes\n * operator payout destinations itself, via BIP-86 over the bonded operation\n * key. From #2440 onward it reads them from the registry instead\n * (`getPayoutScriptAtEpoch`), so an operator can point payouts at cold custody.\n *\n * The two forms are not a transition state that resolves on its own. A payout\n * graph is built once, at BaBe Setup, and is **never rebuilt** — so a vault\n * whose graph was built before its network's #2440 upgrade pays BIP-86 for the\n * rest of its life. Accepting only the registered form makes every such vault\n * permanently unsignable: the depositor can never complete the deposit, and\n * the BTC is already committed by then. Confirmed on devnet 2026-08-04, where\n * a pre-upgrade vault failed exactly this way.\n *\n * ## Why this is not a weakening\n *\n * Both candidates are derived here from on-chain state — the registry read and\n * a local BIP-86 derivation over the bonded key. Neither is taken from the VP's\n * response, so a substituted or attacker-chosen script still fails.\n *\n * The only substitution this permits is between two destinations the *same\n * operator* already controls, which moves that operator's own funds between its\n * own addresses. It cannot redirect value to a third party, and it cannot touch\n * the depositor's output. For the VP commission the value cap\n * (`floor(peginValue × commissionBps / 10_000)`) still bounds depositor\n * exposure independently of where the commission goes. The BIP-86 branch is\n * also precisely what every pre-RFC-006 client pinned, so this is the older\n * rule retained alongside the newer one, not a laxer rule invented for it.\n *\n * ## When this can be removed\n *\n * This validation runs at one point in the lifecycle: depositor payout signing\n * (`PENDING` → `VERIFIED`). It is not re-run on refund or withdrawal. So the\n * BIP-86 branch stops being reachable for a network once every vault\n * registered before that network's #2440 upgrade has left\n * `PendingDepositorSignatures` — signed and activated, or expired past its\n * activation deadline. That is bounded by the vault lifetime, not indefinite.\n *\n * Removal is therefore gated by the **last** network to upgrade, since this\n * code is shared. Concretely: drop the BIP-86 branch only once devnet, testnet\n * and mainnet have all been on #2440 for longer than the activation deadline,\n * and no vault registered before those upgrades is still awaiting signatures.\n * Deleting it earlier strands deposits with BTC already locked; there is no\n * recovery path short of the refund timelock.\n *\n * @internal Helper invoked by {@link assertPayoutOutputLayout}.\n */\nfunction acceptedPayoutScriptHexes(\n  registeredScriptPubKey: string,\n  bondedPubkey: string,\n): string[] {\n  const registered = stripHexPrefix(registeredScriptPubKey).toLowerCase();\n  const legacyBip86 = stripHexPrefix(\n    deriveBip86ScriptPubKeyHex(bondedPubkey),\n  ).toLowerCase();\n  return registered === legacyBip86 ? [registered] : [registered, legacyBip86];\n}\n\n/** Whether `script` matches any of `acceptedHexes`. */\nfunction matchesAnyScript(script: Buffer, acceptedHexes: string[]): boolean {\n  return acceptedHexes.some((hex) => script.equals(Buffer.from(hex, \"hex\")));\n}\n\n/**\n * Validate a payout transaction's output structure for the claimer's role,\n * keyed on `claimerBtcPubkey`. Pins per role: `outs.length`, `outs[0].script`,\n * `outs[last].value` (anchor dust), and — for the VP-claimer —\n * `outs[1].script` plus `outs[1].value`, capped at\n * `floor(peginValue × commissionBps / 10_000)`. Canonical layouts: VP-claimer\n * = [payout, commission, anchor]; depositor/VK-claimer = [payout, anchor].\n *\n * `outs[last].script` (the CPFP anchor) is intentionally not pinned: the value\n * pin above bounds depositor exposure regardless of where that dust goes.\n *\n * `outs[1].script` (the VP commission) was unpinned for the same reason.\n * RFC-006 supersedes that rationale: the commission destination is now an\n * operator-registered scriptPubKey we resolve independently at the vault's\n * frozen `vpKeyEpoch`, so it is pinned too — `vpCommissionScriptPubKey` is a\n * required parameter, so there is no unpinned case. The value cap stays — it is\n * still what bounds exposure — and the script pin is added precision, not a\n * replacement for it.\n *\n * What counts as a match differs by output.\n * {@link acceptedPayoutScriptHexes} accepts two candidates — the registered\n * scriptPubKey and the BIP-86 derivation over the bonded key, the latter as a\n * transitional allowance — and backs the VK-claimer's `outs[0]` and the VP\n * commission `outs[1]`. The VP-claimer and depositor-as-claimer `outs[0]` is\n * pinned to the registered payout script alone.\n *\n * Returns the layout-trusted non-anchor script lengths for the fee band:\n * `out0Len` from the pinned `outs[0]` script, and `out1Len` from the pinned\n * VP-claimer commission output, `undefined` for the other roles. Only\n * `out0Len` is range-checked, against the contract's registration cap\n * `[1, MAX_PAYOUT_SCRIPT_LEN]`; `outs[1]` carries no length cap of its own\n * because `assertStandardPayoutScript` already bounds it to a standard output\n * type, which makes the cap unreachable.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assertPayoutOutputLayout(\n  payoutTx: Transaction,\n  peginValueSats: number,\n  params: PayoutParams,\n): { out0Len: number; out1Len: number | undefined } {\n  const {\n    claimerBtcPubkey,\n    vaultProviderBtcPubkey,\n    depositorBtcPubkey,\n    vaultKeeperBtcPubkeys,\n    registeredPayoutScriptPubKey,\n    commissionBps,\n    vkClaimerPayoutScriptPubKeys,\n    vpCommissionScriptPubKey,\n  } = params;\n\n  if (!isValidHex(registeredPayoutScriptPubKey)) {\n    throw new Error(\"Invalid registeredPayoutScriptPubKey: not valid hex\");\n  }\n\n  const claimer = stripHexPrefix(claimerBtcPubkey).toLowerCase();\n  const vp = stripHexPrefix(vaultProviderBtcPubkey).toLowerCase();\n  const dep = stripHexPrefix(depositorBtcPubkey).toLowerCase();\n  const keepers = vaultKeeperBtcPubkeys.map((k) =>\n    stripHexPrefix(k).toLowerCase(),\n  );\n\n  type Role = \"vp-claimer\" | \"depositor-as-claimer\" | \"vk-claimer\";\n  let role: Role;\n  let expectedOutCount: number;\n  let acceptedOut0ScriptHexes: string[];\n\n  if (claimer === vp) {\n    role = \"vp-claimer\";\n    expectedOutCount = VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n    acceptedOut0ScriptHexes = [stripHexPrefix(registeredPayoutScriptPubKey)];\n  } else if (claimer === dep) {\n    role = \"depositor-as-claimer\";\n    expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n    acceptedOut0ScriptHexes = [stripHexPrefix(registeredPayoutScriptPubKey)];\n  } else if (keepers.includes(claimer)) {\n    role = \"vk-claimer\";\n    expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n    // RFC-006: a keeper's payout goes to the scriptPubKey it registered\n    // on-chain, resolved at the vault's frozen keeper epoch. Deriving BIP-86\n    // locally as the sole expectation would reject a valid payout the moment a\n    // keeper points its payouts at cold custody.\n    //\n    // A missing entry is an error, never a BIP-86 fallback: the registry\n    // backfills BIP-86 itself for keepers that never registered a script, so a\n    // gap here means the resolution was incomplete, not that this keeper is on\n    // the default.\n    const registered = vkClaimerPayoutScriptPubKeys[claimer];\n    if (registered === undefined) {\n      throw new Error(\n        `No registered payout script resolved for vault keeper claimer ${claimer}`,\n      );\n    }\n    assertStandardPayoutScript(\n      registered,\n      `Vault keeper payout script for claimer ${claimer}`,\n    );\n    acceptedOut0ScriptHexes = acceptedPayoutScriptHexes(registered, claimer);\n  } else {\n    throw new Error(\n      `Unknown claimer pubkey ${claimer}: not VP, depositor, or a registered vault keeper`,\n    );\n  }\n\n  if (payoutTx.outs.length !== expectedOutCount) {\n    throw new Error(\n      `Payout transaction has ${payoutTx.outs.length} output(s), ` +\n        `expected exactly ${expectedOutCount} for role ${role}.`,\n    );\n  }\n\n  if (!matchesAnyScript(payoutTx.outs[0].script, acceptedOut0ScriptHexes)) {\n    throw new Error(\n      `Payout transaction output 0 does not pay the expected scriptPubKey for role ${role}. ` +\n        `Accepted: ${acceptedOut0ScriptHexes.join(\" or \")}; ` +\n        `got ${payoutTx.outs[0].script.toString(\"hex\")}`,\n    );\n  }\n\n  const anchorIdx = expectedOutCount - 1;\n  if (payoutTx.outs[anchorIdx].value !== PAYOUT_ANCHOR_DUST_SATS) {\n    throw new Error(\n      `Payout CPFP anchor (out ${anchorIdx}) value ${payoutTx.outs[anchorIdx].value} sats ` +\n        `must equal ${PAYOUT_ANCHOR_DUST_SATS} sats`,\n    );\n  }\n\n  if (role === \"vp-claimer\") {\n    // RFC-006 commission destination. Checked before the value cap so a\n    // substituted destination reports as such rather than surfacing later as a\n    // confusing amount error.\n    assertStandardPayoutScript(\n      vpCommissionScriptPubKey,\n      \"Vault provider commission payout script\",\n    );\n    const acceptedCommissionScriptHexes = acceptedPayoutScriptHexes(\n      vpCommissionScriptPubKey,\n      vp,\n    );\n    if (\n      !matchesAnyScript(payoutTx.outs[1].script, acceptedCommissionScriptHexes)\n    ) {\n      throw new Error(\n        `Payout transaction output 1 does not pay the vault provider's commission scriptPubKey. ` +\n          `Accepted: ${acceptedCommissionScriptHexes.join(\" or \")}; ` +\n          `got ${payoutTx.outs[1].script.toString(\"hex\")}`,\n      );\n    }\n\n    // Structural guard only — a non-negative integer below the bps\n    // denominator, so the cap math `floor(peginValue * bps / 10_000)` is\n    // meaningful. The protocol minimum is enforced at the trust boundary\n    // (`prepareSigningContext`); a too-low value here is fail-safe.\n    if (\n      !Number.isInteger(commissionBps) ||\n      commissionBps < 0 ||\n      commissionBps >= MAX_VP_COMMISSION_BPS_EXCLUSIVE\n    ) {\n      throw new Error(\n        `commissionBps must be an integer in ` +\n          `[0, ${MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${commissionBps}`,\n      );\n    }\n    const maxCommissionSats = Math.floor(\n      (peginValueSats * commissionBps) / BPS_DENOMINATOR,\n    );\n    if (payoutTx.outs[1].value > maxCommissionSats) {\n      throw new Error(\n        `Payout VP commission (out 1) value ${payoutTx.outs[1].value} sats ` +\n          `exceeds cap ${maxCommissionSats} sats ` +\n          `(${commissionBps} bps of peginValue=${peginValueSats})`,\n      );\n    }\n  }\n\n  // The accepted-script set can hold more than one candidate (dual-accept), so\n  // the length the fee band consumes is that of the output actually present —\n  // which the check above has already pinned to one of the accepted values.\n  const out0Len = payoutTx.outs[0].script.length;\n  if (out0Len === 0 || out0Len > MAX_PAYOUT_SCRIPT_LEN) {\n    throw new Error(\n      `Payout receiver scriptPubKey length ${out0Len} is outside the ` +\n        `contract's registration cap [1, ${MAX_PAYOUT_SCRIPT_LEN}]; ` +\n        `refusing to sign payout.`,\n    );\n  }\n  // No length cap on outs[1]: under RFC-006 the commission output is pinned to\n  // the operator's registered script, and `assertStandardPayoutScript` above\n  // already bounds that to a standard type (34 bytes at most). A separate\n  // 128-byte cap here would be unreachable. Still measured, because the fee\n  // floor consumes it — and now from a pinned source rather than a\n  // VP-controlled one.\n  const out1Len =\n    role === \"vp-claimer\" ? payoutTx.outs[1].script.length : undefined;\n\n  return { out0Len, out1Len };\n}\n\n/**\n * Extract Schnorr signature from signed payout PSBT.\n *\n * This function supports two cases:\n * 1. Non-finalized PSBT: Extracts from tapScriptSig field\n * 2. Finalized PSBT: Extracts from witness data\n *\n * The signature is returned as a 64-byte hex string (128 hex characters).\n * Payout signatures must use implicit Taproot SIGHASH_DEFAULT, which is\n * encoded by omitting the sighash byte.\n *\n * @param signedPsbtHex - Signed PSBT hex\n * @param depositorPubkey - Depositor's public key (x-only, 64-char hex)\n * @param inputIndex - Input index to extract signature from (default: 0)\n * @returns 64-byte Schnorr signature (128 hex characters, no sighash flag)\n *\n * @throws If no signature is found in the PSBT\n * @throws If the signature has an unexpected length\n */\nexport function extractPayoutSignature(\n  signedPsbtHex: string,\n  depositorPubkey: string,\n  inputIndex = 0,\n): string {\n  const signedPsbt = Psbt.fromHex(signedPsbtHex);\n\n  if (inputIndex >= signedPsbt.data.inputs.length) {\n    throw new Error(\n      `Input index ${inputIndex} out of range (${signedPsbt.data.inputs.length} inputs)`,\n    );\n  }\n\n  const input = signedPsbt.data.inputs[inputIndex];\n\n  // Case 1: Non-finalized PSBT — extract from tapScriptSig\n  if (input.tapScriptSig && input.tapScriptSig.length > 0) {\n    const depositorPubkeyBytes = hexToUint8Array(depositorPubkey);\n\n    for (const sigEntry of input.tapScriptSig) {\n      if (sigEntry.pubkey.equals(Buffer.from(depositorPubkeyBytes))) {\n        return extractSchnorrSig(sigEntry.signature, inputIndex);\n      }\n    }\n\n    throw new Error(\n      `No signature found for depositor pubkey: ${depositorPubkey} at input ${inputIndex}`,\n    );\n  }\n\n  // Case 2: Finalized PSBT — extract from finalScriptWitness\n  // Taproot single-signature script-path witness: [signature, script, controlBlock].\n  // Enforce the exact stack size so that if a wallet produces an unexpected\n  // finalization (e.g. a multi-signature stack, an annex, or malformed data),\n  // we fail loudly instead of silently returning witnessStack[0] which may\n  // not be the depositor's signature.\n  if (input.finalScriptWitness && input.finalScriptWitness.length > 0) {\n    const witnessStack = parseWitnessStack(input.finalScriptWitness);\n    if (witnessStack.length !== TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE) {\n      throw new Error(\n        `Unexpected finalized witness stack size at input ${inputIndex}: ` +\n          `expected ${TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE} items (signature, script, controlBlock), ` +\n          `got ${witnessStack.length}`,\n      );\n    }\n    return extractSchnorrSig(witnessStack[0], inputIndex);\n  }\n\n  throw new Error(\n    `No tapScriptSig or finalScriptWitness found in signed PSBT at input ${inputIndex}`,\n  );\n}\n\n/**\n * Extract and validate a 64-byte Schnorr signature.\n * Rejects 65-byte signatures because the appended sighash byte changes the\n * Taproot message being signed; stripping it would produce an unverifiable\n * SIGHASH_DEFAULT signature.\n * @internal\n */\nfunction extractSchnorrSig(sig: Uint8Array, inputIndex: number): string {\n  if (sig.length === 64) {\n    return uint8ArrayToHex(new Uint8Array(sig));\n  }\n  if (sig.length === 65) {\n    throw new Error(\n      `Unexpected sighash byte 0x${sig[64].toString(16).padStart(2, \"0\")} at input ${inputIndex}. ` +\n        \"Expected implicit SIGHASH_DEFAULT as a 64-byte signature.\",\n    );\n  }\n  throw new Error(\n    `Unexpected signature length at input ${inputIndex}: ${sig.length}`,\n  );\n}\n\n/**\n * Parse a BIP-141 serialized witness stack into individual stack items.\n * Format: [varint item_count] [varint len, data]...\n *\n * Throws on malformed input (truncated buffer, 8-byte varints, or trailing\n * bytes) so callers never receive silently-corrupted witness items.\n * @internal\n */\nfunction parseWitnessStack(witness: Buffer): Buffer[] {\n  const items: Buffer[] = [];\n  let offset = 0;\n\n  const requireBytes = (n: number): void => {\n    if (offset + n > witness.length) {\n      throw new Error(\n        `Malformed witness data: need ${n} byte(s) at offset ${offset}, only ${witness.length - offset} remaining`,\n      );\n    }\n  };\n\n  const readVarInt = (): number => {\n    requireBytes(1);\n    const first = witness[offset++];\n    if (first < 0xfd) return first;\n    if (first === 0xfd) {\n      requireBytes(2);\n      const val = (witness[offset] | (witness[offset + 1] << 8)) >>> 0;\n      offset += 2;\n      return val;\n    }\n    if (first === 0xfe) {\n      requireBytes(4);\n      const val =\n        (witness[offset] |\n          (witness[offset + 1] << 8) |\n          (witness[offset + 2] << 16) |\n          (witness[offset + 3] << 24)) >>>\n        0;\n      offset += 4;\n      return val;\n    }\n    // 0xff — 8-byte varint. Not used for witness sizes in practice and JS\n    // numbers cannot represent all 64-bit values exactly, so reject rather\n    // than risk silent truncation.\n    throw new Error(\n      `Malformed witness data: 8-byte varint (0xff) not supported at offset ${offset - 1}`,\n    );\n  };\n\n  const count = readVarInt();\n  for (let i = 0; i < count; i++) {\n    const len = readVarInt();\n    requireBytes(len);\n    items.push(Buffer.from(witness.subarray(offset, offset + len)));\n    offset += len;\n  }\n\n  if (offset !== witness.length) {\n    throw new Error(\n      `Malformed witness data: ${witness.length - offset} trailing byte(s) after parsing ${count} item(s)`,\n    );\n  }\n\n  return items;\n}\n","/**\n * Asserts a wallet-returned PSBT encodes the same unsigned transaction\n * as the locally-built PSBT we asked the wallet to sign. Per-input PSBT\n * metadata (witnessUtxo, tapLeafScript, sighashType) is intentionally NOT\n * compared — those fields are committed to the Schnorr sighash and the\n * vault provider's `verify_depositor_signature` rejects mismatches there.\n * This primitive defends the path where a colluding VP would otherwise\n * accept a wallet-substituted signature.\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { Psbt } from \"bitcoinjs-lib\";\n\n/**\n * Thrown when a wallet-returned PSBT encodes a different unsigned\n * transaction than the one the caller asked the wallet to sign.\n */\nexport class PsbtSubstitutionError extends Error {\n  constructor(detail: string) {\n    super(\n      `Wallet returned a PSBT for a different transaction: ${detail}`,\n    );\n    this.name = \"PsbtSubstitutionError\";\n  }\n}\n\nexport interface AssertPsbtUnsignedTxMatchesParams {\n  /** PSBT we built locally and asked the wallet to sign. */\n  requestedPsbtHex: string;\n  /** PSBT the wallet returned after signing. */\n  returnedPsbtHex: string;\n}\n\nfunction parsePsbt(label: \"requested\" | \"returned\", hex: string): Psbt {\n  try {\n    return Psbt.fromHex(hex);\n  } catch (cause) {\n    const reason = cause instanceof Error ? cause.message : String(cause);\n    throw new Error(`Failed to parse ${label} PSBT: ${reason}`);\n  }\n}\n\n/**\n * Length of the hex prefix included in mismatch errors. Short enough that\n * full prevout txids and output scriptPubKeys never reach logs / error\n * trackers, long enough to disambiguate during forensic triage.\n */\nconst REDACTED_HEX_PREFIX_LEN = 8;\n\nfunction redactHex(buf: Buffer): string {\n  return `${buf.toString(\"hex\").slice(0, REDACTED_HEX_PREFIX_LEN)}…`;\n}\n\n/**\n * `bitcoinjs-lib` exposes `txInputs[i].hash` in internal little-endian form;\n * a human reading logs expects the big-endian txid an explorer would show.\n * Reverse before truncating so the surfaced prefix matches what an operator\n * can search for.\n */\nfunction redactTxid(internalHash: Buffer): string {\n  const reversed = Buffer.from(internalHash).reverse();\n  return redactHex(reversed);\n}\n\n/**\n * Compare two PSBTs and throw `PsbtSubstitutionError` unless they encode\n * the same unsigned transaction (version, locktime, inputs, outputs).\n *\n * @throws PsbtSubstitutionError on any mismatch in the unsigned tx\n * @throws Error if either PSBT cannot be parsed\n */\nexport function assertPsbtUnsignedTxMatches(\n  params: AssertPsbtUnsignedTxMatchesParams,\n): void {\n  const requested = parsePsbt(\"requested\", params.requestedPsbtHex);\n  const returned = parsePsbt(\"returned\", params.returnedPsbtHex);\n\n  if (requested.version !== returned.version) {\n    throw new PsbtSubstitutionError(\n      `tx version differs (requested=${requested.version}, returned=${returned.version})`,\n    );\n  }\n  if (requested.locktime !== returned.locktime) {\n    throw new PsbtSubstitutionError(\n      `tx locktime differs (requested=${requested.locktime}, returned=${returned.locktime})`,\n    );\n  }\n  if (requested.txInputs.length !== returned.txInputs.length) {\n    throw new PsbtSubstitutionError(\n      `input count differs (requested=${requested.txInputs.length}, returned=${returned.txInputs.length})`,\n    );\n  }\n  if (requested.txOutputs.length !== returned.txOutputs.length) {\n    throw new PsbtSubstitutionError(\n      `output count differs (requested=${requested.txOutputs.length}, returned=${returned.txOutputs.length})`,\n    );\n  }\n  for (let i = 0; i < requested.txInputs.length; i++) {\n    const r = requested.txInputs[i];\n    const s = returned.txInputs[i];\n    if (!r.hash.equals(s.hash)) {\n      throw new PsbtSubstitutionError(\n        `input ${i} prevout txid differs (requested=${redactTxid(r.hash)}, returned=${redactTxid(s.hash)})`,\n      );\n    }\n    if (r.index !== s.index) {\n      throw new PsbtSubstitutionError(\n        `input ${i} prevout vout differs (requested=${r.index}, returned=${s.index})`,\n      );\n    }\n    if (r.sequence !== s.sequence) {\n      throw new PsbtSubstitutionError(\n        `input ${i} sequence differs (requested=${r.sequence}, returned=${s.sequence})`,\n      );\n    }\n  }\n  for (let i = 0; i < requested.txOutputs.length; i++) {\n    const r = requested.txOutputs[i];\n    const s = returned.txOutputs[i];\n    if (!r.script.equals(s.script)) {\n      throw new PsbtSubstitutionError(\n        `output ${i} scriptPubKey differs (requested=${redactHex(r.script)}, returned=${redactHex(s.script)})`,\n      );\n    }\n    if (r.value !== s.value) {\n      throw new PsbtSubstitutionError(\n        `output ${i} value differs (requested=${r.value}, returned=${s.value})`,\n      );\n    }\n  }\n}\n","/**\n * Independent BIP-340 verification of a wallet-returned Taproot script-path\n * Schnorr signature against an independently-recomputed sighash.\n *\n * Critical Path #7 (CLAUDE.md): the SDK requests script-path signatures with\n * `useTweakedSigner: false, autoFinalized: false`. Wallet support for the\n * untweaked-key flag is inconsistent — older OKX / mobile bridges silently sign\n * with the *tweaked* key, Keystone ignores the flag — and a compromised\n * extension can stuff a 64-byte stub into `tapScriptSig`. A bad signature that\n * the SDK forwards is only caught on broadcast; in the worst case it passes the\n * VP off-chain but Bitcoin rejects it, leaving the depositor's BTC locked in the\n * HTLC until `timelockRefund` matures. This guard rejects such signatures before\n * they are trusted.\n *\n * Why verify against the *locally-built* PSBT, not the wallet-returned one:\n * `assertPsbtUnsignedTxMatches` pins the unsigned transaction but deliberately\n * skips per-input metadata (`witnessUtxo`, `tapLeafScript`). A malicious wallet\n * could rewrite those consistently in the returned PSBT so a wrong-message\n * signature self-validates. The trusted prevout scripts/values and leaf script\n * therefore come from the PSBT we built ourselves (derived from on-chain / WASM\n * sources); only the 64-byte signature comes from the wallet.\n *\n * Reuses the exact primitives `bip322Verify.ts` already depends on — no new\n * dependency:\n *   - `@bitcoin-js/tiny-secp256k1-asmjs` → `verifySchnorr`\n *   - `bitcoinjs-lib` → `Transaction.hashForWitnessV1`\n *   - `../utils/taproot` → `computeTapLeafHash`\n *\n * @module tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature\n */\n\nimport * as ecc from \"@bitcoin-js/tiny-secp256k1-asmjs\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\n\nimport { Buffer } from \"buffer\";\n\nimport {\n  SCHNORR_SIG_HEX_LEN,\n  TAPSCRIPT_LEAF_VERSION,\n  X_ONLY_PUBKEY_HEX_LEN,\n  hexToUint8Array,\n  stripHexPrefix,\n} from \"../utils/bitcoin\";\nimport { computeTapLeafHash } from \"../utils/taproot\";\n\nexport interface VerifyScriptPathSchnorrSignatureParams {\n  /**\n   * Hex of the PSBT we built locally and sent to the wallet (the trusted\n   * source of prevout scripts/values and the leaf script). NOT the\n   * wallet-returned PSBT.\n   */\n  requestedPsbtHex: string;\n  /** The 64-byte Schnorr signature extracted from the wallet's response (128 hex chars). */\n  signatureHex: string;\n  /** X-only public key (64 hex chars) the wallet signed the script-path leaf with. */\n  signerXOnlyPubkeyHex: string;\n  /** Index of the input the signature is for. */\n  inputIndex: number;\n}\n\n/**\n * Assert that `signatureHex` is a valid BIP-340 Schnorr signature by the\n * `signerXOnlyPubkeyHex` key over the Taproot script-path sighash of\n * `requestedPsbtHex` input `inputIndex` (SIGHASH_DEFAULT).\n *\n * @throws If the requested PSBT is malformed, lacks the prevout/leaf data needed\n *         to recompute the sighash, or the signature does not verify.\n */\nexport function assertScriptPathSchnorrSignature(\n  params: VerifyScriptPathSchnorrSignatureParams,\n): void {\n  const { requestedPsbtHex, signatureHex, signerXOnlyPubkeyHex, inputIndex } =\n    params;\n\n  const signatureRaw = stripHexPrefix(signatureHex);\n  if (signatureRaw.length !== SCHNORR_SIG_HEX_LEN) {\n    throw new Error(\n      `Schnorr signature for input ${inputIndex} must be ${SCHNORR_SIG_HEX_LEN} hex chars ` +\n        `(64 bytes), got ${signatureRaw.length}.`,\n    );\n  }\n\n  const signerXOnly = stripHexPrefix(signerXOnlyPubkeyHex);\n  if (signerXOnly.length !== X_ONLY_PUBKEY_HEX_LEN) {\n    throw new Error(\n      `Signer x-only pubkey for input ${inputIndex} must be ${X_ONLY_PUBKEY_HEX_LEN} hex chars ` +\n        `(32 bytes), got ${signerXOnly.length}.`,\n    );\n  }\n\n  const psbt = Psbt.fromHex(requestedPsbtHex);\n\n  if (inputIndex < 0 || inputIndex >= psbt.data.inputs.length) {\n    throw new Error(\n      `Input index ${inputIndex} out of range (${psbt.data.inputs.length} inputs).`,\n    );\n  }\n\n  // Taproot's sighash commits to every input's prevout (script + value), so all\n  // inputs must carry a witnessUtxo. A missing one is a build error, not a\n  // value we can default — fail loudly.\n  const prevOutScripts: Buffer[] = [];\n  const values: number[] = [];\n  for (let i = 0; i < psbt.data.inputs.length; i++) {\n    const witnessUtxo = psbt.data.inputs[i].witnessUtxo;\n    if (!witnessUtxo) {\n      throw new Error(\n        `Cannot verify signature: input ${i} of the requested PSBT has no witnessUtxo ` +\n          `(required to recompute the Taproot sighash).`,\n      );\n    }\n    prevOutScripts.push(witnessUtxo.script);\n    values.push(witnessUtxo.value);\n  }\n\n  // The signed input must expose exactly one tapLeafScript — the leaf the\n  // depositor signs. Zero means we sent the wrong PSBT; more than one means an\n  // ambiguous spend path we never construct for a single-signature input.\n  const tapLeafScripts = psbt.data.inputs[inputIndex].tapLeafScript;\n  if (!tapLeafScripts || tapLeafScripts.length !== 1) {\n    throw new Error(\n      `Cannot verify signature: input ${inputIndex} of the requested PSBT must have exactly ` +\n        `one tapLeafScript, got ${tapLeafScripts?.length ?? 0}.`,\n    );\n  }\n  const leaf = tapLeafScripts[0];\n  if (leaf.leafVersion !== TAPSCRIPT_LEAF_VERSION) {\n    throw new Error(\n      `Cannot verify signature: input ${inputIndex} tapLeafScript has leaf version ` +\n        `0x${leaf.leafVersion.toString(16)}, expected 0x${TAPSCRIPT_LEAF_VERSION.toString(16)}.`,\n    );\n  }\n\n  const leafHash = computeTapLeafHash(leaf.leafVersion, leaf.script);\n\n  // Reconstruct the unsigned transaction from the requested PSBT using only\n  // public bitcoinjs-lib API (same pattern as bip322Verify.ts), then compute the\n  // BIP-341 script-path sighash with SIGHASH_DEFAULT.\n  const tx = new Transaction();\n  tx.version = psbt.version;\n  tx.locktime = psbt.locktime;\n  for (const input of psbt.txInputs) {\n    tx.addInput(input.hash, input.index, input.sequence);\n  }\n  for (const output of psbt.txOutputs) {\n    tx.addOutput(output.script, output.value);\n  }\n\n  const sighash = tx.hashForWitnessV1(\n    inputIndex,\n    prevOutScripts,\n    values,\n    Transaction.SIGHASH_DEFAULT,\n    leafHash,\n  );\n\n  const isValid = ecc.verifySchnorr(\n    sighash,\n    hexToUint8Array(signerXOnly),\n    hexToUint8Array(signatureRaw),\n  );\n\n  if (!isValid) {\n    throw new Error(\n      `Schnorr signature for input ${inputIndex} (signer ${signerXOnly}) does not verify ` +\n        `against the expected Taproot script-path sighash. The wallet may have signed with ` +\n        `the tweaked key, signed a different transaction, or returned an invalid signature.`,\n    );\n  }\n}\n"],"names":["normalizeKey","key","processPublicKeyToXOnly","computeNumLocalChallengers","vaultProviderPubkey","vaultKeeperPubkeys","depositorPubkey","localSet","vk","deriveLocalChallengers","params","claimer","depositor","vaultKeepers","filtered","k","assertWasmPeginSizing","result","expectedCount","expectedClaimValue","computeMinClaimValue","anchor","peginP2aAnchorOutput","anchorValue","expectedPeginFee","computeMinPeginFee","expectedReserve","maxImpliedReserve","MAX_REASONABLE_PEGIN_VBYTES","i","requested","peginAmount","htlcValue","impliedReserve","HtlcOutputMismatchError","message","assertEncodedHtlcOutputsMatch","outputs","htlcValues","htlcScriptPubKeys","encodedValue","encodedScript","expectedScript","AUTH_ANCHOR_HASH_HEX_LEN","HEX_PATTERN","buildPrePeginPsbt","authAnchorHash","normalizeAuthAnchorHash","createPrePeginTransaction","minPeginFee","parsed","parseUnfundedWasmTransaction","totalOutputValue","sum","o","authAnchorVout","value","cleaned","buildPeginTxFromFundedPrePegin","buildPeginTxFromPrePegin","assertPeginTxShape","PEGIN_BASE_OUTPUT_COUNT","PEGIN_DEPOSITOR_CLAIM_VOUT","version","validatePeginP2aAnchor","expectedOutputCount","peginTx","Transaction","stripHexPrefix","fundedPrePeginTxid","inputTxid","uint8ArrayToHex","requestedAmount","encodedVaultOut","encodedVaultScript","expectedVaultScript","encodedTxid","encodedClaimOut","expectedClaimScript","deriveDepositorClaimScriptPubKey","claimLeafScript","bscript","Buffer","hexToUint8Array","opcodes","output","payments","tapInternalPubkey","createPayoutScript","connector","createPayoutConnector","COMPACT_SIZE_UINT16_PREFIX","COMPACT_SIZE_UINT32_PREFIX","COMPACT_SIZE_UINT16_MAX","COMPACT_SIZE_UINT32_MAX","TAPLEAF_TAG","TAPBRANCH_TAG","TAPTWEAK_TAG","CONTROL_BLOCK_PREFIX_LEN","CONTROL_BLOCK_NODE_LEN","CONTROL_BLOCK_MAX_NODES","CONTROL_BLOCK_PARITY_MASK","CONTROL_BLOCK_LEAF_VERSION_MASK","P2TR_SCRIPT_PUBKEY_PREFIX","encodeCompactSize","n","computeTapLeafHash","leafVersion","script","preimage","bcrypto","computeTaprootScriptPubKey","binding","controlBlock","pathLen","controlLeafVersion","internalKey","node","offset","sibling","tweak","tweaked","ecc","expectedParity","MAX_FLOOR_MODEL_KEEPERS","MAX_UNIVERSAL_CHALLENGERS","MAX_FLOOR_MODEL_COUNCIL_SIZE","MAX_SANE_FEE_RATE_SAT_PER_VB","MAX_PAYOUT_VSIZE_BASE","MAX_PAYOUT_VSIZE_PER_PARTICIPANT","PAYOUT_BOUND_ASSUMED_SCRIPT_LEN","assertPayoutFeeBandDomain","role","count","max","reason","assertPayoutFeeInBand","measured","implicitFeeSats","out0Len","out1Len","numParticipants","implicitFee","scriptExcess","maxPayoutVsize","maxFeeSats","minFeeSats","computePayoutFeeFloor","P2WPKH_LEN","P2WSH_LEN","P2TR_LEN","P2PKH_LEN","P2SH_LEN","isP2wpkh","s","isP2wsh","isP2tr","isP2pkh","isP2sh","assertStandardPayoutScript","scriptHex","label","TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE","buildPayoutPsbt","feeBandParams","payoutTx","assertTx","DEPOSITOR_PAYOUT_INPUT_COUNT","PAYOUT_TX_VERSION","PAYOUT_TX_LOCKTIME","peginPrevOut","requirePrevOut","PEGIN_VAULT_OUTPUT_INDEX","assertPrevOut","ASSERT_PAYOUT_OUTPUT_INDEX","assertPayoutOutputLayout","inputValueSats","outputValueSats","out","payoutConnector","assertPayoutLeaf","resolveAssertPayoutLeaf","assemblePayoutPsbt","payoutScript","payoutControlBlock","getAssertPayoutScriptInfo","boundScriptPubKey","TAPSCRIPT_LEAF_VERSION","input","inputIndex","parentTx","parentLabel","expectedVout","parentTxid","prevOut","psbt","Psbt","input0","input1","acceptedPayoutScriptHexes","registeredScriptPubKey","bondedPubkey","registered","legacyBip86","deriveBip86ScriptPubKeyHex","matchesAnyScript","acceptedHexes","hex","peginValueSats","claimerBtcPubkey","vaultProviderBtcPubkey","depositorBtcPubkey","vaultKeeperBtcPubkeys","registeredPayoutScriptPubKey","commissionBps","vkClaimerPayoutScriptPubKeys","vpCommissionScriptPubKey","isValidHex","vp","dep","keepers","expectedOutCount","acceptedOut0ScriptHexes","VP_CLAIMER_PAYOUT_OUTPUT_COUNT","NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT","anchorIdx","PAYOUT_ANCHOR_DUST_SATS","acceptedCommissionScriptHexes","MAX_VP_COMMISSION_BPS_EXCLUSIVE","maxCommissionSats","BPS_DENOMINATOR","MAX_PAYOUT_SCRIPT_LEN","extractPayoutSignature","signedPsbtHex","signedPsbt","depositorPubkeyBytes","sigEntry","extractSchnorrSig","witnessStack","parseWitnessStack","sig","witness","items","requireBytes","readVarInt","first","val","len","PsbtSubstitutionError","detail","parsePsbt","cause","REDACTED_HEX_PREFIX_LEN","redactHex","buf","redactTxid","internalHash","reversed","assertPsbtUnsignedTxMatches","returned","r","assertScriptPathSchnorrSignature","requestedPsbtHex","signatureHex","signerXOnlyPubkeyHex","signatureRaw","SCHNORR_SIG_HEX_LEN","signerXOnly","X_ONLY_PUBKEY_HEX_LEN","prevOutScripts","values","witnessUtxo","tapLeafScripts","leaf","leafHash","tx","sighash"],"mappings":"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"}