LearnBitcoin

Bitcoin Dev Archive 2026

Standardizing public key exposure classification for existing outputs

5 messages, 4 to 18 Sep 2026. Started by duncan0k.

duncan0k (4), conduition

Permalink Original thread on gnusha.org

duncan0k permalink original

Hi all,

I would like to gauge interest in a specification that defines how to classify an existing Bitcoin output by whether the public key needed to spend it has already been published on-chain.

The gap: 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 assume the ecosystem can answer "which existing outputs are exposed?" -- but no BIP specifies how that question is answered.

Why this is a practical problem: published estimates of the exposed supply currently range from roughly 25% to over 34%. As far as I can tell the spread is definitional, not measurement error. Recurring disagreements:

- Is a P2TR output exposed at rest? Its 32-byte output key is a public
  key, and solving its discrete log yields a working key-path spend
  regardless of how the internal key was chosen -- a NUMS internal key
  does not help, since consensus never checks how Q was constructed.
- Is a spent-from P2PKH address that still holds a balance "exposed at
  rest" or "exposed on spend"? To an adversary these are the same
  situation, but tools label them differently.
- What should a classifier report for a P2SH/P2WSH output whose script
  has never been revealed?
- What about keys disclosed off-chain -- xpubs handed to service
  providers, or spends of the corresponding output on a 2017 fork?

If BIP 361 or something like it advances, "is this output legacy-vulnerable" becomes a question asked at scale by wallets, explorers and custodians, with money attached. Users consulting two tools should not get two answers about the same coins.

What the draft does: it specifies four levels -- EXPOSED_AT_REST, EXPOSED_ON_SPEND, NOT_EXPOSED, UNDETERMINED -- a per-output-type assignment table, and a fail-closed rule: where data is insufficient to distinguish two levels, the more-exposed level must be assigned. Two points I would particularly like feedback on:

1. Disclosure mechanism does not change the level. A reused,
   spent-from script that retains a balance is EXPOSED_AT_REST, the
   same as P2PK. I think separating these invites the reading that
   reused addresses are safer than P2PK, which is false. Some existing
   tools (including an earlier iteration of my own) do separate them.
2. UNDETERMINED is mandatory for unrecognized output types, rather
   than defaulting them to unexposed. Any classifier predating a
   future output type will encounter outputs it cannot reason about,
   and reporting those as safe asserts a property it has not
   established.

The draft also specifies a constant-time floor: if a script's confirmed spent-output count is greater than zero, a spend has occurred, and for every type except P2MR that publishes the material the script commits to. This is what prevents a truncated history scan from producing a false NOT_EXPOSED -- without it, every implementation independently decides what to report when it runs out of scan budget, and the divergence reappears at the engineering layer. (The one over-approximation -- a revealed script that requires no signature -- errs in the conservative direction, which the fail-closed rule permits; see the v0.2.0 changelog.)

Explicitly out of scope: risk scoring. Turning exposure into a number requires weighing balance, dormancy and CRQC timelines, none of which are observable on-chain and all of which are contested. The draft stops at the observable so that the numbers people compute on top of it are at least computed over the same facts.

Draft, reference implementation and test vectors: https://github.com/duncan0k/pubkey-exposure-classification

I posted this to Delving Bitcoin a couple of days ago and am cross-posting here for wider review: https://delvingbitcoin.org/t/standardizing-an-exposure-classification-for-existing-outputs-pre-bip/2866

Before I invest further: is this something the list considers worth specifying, or is it better left to each implementation? I am also interested in whether anyone has prior art I have missed -- I could not find a canonical definition, but this is the kind of thing that gets rediscovered.

duncan0k

conduition in reply to duncan0k permalink original

Hi Duncan,

Disclaimer: I haven't read the draft BIP; it appears to be AI-generated. If you don't have time to write your own BIP, you should not expect others to have time to read it.

Quoted text (2 lines)
> Before I invest further: is this something the list considers worth
> specifying, or is it better left to each implementation?

I think it is an important question to answer. A deterministic standardized way to index what "exposed pubkey" means is beneficial to everyone involved in the PQ conversation.

My work on DropKick and Tadge Dryja's work on Lifeboat are both contingent on this source of truth, but nobody has yet spent the cycles to figure out how that would work.

Methodology is still an open problem too. Would every full node go back and reindex the entire chain to collect a list of exposed pubkeys/addresses? That seems expensive, but at least an upgraded node could start preparing early, before any fork is activated.

Perhaps this is an excuse for bitcoind to finally include an address index? This would basically solve the engineering problem as a drive-by.

Quoted text (3 lines)
> I am also interested in whether anyone has prior art I have missed -- I could
> not find a canonical definition, but this is the kind of thing that
> gets rediscovered.

I would check out Project Eleven's dashboard [2] if you haven't already.

Quoted text (1 line)
> Is a P2TR output exposed at rest?

If you consider the internal key + MAST root to be a preimage, then no: P2TR is actually usable without exposing that preimage, unless you do a script spend.

However, not every P2TR address is guaranteed to HAVE an internal key. Some non-standard implementations might have disregarded BIP341's recommendations, and put a bare untweaked pubkey on chain. It's impossible to know for sure.

Personally I doubt this is a significant share of the P2TR supply - Every codebase i've ever seen that implements P2TR does so with an empty tweak, as recommended by the spec.

Still, it means when considering rescue protocols that locking P2TR coins under a new encumbrance that says "you can only spend by proving knowledge of the internal key" will confiscate some unknown (probably tiny) fraction of P2TR coins.

Quoted text (1 line)
> Is a spent-from P2PKH address that still holds a balance "exposed at rest" or "exposed on spend"?

Exposed at rest, i.e. vulnerable to 'long exposure' attacks.

Quoted text (1 line)
> What should a classifier report for a P2SH/P2WSH output whose script has never been revealed?

I would say "unexposed" for this. The signer definitely knows something (a script) that the CRQC doesn't.

Quoted text (2 lines)
> What about keys disclosed off-chain -- xpubs handed to service
> providers, or spends of the corresponding output on a 2017 fork?

I strongly doubt we can do much about off-chain exposure.

regards, conduition

[1]: https://github.com/bitcoin/bips/pull/1895#pullrequestreview-4110112784 [2]: https://www.projecteleven.com/bitcoin-risq-list

On Friday, September 4th, 2026 at 12:25 PM, 'duncan0k' via Bitcoin Development Mailing List <bitcoindev@googlegroups•com> wrote:

Quoted text (92 lines)
> Hi all,
>

> I would like to gauge interest in a specification that defines how to
> classify an existing Bitcoin output by whether the public key needed to
> spend it has already been published on-chain.
>

> The gap: 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 assume the ecosystem can answer
> "which existing outputs are exposed?" -- but no BIP specifies how that
> question is answered.
>

> Why this is a practical problem: published estimates of the exposed
> supply currently range from roughly 25% to over 34%. As far as I can
> tell the spread is definitional, not measurement error. Recurring
> disagreements:
>

> - Is a P2TR output exposed at rest? Its 32-byte output key is a public
>   key, and solving its discrete log yields a working key-path spend
>   regardless of how the internal key was chosen -- a NUMS internal key
>   does not help, since consensus never checks how Q was constructed.
>

> - Is a spent-from P2PKH address that still holds a balance "exposed at
>   rest" or "exposed on spend"? To an adversary these are the same
>   situation, but tools label them differently.
>

> - What should a classifier report for a P2SH/P2WSH output whose script
>   has never been revealed?
>

> - What about keys disclosed off-chain -- xpubs handed to service
>   providers, or spends of the corresponding output on a 2017 fork?
>

> If BIP 361 or something like it advances, "is this output
> legacy-vulnerable" becomes a question asked at scale by wallets,
> explorers and custodians, with money attached. Users consulting two
> tools should not get two answers about the same coins.
>

> What the draft does: it specifies four levels -- EXPOSED_AT_REST,
> EXPOSED_ON_SPEND, NOT_EXPOSED, UNDETERMINED -- a per-output-type
> assignment table, and a fail-closed rule: where data is insufficient
> to distinguish two levels, the more-exposed level must be assigned.
> Two points I would particularly like feedback on:
>

> 1. Disclosure mechanism does not change the level. A reused,
>    spent-from script that retains a balance is EXPOSED_AT_REST, the
>    same as P2PK. I think separating these invites the reading that
>    reused addresses are safer than P2PK, which is false. Some existing
>    tools (including an earlier iteration of my own) do separate them.
>

> 2. UNDETERMINED is mandatory for unrecognized output types, rather
>    than defaulting them to unexposed. Any classifier predating a
>    future output type will encounter outputs it cannot reason about,
>    and reporting those as safe asserts a property it has not
>    established.
>

> The draft also specifies a constant-time floor: if a script's
> confirmed spent-output count is greater than zero, a spend has
> occurred, and for every type except P2MR that publishes the material
> the script commits to. This is what prevents a truncated history scan
> from producing a false NOT_EXPOSED -- without it, every implementation
> independently decides what to report when it runs out of scan budget,
> and the divergence reappears at the engineering layer. (The one
> over-approximation -- a revealed script that requires no signature --
> errs in the conservative direction, which the fail-closed rule
> permits; see the v0.2.0 changelog.)
>

> Explicitly out of scope: risk scoring. Turning exposure into a number
> requires weighing balance, dormancy and CRQC timelines, none of which
> are observable on-chain and all of which are contested. The draft
> stops at the observable so that the numbers people compute on top of
> it are at least computed over the same facts.
>

> Draft, reference implementation and test vectors:
> https://github.com/duncan0k/pubkey-exposure-classification
>

> I posted this to Delving Bitcoin a couple of days ago and am
> cross-posting here for wider review:
> https://delvingbitcoin.org/t/standardizing-an-exposure-classification-for-existing-outputs-pre-bip/2866
>

> Before I invest further: is this something the list considers worth
> specifying, or is it better left to each implementation? I am also
> interested in whether anyone has prior art I have missed -- I could
> not find a canonical definition, but this is the kind of thing that
> gets rediscovered.
>

> duncan0k
>

> --
> You received this message because you are subscribed to the Google Groups "Bitcoin Development Mailing List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to bitcoindev+unsubscribe@googlegroups•com.
> To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/010001a06dd4cdd9-b8082042-8750-4e9a-917e-2053c919e4c4-000000%40email.amazonses.com.
>
duncan0k in reply to conduition permalink original

Hi conduition,

Thanks for engaging with the substance despite your reservations. Answering the disclaimer straight first:

Quoted text (2 lines)
> Disclaimer: I haven't read the draft BIP; it appears to be
> AI-generated.

Partially fair, so I'll be precise about it: I used an LLM to help draft the English text, and I won't pretend otherwise. What I'd push back on is the implication that the content is machine-generated. The classification was extracted from a scanner running in production against mainnet; the fail-closed rules each correspond to a failure mode we actually hit (truncated indexer history, the P2PK blind spot of address-indexed APIs); the v0.2.0 revisions came from review finding real holes. The test vectors are independently verifiable either way. If the prose style gets in the way of review, I'll rewrite it plainer -- the semantics are what I'm asking people to check.

Quoted text (2 lines)
> My work on DropKick and Tadge Dryja's work on Lifeboat are both
> contingent on this source of truth

This is the kind of consumer I hoped existed. I'd like to understand what shape DropKick needs: per-output levels at a given height? A committed set? That determines whether script-level classification is sufficient or an outpoint-level profile is needed.

Quoted text (2 lines)
> Would every full node go back and reindex the entire chain to
> collect a list of exposed pubkeys/addresses?

The draft deliberately stops at semantics -- what conclusion is sound given what data -- so it doesn't presuppose any index. The constant-time floor exists for implementations that only have aggregate spent-counts. A node-level exposure index (or the address-index drive-by) would be the natural next layer, and worth its own thread; the semantics shouldn't depend on it.

Quoted text (1 line)
> I would check out Project Eleven's dashboard

I have -- the RISQ list is the closest prior art I know of. The gap it leaves is that a dashboard applies a methodology; it doesn't specify one that implementations can be checked against. Different tools citing 25%-34% is the symptom.

Quoted text (2 lines)
> If you consider the internal key + MAST root to be a preimage,
> then no: P2TR is actually usable without exposing that preimage

I think we're answering two different questions, and the draft should name both. Mine: can a CRQC spend it under current consensus rules? For P2TR yes -- solving the dlog of Q yields a key-path spend; the attacker never needs P or the MAST root. Yours: does the holder retain a secret a CRQC lacks, usable under a future rescue encumbrance ("prove knowledge of the internal key")? For BIP341-conformant outputs yes -- and your own point that some outputs may be bare untweaked keys, indistinguishable on-chain, is exactly why any such rescue confiscates an unknown fraction, and why the draft classifies by attacker-spendability (observable) rather than rescue-provability (not). I'll add a note distinguishing the two, crediting this exchange.

Quoted text (2 lines)
> I would say "unexposed" for this. The signer definitely knows
> something (a script) that the CRQC doesn't.

I believe we agree on substance and my naming misled you -- useful signal in itself. That case is EXPOSED_ON_SPEND in the draft, defined as "not disclosed; spending will necessarily publish key material" -- safe at rest, your reasoning exactly. NOT_EXPOSED is reserved for outputs whose spend reveals nothing reusable (P2MR script paths). If EXPOSED_ON_SPEND reads as "already exposed", the label is doing harm; EXPOSED_WHEN_SPENT may be better. Open to suggestions.

Quoted text (1 line)
> I strongly doubt we can do much about off-chain exposure.

Agreed -- the draft scopes it out for that reason. The levels state what the chain shows, nothing more.

I'll fold the P2TR note and the naming question into v0.3.0.

duncan0k

duncan0k in reply to duncan0k permalink original

A short update: the draft is at v0.4.0.

Changes since the original post, with credit:

- v0.2.0: the derived condition now requires a complete spend recipe;
  the partial-history UNDETERMINED rule is scoped to history-dependent
  classifications; the aggregate-counter floor notes its conservative
  over-approximation for revealed scripts that need no signature.
- v0.3.0, after conduition's review here: attacker-spendability is
  distinguished from holder-provability, with the P2TR discussion
  naming both questions. The EXPOSED_ON_SPEND naming problem is
  recorded as open, EXPOSED_WHEN_SPENT the leading alternative.
- v0.4.0, after wallet feedback on Delving: the Abstract and
  Motivation are rewritten to state the reasoning directly, opening
  with the misclassification in my own tooling that started this.
  Appendix A (informative) gives each level a fixed action key and a
  one-line floor; wallets own the wording above it.

Draft, reference implementation and vectors: https://github.com/duncan0k/pubkey-exposure-classification

I would still like to hear from anyone who considers this out of scope for a BIP. Absent that, I intend to open a pull request against bitcoin/bips in the coming weeks.

duncan0k

duncan0k in reply to duncan0k permalink original

The draft is now at v0.5.1 and submitted to the BIPs repository as an Informational BIP:

  https://github.com/bitcoin/bips/pull/2294

Two things changed since the v0.4.0 note, both after murch's review on Delving:

- The first two levels are now stated as BIP 360's long exposure and
  short exposure vulnerabilities, per output, and the draft cites that
  section as the source of the taxonomy.
- P2MR was wrong. My original post said a spend publishes key material
  for every type except P2MR. That is false: a leaf spend puts the leaf
  script, its Merkle path and a secp256k1 key in the mempool, the same
  race as P2WSH. P2MR is EXPOSED_ON_SPEND until post-quantum leaves
  exist, and NOT_EXPOSED has no member today.

Draft, reference implementation and 25 vectors:

  https://github.com/duncan0k/pubkey-exposure-classification

Publication of an Informational BIP depends on feedback on the PR, so if you have a view either way, that is where it counts.

duncan0k