<p>When the script interpreter encounters an unknown opcode (the <code>default:</code> case inside the switch statement), it returns <code>false</code>. That aborts verification and considers the transaction to be invalid. This was the case before 0.3.6, in 0.3.6, and still today.</p>
<p>Thus, certain transactions could have been created which pre-0.3.6 clients consider invalid, and post-0.3.6 clients consider valid - a backward incompatible consensus change or hardfork. However, as far as we know, no such transactions were ever mined (or even created), so while it is a backward incompatible change, without such transactions, the incompatibility wasn't actually observable.</p>

CC BY-SA 4.0
2022-06-09T14:59:46.477Z
"However, as far as we know, no such transactions were ever mined (or even created)" Can you expand upon this? Are you saying that in the entire blockchain history, no one ever spent a transaction from a locking script that included OP_NOP2 or OP_NOP3? I see that OP_NOP1 was never used because OP_EVAL was abandoned. But it looks like OP_NOP2 became OP_CLTV and started being used on December 8, 2015. As I understand it, the first OP_CLTV spend should have triggered a hard fork condition for pre-v0.3.6 clients.
Jameson Lopp
CC BY-SA 4.0
2022-06-09T15:06:02.947Z
No, I meant: there were no two competing chains that we know of. And no failed attempts by miners to get transactions with the then-new OP_NOPn opcodes into blocks (failing due to other miners/nodes not having upgraded). AFAIK, the first usage of OP_NOPn was long (possibly years, but I haven't checked) after the entire network had migrated to 0.3.6+, meaning that while there was a hardforking change, there was no actual fork.
Pieter Wuille
CC BY-SA 4.0
2022-06-08T12:51:54.243Z
2022-06-08T12:51:54.243Z
Pieter Wuille
<p>There is nothing to suggest in the code diff you linked to that older clients ignored unknown opcodes in script execution and effectively treated them as OP_NOPs. That doesn't guarantee they didn't of course without looking through the code of all older clients.</p>
<p>However, it wouldn't make much sense to have all unknown opcodes treated as OP_NOPs and then later specify that 10 particular opcodes were OP_NOPs (OP_NOP1 through OP_NOP10). OP_SUCCESS hadn't been implemented at this time so the only two options for unknown opcodes were treat them as OP_NOPs or fail the script. Going from failing the script to being treated as a OP_NOP is a relaxation (rather than restriction) of the consensus rules and hence would be a hard fork.</p>
<p>(Of course in 2010 the terms soft fork and hard fork hadn't been invented so it wouldn't have been a conscious decision to &quot;hard fork&quot; the protocol!)</p>

CC BY-SA 4.0
2022-06-08T11:26:21.370Z
2022-06-08T11:45:15.130Z
2022-06-08T11:45:15.130Z
Michael Folkson
<p>I'm doing some deeper research into historical consensus changes and it seems like most folks consider the addition of the OP_NOP codes to be a hard fork. Here's the diff for their addition: <a href="https://github.com/bitcoin/bitcoin/commit/a75560d828464c3f1138f52cf247e956fc8f937d#" rel="noreferrer">https://github.com/bitcoin/bitcoin/commit/a75560d828464c3f1138f52cf247e956fc8f937d#</a></p>
<p>However, some time ago gmax mentioned to me that it was effectively a soft fork because older clients ignored unknown opcodes. After perusing the code I can see why that might be the case, but I could use some input to gain consensus from people with superior C++ skills.</p>

<consensus><history><c++><source-code><opcodes>
Was the addition of OP_NOP codes in Bitcoin 0.3.6 a hard or soft fork?
Jameson Lopp
# Was the addition of OP_NOP codes in Bitcoin 0.3.6 a hard or soft fork?

(Asked by: username_0 on 2022-06-08)

I'm doing some deeper research into historical consensus changes and it seems like most folks consider the addition of the OP\_NOP codes to be a hard fork. Here's the diff for their addition: <https://github.com/bitcoin/bitcoin/commit/a75560d828464c3f1138f52cf247e956fc8f937d#>

However, some time ago gmax mentioned to me that it was effectively a soft fork because older clients ignored unknown opcodes. After perusing the code I can see why that might be the case, but I could use some input to gain consensus from people with superior C++ skills.

**Tags:** consensus, history, c++, source-code, opcodes


---

## 2 answers

### Answer by username_1 on 2022-06-08. Score: 9

When the script interpreter encounters an unknown opcode (the `default:` case inside the switch statement), it returns `false`. That aborts verification and considers the transaction to be invalid. This was the case before 0.3.6, in 0.3.6, and still today.

Thus, certain transactions could have been created which pre-0.3.6 clients consider invalid, and post-0.3.6 clients consider valid - a backward incompatible consensus change or hardfork. However, as far as we know, no such transactions were ever mined (or even created), so while it is a backward incompatible change, without such transactions, the incompatibility wasn't actually observable.

> **Comments:**
> **username_0**: "However, as far as we know, no such transactions were ever mined (or even created)" Can you expand upon this? Are you saying that in the entire blockchain history, no one ever spent a transaction from a locking script that included OP_NOP2 or OP_NOP3? I see that OP_NOP1 was never used because OP_EVAL was abandoned. But it looks like OP_NOP2 became OP_CLTV and started being used on December 8, 2015. As I understand it, the first OP_CLTV spend should have triggered a hard fork condition for pre-v0.3.6 clients.  
> **username_1**: No, I meant: there were no two competing chains that we know of. And no failed attempts by miners to get transactions with the then-new OP_NOPn opcodes into blocks (failing due to other miners/nodes not having upgraded). AFAIK, the first usage of OP_NOPn was long (possibly years, but I haven't checked) after the entire network had migrated to 0.3.6+, meaning that while there was a hardforking change, there was no actual fork.  

---

### Answer by username_2 on 2022-06-08. Score: 3

There is nothing to suggest in the code diff you linked to that older clients ignored unknown opcodes in script execution and effectively treated them as OP\_NOPs. That doesn't guarantee they didn't of course without looking through the code of all older clients.

However, it wouldn't make much sense to have all unknown opcodes treated as OP\_NOPs and then later specify that 10 particular opcodes were OP\_NOPs (OP\_NOP1 through OP\_NOP10). OP\_SUCCESS hadn't been implemented at this time so the only two options for unknown opcodes were treat them as OP\_NOPs or fail the script. Going from failing the script to being treated as a OP\_NOP is a relaxation (rather than restriction) of the consensus rules and hence would be a hard fork.

(Of course in 2010 the terms soft fork and hard fork hadn't been invented so it wouldn't have been a conscious decision to "hard fork" the protocol!)


---

