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.

2 Answers 2


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.

  • "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. Jun 9, 2022 at 14:59
  • 4
    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. Jun 9, 2022 at 15:06

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!)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.