Skip to content

BIP-375: correct labeled test vectors to include labeled-spend key in PSBT_OUT_SP_V0_INFO - #2207

Open
macgyver13 wants to merge 2 commits into
bitcoin:masterfrom
macgyver13:bip375-labeled-spend
Open

BIP-375: correct labeled test vectors to include labeled-spend key in PSBT_OUT_SP_V0_INFO#2207
macgyver13 wants to merge 2 commits into
bitcoin:masterfrom
macgyver13:bip375-labeled-spend

Conversation

@macgyver13

Copy link
Copy Markdown
Contributor

Includes two changes to align the labeled test vectors and validation reference with BIP-375:

  • Sort silent payment codes lexicographically then by output index before assigning k values.
  • PSBT_OUT_SP_V0_INFO should carry the labeled-spend public key, not the base-spend public key.

Changed vectors:

  • can finalize: one P2WPKH input / two mixed outputs - labeled sp output and BIP 32 change
  • can finalize: one input / two sp outputs - output 0 has no label / output 1 uses label=0 convention for sp change
  • can finalize: three sp outputs (same scan key) - output 0 uses label=1, outputs 1 and 2 uses label=2 (same spend key)

Test Runner Output

Summary: 41 passed, 0 failed

Now PSBT_OUT_SP_V0_INFO provides labeled-spend public key not the base-spend
public key.

Modify test vector with labeled-spend key to the same recipient from:
two sp outputs - output 0 uses label=3 / output 1 uses label=1
to:
three sp outputs (same scan key) - output 0 uses label=1, outputs 1 and 2 uses label=2 (same spend key)
This vector excercises the BIP375 sorting outputs with same scan key properly.

The next commit will fix the validation logic and address this failing test.

bump ChangeLog to 0.1.2
Fix output script validation for silent payment output ordering when multiple 
outputs share the same scan key (labeled addresses of one recipient).
@macgyver13
macgyver13 marked this pull request as ready for review July 7, 2026 18:09
@murchandamus

Copy link
Copy Markdown
Member

cc BIP Owners for review: @andrewtoth, @achow101, @josibake

@murchandamus murchandamus added Proposed BIP modification PR by non-owner to update BIP content Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified labels Jul 7, 2026
@fametrano

fametrano commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Implementer's note. btclib has a BIP375 implementation and currently
assigns k in output index order, so this PR would change what it
derives. Working notes and the full measurement are in
btclib-org/btclib#768.

Measured, in both directions, with bip-0375/test_runner.py on
Python 3.12:

tree result
master 60f5b33b, unchanged 41 passed, 0 failed
master, validate_output_scripts patched to assign k by ascending PSBT_OUT_SP_V0_INFO with output index as tie-break 40 passed, 1 failed: valid[8], "two sp outputs - output 0 uses label=3 / output 1 uses label=1"
master's validator against this PR's vector file 40 passed, 1 failed: "three sp outputs (same scan key) - output 0 uses label=1, outputs 1 and 2 uses label=2"
this PR at 8f7b632f, both halves 41 passed, 0 failed

No invalid vector changes verdict under either rule. The two invalid
vectors named after ordering do not discriminate between the rules: in
invalid[20] the spend keys are already ascending, so index and
lexicographic order coincide, and each of the two carries a k
assignment no ascending rule produces (the two values swapped in one,
three permuted in the other). So the discrepancy is confined to the
labeled multi-output vectors, and this PR closes it.

Two remarks, one per half of the PR.

The PSBT_OUT_SP_V0_INFO half looks unambiguous — the field should
carry the labeled spend key — and is independent of the ordering
question.

On the ordering half, a question about the rule rather than about which
artefact is normative.
What the rule has to buy appears to be
inter-party determinism only: the Signer sets PSBT_OUT_SCRIPT and the
Extractor recomputes it, so both must land on the same bytes. Nothing
else seems to depend on it — a BIP352 receiver scans by deriving P_k
for k = 0, 1, 2 … and matching against the transaction's outputs, so it
never learns which output was assigned which k; and permuting k
within a scan-key group still pays each recipient the same amount, since
an output's script is derived from its own spend key. If that is right,
any deterministic rule is equally correct and the choice is a matter of
cost, where two things differ:

  • lexicographic order needs a canonical encoding of a "code" and a total
    order on it. The prose says "sort the codes", which could be the
    66-byte PSBT_OUT_SP_V0_INFO, the bech32m address string, or the
    (scan, spend) pair; this PR picks the first, in code. Output indices
    need no such definition and are already in the psbt;
  • the property lexicographic order would buy — invariance under output
    reordering — looks unavailable here: L185 requires a Signer setting a
    missing PSBT_OUT_SCRIPT to clear the Inputs Modifiable and Outputs
    Modifiable flags, and a psbt carrying a script with
    PSBT_GLOBAL_TX_MODIFIABLE non-zero is invalid (invalid[4]). Outputs
    cannot be reordered once the scripts exist. The tie-break for identical
    codes is output index in any case.

So index order looks like the cheaper rule, and it is what all three
shipped artefacts did before this PR. That is an argument for amending
the two sentences at L240-241 instead, and reducing this PR to its
PSBT_OUT_SP_V0_INFO half.

We have no stake beyond interoperability and will follow whichever way
this is settled. If the lexicographic rule is kept, one edit would
help
: have the prose name the sort key — the serialized
PSBT_OUT_SP_V0_INFO, ascending, ties by output index, which is what
this PR implements. As written, "the codes" leaves the next implementer
the same choice that produced this divergence.

Unrelated and not touched here, noted while checking the above: L149
says "The PSBT_OUT_SP_V0_INFO should be serialized as a zero byte for the
version, followed by the 33 bytes of the scan key and then 33 bytes for
the spend key", which names the field, whose table entry at L124 is
<33 byte scan key> <33 byte spend key> — 66 bytes, which is what the
vectors carry and what the validator requires (invalid[1] reports
"wrong length (65 bytes, expected 66)"). Read in its own section the
sentence is about the substitute output script used for unique
identification, where a version byte prefix contradicts nothing; that is
how btclib implemented it. Since the sentence names the field rather than
the substitution, one line of disambiguation would help. Glad to send it
separately.

Edited: the last paragraph originally called this an inconsistency about
the field's length. In its section it is a question of wording, not a
contradiction.

@jonatack

Copy link
Copy Markdown
Member

@fametrano Thank you for the report. It is a little hard to understand, can you summarize more concisely in your own words, please?

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

Labels

Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified Proposed BIP modification PR by non-owner to update BIP content

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants