I've always struggled to see where OP_CODESEPARATOR would really come in handy. Some really old comments from the early days hint that it might have stemmed from a poor choice (like how scripts were originally concatenated and executed). Now with Taproot, this OP code seems even more pointless in Tapscripts, yet it still exists. So I'm curious if I'm missing something here. Are there any solid script examples that show how OP_CODESEPARATOR addresses a problem that can't be tackled any other way?
Where is OP_CODESEPARATOR actually useful?
19 replies 175 views
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.
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).
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
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.
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.
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".
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.
bridge_lordMember
Posts: 10 · Reputation: 110
#8Oct 21, 2022, 06:16 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:
When you spend it with something like
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?
It is shorter and achieves the same thing without OP_CODESEPARATOR.
Maybe no IF is needed at all if there are many cases. Something like this should solve it:
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.
Good point, maybe it should be placed between OP_PICK and OP_CHECKLOCKTIMEVERIFY? I am not sure, it needs more testing.
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?
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).
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.
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).
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
I thought this hashes are scrambled using the timelock how then will it be recalculated so as to make changes on the previous transaction
ninja_viperFull Member
Posts: 178 · Reputation: 785
#19Oct 26, 2022, 08:02 AM
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.
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.
?Reply
Sign in to reply to this topic
Related topics
- Bitcoin Core displaying coins (UTXOs) in a new tab 13
- Are you in favor of BIP-110? Let's get a Bitcoin poll going. 0
- Erlay seems to have some issues here’s a better proposal for a bitcoin protocol without invites 3
- New Optional Hourglass Implementation is Live 3
- Ways to earn some sats by contributing to bitcoin core development 5
- Exploring the Potential and Challenges of a Kardashev-Scale Bitcoin Network 3