Skip to content

Instantly share code, notes, and snippets.

@Mengerian
Last active December 15, 2018 20:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mengerian/d200f8b3a238a256642e498ca796e7c4 to your computer and use it in GitHub Desktop.
Save Mengerian/d200f8b3a238a256642e498ca796e7c4 to your computer and use it in GitHub Desktop.
layout title date activation version
specification
OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY Specification
2018-08-06
1542300000
0.1

OP_CHECKDATASIG

OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY check whether a signature is valid with respect to a message hash and and a public key. The message hash is restricted to exactly 256 bits.

OP_CHECKDATASIG permits data to be imported into a script, and have its validity checked against some signing authority such as an "Oracle".

OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY are designed to be implemented similarly to OP_CHECKSIG [1]. Conceptually, one could imagine OP_CHECKSIG functionality being replaced by OP_CHECKDATASIG, along with a separate Op Code to create a hash from the transaction based on the SigHash algorithm.

OP_CHECKDATASIG Specification

Semantics

OP_CHECKDATASIG fails immediately if the stack is not well formed. To be well formed, the stack must contain at least three elements [<sig>, <msgHash>, <pubKey>] in this order where <pubKey> is the top element and

  • <pubKey> must be a validly encoded public key
  • <msgHash> must be a string of exactly 256 bits
  • <sig> must follow the strict DER encoding as described in [2] and the S-value of <sig> must be at most the curve oder divided by 2 as described in [3]

If the stack is well formed, then OP_CHECKDATASIG pops the top three elements [<sig>, <msgHash>, <pubKey>] from the stack and pushes true onto the stack if the signature is valid with respect to the message hash and the public key using the secp256k1 elliptic curve. Otherwise, it pops three elements and pushes false onto the stack in the case that <sig> is the empty string and fails in all other cases.

Nullfail is enforced the same as for OP_CHECKSIG [3]. If the signature does not match the supplied public key and message hash, and the signature is not an empty byte array, the entire script fails.

Opcode Number

OP_CHECKDATASIG uses the previously unused opcode number 186 (0xba in hex encoding)

SigOps

Signature operations accounting for OP_CHECKDATASIG shall be calculated the same as OP_CHECKSIG. This means that each OP_CHECKDATASIG shall be counted as one (1) SigOp.

Activation

Use of OP_CHECKDATASIG, unless occuring in an unexecuted OP_IF branch, will make the transaction invalid if it is included in a block where the median timestamp of the prior 11 blocks is less than 1542300000.

Unit Tests

  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG fails if 15 November 2018 protocol upgrade is not yet activated.
  • <sig> <msgHash> OP_CHECKDATASIG fails if there are fewer than 3 item on stack.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG fails if <msgHash> is not a string of 256 bits (32 bytes).
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG fails if <pubKey> is not a validly encoded public key.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG fails if <sig> is not a validly encoded signature with strict DER encoding.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG fails if signature <sig> is not empty and does not pass the Low S check.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG fails if signature <sig> is not empty and does not pass signature validation of <msg> and <pubKey>.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG pops three elements and pushes false onto the stack if <sig> is an empty byte array.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIG pops three elements and pushes true onto the stack if <sig> is a valid signature of <msgHash> with respect to <pubKey>.

OP_CHECKDATASIGVERIFY Specification

Semantics

OP_CHECKDATASIGVERIFY is equivalent to OP_CHECKDATASIG followed by OP_VERIFY. It leaves nothing on the stack, and will cause the script to fail immediately if the signature check does not pass.

Opcode Number

OP_CHECKDATASIGVERIFY uses the previously unused opcode number 187 (0xbb in hex encoding)

SigOps

Signature operations accounting for OP_CHECKDATASIGVERIFY shall be calculated the same as OP_CHECKSIGVERIFY. This means that each OP_CHECKDATASIGVERIFY shall be counted as one (1) SigOp.

Activation

Use of OP_CHECKDATASIGVERIFY, unless occuring in an unexecuted OP_IF branch, will make the transaction invalid if it is included in a block where the median timestamp of the prior 11 blocks is less than 1542300000.

Unit Tests

  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFY fails if 15 November 2018 protocol upgrade is not yet activated.
  • <sig> <msgHash> OP_CHECKDATASIGVERIFY fails if there are fewer than 3 item on stack.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFY fails if <msgHash> is not a string of 256 bits (32 bytes).
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFYfails if <pubKey> is not a validly encoded public key.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFY fails if <sig> is not a validly encoded signature with strict DER encoding.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFY fails if signature <sig> is not empty and does not pass the Low S check.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFY fails if <sig> is not a valid signature of <msgHash> with respect to <pubKey>.
  • <sig> <msgHash> <pubKey> OP_CHECKDATASIGVERIFY pops the top three stack elements if <sig> is a valid signature of <msgHash> with respect to <pubKey>.

Sample Implementation [4]

                    case OP_CHECKDATASIG:
                    case OP_CHECKDATASIGVERIFY: {
                        // Make sure this remains an error before activation.
                        if ((flags & SCRIPT_ENABLE_CHECKDATASIG) == 0) {
                            return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
                        }

                        // (sig message pubkey -- bool)
                        if (stack.size() < 3) {
                            return set_error(
                                serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                        }

                        valtype &vchSig = stacktop(-3);
                        valtype &vchMessage = stacktop(-2);
                        valtype &vchPubKey = stacktop(-1);

                        // The size of the message must be 32 bytes.
                        if (vchMessage.size() != 32) {
                            return set_error(serror,
                                             SCRIPT_ERR_INVALID_OPERAND_SIZE);
                        }

                        if (!CheckDataSignatureEncoding(vchSig, flags,
                                                        serror) ||
                            !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
                            // serror is set
                            return false;
                        }

                        bool fSuccess = false;
                        if (vchSig.size()) {
                            uint256 message(vchMessage);
                            CPubKey pubkey(vchPubKey);
                            fSuccess = pubkey.Verify(message, vchSig);
                        }

                        if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) &&
                            vchSig.size()) {
                            return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
                        }

                        popstack(stack);
                        popstack(stack);
                        popstack(stack);
                        stack.push_back(fSuccess ? vchTrue : vchFalse);
                        if (opcode == OP_CHECKDATASIGVERIFY) {
                            if (fSuccess) {
                                popstack(stack);
                            } else {
                                return set_error(serror,
                                                 SCRIPT_ERR_CHECKDATASIGVERIFY);
                            }
                        }
                    } break;

History

This specification is based on Andrew Stone’s OP_DATASIGVERIFY proposal [5, 6]. It is modified from Stone's original proposal based on a synthesis of all the peer-review and feedback received [7].

References

[1] OP_CHECKSIG

[2] Strict DER Encoding

[3] Low-S and Nullfail Specification

[4] Bitcoin ABC implementation

[5] Andrew Stone’s OP_DATASIGVERIFY

[6] Andrew Stone article on Scripting

[7] Peer Review of Stone Proposal

@ClemensLey
Copy link

ClemensLey commented Jun 13, 2018

I agree almost 100% with this proposal (I have not reviewed the source code, however). The only nitpick I can find is that I think it would be a little bit cleaner if OP_CHECKDATASIG would not hash the input data. While I understand that it is recommended to sign the hash of a document instead of the document itself (if it is long), I think this is a decision that should be left to the programmer. The semantics would become even simpler: "the public key is used to check the supplied signature against the data".

One more thing, you could consider changing the wording from "data" to "message". Means exactly the same thing but is more in line with the lingo used in crypto textbooks.

@Mengerian
Copy link
Author

Thanks Clemens, OK, I changed the wording from "data" to "message"

For the signing of the hash, seems to me that if OP_CHECKDATASIG does not hash the message, then it should enforce that the message be a 256-bit number. This way, if a script author wanted to supply raw data, they would do OP_HASH256 on it before it gets passed in to OP_CHECKDATASIG. Part of the issue is that the way OP_CHECKSIG is currently implemented, the function that verifies the signature expects a 256-bit value as input, and it seems good to stay close and re-use OP_CHECKSIG implementation as much as possible.

@Mengerian
Copy link
Author

Clemens, for the sake of showing what it would look like, I updated the proposal so that it expects the "Message" input to be a 256-bit hash.

I'm not 100% sure this is the route we want to go, but I think it's worth considering. It follows the philosophy keeping the instruction minimal and only doing one thing, but in a way that can be used in Script very flexibly.

@ClemensLey
Copy link

Thanks for your clarification Mengerian. Given your explanation, I'm now also unsure what the best option is :) More discussion seems to be the way to go...

@ClemensLey
Copy link

Great progress on the proposal guys. Mengerian has asked me to give feedback, so here we go: I support the current state of the draft, but I think we can do a bit more work on the presentation. Have set up a proposal at (https://gist.github.com/ClemensLey/5fdec17f84142fe5b06e3171f907503b). My intention was not to change the meaning of the existing proposal but just to make it easier to understand. Please feel free to incorporate anything you like from my proposal into this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment