BIP 360 gives us an output type whose key stays off-chain until a script-path
spend, and BIP 361 proposes a phased sunset of legacy signature verification.
Both presuppose something no BIP currently specifies: a shared answer to
“which existing outputs are exposed?”
I’ve drafted a specification for exactly that, and I’d like this forum’s
scrutiny before taking it further.
Why I think it’s worth specifying. Published estimates of the exposed
supply range from ~25% to over 34%. Having chased the sources, I’m fairly
convinced the spread is definitional rather than measurement error — tools
disagree on whether P2TR counts as exposed at rest, whether a spent-from
address holding a balance differs from P2PK, and what to report for a P2SH
whose script was never revealed. If a BIP 361-style migration activates,
that question gets asked at scale, with money attached, by software that
ought to agree.
What the draft does. Four levels — EXPOSED_AT_REST,
EXPOSED_ON_SPEND, NOT_EXPOSED, UNDETERMINED — with a per-output-type
assignment table and a fail-closed rule: where the data can’t distinguish two
levels, the more-exposed one must be assigned. Two consequences are likely to
be the contentious ones:
A reused, spent-from P2PKH that still holds a balance is EXPOSED_AT_REST,
the same level as P2PK. To an adversary they’re the same situation, and
labelling them differently quietly implies reuse is safer than P2PK.
P2TR is at rest whether or not the internal key is NUMS, since consensus
never checks how Q was constructed. As I read it, that’s precisely why
BIP 360 removes the key path — the discussion in the BIP-360 changes
thread
is what convinced me to state it this plainly.
Draft, a dependency-free Python reference implementation, and test vectors:
It’s a draft, not a finished BIP. What I’d most like to know is whether the
four-level partition holds up against situations you’ve hit in practice, and
whether this belongs in a BIP at all versus staying an implementation detail.
If the answer is the latter, I’d rather hear it now.
From a wallet user’s point of view, each level may be more useful if it also has a clear recommended action—for example, no action needed, avoid reusing the address, or move the funds when a safer option is available. Is that guidance intended to be standardized, or left to each wallet? Otherwise, different wallets could show very different warnings for the same situation.
Apologies for the slow reply — this got lost behind the mailing-list thread.
This is a distinction I should have drawn in the draft and didn’t. I excluded
risk scoring on purpose — weighing balance, dormancy and CRQC timelines is
subjective and not chain-observable. But a recommended action is different:
each level implies one almost deterministically, and you’re right that leaving
it unstated just moves the divergence from “which coins are exposed” to “what
should I do about it”.
What I think each level implies, as a floor:
EXPOSED_AT_REST — move to a non-exposed output type as soon as one you
trust exists; until then, stop adding to it.
EXPOSED_ON_SPEND — nothing needed at rest; when you do spend, sweep the
whole balance and never send change or new funds back, since the first spend
makes it AT_REST.
NOT_EXPOSED — nothing.
UNDETERMINED — treat as AT_REST until your software can classify it.
The awkward part is “when a safer option is available”: before something like
BIP 360 activates, the only safer option is fresh-key hygiene; after, there’s
an actual destination. So the action text can’t hardcode a target type.
Proposal for v0.4.0: an informative appendix mapping each level to a
SHOULD-level minimum action, with wallets free to say more but not to
contradict it. That standardizes the floor without dragging the spec into
scoring. In the scanner I run, the four levels already map to exactly four
advice strings, and that’s held up in production — so a deterministic mapping
seems workable.
From a wallet’s side: is that granularity useful, and would you want the
action as a machine-readable key alongside the level, or is prose enough?
(v0.3.0 is up, addressing the P2TR points raised on the mailing list.)
From the user side, clear prose is essential. Having a shared action key behind it also seems useful, so different wallets can translate and present the same basic advice consistently. I would keep the actions simple, with each wallet explaining the reason and urgency in plain language.
Thanks, that settles it. v0.4.0 will carry an informative appendix with a small fixed set of action keys, one per level, and a one-line SHOULD floor for each. Wallets own the wording, the reasons and the urgency on top of that. I’ll post here when it’s up.
Appendix A gives each level one action key — MIGRATE, SWEEP_WHEN_SPENDING, NONE, TREAT_AS_MIGRATE — plus a one-line floor. Wallets own everything above that. The Abstract and Motivation were also rewritten to state the reasoning directly. If the floor wording needs adjusting from the wallet side, say so and I’ll fold it in.