Bitcoin Forum

Bitcoin => Development & Technical Discussion => Topic started by: pooya87 on December 08, 2021, 05:32:47 AM



Title: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on December 08, 2021, 05:32:47 AM
I never could figure out where would OP_CODESEPARATOR be useful and some very old comments from early days suggest it may be a byproduct of a bad decision (eg. scripts were concatenated then executed at first).
While going through Taproot this OP code makes even less sense in Tapscripts and yet it is there. So I was wondering if I'm missing something and whether there is any good script examples I could look at where OP_CODESEPARATOR solves a problem that can't be solved in any other way.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: NotATether on December 08, 2021, 05:47:29 AM
Looking at this diagram of OP_CHECKSIG steps (https://en.bitcoin.it/w/images/en/7/70/Bitcoin_OpCheckSig_InDetail.png) and the Bitcoin Script wiki (https://en.bitcoin.it/wiki/Script), OP_CODESEPARATOR is used to make OP_CHECKSIG check only part of the scriptPubKey. Essentially, only the script that goes after the last OP_CODESEPARATOR is used to sign the transaction and hence evaluated by OP_CHECKSIG.

In theory, a spending transaction could change the part of the scriptPubKey before the last OP_CODESEPARATOR of the input transaction.

However, there's a problem. Since scriptPubKey comes from the input transaction, you can't actually modify any part of the scriptPubKey (even the part before the last OP_CODESEPARATOR) without changing the input transaction hash. Changing the input transaction hash breaks the link for any transaction trying to spend outputs from the unspent input transaction, making it impossible to use OP_CODESEPARATOR in practice.

I wasn't able to find any cases in practice where people have successfully used OP_CODESEPARATOR for a useful purpose, although it does show up in the blockchain. See this (http://sourceforge.net/p/bitcoin/mailman/message/29593157/), this (https://github.com/bitcoin/bips/blob/master/bip-0017.mediawiki) and this (https://bitcointalk.org/index.php?topic=164655.0) (and the thread you referenced) for core developers commenting on or discussing possible uses for OP_CODESEPARATOR.

It has no practical use because it has a side effect that any part of the public key you modify will change the transaction hash.

Granted, I've only ever seen OP_CODESEPARATOR scripts inside Bitcoin Core unit tests.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on December 08, 2021, 06:20:07 AM
I'm aware of the mechanics, the same mechanics that no longer exist in Tapscripts since the script is no longer changed, we just add the OP position to the bytes we are hashing when computing the sighash. Which raises the question of why wasn't this OP code removed/disabled like OP_CHECKMULTISIG(VERIFY).

It has no practical use because it has a side effect that any part of the public key you modify will change the transaction hash.
You don't need to modify the pubkey scripts and also there are redeem scripts that this OP code could be used in. And it seems that there are some use cases (which still don't make sense to me since there are alternative ways) that this OP code could come in play so I'm hoping for more or better examples.
Example: https://lists.linuxfoundation.org/pipermail/lightning-dev/2016-March/000455.html


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: garlonicon on December 08, 2021, 06:30:46 AM
If you have "<pubkey> OP_CHECKSIG" in scriptPubKey and "<sig>" in scriptSig, you don't need OP_CODESEPARATOR. But if you have "<sig> <pubkey> OP_CHECKSIG" in scriptPubKey, then your signature has to sign itself, because you put scriptPubKey of the previous output when calculating z-value to sign for transaction. On the other hand, if you have "<sig> OP_CODESEPARATOR <pubkey> OP_CHECKSIG" in scriptPubKey, then your z-value is independent from your "<sig>", which means you can put signatures in your output scripts.

The practical use case I can think of is to limit possible transactions, where you can spend your input. If your "<sig>" uses SIGHASH_ALL, that means you can pre-calculate your next transaction, get your z-value, make a signature, and then create your output, that will be spendable only in this pre-computed transaction and nowhere else. Probably, if you combine it with other sighashes or use some kind of OP_IF or OP_PICK to choose one of N signatures from scriptPubKey, then you can decide, in which of N transactions your input can be included.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on December 08, 2021, 06:41:46 AM
But if you have "<sig> <pubkey> OP_CHECKSIG" in scriptPubKey, then your signature has to sign itself,
No you don't. When computing sighash (for legacy and witness version 0) the FindAndDelete method is called which would remove the signature. So the script that would be used in signing becomes "<pubkey> OP_CHECKSIG".

For witness version 1 (Tapscripts) things are very different and the leaf hash is used.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: vjudeu on December 17, 2021, 02:23:00 PM
This is a great question, but I am worried, that the answer could be "no", when it comes to "a problem that can't be solved in any other way".


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: PawGo on December 17, 2021, 03:19:26 PM
I think you are not the first who investigates OP_CODESEPARATOR
https://bitcointalk.org/index.php?topic=255145.msg2757093#msg2757093

In other words - conclusion is that as it is a source of potential problems, it is not used at all.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: RobinLinus on February 12, 2022, 11:03:31 AM
I never could figure out where would OP_CODESEPARATOR be useful and some very old comments from early days suggest it may be a byproduct of a bad decision (eg. scripts were concatenated then executed at first).
While going through Taproot this OP code makes even less sense in Tapscripts and yet it is there. So I was wondering if I'm missing something and whether there is any good script examples I could look at where OP_CODESEPARATOR solves a problem that can't be solved in any other way.

Here is an example how you could use it in an interesting way: https://github.com/coins/bitcoin-scripts/blob/master/op-codeseparator.md


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on February 13, 2022, 03:58:39 AM
Here is an example how you could use it in an interesting way: https://github.com/coins/bitcoin-scripts/blob/master/op-codeseparator.md
I think I saw this 2 months ago when I started the topic and it still doesn't make any sense. Take the last example:
Code:
OP_IF   
    # Case 1
    OP_CODESEPARATOR
    600300   
OP_ENDIF


OP_IF
    # Case 2
    OP_CODESEPARATOR
    600200   
OP_ENDIF


OP_IF 
    # Case 3
    OP_CODESEPARATOR
    600100   
OP_ENDIF


OP_CHECKLOCKTIMEVERIFY
OP_DROP

2
<Alice.Pubkey>
<Bob.Pubkey>
2
OP_CHECKMULTISIG 
When you spend it with something like
Code:
<Bob.SignatureCase1>
<Alice.SignatureCase1>
0 0 1
The first IF pops 1 but the second IF is not popping the 0, instead it pops 600300 that was pushed to the stack inside the conditional branch! And OP_CODESEPARATOR doesn't seem to be doing anything here. Then 600200 will be pushed to the stack to be popped by the next IF and finally 600100 which will be popped by OP_CHECKLOCKTIMEVERIFY.
So why not use a nested IF?
Code:
OP_IF
  600300
OP_ELSE
  OP_IF
    600200
  OP_ELSE
    600100
  OP_ENDIF
OP_ENDIF

OP_CHECKLOCKTIMEVERIFY
OP_DROP

2
<Alice.Pubkey>
<Bob.Pubkey>
2
OP_CHECKMULTISIG
It is shorter and achieves the same thing without OP_CODESEPARATOR.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: garlonicon on February 13, 2022, 06:50:47 AM
Quote
So why not use a nested IF?
Maybe no IF is needed at all if there are many cases. Something like this should solve it:
Code:
scriptPubKey: OP_TOALTSTACK OP_CODESEPARATOR 600300 600200 600100 OP_FROMALTSTACK OP_PICK OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_2DROP 2 <Alice.Pubkey> <Bob.Pubkey> 2 OP_CHECKMULTISIG
scriptSig: <Bob.SignatureCase1> <Alice.SignatureCase1> 1


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on February 13, 2022, 10:04:34 AM
Quote
So why not use a nested IF?
Maybe no IF is needed at all if there are many cases. Something like this should solve it:
Code:
scriptPubKey: OP_TOALTSTACK OP_CODESEPARATOR 600300 600200 600100 OP_FROMALTSTACK OP_PICK OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_2DROP 2 <Alice.Pubkey> <Bob.Pubkey> 2 OP_CHECKMULTISIG
scriptSig: <Bob.SignatureCase1> <Alice.SignatureCase1> 1
That's an interesting approach for the same script but why use OP_CODESEPARATOR here at all? It doesn't change anything apart from affecting whether or not you hash OP_TOALTSTACK while computing the sighash which seems pointless to me.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: garlonicon on February 13, 2022, 10:16:52 AM
Quote
why use OP_CODESEPARATOR here at all?
Good point, maybe it should be placed between OP_PICK and OP_CHECKLOCKTIMEVERIFY? I am not sure, it needs more testing.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: vjudeu on June 26, 2022, 08:00:33 PM
I think I found some use case for OP_CODESEPARATOR: it allows you to sign some unknown script, chosen by the second party, as long as the transaction will match your signature (including your sighashes, not limited to SIGHASH_ALL). So, if you will have "OP_CODESEPARATOR <yourPubKey> OP_CHECKSIG", then only that part will be signed. By getting previous transaction hash and index from the second party, as long as you generated a fresh private key that was never used, you can safely provide your public key, then another party will prepare a transaction, and you can sign it. Then, there is no way to cheat, because the other party can only block coins, but not steal them (because there is no OP_SUCCESS outside TapScript).

Edit: Also, what about this?
Code:
<locktimeAlice> OP_CODESEPARATOR OP_CHECKLOCKTIMEVERIFY OP_DROP <alicePubKey> OP_CHECKSIGVERIFY <locktimeBob> OP_CODESEPARATOR OP_CHECKLOCKTIMEVERIFY OP_DROP <bobPubKey> OP_CHECKSIG


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on June 27, 2022, 03:01:08 AM
Edit: Also, what about this?
Code:
<locktimeAlice> OP_CODESEPARATOR OP_CHECKLOCKTIMEVERIFY OP_DROP <alicePubKey> OP_CHECKSIGVERIFY <locktimeBob> OP_CODESEPARATOR OP_CHECKLOCKTIMEVERIFY OP_DROP <bobPubKey> OP_CHECKSIG
The first time value (locktimeAlice) is pointless since it will never be signed by any of the keys so it can be changed to any value by anyone. Apart from that, it was interesting.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: vjudeu on June 27, 2022, 04:32:58 AM
Quote
The first time value (locktimeAlice) is pointless since it will never be signed by any of the keys so it can be changed to any value by anyone. Apart from that, it was interesting.
That's the point. Imagine that Alice and Bob signed some script that was prepared by Charlie, so they don't have to know the opcodes that were before. They only need a previous transaction hash and a previous index, by having those 36 bytes, they can sign it with any sighashes (also note that Alice can use different sighashes than Bob, that's another interesting part of multisig, that is not widely explored, because you cannot concatenate multisigs with different sighashes into a single signature). Also note what is signed by Bob, so how many opcodes is needed to pass to him. And imagine there are N people, where each of them can know only some part of the script. It could also work with Taproot, but then, there is a need to prove that no OP_SUCCESS is present in any previous opcodes.

Edit: Also note that the previous transaction hash can be used to reveal only some part of the previous transaction. For example, it is possible to reveal the result of the first SHA-256 only. It is possible to reveal that, and the last SHA-256 block (then, it is possible to know some last bytes, the size of the whole transaction, and the hash of the previous part). SHA-256 is executed in 512-bit blocks. That can be used to form very interesting protocols, because you can reveal some parts of the message, and hide the rest, as long as you can set any Initialization Vector and any Exit Hash, and require revealing a matching part in-between, you can cryptographically force another party to cooperate. Unfortunately, the current Script allows only executing hash functions with default IVs (but still, it can be used to compute in-between hashes, and then require revealing some partial messages).


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on June 27, 2022, 06:17:10 AM
That's the point. Imagine that Alice and Bob signed some script that was prepared by Charlie, so they don't have to know the opcodes that were before.
I understood that, my point was that since Alice is seeing and signing OP_CHECKLOCKTIMEVERIFY, she also has to see and sign the locktime value or OP_CODESEPARATOR has to come after OP_CLV so that Alice doesn't even see that OP code to know there is a locktime involved.

P.S. Now that I look at it again, only the part where I said "locktime value can be changed" was wrong since the script is protected by the hash of the it found in the output script that is being spent.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: vjudeu on June 27, 2022, 12:13:30 PM
Quote
P.S. Now that I look at it again, only the part where I said "locktime value can be changed" was wrong since the script is protected by the hash of the it found in the output script that is being spent.
You can still change it, if it will be a part of the input script. And note you have opcodes like OP_MIN, OP_MAX or OP_WITHIN.

Edit: Also, there is another thing planned, called SIGHASH_ANYPREVOUT. Or rather, it could be a little more complex: "txid:vout" can be used to select the output, and then tweaked accordingly, by changing the previous transaction:

SIGHASH_PREVOUT_ALL (default, no change),
SIGHASH_PREVOUT_SINGLE (sign only one corresponding input, selected by vout),
SIGHASH_PREVOUT_NONE (sign no inputs of the previous transaction),
SIGHASH_PREVOUT_ANYONECANPAY (sign only this "txid:vout", all other outputs cleared, can be combined with other sighashes).

Then, it could be possible to create a chain of transaction, that would allow "flow control", because you could use SIGHASH_PREVOUT_NONE|SIGHASH_PREVOUT_ANYONECANPAY, and make it resistant to any changes of the previous transaction inputs and outputs (but still, fields like version and locktime will still be signed in that case).


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: Zilon on June 27, 2022, 06:21:17 PM
You can still change it, if it will be a part of the input script. And note you have opcodes like OP_MIN, OP_MAX or OP_WITHIN.
Input script that validates the spending signature i guess?. How about in cases of Op_Hash160 where the input is encoded twice how would this change get into the input script will there be room for this time lock to get an OP_MIN, OP_MAX or OP_WITHIN

Quote
Edit: Also, there is another thing planned, called SIGHASH_ANYPREVOUT. Or rather, it could be a little more complex: "txid:vout" can be used to select the output, and then tweaked accordingly, by changing the previous transaction:

SIGHASH_PREVOUT_ALL (default, no change),
SIGHASH_PREVOUT_SINGLE (sign only one corresponding input, selected by vout),
SIGHASH_PREVOUT_NONE (sign no inputs of the previous transaction),
SIGHASH_PREVOUT_ANYONECANPAY (sign only this "txid:vout", all other outputs cleared, can be combined with other sighashes).

Then, it could be possible to create a chain of transaction, that would allow "flow control", because you could use SIGHASH_PREVOUT_NONE|SIGHASH_PREVOUT_ANYONECANPAY, and make it resistant to any changes of the previous transaction inputs and outputs (but still, fields like version and locktime will still be signed in that case).
I thought this hashes are scrambled using the timelock how then will it be recalculated so as to make changes on the previous transaction


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: Majestic-milf on June 27, 2022, 06:51:55 PM
They only need a previous transaction hash and a previous index

 Which in this case is the pre-image hash? Because Bob will not be able to access the funds without them. Now for the case of "txid:vout", you will have to provide a script as proof that it was committed to by the output.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on June 28, 2022, 03:06:58 AM
You can still change it, if it will be a part of the input script.
I don't see how.
If for example you are spending a P2SH or P2WSH output, the hash of the entire script is already locked in the previous transaction being spent and can not change without changing that transaction also.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: vjudeu on June 28, 2022, 04:30:54 AM
Quote
I don't see how.
In exactly the same way, how you don't have to provide a public key in your output. You don't have to use "<pubkey> OP_CHECKSIG" as your output and "<signature>" as your input, you can also use "OP_CHECKSIG" as your output, and "<signature> <pubkey>" as your input. So, in the same way, you can use "OP_CHECKLOCKTIMEVERIFY OP_DROP OP_TRUE" as your output, and "<locktime>" as your input. So yes, the hash of the previous transaction is now fixed, so you always commit to the whole script, but on the other hand, that script can be flexible enough to allow you to change the locktime.

So, what about that script?
Code:
OP_DUP <minLocktime> <maxLocktime> OP_WITHIN OP_VERIFY OP_CHECKLOCKTIMEVERIFY OP_DROP OP_TRUE
Also note that there are only N possible valid input scripts, that means there are only N possible transaction hashes, that you can reach. You can imagine, how it could affect the next transaction, if it will be used as an input, and if there is no Segwit, so the input script affects the transaction hash (other inputs can use Segwit, only this input can be non-Segwit on purpose, just to reach N possible options, another option could require deterministic signatures, but it is hard to enforce in the current script).


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on June 28, 2022, 08:20:38 AM
In exactly the same way, how you don't have to provide a public key in your output. You don't have to use "<pubkey> OP_CHECKSIG" as your output and "<signature>" as your input, you can also use "OP_CHECKSIG" as your output, and "<signature> <pubkey>" as your input.
True, but the problem is that such scripts make no sense since anybody could spend a OP_CHECKSIG output script.

Quote
So, in the same way, you can use "OP_CHECKLOCKTIMEVERIFY OP_DROP OP_TRUE" as your output, and "<locktime>" as your input.
Similarly it won't make sense to use OP_CHECKLOCKTIMEVERIFY in first place if the spender is setting it and it can be changed specially if you add OP_CODESEPERATOR which would mean it would also make the transaction malleable.

Quote
So, what about that script?
Code:
OP_DUP <minLocktime> <maxLocktime> OP_WITHIN OP_VERIFY OP_CHECKLOCKTIMEVERIFY OP_DROP OP_TRUE
That would work fine since the locking script is defining a strict condition of the locktime so it can't be arbitrary. Although I'm not sure you need the final OP_TRUE though.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: garlonicon on July 07, 2022, 05:21:37 PM
What about conditional multisig? It is non-standard, but it is possible with OP_CODESEPARATOR.

In a non-segwit transaction, any change in the signature can change the transaction ID. It is usually not desired, but it can be useful in some cases, for example: it is possible to make a conditional multisig, where coins will be signed by one key, or by two keys, and when they will be signed only by one key, the second key will be entirely hidden.

The output script is: "<pubkeyAlice> OP_CHECKSIG". And then, the whole magic is in using OP_CODESEPARATOR inside the input script.
The input script is: "<signatureBob> <pubkeyBob> OP_CHECKSIGVERIFY OP_CODESEPARATOR <signatureAlice>".

Then, both parties can make their signatures upfront, so the next transaction could use the previous transaction hash as an input. And that transaction could trigger something, and will be valid only if both parties will sign everything with their upfront-prepared, deterministic signatures. But if anyone will refuse to sign, or use another signature, then this transaction will not be confirmed, because of no matching txid.

The benefit is that if Alice will use any signature on such output, it will be perfectly valid and standard, so nobody will notice that any multisig was even planned.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on July 08, 2022, 02:43:36 AM
The output script is: "<pubkeyAlice> OP_CHECKSIG". And then, the whole magic is in using OP_CODESEPARATOR inside the input script.
The input script is: "<signatureBob> <pubkeyBob> OP_CHECKSIGVERIFY OP_CODESEPARATOR <signatureAlice>".
The bold part of the signature script can be discarded or be replaced by anything else and the transaction would still be valid. In other words your scripts are examples of the malleability attack.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: garlonicon on July 08, 2022, 04:13:23 AM
Quote
or be replaced by anything else
Any signature can be replaced by anything else, because we don't have deterministic signatures, so "<signatureAlice>" can be also replaced. Anyone using legacy addresses should be aware of that.

Quote
The bold part of the signature script can be discarded
It can be discarded in the same way, as any pushes in P2SH before the script itself can be discarded, and in the same way, as all Segwit inputs can be. So, they can be discarded, because there is no soft-fork preventing people from doing so. But I can imagine a Taproot implementation, where some part of the input is committed to the public key, then that system would be safe.

So, historically, P2SH could be introduced as a OP_CODESEPARATOR commitment to the public key, instead of creating a new address type. It is all about privacy, where you can hide anything behind some output that looks the same. But yes, in Taproot, we can currently reach the same thing.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on July 08, 2022, 06:18:21 AM
Quote
or be replaced by anything else
Any signature can be replaced by anything else, because we don't have deterministic signatures, so "<signatureAlice>" can be also replaced. Anyone using legacy addresses should be aware of that.
By anything I meant literary anything not just another signature. For example it could be replaced by an arbitrary message like <foobar> OP_CODESEPARATOR

Quote
Quote
The bold part of the signature script can be discarded
It can be discarded in the same way, as any pushes in P2SH before the script itself can be discarded, and in the same way, as all Segwit inputs can be.
If it can be discarded that means it is pointless and is not solving anything.
You see, the locking script should be designed in a way that it mandates all the unlocking script parts. For example in a P2PKH locking script, it requires 2 items: a signature and a public key and neither one can change meaning you can't use someone else's public key or use a different script to spend that output.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: garlonicon on July 08, 2022, 10:39:24 AM
Quote
By anything I meant literary anything not just another signature. For example it could be replaced by an arbitrary message like <foobar> OP_CODESEPARATOR
Well, any miner can do that today on any legacy address, just because it is possible.

Quote
If it can be discarded that means it is pointless and is not solving anything.
It can be discarded in the current consensus. But OP_CODESEPARATOR can decide, what is signed, and what is not, so by introducing new sighashes, the signed parts of the input script can be controlled by signatures.

Quote
you can't use someone else's public key or use a different script to spend that output
You can always use a different script, because you can always extend it, if you are a miner. In Segwit, it just does not matter, because txid is not affected.


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: vjudeu on February 10, 2023, 04:06:37 PM
I found some clue, why OP_CODESEPARATOR is still in Taproot: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-February/021448.html
Quote
Code:
[2] In Taproot, if you want to prevent signatures migrating to another
    branch or within a branch, you can use the CODESEPARATOR opcode
    which was redisegned in Taproot for exactly this purpose... we
    really did about witness malleation in its design!


Title: Re: Is there any useful place where OP_CODESEPARATOR is used?
Post by: pooya87 on February 11, 2023, 05:29:41 AM
I found some clue, why OP_CODESEPARATOR is still in Taproot: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-February/021448.html
Quote
Code:
[2] In Taproot, if you want to prevent signatures migrating to another
    branch or within a branch, you can use the CODESEPARATOR opcode
    which was redisegned in Taproot for exactly this purpose... we
    really did about witness malleation in its design!
Thanks for the update.
I need to investigate the implementation again but I don't think it works the way that is explained here because of how Taproot scripts were designed so that the leaf hash is covered by the signature.