<p>For pre-Tapscript opcodes (<code>CHECKSIG</code> and <code>CHECKMULTISIG</code>), the empty signature <a href="https://github.com/bitcoin/bitcoin/blob/b5a271334ca81a6adcb1c608d85c83621a9eae47/src/script/interpreter.cpp#L320-L344" rel="nofollow noreferrer">is checked</a> against the public key. Note that <em>by standardness</em> if the check fails, the signature is required to be the empty vector.</p>
<p>For Tapscript opcodes (<code>CHECKSIG</code> and <code>CHECKSIGADD</code>), the signature check <a href="https://github.com/bitcoin/bitcoin/blob/b5a271334ca81a6adcb1c608d85c83621a9eae47/src/script/interpreter.cpp#L346-L384" rel="nofollow noreferrer">is not performed</a> at all if the empty vector is passed as the signature. Note that <em>by consensus</em> if the check fails, the signature is required to be the empty vector.</p>

CC BY-SA 4.0
2023-11-24T22:41:08.803Z
What is the purpose of OP_CHECKSIGVERIFY then? According to what you said, OP_CHECKSIG and OP_CHECKSIGVERIFY then have the same meaning.
Cosmos
CC BY-SA 4.0
2023-11-24T22:52:26.757Z
Nvm, I found it in BIP342.
Cosmos
CC BY-SA 4.0
2023-11-24T23:06:51.117Z
Yes, `CHECKSIG` and `CHECKSIGVERIFY` behave in the very same way with regard to signature checking. `CHECKSIGVERIFY` just immediately stops execution if the signature check failed.
Antoine Poinsot
CC BY-SA 4.0
2023-11-24T23:08:55.727Z
Well I missed to add "in Taproot". In taproot there is **no difference** between CHECKSIG and CHECKSIGVERIFY, they will both immediately stop execution if the signature check fail.
Cosmos
CC BY-SA 4.0
2023-11-24T23:11:09.413Z
Only difference, as I see from the BIP342, is that if the check is valid, CHECKSIG will push TRUE (0x01) on the stack, while CHECKSIGVERIFY won't do anything. Also, if the signature is empty vector, CHECKSIGVERIFY will terminate the execution and invalidate script while CHECKSIG will push empty vector (even though I do not know how can you push an empty vector; its like pushing air?).
Cosmos
CC BY-SA 4.0
2023-11-24T23:32:08.543Z
OP_CHECKSIGVERIFY is exactly equivalent to OP_CHECKSIG by OP_VERIFY. The stack consists of a list of byte vectors. E.g. OP_0 pushes the empty vector, which represents the number 0 (to numeric opcodesk).
Pieter Wuille
CC BY-SA 4.0
2023-11-25T00:06:31.647Z
@PieterWuille Yes, but in non-Tapscript. However, as I see from the BIP342, in Tapscript they are literally the same (plus this additional differences I wrote above). They both terminate and invalidate script if the signature is not valid (unlike in non-Tapscript where CHECKSIG would not invalidate it but only put `0x00`-empty vector on the stack). So it would even be okay if one of these two opcodes was removed. Right?
Cosmos
CC BY-SA 4.0
2023-11-25T01:32:35.233Z
Even in BIP342 Tapscript, OP_CHECKSIGVERIFY is equivalent to OP_CHECKSIG plus OP_VERIFY. All the OP_*VERIFY opcodes are redundant, as they are all equivalent to another opcodes plus OP_VERIFY.
Pieter Wuille
CC BY-SA 4.0
2023-11-25T02:18:00.323Z
@PieterWuille I want to say that in Tapscript you do not even need OP_VERIFY after OP_CHECKSIG to get the same effect as OP_CHECKSIGVERIFY opcode since OP_CHECKSIG will also immediately invalidate (halt) script if the signature is not correct (same as OP_CHECKSIGVERIFY).
Cosmos
CC BY-SA 4.0
2023-11-25T02:37:24.947Z
That's true for *invalid* signatures in general, but not for empty signatures, which causes OP_CHECKSIG to leave an empty element on the stack, while it causes OP_CHECKSIGVERIFY to abort.
Pieter Wuille
CC BY-SA 4.0
2023-11-24T22:35:20.170Z
2023-11-25T14:20:10.870Z
2023-11-25T14:20:10.870Z
Antoine Poinsot
<p>In this <a href="https://bitcoin.stackexchange.com/a/120607/146389">answer</a> <em>Vojtěch Strnad</em> wrote the following:</p>
<blockquote>
<p>OP_CHECKSIGADD always performs one check per signature, since unused
public keys are required to be paired with an empty witness item and
are skipped.</p>
</blockquote>
<p>I'm interested in the following. If we provide zero (<strong>OP_0</strong> / <strong>no item on a stack</strong> / <strong>empty witness item</strong>) and public key to OP_CHECKSIG (pre-segWit/post-segWit), to OP_CHECKSIGADD or to OP_CHECKMULTISIG (pre-segWit/post-segWit) will it perform any check operation or it will just skip the check (as <em>Vojtěch</em> said) and put 0 on the stack?</p>

<script><multi-signature><signature><taproot><opcodes>
Is the signature check performed when the signature is the empty vector?
Cosmos
# Is the signature check performed when the signature is the empty vector?

(Asked by: username_0 on 2023-11-24)

In this [answer](https://bitcoin.stackexchange.com/a/120607/146389) *Vojtěch Strnad* wrote the following:

> OP\_CHECKSIGADD always performs one check per signature, since unused
> public keys are required to be paired with an empty witness item and
> are skipped.

I'm interested in the following. If we provide zero (**OP\_0** / **no item on a stack** / **empty witness item**) and public key to OP\_CHECKSIG (pre-segWit/post-segWit), to OP\_CHECKSIGADD or to OP\_CHECKMULTISIG (pre-segWit/post-segWit) will it perform any check operation or it will just skip the check (as *Vojtěch* said) and put 0 on the stack?

**Tags:** script, multi-signature, signature, taproot, opcodes


---

## 1 answers

### Answer by username_1 on 2023-11-24. Score: 3 | Accepted answer

For pre-Tapscript opcodes (`CHECKSIG` and `CHECKMULTISIG`), the empty signature [is checked](https://github.com/bitcoin/bitcoin/blob/b5a271334ca81a6adcb1c608d85c83621a9eae47/src/script/interpreter.cpp#L320-L344) against the public key. Note that *by standardness* if the check fails, the signature is required to be the empty vector.

For Tapscript opcodes (`CHECKSIG` and `CHECKSIGADD`), the signature check [is not performed](https://github.com/bitcoin/bitcoin/blob/b5a271334ca81a6adcb1c608d85c83621a9eae47/src/script/interpreter.cpp#L346-L384) at all if the empty vector is passed as the signature. Note that *by consensus* if the check fails, the signature is required to be the empty vector.

> **Comments:**
> **username_0**: What is the purpose of OP_CHECKSIGVERIFY then? According to what you said, OP_CHECKSIG and OP_CHECKSIGVERIFY then have the same meaning.  
> **username_0**: Nvm, I found it in BIP342.  
> **username_1**: Yes, `CHECKSIG` and `CHECKSIGVERIFY` behave in the very same way with regard to signature checking. `CHECKSIGVERIFY` just immediately stops execution if the signature check failed.  
> **username_0**: Well I missed to add "in Taproot". In taproot there is **no difference** between CHECKSIG and CHECKSIGVERIFY, they will both immediately stop execution if the signature check fail.  
> **username_0**: Only difference, as I see from the BIP342, is that if the check is valid, CHECKSIG will push TRUE (0x01) on the stack, while CHECKSIGVERIFY won't do anything. Also, if the signature is empty vector, CHECKSIGVERIFY will terminate the execution and invalidate script while CHECKSIG will push empty vector (even though I do not know how can you push an empty vector; its like pushing air?).  
> **username_2**: OP_CHECKSIGVERIFY is exactly equivalent to OP_CHECKSIG by OP_VERIFY. The stack consists of a list of byte vectors. E.g. OP_0 pushes the empty vector, which represents the number 0 (to numeric opcodesk).  
> **username_0**: @username_2 Yes, but in non-Tapscript. However, as I see from the BIP342, in Tapscript they are literally the same (plus this additional differences I wrote above). They both terminate and invalidate script if the signature is not valid (unlike in non-Tapscript where CHECKSIG would not invalidate it but only put `0x00`-empty vector on the stack). So it would even be okay if one of these two opcodes was removed. Right?  
> **username_2**: Even in BIP342 Tapscript, OP_CHECKSIGVERIFY is equivalent to OP_CHECKSIG plus OP_VERIFY. All the OP_*VERIFY opcodes are redundant, as they are all equivalent to another opcodes plus OP_VERIFY.  
> **username_0**: @username_2 I want to say that in Tapscript you do not even need OP_VERIFY after OP_CHECKSIG to get the same effect as OP_CHECKSIGVERIFY opcode since OP_CHECKSIG will also immediately invalidate (halt) script if the signature is not correct (same as OP_CHECKSIGVERIFY).  
> **username_2**: That's true for *invalid* signatures in general, but not for empty signatures, which causes OP_CHECKSIG to leave an empty element on the stack, while it causes OP_CHECKSIGVERIFY to abort.  

---

