1

I have this Script P2PKH

<A Signature> <A Public Key> 
OP_DUP OP_HASH160 <A Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

The bitcoin.org documentation say this

The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise.

Now I want to understand how the code is running inside the stack and how the OP_CHECKSIG work.

My question comes from the fact, why use another operator (OP_CHECKSIG) if is used the operator OP_EQUALVERIFY to does control ?

But I have another question on OP_DUP, the documentation say this

Duplicates the top stack item.

Why when the bitcoin run the script need to duplicate the items inside the stack?

to close, exist other documentation on Bitcoin Script language?

2 Answers 2

5

Let's run through the program.

  • The stack starts as <A sig> <A pubkey>.
  • You run OP_DUP. The stack is now <A sig> <A pubkey> <A pubkey>.
  • You run OP_HASH160. The stack is now <A sig> <A pubkey> <A pubkeyhash>.
  • You push (A expected pubkeyhash). The stack is now <A sig> <A pubkey> <A pubkeyhash> <A expected pubkeyhash>.
  • You run OP_EQUALVERIFY. If the initial public key on the stack does matches the expected one, the execution aborts and the script fails. If it matches, the resulting stack is <A sig> <A pubkey>.
  • You run OP_CHECKSIG. If the signature matches, the resulting stack is 1, which means success.

Without the OP_DUP, it would be impossible to verify the public key both against the signature and against the expected public key hash.

1
  • This kind of stack-oriented scripting reminds me of Postscript which has operators such as dup, exch and pop. I have often used dup at the start of a Postscript subroutine so that a parameter can be used for multiple operations, each of which consumes items from the stack. Commented Sep 17, 2019 at 15:19
0

... other documentation on Bitcoin Script language?

Looking at Bitcoin Core's interpreter.cpp can be helpful. It contains a big switch(opcode) ... statement for all the opcodes.

Now I want to understand how the code is running inside the stack and how the OP_CHECKSIG work.

The OP_CHECKSIG and OP_CHECKSIGVERIFY cases are handled together:

case OP_CHECKSIG:
case OP_CHECKSIGVERIFY:
{
    // (sig pubkey -- bool)
    if (stack.size() < 2)
        return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

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

    bool fSuccess = true;
    if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, fSuccess)) return false;
    popstack(stack);
    popstack(stack);
    stack.push_back(fSuccess ? vchTrue : vchFalse);
    if (opcode == OP_CHECKSIGVERIFY)
    {
        if (fSuccess)
            popstack(stack);
        else
            return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY);
    }
}
break;

The code:

  1. Checks there are at least two items on top of the stack
  2. Reads the Signature and PubKey as the top two items
  3. Uses EvalChecksig to check if Signature matches PubKey; fSuccess is false if not
  4. Removes the Signature and PubKey from the stack
  5. Puts fSuccess on the stack
  6. If it's OP_CHECKSIGVERIFY it removes fSuccess and throws an error (causing transaction failure) if fSuccess == False

To go deeper, you can follow the path of function calls from EvalChecksig(.. onwards. E.g.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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