public inbox for bitcoindev@googlegroups.com
 help / color / mirror / Atom feed
* [bitcoindev] OP_CIV - Post-Quantum Signature Aggregation
@ 2025-11-01 17:11 Tadge Dryja
  2025-11-01 22:56 ` 'conduition' via Bitcoin Development Mailing List
  0 siblings, 1 reply; 4+ messages in thread
From: Tadge Dryja @ 2025-11-01 17:11 UTC (permalink / raw)
  To: Bitcoin Development Mailing List


[-- Attachment #1.1: Type: text/plain, Size: 7705 bytes --]

Hello-

Here's an idea for Post-Quantum cross-input signature aggregation.  It's 
not quite "signature aggregation" the way we normally think of it, but 
gives similar benefits while not being tied to a particular signature 
scheme.
 
Folks have discussed Cross-input signature aggregation (CISA) in Bitcoin a 
while now, and while related research such as MuSig2, FROST, and ROAST have 
been implemented in wallets, so far there is no consensus change in bitcoin 
to enable CISA.  My hunch is that one of the reasons this hasn't been 
adopted is that the space savings aren't that large.  With taproot outputs, 
signatures are 64 bytes, and discounted to 16 vBytes. 
 https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/savings.org 
shows a 7.1% vByte savings using full aggregation.  Signatures just aren't 
that big of a part of the transaction, especially after the 75% segwit 
discount.

One place where the size of signatures *is* a problem is with post-quantum 
signatures.  The two most discussed PQ signature schemes, SPHINCS+ and 
CRYSTALS-Dilithium, both have pubkey+signature sizes in the kilobytes 
range.  This would be a great opportunity for CISA, since even with a 75% 
witness discount, signatures would cost over 90% of the vBytes in a 
transaction.

Unfortunately all the great EC based signature aggregation tools people 
have built don't work for lattices and hash-based signatures.  Here's a way 
to get some of the same effects which would work with any signature type 
(including EC signatures, but if you've got EC signatures, existing CISA 
techniques are much better).  I'm not attached to the name but for people 
familiar with bitcoin, the easiest to understand would be OP_CIV or 
OP_CHECKINPUTVERIFY.

The basic idea is that a transaction input can prove a linkage to another 
input within the same transaction, and by pointing to another input say 
"that's the signature I'm using", without providing one of its own.  Take 
for example a transaction with 2 inputs: input 0 and input 1.  Input 0 has 
a normal (perhaps PQ) SIGHASH_ALL signature.  Input 1 has a proof pointing 
to input 0.  Since input 0 exists within the transaction, input 1 is valid.

The arguments and usage of the would be:

<input_index> <output_index> <txid> <nonce> <OP_CIV> 

Where <input_index> is the input number in the current transaction being 
validated to look.  If this stack element isn't a number, or the number 
exceeds the number of inputs in the transaction, the opcode fails.

<output_index> and <txid> together form the outpoint, or UTXO identifier to 
look for at the <input_index> location.  If these two stack elements are 
malformed, or the resulting outpoint does not match the outpoint seen in 
the transaction, the opcode fails.

<nonce> is popped off the stack and discarded.  It can be OP_0, but random 
bytes here can protect privacy.  After an output is spent revealing the 
taptree, someone could try to grind through other possible outpoints to see 
if they show up elsewhere in the tree, trying to assign UTXOs to the same 
owner.  This nonce would prevent such an attack.

That's pretty much it for script evaluation.

The idea would be that a taproot tree would have at the root a "normal" 
pubkey capable of creating arbitrary signatures.  Lower down in the tree, 
there would be several / many OP_CIV scripts, each one pointing to a 
different outpoint.  When a UTXO is being spent, if it is being spent in 
the same transaction as any of the UTXOs pointed to by the OP_CIV scripts, 
one of those can be revealed instead of supplying a signature.  At least 
one input in a transaction would have a normal signature; it's not possible 
for every input in a transaction to use OP_CIV since that would require a 
hash cycle.

For the wallet side implementation, every time a wallet generates a new 
address, it looks up some or all of the current UTXOs in the wallet, and 
adds a branch for each of them in the taproot tree.  The wallet adds 
blinding data to each OP_CIV script to prevent an attacker from being able 
to guess other UTXO linkages other than those explicitly revealed.  The 
last argument, <input_index>, is left empty in the script and supplied at 
spending time.  To avoid the need to generate and store additional entropy, 
the wallet can generate the blinding data deterministically, using the root 
pubkey's private key and the outpoint being pointed to, somewhat like the 
use of RFC6979 for ECDSA nonces.  (Eg nonce = hash(private_key, outpoint)).

Wallets constructed in such a way would often only need 1 signature per 
transaction, as all other UTXOs could point to the oldest input in the 
transaction.  This savings doesn't work when a new wallet generates many 
addresses at once, and then over time coins are sent to those addresses. 
 In that case a wallet would end up with a number of UTXOs which don't 
point to each other.  Those UTXOs would all need to sign, but they might be 
paired with later UTXOs which point to them. 

Deterministic key wallets

One complication is key recovery for deterministic wallets.  If only the 
master key / seed phrase is known, all the root pubkeys can be recovered, 
but the wallet has "forgotten" which pubkeys point to which UTXOs. 
 Deterministic nonces make recovery possible, but in a naive 
implementation, there would be an exponential blowup if when addresses are 
created they point to all existing UTXOs in the wallet.  There are several 
workarounds, such as limiting the number of OP_CIV scripts in the tree to 
eg 10 (resulting in a ~1000X slowdown in recovery while maintaining a good 
chance of OP_CIV use), or including OP_CIV scripts pointing to all TXOs 
that the wallet knows have already been spent, increasing taptree size but 
reducing the number of guesses needed for recovery.

Address re-use and replay attacks 

I don't think replay attacks are too much of a problem here.  I thought it 
might be, but OP_CIV points to outpoints, not addresses or keys, so address 
reuse for the UTXOs being pointed to shouldn't matter.  For address re-use 
with addresses that have OP_CIV scripts in them, replay attacks are avoided 
by using SIGHASH_ALL in the input that does sign, so that even if an 
attacker learns the full taptree of all the UTXOs of a wallet, they can't 
construct or modify a transaction without the ability to sign.

Other uses

There might be other contract use cases for such an opcode even today.  I 
haven't come up with one, but it gives a tool where you can reveal a secret 
(a spend path & nonce) that allows someone to take a UTXO, but only if they 
already control a different specified UTXO.  I think it's mostly useful for 
making PQ transactions smaller, but transaction introspection opcodes often 
have interesting use cases and OP_CIV may as well

Real life example of OP_CIV commitments

I gave a talk about this at TABConf a couple weeks ago; I was hoping to 
have sent this writeup out before the talk but didn't have time.  That 
means that my TABConf talk was not able to link to this mailing list post. 
 But that also means that this mailing list post is able to link to the 
TABConf talk: https://www.youtube.com/watch?v=cqjo3rmd6hY.

Wonder if anyone has ideas / improvements / downsides to this idea.  Thanks 
for any feedback!
-Tadge

-- 
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/05195086-ee52-472c-962d-0df2e0b9dca2n%40googlegroups.com.

[-- Attachment #1.2: Type: text/html, Size: 8300 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [bitcoindev] OP_CIV - Post-Quantum Signature Aggregation
  2025-11-01 17:11 [bitcoindev] OP_CIV - Post-Quantum Signature Aggregation Tadge Dryja
@ 2025-11-01 22:56 ` 'conduition' via Bitcoin Development Mailing List
  2025-11-02  1:34   ` Boris Nagaev
  0 siblings, 1 reply; 4+ messages in thread
From: 'conduition' via Bitcoin Development Mailing List @ 2025-11-01 22:56 UTC (permalink / raw)
  To: Tadge Dryja; +Cc: Bitcoin Development Mailing List


[-- Attachment #1.1.1: Type: text/plain, Size: 9894 bytes --]

Neat idea! The need to commit each script pubkey to other prevouts in the TX would probably hold the concept back from being practical, especially for deterministic backup wallets which is likely the bulk of modern Bitcoin usage. I could imagine offline/hardware wallets having a very tough time with this.

Consider a more conservative (but also very common) use case: Aggregating inputs controlled by the same owner. In this context, what the sender is really trying to prove here isn't whether UTXO A committed to UTXO B. For signature aggregation across commonly-owned inputs, they just need to be able to prove that UTXO A and UTXO B are spendable under the same pubkey, and that they, the pubkey owner, authorized both of them via a single signature.

So instead of committing a taptree to pre-existing UTXOs (which creates statefulness), you could commit a taptree to a deterministic set of pubkeys, such as "the nearest 100 addresses in the same BIP32 account". At spending time, we reveal the same pubkey's script leaf on all inputs, plus a signature that covers all the inputs. This would allow stateless address generation, while also allowing a single signature to cover all common inputs in a wallet.

This would have pretty bad effects on UTXO privacy, because the common-owner heuristic would become even stronger and would be provable on-chain, but OP_CIV would also likely have a similar effect on chain forensics. Maybe the fee savings would be worth it, esp for big exchanges which consolidate hundreds or thousands of UTXOs at a time.

-conduition
On Saturday, November 1st, 2025 at 2:02 PM, Tadge Dryja <rx@awsomnet•org> wrote:

> Hello-
> 

> Here's an idea for Post-Quantum cross-input signature aggregation. It's not quite "signature aggregation" the way we normally think of it, but gives similar benefits while not being tied to a particular signature scheme.
> 

> Folks have discussed Cross-input signature aggregation (CISA) in Bitcoin a while now, and while related research such as MuSig2, FROST, and ROAST have been implemented in wallets, so far there is no consensus change in bitcoin to enable CISA. My hunch is that one of the reasons this hasn't been adopted is that the space savings aren't that large. With taproot outputs, signatures are 64 bytes, and discounted to 16 vBytes. https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/savings.org shows a 7.1% vByte savings using full aggregation. Signatures just aren't that big of a part of the transaction, especially after the 75% segwit discount.
> 

> One place where the size of signatures *is* a problem is with post-quantum signatures. The two most discussed PQ signature schemes, SPHINCS+ and CRYSTALS-Dilithium, both have pubkey+signature sizes in the kilobytes range. This would be a great opportunity for CISA, since even with a 75% witness discount, signatures would cost over 90% of the vBytes in a transaction.
> 

> Unfortunately all the great EC based signature aggregation tools people have built don't work for lattices and hash-based signatures. Here's a way to get some of the same effects which would work with any signature type (including EC signatures, but if you've got EC signatures, existing CISA techniques are much better). I'm not attached to the name but for people familiar with bitcoin, the easiest to understand would be OP_CIV or OP_CHECKINPUTVERIFY.
> 

> The basic idea is that a transaction input can prove a linkage to another input within the same transaction, and by pointing to another input say "that's the signature I'm using", without providing one of its own. Take for example a transaction with 2 inputs: input 0 and input 1. Input 0 has a normal (perhaps PQ) SIGHASH_ALL signature. Input 1 has a proof pointing to input 0. Since input 0 exists within the transaction, input 1 is valid.
> 

> The arguments and usage of the would be:
> 

> <input_index> <output_index> <txid> <nonce> <OP_CIV>
> 

> Where <input_index> is the input number in the current transaction being validated to look. If this stack element isn't a number, or the number exceeds the number of inputs in the transaction, the opcode fails.
> 

> <output_index> and <txid> together form the outpoint, or UTXO identifier to look for at the <input_index> location. If these two stack elements are malformed, or the resulting outpoint does not match the outpoint seen in the transaction, the opcode fails.
> 

> <nonce> is popped off the stack and discarded. It can be OP_0, but random bytes here can protect privacy. After an output is spent revealing the taptree, someone could try to grind through other possible outpoints to see if they show up elsewhere in the tree, trying to assign UTXOs to the same owner. This nonce would prevent such an attack.
> 

> That's pretty much it for script evaluation.
> 

> The idea would be that a taproot tree would have at the root a "normal" pubkey capable of creating arbitrary signatures. Lower down in the tree, there would be several / many OP_CIV scripts, each one pointing to a different outpoint. When a UTXO is being spent, if it is being spent in the same transaction as any of the UTXOs pointed to by the OP_CIV scripts, one of those can be revealed instead of supplying a signature. At least one input in a transaction would have a normal signature; it's not possible for every input in a transaction to use OP_CIV since that would require a hash cycle.
> 

> For the wallet side implementation, every time a wallet generates a new address, it looks up some or all of the current UTXOs in the wallet, and adds a branch for each of them in the taproot tree. The wallet adds blinding data to each OP_CIV script to prevent an attacker from being able to guess other UTXO linkages other than those explicitly revealed. The last argument, <input_index>, is left empty in the script and supplied at spending time. To avoid the need to generate and store additional entropy, the wallet can generate the blinding data deterministically, using the root pubkey's private key and the outpoint being pointed to, somewhat like the use of RFC6979 for ECDSA nonces. (Eg nonce = hash(private_key, outpoint)).
> 

> Wallets constructed in such a way would often only need 1 signature per transaction, as all other UTXOs could point to the oldest input in the transaction. This savings doesn't work when a new wallet generates many addresses at once, and then over time coins are sent to those addresses. In that case a wallet would end up with a number of UTXOs which don't point to each other. Those UTXOs would all need to sign, but they might be paired with later UTXOs which point to them.
> 

> Deterministic key wallets
> 

> One complication is key recovery for deterministic wallets. If only the master key / seed phrase is known, all the root pubkeys can be recovered, but the wallet has "forgotten" which pubkeys point to which UTXOs. Deterministic nonces make recovery possible, but in a naive implementation, there would be an exponential blowup if when addresses are created they point to all existing UTXOs in the wallet. There are several workarounds, such as limiting the number of OP_CIV scripts in the tree to eg 10 (resulting in a ~1000X slowdown in recovery while maintaining a good chance of OP_CIV use), or including OP_CIV scripts pointing to all TXOs that the wallet knows have already been spent, increasing taptree size but reducing the number of guesses needed for recovery.
> 

> Address re-use and replay attacks
> 

> I don't think replay attacks are too much of a problem here. I thought it might be, but OP_CIV points to outpoints, not addresses or keys, so address reuse for the UTXOs being pointed to shouldn't matter. For address re-use with addresses that have OP_CIV scripts in them, replay attacks are avoided by using SIGHASH_ALL in the input that does sign, so that even if an attacker learns the full taptree of all the UTXOs of a wallet, they can't construct or modify a transaction without the ability to sign.
> 

> Other uses
> 

> There might be other contract use cases for such an opcode even today. I haven't come up with one, but it gives a tool where you can reveal a secret (a spend path & nonce) that allows someone to take a UTXO, but only if they already control a different specified UTXO. I think it's mostly useful for making PQ transactions smaller, but transaction introspection opcodes often have interesting use cases and OP_CIV may as well
> 

> Real life example of OP_CIV commitments
> 

> I gave a talk about this at TABConf a couple weeks ago; I was hoping to have sent this writeup out before the talk but didn't have time. That means that my TABConf talk was not able to link to this mailing list post. But that also means that this mailing list post is able to link to the TABConf talk: https://www.youtube.com/watch?v=cqjo3rmd6hY.
> 

> Wonder if anyone has ideas / improvements / downsides to this idea. Thanks for any feedback!
> -Tadge
> 

> --
> 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/05195086-ee52-472c-962d-0df2e0b9dca2n%40googlegroups.com.

-- 
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/kBNJjQ46doVAAXAOjzIoBdtX4UikDa2YCqKdPbzEK-QjbBUHdl2V_5T-Ay6Z76lnj5BHmrAWg9FS1eHT_PgJmoEFFuPkWt5i8LPEvY3wmKk%3D%40proton.me.

[-- Attachment #1.1.2.1: Type: text/html, Size: 11626 bytes --]

[-- Attachment #1.2: publickey - conduition@proton.me - 0x474891AD.asc --]
[-- Type: application/pgp-keys, Size: 649 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 343 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [bitcoindev] OP_CIV - Post-Quantum Signature Aggregation
  2025-11-01 22:56 ` 'conduition' via Bitcoin Development Mailing List
@ 2025-11-02  1:34   ` Boris Nagaev
  2025-11-02 18:47     ` adiabat
  0 siblings, 1 reply; 4+ messages in thread
From: Boris Nagaev @ 2025-11-02  1:34 UTC (permalink / raw)
  To: Bitcoin Development Mailing List


[-- Attachment #1.1: Type: text/plain, Size: 11203 bytes --]

Hi Tadge, Conduition, and all,

I think Conduition's stateless take can go a little further with a simple 
indexing trick. Give every address a monotonic index i, and from the seed 
derive a long sequence of shared keys K_0, K_1, .... When we create address 
i, we add taproot leaves for K_i through K_{i+N-1}. That is a sliding 
window of size N.

When a spend gathers a set of our inputs, let i_min be the smallest index 
and i_max the largest. If i_max - i_min < N, every input already has a leaf 
for K_{i_max}, so we reveal that leaf everywhere and sign once under 
K_{i_max}. No state needed: the rule is deterministic.

Because an index i is spent only once, the first spend that touches it is 
the only transaction that ever reveals K_i. Backups stay simple too: any 
device with the master seed can recompute the indices and shared keys 
without knowing past wallet state, as long as it knows N.

Larger N means more leaves per address but keeps aggregation working across 
older UTXOs. Wallets that need giant sweeps can still consolidate inside 
windows. The number of full signatures in a transaction is the number of 
windows inputs belong to.

Curious whether this sounds workable.

Best,
Boris

On Saturday, November 1, 2025 at 8:09:52 PM UTC-3 conduition wrote:

Neat idea! The need to commit each script pubkey to other prevouts in the 
TX would probably hold the concept back from being practical, especially 
for deterministic backup wallets which is likely the bulk of modern Bitcoin 
usage. I could imagine offline/hardware wallets having a very tough time 
with this.

Consider a more conservative (but also very common) use case: Aggregating 
inputs controlled by the same owner. In this context, what the sender is 
really trying to prove here isn't whether UTXO A committed to UTXO B. For 
signature aggregation across commonly-owned inputs, they just need to be 
able to prove that UTXO A and UTXO B are spendable under the same pubkey, 
and that they, the pubkey owner, authorized both of them via a single 
signature.

So instead of committing a taptree to pre-existing UTXOs (which creates 
statefulness), you could commit a taptree to a deterministic set of 
pubkeys, such as "the nearest 100 addresses in the same BIP32 account". At 
spending time, we reveal the same pubkey's script leaf on all inputs, plus 
a signature that covers all the inputs. This would allow stateless address 
generation, while also allowing a single signature to cover all common 
inputs in a wallet.

This would have pretty bad effects on UTXO privacy, because the 
common-owner heuristic would become even stronger and would be provable 
on-chain, but OP_CIV would also likely have a similar effect on chain 
forensics. Maybe the fee savings would be worth it, esp for big exchanges 
which consolidate hundreds or thousands of UTXOs at a time.

-conduition
On Saturday, November 1st, 2025 at 2:02 PM, Tadge Dryja <r...@awsomnet•org> 
wrote:

Hello-

Here's an idea for Post-Quantum cross-input signature aggregation. It's not 
quite "signature aggregation" the way we normally think of it, but gives 
similar benefits while not being tied to a particular signature scheme.
Folks have discussed Cross-input signature aggregation (CISA) in Bitcoin a 
while now, and while related research such as MuSig2, FROST, and ROAST have 
been implemented in wallets, so far there is no consensus change in bitcoin 
to enable CISA. My hunch is that one of the reasons this hasn't been 
adopted is that the space savings aren't that large. With taproot outputs, 
signatures are 64 bytes, and discounted to 16 vBytes. 
https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/savings.org 
shows a 7.1% vByte savings using full aggregation. Signatures just aren't 
that big of a part of the transaction, especially after the 75% segwit 
discount.

One place where the size of signatures *is* a problem is with post-quantum 
signatures. The two most discussed PQ signature schemes, SPHINCS+ and 
CRYSTALS-Dilithium, both have pubkey+signature sizes in the kilobytes 
range. This would be a great opportunity for CISA, since even with a 75% 
witness discount, signatures would cost over 90% of the vBytes in a 
transaction.

Unfortunately all the great EC based signature aggregation tools people 
have built don't work for lattices and hash-based signatures. Here's a way 
to get some of the same effects which would work with any signature type 
(including EC signatures, but if you've got EC signatures, existing CISA 
techniques are much better). I'm not attached to the name but for people 
familiar with bitcoin, the easiest to understand would be OP_CIV or 
OP_CHECKINPUTVERIFY.

The basic idea is that a transaction input can prove a linkage to another 
input within the same transaction, and by pointing to another input say 
"that's the signature I'm using", without providing one of its own. Take 
for example a transaction with 2 inputs: input 0 and input 1. Input 0 has a 
normal (perhaps PQ) SIGHASH_ALL signature. Input 1 has a proof pointing to 
input 0. Since input 0 exists within the transaction, input 1 is valid.

The arguments and usage of the would be:

<input_index> <output_index> <txid> <nonce> <OP_CIV> 

Where <input_index> is the input number in the current transaction being 
validated to look. If this stack element isn't a number, or the number 
exceeds the number of inputs in the transaction, the opcode fails.

<output_index> and <txid> together form the outpoint, or UTXO identifier to 
look for at the <input_index> location. If these two stack elements are 
malformed, or the resulting outpoint does not match the outpoint seen in 
the transaction, the opcode fails.

<nonce> is popped off the stack and discarded. It can be OP_0, but random 
bytes here can protect privacy. After an output is spent revealing the 
taptree, someone could try to grind through other possible outpoints to see 
if they show up elsewhere in the tree, trying to assign UTXOs to the same 
owner. This nonce would prevent such an attack.

That's pretty much it for script evaluation.

The idea would be that a taproot tree would have at the root a "normal" 
pubkey capable of creating arbitrary signatures. Lower down in the tree, 
there would be several / many OP_CIV scripts, each one pointing to a 
different outpoint. When a UTXO is being spent, if it is being spent in the 
same transaction as any of the UTXOs pointed to by the OP_CIV scripts, one 
of those can be revealed instead of supplying a signature. At least one 
input in a transaction would have a normal signature; it's not possible for 
every input in a transaction to use OP_CIV since that would require a hash 
cycle.

For the wallet side implementation, every time a wallet generates a new 
address, it looks up some or all of the current UTXOs in the wallet, and 
adds a branch for each of them in the taproot tree. The wallet adds 
blinding data to each OP_CIV script to prevent an attacker from being able 
to guess other UTXO linkages other than those explicitly revealed. The last 
argument, <input_index>, is left empty in the script and supplied at 
spending time. To avoid the need to generate and store additional entropy, 
the wallet can generate the blinding data deterministically, using the root 
pubkey's private key and the outpoint being pointed to, somewhat like the 
use of RFC6979 for ECDSA nonces. (Eg nonce = hash(private_key, outpoint)).

Wallets constructed in such a way would often only need 1 signature per 
transaction, as all other UTXOs could point to the oldest input in the 
transaction. This savings doesn't work when a new wallet generates many 
addresses at once, and then over time coins are sent to those addresses. In 
that case a wallet would end up with a number of UTXOs which don't point to 
each other. Those UTXOs would all need to sign, but they might be paired 
with later UTXOs which point to them. 

Deterministic key wallets

One complication is key recovery for deterministic wallets. If only the 
master key / seed phrase is known, all the root pubkeys can be recovered, 
but the wallet has "forgotten" which pubkeys point to which UTXOs. 
Deterministic nonces make recovery possible, but in a naive implementation, 
there would be an exponential blowup if when addresses are created they 
point to all existing UTXOs in the wallet. There are several workarounds, 
such as limiting the number of OP_CIV scripts in the tree to eg 10 
(resulting in a ~1000X slowdown in recovery while maintaining a good chance 
of OP_CIV use), or including OP_CIV scripts pointing to all TXOs that the 
wallet knows have already been spent, increasing taptree size but reducing 
the number of guesses needed for recovery.

Address re-use and replay attacks 

I don't think replay attacks are too much of a problem here. I thought it 
might be, but OP_CIV points to outpoints, not addresses or keys, so address 
reuse for the UTXOs being pointed to shouldn't matter. For address re-use 
with addresses that have OP_CIV scripts in them, replay attacks are avoided 
by using SIGHASH_ALL in the input that does sign, so that even if an 
attacker learns the full taptree of all the UTXOs of a wallet, they can't 
construct or modify a transaction without the ability to sign.

Other uses

There might be other contract use cases for such an opcode even today. I 
haven't come up with one, but it gives a tool where you can reveal a secret 
(a spend path & nonce) that allows someone to take a UTXO, but only if they 
already control a different specified UTXO. I think it's mostly useful for 
making PQ transactions smaller, but transaction introspection opcodes often 
have interesting use cases and OP_CIV may as well

Real life example of OP_CIV commitments

I gave a talk about this at TABConf a couple weeks ago; I was hoping to 
have sent this writeup out before the talk but didn't have time. That means 
that my TABConf talk was not able to link to this mailing list post. But 
that also means that this mailing list post is able to link to the TABConf 
talk: https://www.youtube.com/watch?v=cqjo3rmd6hY.

Wonder if anyone has ideas / improvements / downsides to this idea. Thanks 
for any feedback!
-Tadge 

-- 
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+...@googlegroups•com.
To view this discussion visit 
https://groups.google.com/d/msgid/bitcoindev/05195086-ee52-472c-962d-0df2e0b9dca2n%40googlegroups.com
.


-- 
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/19701913-9225-45b3-8a2c-d620c53d8873n%40googlegroups.com.

[-- Attachment #1.2: Type: text/html, Size: 13141 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [bitcoindev] OP_CIV - Post-Quantum Signature Aggregation
  2025-11-02  1:34   ` Boris Nagaev
@ 2025-11-02 18:47     ` adiabat
  0 siblings, 0 replies; 4+ messages in thread
From: adiabat @ 2025-11-02 18:47 UTC (permalink / raw)
  To: Bitcoin Development Mailing List

Hi Conduition & Boris-

(I wrote a response yesterday but it's gone, I think I used the google
groups interface instead of the e-mail client... anyway if a similar /
duplicate response ends up on this mailing list, oops)

I agree that using "addresses" (scriptPubKeys) (yeah I know they're
not quite the same thing but close enough :) ) instead of outpoints
would probably work, and would have some advantages.  But I lean
towards using outpoints mostly for privacy reasons.

It seems that if you're OK with having 1 address link to another
address, then an obvious optimization would be to have the addresses
implicitly linked to themselves.  So if a transaction has 2 inputs,
both with the same scriptPubKey, only 1 of them needs to sign.  Why
bother sending and verifying 2 signatures from the same key?

But if you do implement that, then the incentive for all wallets is to
just use 1 address.  Because not only is it simpler, it's also
cheaper.

Part of why I like linking to outpoints is that it disincentivizes
address re-use.  If you just keep using the same address many times,
you can't use OP_CIV, and can't get any fee savings through it.  If
instead you generate a new address each time you need one, and that
address commits to some / most of your UTXOs at the time, then you can
use OP_CIV and will have lower fees when spending.  You could, in
theory, use the same root pubkey and *only* change which OP_CIV
branches you add, but that seems like a bad idea: if you're changing
the address anyway, why not change the pubkey.

A related reason is that in SPHINCS+ and other hash-based signature
designs, one of the most important parameters is how many times a
pubkey can be re-used before becoming insecure.  Since in bitcoin we
tend to want to discourage address re-use, it makes sense to reduce
that parameter, which would give smaller, faster to verify signatures,
at the cost of not allowing many instances of address re-use.

These are somewhat subjective reasons, and I agree that in terms of
coding a wallet to do this stuff, having addresses point to each other
is probably easier than having addresses point to outpoints.  I think
the privacy gains could be worth using outpoints, but even then, maybe
there's a better way that can really preserve privacy, and be used
with coinjoin transactions.  (OP_CIV maybe could but only with
multisig outputs set up beforehand, which seems impractical.)

Thanks for feedback on this & I will also keep looking at it

-Tadge







On Sat, Nov 1, 2025 at 9:45 PM Boris Nagaev <bnagaev@gmail•com> wrote:
>
> Hi Tadge, Conduition, and all,
>
> I think Conduition's stateless take can go a little further with a simple indexing trick. Give every address a monotonic index i, and from the seed derive a long sequence of shared keys K_0, K_1, .... When we create address i, we add taproot leaves for K_i through K_{i+N-1}. That is a sliding window of size N.
>
> When a spend gathers a set of our inputs, let i_min be the smallest index and i_max the largest. If i_max - i_min < N, every input already has a leaf for K_{i_max}, so we reveal that leaf everywhere and sign once under K_{i_max}. No state needed: the rule is deterministic.
>
> Because an index i is spent only once, the first spend that touches it is the only transaction that ever reveals K_i. Backups stay simple too: any device with the master seed can recompute the indices and shared keys without knowing past wallet state, as long as it knows N.
>
> Larger N means more leaves per address but keeps aggregation working across older UTXOs. Wallets that need giant sweeps can still consolidate inside windows. The number of full signatures in a transaction is the number of windows inputs belong to.
>
> Curious whether this sounds workable.
>
> Best,
> Boris
>
> On Saturday, November 1, 2025 at 8:09:52 PM UTC-3 conduition wrote:
>
> Neat idea! The need to commit each script pubkey to other prevouts in the TX would probably hold the concept back from being practical, especially for deterministic backup wallets which is likely the bulk of modern Bitcoin usage. I could imagine offline/hardware wallets having a very tough time with this.
>
> Consider a more conservative (but also very common) use case: Aggregating inputs controlled by the same owner. In this context, what the sender is really trying to prove here isn't whether UTXO A committed to UTXO B. For signature aggregation across commonly-owned inputs, they just need to be able to prove that UTXO A and UTXO B are spendable under the same pubkey, and that they, the pubkey owner, authorized both of them via a single signature.
>
> So instead of committing a taptree to pre-existing UTXOs (which creates statefulness), you could commit a taptree to a deterministic set of pubkeys, such as "the nearest 100 addresses in the same BIP32 account". At spending time, we reveal the same pubkey's script leaf on all inputs, plus a signature that covers all the inputs. This would allow stateless address generation, while also allowing a single signature to cover all common inputs in a wallet.
>
> This would have pretty bad effects on UTXO privacy, because the common-owner heuristic would become even stronger and would be provable on-chain, but OP_CIV would also likely have a similar effect on chain forensics. Maybe the fee savings would be worth it, esp for big exchanges which consolidate hundreds or thousands of UTXOs at a time.
>
> -conduition
> On Saturday, November 1st, 2025 at 2:02 PM, Tadge Dryja <r...@awsomnet•org> wrote:
>
> Hello-
>
> Here's an idea for Post-Quantum cross-input signature aggregation. It's not quite "signature aggregation" the way we normally think of it, but gives similar benefits while not being tied to a particular signature scheme.
> Folks have discussed Cross-input signature aggregation (CISA) in Bitcoin a while now, and while related research such as MuSig2, FROST, and ROAST have been implemented in wallets, so far there is no consensus change in bitcoin to enable CISA. My hunch is that one of the reasons this hasn't been adopted is that the space savings aren't that large. With taproot outputs, signatures are 64 bytes, and discounted to 16 vBytes. https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/savings.org shows a 7.1% vByte savings using full aggregation. Signatures just aren't that big of a part of the transaction, especially after the 75% segwit discount.
>
> One place where the size of signatures *is* a problem is with post-quantum signatures. The two most discussed PQ signature schemes, SPHINCS+ and CRYSTALS-Dilithium, both have pubkey+signature sizes in the kilobytes range. This would be a great opportunity for CISA, since even with a 75% witness discount, signatures would cost over 90% of the vBytes in a transaction.
>
> Unfortunately all the great EC based signature aggregation tools people have built don't work for lattices and hash-based signatures. Here's a way to get some of the same effects which would work with any signature type (including EC signatures, but if you've got EC signatures, existing CISA techniques are much better). I'm not attached to the name but for people familiar with bitcoin, the easiest to understand would be OP_CIV or OP_CHECKINPUTVERIFY.
>
> The basic idea is that a transaction input can prove a linkage to another input within the same transaction, and by pointing to another input say "that's the signature I'm using", without providing one of its own. Take for example a transaction with 2 inputs: input 0 and input 1. Input 0 has a normal (perhaps PQ) SIGHASH_ALL signature. Input 1 has a proof pointing to input 0. Since input 0 exists within the transaction, input 1 is valid.
>
> The arguments and usage of the would be:
>
> <input_index> <output_index> <txid> <nonce> <OP_CIV>
>
> Where <input_index> is the input number in the current transaction being validated to look. If this stack element isn't a number, or the number exceeds the number of inputs in the transaction, the opcode fails.
>
> <output_index> and <txid> together form the outpoint, or UTXO identifier to look for at the <input_index> location. If these two stack elements are malformed, or the resulting outpoint does not match the outpoint seen in the transaction, the opcode fails.
>
> <nonce> is popped off the stack and discarded. It can be OP_0, but random bytes here can protect privacy. After an output is spent revealing the taptree, someone could try to grind through other possible outpoints to see if they show up elsewhere in the tree, trying to assign UTXOs to the same owner. This nonce would prevent such an attack.
>
> That's pretty much it for script evaluation.
>
> The idea would be that a taproot tree would have at the root a "normal" pubkey capable of creating arbitrary signatures. Lower down in the tree, there would be several / many OP_CIV scripts, each one pointing to a different outpoint. When a UTXO is being spent, if it is being spent in the same transaction as any of the UTXOs pointed to by the OP_CIV scripts, one of those can be revealed instead of supplying a signature. At least one input in a transaction would have a normal signature; it's not possible for every input in a transaction to use OP_CIV since that would require a hash cycle.
>
> For the wallet side implementation, every time a wallet generates a new address, it looks up some or all of the current UTXOs in the wallet, and adds a branch for each of them in the taproot tree. The wallet adds blinding data to each OP_CIV script to prevent an attacker from being able to guess other UTXO linkages other than those explicitly revealed. The last argument, <input_index>, is left empty in the script and supplied at spending time. To avoid the need to generate and store additional entropy, the wallet can generate the blinding data deterministically, using the root pubkey's private key and the outpoint being pointed to, somewhat like the use of RFC6979 for ECDSA nonces. (Eg nonce = hash(private_key, outpoint)).
>
> Wallets constructed in such a way would often only need 1 signature per transaction, as all other UTXOs could point to the oldest input in the transaction. This savings doesn't work when a new wallet generates many addresses at once, and then over time coins are sent to those addresses. In that case a wallet would end up with a number of UTXOs which don't point to each other. Those UTXOs would all need to sign, but they might be paired with later UTXOs which point to them.
>
> Deterministic key wallets
>
> One complication is key recovery for deterministic wallets. If only the master key / seed phrase is known, all the root pubkeys can be recovered, but the wallet has "forgotten" which pubkeys point to which UTXOs. Deterministic nonces make recovery possible, but in a naive implementation, there would be an exponential blowup if when addresses are created they point to all existing UTXOs in the wallet. There are several workarounds, such as limiting the number of OP_CIV scripts in the tree to eg 10 (resulting in a ~1000X slowdown in recovery while maintaining a good chance of OP_CIV use), or including OP_CIV scripts pointing to all TXOs that the wallet knows have already been spent, increasing taptree size but reducing the number of guesses needed for recovery.
>
> Address re-use and replay attacks
>
> I don't think replay attacks are too much of a problem here. I thought it might be, but OP_CIV points to outpoints, not addresses or keys, so address reuse for the UTXOs being pointed to shouldn't matter. For address re-use with addresses that have OP_CIV scripts in them, replay attacks are avoided by using SIGHASH_ALL in the input that does sign, so that even if an attacker learns the full taptree of all the UTXOs of a wallet, they can't construct or modify a transaction without the ability to sign.
>
> Other uses
>
> There might be other contract use cases for such an opcode even today. I haven't come up with one, but it gives a tool where you can reveal a secret (a spend path & nonce) that allows someone to take a UTXO, but only if they already control a different specified UTXO. I think it's mostly useful for making PQ transactions smaller, but transaction introspection opcodes often have interesting use cases and OP_CIV may as well
>
> Real life example of OP_CIV commitments
>
> I gave a talk about this at TABConf a couple weeks ago; I was hoping to have sent this writeup out before the talk but didn't have time. That means that my TABConf talk was not able to link to this mailing list post. But that also means that this mailing list post is able to link to the TABConf talk: https://www.youtube.com/watch?v=cqjo3rmd6hY.
>
> Wonder if anyone has ideas / improvements / downsides to this idea. Thanks for any feedback!
> -Tadge
>
> --
> 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+...@googlegroups•com.
> To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/05195086-ee52-472c-962d-0df2e0b9dca2n%40googlegroups.com.
>
>
> --
> 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/19701913-9225-45b3-8a2c-d620c53d8873n%40googlegroups.com.

-- 
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/CAKEeUhhagHCBCd%2Bdgddq8xG4285u_2Li0vKLxvVsdQkGH3gL-w%40mail.gmail.com.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-11-02 19:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-01 17:11 [bitcoindev] OP_CIV - Post-Quantum Signature Aggregation Tadge Dryja
2025-11-01 22:56 ` 'conduition' via Bitcoin Development Mailing List
2025-11-02  1:34   ` Boris Nagaev
2025-11-02 18:47     ` adiabat

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox