Skip to content

Instantly share code, notes, and snippets.

@ClemensLey
Created July 7, 2018 01:55
Show Gist options
  • Save ClemensLey/49ecb94165c19fac3b9729d17ea5ff9b to your computer and use it in GitHub Desktop.
Save ClemensLey/49ecb94165c19fac3b9729d17ea5ff9b to your computer and use it in GitHub Desktop.
This is a review of the OP_CHECKDATASIGVERIFY proposal https://gist.github.com/Mengerian/d200f8b3a238a256642e498ca796e7c4. The goal is not to change the meaning of the existing proposal, but to improve the presentation. Comments welcome.

OP_CHECKDATASIG

OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY check whether a signature is valid with respect to a message and and a public key. The message is restricted to exactly 256 bits, if longer messages are to be signed, it is recommended to sign it's hash.

This 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 [<msg>, <pubKey>, <sig>] in this order where <msg> is the top element and
  • <msg> must be a string of exactly 256 bits
  • <pubKey> must be a ... encoded public key
  • <sig> must be a DER encoded signature that
    • is in strict DER encoding as described in [2]
    • the S-value of <sig> is 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 [<msg>, <pubKey>, <sig>] from the stack and pushes true onto the stack if the signature is valid with respect to the message and the public key using the secp256k1 elliptic curve. Otherwise it pops three elements and pushes false onto the stack.

Opcode Number

OP_CHECKDATASIGVERIFY uses the currently 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 SigOp.

Unit Tests

  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if 15 November 2018 protocol upgrade is not yet activated.
  • <sig> <pubKey> OP_CHECKDATASIG fails if fewer than 3 item on stack causes script failure
  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if <msg> is not a string of 256 bits
  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if <pubKey> is not a validly encoded public key (todo: more details about encoding).
  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if <sig> is not a validly encoded signature with strict DER encoding.
  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if signature <sig> does not pass the Low S check and is a non empty byte array.
  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if <sig> is not an empty byte array, and does not pass signature validation of <msg> and <pubKey> (question: I would have expected that this case returns false instead of failing)
  • <sig> <pubKey> <msg> OP_CHECKDATASIG pops three elements and pushes false onto the stack if <sig> is an empty byte array.
  • <sig> <pubKey> <msg> OP_CHECKDATASIG pops three elements and pushes true onto the stack if <sig> is a valid signature of <msg> and <pubKey>.
  • <sig> <pubKey> <msg> OP_CHECKDATASIG pops three elements and pushes false onto the stack if <sig> is not a valid signature of <msg> and <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 currently unused opcode number 187 (0xbb in hex encoding)

SigOps

Todo

Unit Tests

  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY fails if 15 November 2018 protocol upgrade is not yet activated.
  • <sig> <pubKey> OP_CHECKDATASIGVERIFY fails if fewer than 3 item on stack causes script failure
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY fails if <msg> is not a string of 256 bits
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFYfails if <pubKey> is not a validly encoded public key (todo: more details about encoding).
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY fails if is not a validly encoded signature with strict DER encoding.
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY fails if signature <sig> does not pass the Low S check and is a non empty byte array.
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY fails if <sig> is not an empty byte array, and does not pass signature validation of <msg> and <pubKey> .
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY fails if is an empty byte array.
  • <sig> <pubKey> <msg> OP_CHECKDATASIGVERIFY pops the top three stack elements if is a valid signature of and .
  • <sig> <pubKey> <msg> OP_CHECKDATASIG fails if <sig> is not a valid signature of <msg> and <pubKey>.

Note that the only test case that distinguishes between OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY is the last one.

Sample Implementation

case OP_CHECKDATASIG:
case OP_CHECKSIGVERIFY: {
  if (!enableCheckDataSig)
    return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
  if (stack.size() < 3)
    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

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

  if (messageHash.size() != 32)
    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

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

  bool fSuccess = checker.VerifySignature(vchSig, vchPubKey, messageHash);

  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_CHECKSIGVERIFY);
    }
  }
} break;

History

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

List of differences from OP_DATASIGVERIFY proposal:

  • Includes “non-verify” version, which does not immediately mark transaction as invalid if it fails, simply returns “False”. Returns “True” if successful. For verify behavior, use OP_CHECKDATASIGVERIFY.
  • Does not leave input MessageHash on the stack, all three input values are removed.
  • Order of inputs is different. Message hash input is the top stack item, rather than the bottom item.
  • Takes Pubkey as input, rather the Pubkeyhash.
  • Signature format same as OP_CHECKSIG, rather than Pubkey-recoverable signature.
  • No “type” field with the signature. All signatures are treated as strict DER encoded ECDSA.
  • Expects "MessageHash" to be a 256-bit hash, fails if it is not a 256-bit value.

References

[1] OP_CHECKSIG

[2] Strict DER Encoding

[3] Low-S and Nullfail Specification

[4] Andrew Stone’s OP_DATASIGVERIFY

[5] Andrew Stone article on Scripting

[6] Peer Review of Stone Proposal

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