比特币源码学习笔记(四)

g2com 发布在 技术指南 10 14410

比特币带有一个基于栈的脚本语言。每笔交易的输出部分都嵌有一段脚本。要想花费一笔交易中所携带的币,接收方必须提供他的公钥,以使脚本能够成功执行。

本篇将为大家介绍比特币的脚本语言。读完本篇之后,你将会明白在第一篇文章当中介绍的脚本A与脚本B的签名是如何验证的。

本篇所涉及到的全部类及函数位于script.h或者script.cpp。

bitcoin-code

比特币脚本语言定义了一组操作符,例如OP_FALSE,OP_RIPEMD160,OP_HASH256等等。操作符作用于栈里面的数据。在使用操作符对栈当中的数据进行操作之后,输出的数据将被再次压回栈中。该脚本语言中包含两种不同类型的对象:操作符与操作数。操作符位于enum opcodetype,每一条对应一个操作符。操作数则是操作符的输入数据。它们的类型为valtype,由一个unsigned char容器构成(typedef vector<unsigned char> valtype)。一个操作符与它的操作数,如果有的话,合起来称为一条指令。

enum opcodetype

enum opcodetype
{
    // push value
    OP_0=0,
    OP_FALSE=OP_0,
    OP_PUSHDATA1=76,
    OP_PUSHDATA2,
    OP_PUSHDATA4,
    OP_1NEGATE,
    OP_RESERVED,
    OP_1,
    OP_TRUE=OP_1,
    OP_2,
    OP_3,
    OP_4,
    OP_5,
    OP_6,
    OP_7,
    OP_8,
    OP_9,
    OP_10,
    OP_11,
    OP_12,
    OP_13,
    OP_14,
    OP_15,
    OP_16,

    // control
    OP_NOP,
    OP_VER,
    OP_IF,
    OP_NOTIF,
    OP_VERIF,
    OP_VERNOTIF,
    OP_ELSE,
    OP_ENDIF,
    OP_VERIFY,
    OP_RETURN,

    // stack ops
    OP_TOALTSTACK,
    OP_FROMALTSTACK,
    OP_2DROP,
    OP_2DUP,
    OP_3DUP,
    OP_2OVER,
    OP_2ROT,
    OP_2SWAP,
    OP_IFDUP,
    OP_DEPTH,
    OP_DROP,
    OP_DUP,
    OP_NIP,
    OP_OVER,
    OP_PICK,
    OP_ROLL,
    OP_ROT,
    OP_SWAP,
    OP_TUCK,

    // splice ops
    OP_CAT,
    OP_SUBSTR,
    OP_LEFT,
    OP_RIGHT,
    OP_SIZE,

    // bit logic
    OP_INVERT,
    OP_AND,
    OP_OR,
    OP_XOR,
    OP_EQUAL,
    OP_EQUALVERIFY,
    OP_RESERVED1,
    OP_RESERVED2,

    // numeric
    OP_1ADD,
    OP_1SUB,
    OP_2MUL,
    OP_2DIV,
    OP_NEGATE,
    OP_ABS,
    OP_NOT,
    OP_0NOTEQUAL,

    OP_ADD,
    OP_SUB,
    OP_MUL,
    OP_DIV,
    OP_MOD,
    OP_LSHIFT,
    OP_RSHIFT,

    OP_BOOLAND,
    OP_BOOLOR,
    OP_NUMEQUAL,
    OP_NUMEQUALVERIFY,
    OP_NUMNOTEQUAL,
    OP_LESSTHAN,
    OP_GREATERTHAN,
    OP_LESSTHANOREQUAL,
    OP_GREATERTHANOREQUAL,
    OP_MIN,
    OP_MAX,

    OP_WITHIN,

    // crypto
    OP_RIPEMD160,
    OP_SHA1,
    OP_SHA256,
    OP_HASH160,
    OP_HASH256,
    OP_CODESEPARATOR,
    OP_CHECKSIG,
    OP_CHECKSIGVERIFY,
    OP_CHECKMULTISIG,
    OP_CHECKMULTISIGVERIFY,

    // multi-byte opcodes
    OP_SINGLEBYTE_END = 0xF0,
    OP_DOUBLEBYTE_BEGIN = 0xF000,

    // template matching params
    OP_PUBKEY,
    OP_PUBKEYHASH,

    OP_INVALIDOPCODE = 0xFFFF,
};

这里共有106个操作符,加上OP_FALSE与OP_TRUE,它们分别是OP_0与OP_1的假名。操作符代码并不是连贯的。从OP_PUSHDATA = 76 = 0x4C开始,代码逐渐递增至OP_CHECKMULTISIGVERIFY = 175 = 0xAF。接下来,代码的值为OP_SINGLEBYTE_END = 0xF0。在它之后的是OP_DOUBLEBYTE_BEGIN = 0xF000。所有在它后面的代码值占用两个字节。

CScript

CScript类包含一个将被解析并执行的脚本。一个脚本只不过是一个字符流,所以CScript只不过是一个vector<unsigned char>。脚本通过重载操作符<<被插入到CScript当中。它接受多种输入类型(不要与比特币脚本中的操作符搞混)。

CScript的操作符<<

class CScript : public vector<unsigned char>
{
protected:
    CScript& push_int64(int64 n)
    {
        if (n == -1 || (n >= 1 && n <= 16))
        {
            push_back(n + (OP_1 - 1));
        }
        else
        {
            CBigNum bn(n);
            *this << bn.getvch();
        }
        return (*this);
    }

    CScript& push_uint64(uint64 n)
    {
        if (n == -1 || (n >= 1 && n <= 16))
        {
            push_back(n + (OP_1 - 1));
        }
        else
        {
            CBigNum bn(n);
            *this << bn.getvch();
        }
        return (*this);
    }

public:
    CScript() { }
    CScript(const CScript& b) : vector<unsigned char>(b.begin(), b.end()) { }
    CScript(const_iterator pbegin, const_iterator pend) : vector<unsigned char>(pbegin, pend) { }
#ifndef _MSC_VER
    CScript(const unsigned char* pbegin, const unsigned char* pend) : vector<unsigned char>(pbegin, pend) { }
#endif

    CScript& operator+=(const CScript& b)
    {
        insert(end(), b.begin(), b.end());
        return *this;
    }

    friend CScript operator+(const CScript& a, const CScript& b)
    {
        CScript ret = a;
        ret += b;
        return (ret);
    }

    explicit CScript(char b)           { operator<<(b); }
    explicit CScript(short b)          { operator<<(b); }
    explicit CScript(int b)            { operator<<(b); }
    explicit CScript(long b)           { operator<<(b); }
    explicit CScript(int64 b)          { operator<<(b); }
    explicit CScript(unsigned char b)  { operator<<(b); }
    explicit CScript(unsigned int b)   { operator<<(b); }
    explicit CScript(unsigned short b) { operator<<(b); }
    explicit CScript(unsigned long b)  { operator<<(b); }
    explicit CScript(uint64 b)         { operator<<(b); }

    explicit CScript(opcodetype b)     { operator<<(b); }
    explicit CScript(const uint256& b) { operator<<(b); }
    explicit CScript(const CBigNum& b) { operator<<(b); }
    explicit CScript(const vector<unsigned char>& b) { operator<<(b); }

    CScript& operator<<(char b)           { return (push_int64(b)); }
    CScript& operator<<(short b)          { return (push_int64(b)); }
    CScript& operator<<(int b)            { return (push_int64(b)); }
    CScript& operator<<(long b)           { return (push_int64(b)); }
    CScript& operator<<(int64 b)          { return (push_int64(b)); }
    CScript& operator<<(unsigned char b)  { return (push_uint64(b)); }
    CScript& operator<<(unsigned int b)   { return (push_uint64(b)); }
    CScript& operator<<(unsigned short b) { return (push_uint64(b)); }
    CScript& operator<<(unsigned long b)  { return (push_uint64(b)); }
    CScript& operator<<(uint64 b)         { return (push_uint64(b)); }

    CScript& operator<<(opcodetype opcode)
    {
        if (opcode <= OP_SINGLEBYTE_END)
        {
            insert(end(), (unsigned char)opcode);
        }
        else
        {
            assert(opcode >= OP_DOUBLEBYTE_BEGIN);
            insert(end(), (unsigned char)(opcode >> 8));
            insert(end(), (unsigned char)(opcode & 0xFF));
        }
        return (*this);
    }

    CScript& operator<<(const uint160& b)
    {
        insert(end(), sizeof(b));
        insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b));
        return (*this);
    }

    CScript& operator<<(const uint256& b)
    {
        insert(end(), sizeof(b));
        insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b));
        return (*this);
    }

    CScript& operator<<(const CBigNum& b)
    {
        *this << b.getvch();
        return (*this);
    }

    CScript& operator<<(const vector<unsigned char>& b)
    {
        if (b.size() < OP_PUSHDATA1)
        {
            insert(end(), (unsigned char)b.size());
        }
        else if (b.size() <= 0xff)
        {
            insert(end(), OP_PUSHDATA1);
            insert(end(), (unsigned char)b.size());
        }
        else
        {
            insert(end(), OP_PUSHDATA2);
            unsigned short nSize = b.size();
            insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize));
        }
        insert(end(), b.begin(), b.end());
        return (*this);
    }

    CScript& operator<<(const CScript& b)
    {
        // I'm not sure if this should push the script or concatenate scripts.
        // If there's ever a use for pushing a script onto a script, delete this member fn
        assert(("warning: pushing a CScript onto a CScript with << is probably not intended, use + to concatenate", false));
        return (*this);
    }

    bool GetOp(iterator& pc, opcodetype& opcodeRet, vector<unsigned char>& vchRet)
    {
         // This is why people hate C++
         const_iterator pc2 = pc;
         bool fRet = GetOp(pc2, opcodeRet, vchRet);
         pc = begin() + (pc2 - begin());
         return fRet;
    }

    bool GetOp(const_iterator& pc, opcodetype& opcodeRet, vector<unsigned char>& vchRet) const
    {
        opcodeRet = OP_INVALIDOPCODE;
        vchRet.clear();
        if (pc >= end())
            return false;

        // Read instruction
        unsigned int opcode = *pc++;
        if (opcode >= OP_SINGLEBYTE_END)
        {
            if (pc + 1 > end())
                return false;
            opcode <<= 8;
            opcode |= *pc++;
        }

        // Immediate operand
        if (opcode <= OP_PUSHDATA4)
        {
            unsigned int nSize = opcode;
            if (opcode == OP_PUSHDATA1)
            {
                if (pc + 1 > end())
                    return false;
                nSize = *pc++;
            }
            else if (opcode == OP_PUSHDATA2)
            {
                if (pc + 2 > end())
                    return false;
                nSize = 0;
                memcpy(&nSize, &pc[0], 2);
                pc += 2;
            }
            else if (opcode == OP_PUSHDATA4)
            {
                if (pc + 4 > end())
                    return false;
                memcpy(&nSize, &pc[0], 4);
                pc += 4;
            }
            if (pc + nSize > end())
                return false;
            vchRet.assign(pc, pc + nSize);
            pc += nSize;
        }

        opcodeRet = (opcodetype)opcode;
        return true;
    }

    void FindAndDelete(const CScript& b)
    {
        iterator pc = begin();
        opcodetype opcode;
        vector<unsigned char> vchPushValue;
        int count = 0;
        do
        {
            while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
            {
                erase(pc, pc + b.size());
                count++;
            }
        }
        while (GetOp(pc, opcode, vchPushValue));
        //printf("FindAndDeleted deleted %d items\n", count); /// debug
    }

    void PrintHex() const
    {
        printf("CScript(%s)\n", HexStr(begin(), end()).c_str());
    }

    string ToString() const
    {
        string str;
        opcodetype opcode;
        vector<unsigned char> vch;
        const_iterator it = begin();
        while (GetOp(it, opcode, vch))
        {
            if (!str.empty())
                str += " ";
            if (opcode <= OP_PUSHDATA4)
                str += ValueString(vch);
            else
                str += GetOpName(opcode);
        }
        return str;
    }

    void print() const
    {
        printf("%s\n", ToString().c_str());
    }
};

重载之后的CScript的<<操作符接受的类型为char,short,int,long,并隐性将它们转换为int64(又称为long long),并调用push_int64(int64 n)将它们放入CScript的内部存储当中(由于CScript延伸了vector<unsigned char>,它的内部存储即为后者。你可以将它看做一个unsigned char类型的动态数组。通过检查push_int64(int64 n)的主体部分,显示如果n为-1或位于1与16之间,n则被压入为n + (OP_1 – 1),即n + (81 -1) = n + 80 (OP_1 = 0×51)。于是,如果n为-1,则以单字节值79被压入栈,即操作符代码值OP_1NEGATE。如果n位于1与16之间,则以单字节值81至96被压入栈,正是操作符代码值OP_1至OP_16。对于其他n的值,则将其以CBigNum对待。简而言之:

  • 当n为-1时,n被看作为操作符OP_1NEGATE(79)。
  • 当n为1-16之间的数字时,n被看作为OP_1(81)与OP_16(96)之间的一个操作符。
  • 否则,n被当作CBigNum类型,并返回bn.getvch()的值,该值为一个字符类容器,并被压入内部存储(第12行)。我将在后面对CBigNum进行介绍。
重载后的操作符<<同样接受usigned版本的char,short,int,long和uint64作为输入类型。它们对应的重载函数与它们的signed版本的逻辑相同。
现在,我们来检查一下其他输入类型的操作符重载函数。
  • 对于输入类型uint160,首先将数据的大小压入内部存储(第51行),接着是数据本身(第52行)。该过程同样被应用于输入类型uint256(第55行)。这两种类型被用于存放哈希码。
  • 对于输入类型opcodetype,例如操作符类型,则将代码值直接压入内部存储当中(第39行)。如果代码值占用2个字节,它们将被先后压入(第44至45行)。注意最高有效字节首先被压入(第44行)。所以代码0xF001将被压入为0xF001,而不是0x01F0。我将在接下来CScript::GetOp()中详细介绍为什么这么做很重要。
  • 对于输入类型CBigNum,一个字符类容器用于表示被压入的big number类型(第63行)。这与push_int64()处理CBigNum的方式一致。
  • 对于输入类型vector <unsigned char>,过程则更加复杂:
    • 如果输入b的大小小于76(OP_PUSHDATA1的代码值),则b的大小将首先以单字节值被压入(第70行),接着是数据b本身(第83行)。
    • 如果b的大小在76与255之间(包括两者),操作符OP_PUSHDATA1首先被压入(第74行),接着是b的大小以单字节值的形式(第75行),接着是数据b本身(第83行)。
    • 否则,操作符OP_PUSHDATA2首先被压入(第2行),接着是b的大小以双字节(short)值的形式(第80至81行),接着是数据b本身(第83行)。
重载之后的操作符<<将指令压入CScript,函数CScript::GetOp()则将指令提取出CScript。

CScript::GetOp()

以下是CScript::GetOp()的源码。
    bool GetOp(const_iterator& pc, opcodetype& opcodeRet, vector<unsigned char>& vchRet) const
    {
        opcodeRet = OP_INVALIDOPCODE;
        vchRet.clear();
        if (pc >= end())
            return false;

        // Read instruction
        unsigned int opcode = *pc++;
        if (opcode >= OP_SINGLEBYTE_END)
        {
            if (pc + 1 > end())
                return false;
            opcode <<= 8;
            opcode |= *pc++;
        }

        // Immediate operand
        if (opcode <= OP_PUSHDATA4)
        {
            unsigned int nSize = opcode;
            if (opcode == OP_PUSHDATA1)
            {
                if (pc + 1 > end())
                    return false;
                nSize = *pc++;
            }
            else if (opcode == OP_PUSHDATA2)
            {
                if (pc + 2 > end())
                    return false;
                nSize = 0;
                memcpy(&nSize, &pc[0], 2);
                pc += 2;
            }
            else if (opcode == OP_PUSHDATA4)
            {
                if (pc + 4 > end())
                    return false;
                memcpy(&nSize, &pc[0], 4);
                pc += 4;
            }
            if (pc + nSize > end())
                return false;
            vchRet.assign(pc, pc + nSize);
            pc += nSize;
        }

        opcodeRet = (opcodetype)opcode;
        return true;
    }
该函数从pc中读取一个指令(如果必须,一个操作符或操作数),并将它们分别放入opcodeRet和vchRet。迭代器pc指向CScript的内部存储中将要读取数据的位置。以下是该函数的用途:
  1. 它区分单字节与双字节的操作符。如果第一个字节大于0xF0(第14行),则下一个字节将被提取,并将这两个字节合并成一个双字节操作符(第16、17行)。这便是为什么双字节操作符中最高有效字节首先被压入。所有的双字节操作符的第一个字节均大于0xF0,而所有单字节操作符的第一个字节均小于0xF0。
  2. 在第20行,如果第一个字节小于或等于78(OP_PUSHDATA4的代码值),则根据值的大小,该函数有4种不同情况:
  • 如果代码值是76(OP_PUSHDATA1的代码值),它读取接下来一个字节作为数据的大小至nSize(第27行),接着读取nSize个字节至vchRet(第46行)。所以,假设nSize = 100,则读取接下来100个字节至vchRet。
  • 如果代码值是77(OP_PUSHDATA2的代码值),它读取接下来两个字节作为数据的大小(第34行),接着读取数据本身至vchRet(第46行)。
  • 如果代码值是78(OP_PUSHDATA4的代码值),它读取接下来四个字节作为数据的大小(第41行),接着读取数据本身至vchRet(第46行)。
  • 否则,如果代码值位于0与75之间(包括两者),该值被当作数据的大小(第22行)。该函数将读取相同大小的字节至vchRet(第46行)。你或许会奇怪这样将会返回哪种操作符。返回的操作符代码是提取出的一个位于0与75之间的代码,其实是UNKNOWN_OPCODE(除了0之外,表示OP_0或OP_FALSE)。这不会引起任何问题,当你了解在指令执行过程当中如处理UNKNOWN_OPCODE的之后就会明白。

EvalScript()

该函数依次执行所传入脚本中包含的指令。所返回的结果为布尔值true或false。它通过一个while循环(第13行)依次提取脚本中的指令。在循环里,根据当前指令的操作符,执行过程会进入相应的分支并执行指令。以下是该函数的源码:
bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType,
                vector<vector<unsigned char> >* pvStackRet)
{
    CAutoBN_CTX pctx;
    CScript::const_iterator pc = script.begin();
    CScript::const_iterator pend = script.end();
    CScript::const_iterator pbegincodehash = script.begin();
    vector<bool> vfExec;
    vector<valtype> stack;
    vector<valtype> altstack;
    if (pvStackRet)
        pvStackRet->clear();

    while (pc < pend)
    {
        bool fExec = !count(vfExec.begin(), vfExec.end(), false);

        //
        // Read instruction
        //
        opcodetype opcode;
        valtype vchPushValue;
        if (!script.GetOp(pc, opcode, vchPushValue))
            return false;

        if (fExec && opcode <= OP_PUSHDATA4)
            stack.push_back(vchPushValue);
        else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
        switch (opcode)
        {
            //
            // Push value
            //
            case OP_1NEGATE:
            case OP_1:
            case OP_2:
            case OP_3:
            case OP_4:
            case OP_5:
            case OP_6:
            case OP_7:
            case OP_8:
            case OP_9:
            case OP_10:
            case OP_11:
            case OP_12:
            case OP_13:
            case OP_14:
            case OP_15:
            case OP_16:
            {
                // ( -- value)
                CBigNum bn((int)opcode - (int)(OP_1 - 1));
                stack.push_back(bn.getvch());
            }
            break;

            //
            // Control
            //
            case OP_NOP:
            break;

            case OP_VER:
            {
                CBigNum bn(VERSION);
                stack.push_back(bn.getvch());
            }
            break;

            case OP_IF:
            case OP_NOTIF:
            case OP_VERIF:
            case OP_VERNOTIF:
            {
                // <expression> if [statements] [else [statements]] endif
                bool fValue = false;
                if (fExec)
                {
                    if (stack.size() < 1)
                        return false;
                    valtype& vch = stacktop(-1);
                    if (opcode == OP_VERIF || opcode == OP_VERNOTIF)
                        fValue = (CBigNum(VERSION) >= CBigNum(vch));
                    else
                        fValue = CastToBool(vch);
                    if (opcode == OP_NOTIF || opcode == OP_VERNOTIF)
                        fValue = !fValue;
                    stack.pop_back();
                }
                vfExec.push_back(fValue);
            }
            break;

            case OP_ELSE:
            {
                if (vfExec.empty())
                    return false;
                vfExec.back() = !vfExec.back();
            }
            break;

            case OP_ENDIF:
            {
                if (vfExec.empty())
                    return false;
                vfExec.pop_back();
            }
            break;

            case OP_VERIFY:
            {
                // (true -- ) or
                // (false -- false) and return
                if (stack.size() < 1)
                    return false;
                bool fValue = CastToBool(stacktop(-1));
                if (fValue)
                    stack.pop_back();
                else
                    pc = pend;
            }
            break;

            case OP_RETURN:
            {
                pc = pend;
            }
            break;

            //
            // Stack ops
            //
            case OP_TOALTSTACK:
            {
                if (stack.size() < 1)
                    return false;
                altstack.push_back(stacktop(-1));
                stack.pop_back();
            }
            break;

            case OP_FROMALTSTACK:
            {
                if (altstack.size() < 1)
                    return false;
                stack.push_back(altstacktop(-1));
                altstack.pop_back();
            }
            break;

            case OP_2DROP:
            {
                // (x1 x2 -- )
                stack.pop_back();
                stack.pop_back();
            }
            break;

            case OP_2DUP:
            {
                // (x1 x2 -- x1 x2 x1 x2)
                if (stack.size() < 2)
                    return false;
                valtype vch1 = stacktop(-2);
                valtype vch2 = stacktop(-1);
                stack.push_back(vch1);
                stack.push_back(vch2);
            }
            break;

            case OP_3DUP:
            {
                // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
                if (stack.size() < 3)
                    return false;
                valtype vch1 = stacktop(-3);
                valtype vch2 = stacktop(-2);
                valtype vch3 = stacktop(-1);
                stack.push_back(vch1);
                stack.push_back(vch2);
                stack.push_back(vch3);
            }
            break;

            case OP_2OVER:
            {
                // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
                if (stack.size() < 4)
                    return false;
                valtype vch1 = stacktop(-4);
                valtype vch2 = stacktop(-3);
                stack.push_back(vch1);
                stack.push_back(vch2);
            }
            break;

            case OP_2ROT:
            {
                // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
                if (stack.size() < 6)
                    return false;
                valtype vch1 = stacktop(-6);
                valtype vch2 = stacktop(-5);
                stack.erase(stack.end()-6, stack.end()-4);
                stack.push_back(vch1);
                stack.push_back(vch2);
            }
            break;

            case OP_2SWAP:
            {
                // (x1 x2 x3 x4 -- x3 x4 x1 x2)
                if (stack.size() < 4)
                    return false;
                swap(stacktop(-4), stacktop(-2));
                swap(stacktop(-3), stacktop(-1));
            }
            break;

            case OP_IFDUP:
            {
                // (x - 0 | x x)
                if (stack.size() < 1)
                    return false;
                valtype vch = stacktop(-1);
                if (CastToBool(vch))
                    stack.push_back(vch);
            }
            break;

            case OP_DEPTH:
            {
                // -- stacksize
                CBigNum bn(stack.size());
                stack.push_back(bn.getvch());
            }
            break;

            case OP_DROP:
            {
                // (x -- )
                if (stack.size() < 1)
                    return false;
                stack.pop_back();
            }
            break;

            case OP_DUP:
            {
                // (x -- x x)
                if (stack.size() < 1)
                    return false;
                valtype vch = stacktop(-1);
                stack.push_back(vch);
            }
            break;

            case OP_NIP:
            {
                // (x1 x2 -- x2)
                if (stack.size() < 2)
                    return false;
                stack.erase(stack.end() - 2);
            }
            break;

            case OP_OVER:
            {
                // (x1 x2 -- x1 x2 x1)
                if (stack.size() < 2)
                    return false;
                valtype vch = stacktop(-2);
                stack.push_back(vch);
            }
            break;

            case OP_PICK:
            case OP_ROLL:
            {
                // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
                // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
                if (stack.size() < 2)
                    return false;
                int n = CBigNum(stacktop(-1)).getint();
                stack.pop_back();
                if (n < 0 || n >= stack.size())
                    return false;
                valtype vch = stacktop(-n-1);
                if (opcode == OP_ROLL)
                    stack.erase(stack.end()-n-1);
                stack.push_back(vch);
            }
            break;

            case OP_ROT:
            {
                // (x1 x2 x3 -- x2 x3 x1)
                //  x2 x1 x3  after first swap
                //  x2 x3 x1  after second swap
                if (stack.size() < 3)
                    return false;
                swap(stacktop(-3), stacktop(-2));
                swap(stacktop(-2), stacktop(-1));
            }
            break;

            case OP_SWAP:
            {
                // (x1 x2 -- x2 x1)
                if (stack.size() < 2)
                    return false;
                swap(stacktop(-2), stacktop(-1));
            }
            break;

            case OP_TUCK:
            {
                // (x1 x2 -- x2 x1 x2)
                if (stack.size() < 2)
                    return false;
                valtype vch = stacktop(-1);
                stack.insert(stack.end()-2, vch);
            }
            break;

            //
            // Splice ops
            //
            case OP_CAT:
            {
                // (x1 x2 -- out)
                if (stack.size() < 2)
                    return false;
                valtype& vch1 = stacktop(-2);
                valtype& vch2 = stacktop(-1);
                vch1.insert(vch1.end(), vch2.begin(), vch2.end());
                stack.pop_back();
            }
            break;

            case OP_SUBSTR:
            {
                // (in begin size -- out)
                if (stack.size() < 3)
                    return false;
                valtype& vch = stacktop(-3);
                int nBegin = CBigNum(stacktop(-2)).getint();
                int nEnd = nBegin + CBigNum(stacktop(-1)).getint();
                if (nBegin < 0 || nEnd < nBegin)
                    return false;
                if (nBegin > vch.size())
                    nBegin = vch.size();
                if (nEnd > vch.size())
                    nEnd = vch.size();
                vch.erase(vch.begin() + nEnd, vch.end());
                vch.erase(vch.begin(), vch.begin() + nBegin);
                stack.pop_back();
                stack.pop_back();
            }
            break;

            case OP_LEFT:
            case OP_RIGHT:
            {
                // (in size -- out)
                if (stack.size() < 2)
                    return false;
                valtype& vch = stacktop(-2);
                int nSize = CBigNum(stacktop(-1)).getint();
                if (nSize < 0)
                    return false;
                if (nSize > vch.size())
                    nSize = vch.size();
                if (opcode == OP_LEFT)
                    vch.erase(vch.begin() + nSize, vch.end());
                else
                    vch.erase(vch.begin(), vch.end() - nSize);
                stack.pop_back();
            }
            break;

            case OP_SIZE:
            {
                // (in -- in size)
                if (stack.size() < 1)
                    return false;
                CBigNum bn(stacktop(-1).size());
                stack.push_back(bn.getvch());
            }
            break;

            //
            // Bitwise logic
            //
            case OP_INVERT:
            {
                // (in - out)
                if (stack.size() < 1)
                    return false;
                valtype& vch = stacktop(-1);
                for (int i = 0; i < vch.size(); i++)
                    vch[i] = ~vch[i];
            }
            break;

            case OP_AND:
            case OP_OR:
            case OP_XOR:
            {
                // (x1 x2 - out)
                if (stack.size() < 2)
                    return false;
                valtype& vch1 = stacktop(-2);
                valtype& vch2 = stacktop(-1);
                MakeSameSize(vch1, vch2);
                if (opcode == OP_AND)
                {
                    for (int i = 0; i < vch1.size(); i++)
                        vch1[i] &= vch2[i];
                }
                else if (opcode == OP_OR)
                {
                    for (int i = 0; i < vch1.size(); i++)
                        vch1[i] |= vch2[i];
                }
                else if (opcode == OP_XOR)
                {
                    for (int i = 0; i < vch1.size(); i++)
                        vch1[i] ^= vch2[i];
                }
                stack.pop_back();
            }
            break;

            case OP_EQUAL:
            case OP_EQUALVERIFY:
            //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
            {
                // (x1 x2 - bool)
                if (stack.size() < 2)
                    return false;
                valtype& vch1 = stacktop(-2);
                valtype& vch2 = stacktop(-1);
                bool fEqual = (vch1 == vch2);
                // OP_NOTEQUAL is disabled because it would be too easy to say
                // something like n != 1 and have some wiseguy pass in 1 with extra
                // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
                //if (opcode == OP_NOTEQUAL)
                //    fEqual = !fEqual;
                stack.pop_back();
                stack.pop_back();
                stack.push_back(fEqual ? vchTrue : vchFalse);
                if (opcode == OP_EQUALVERIFY)
                {
                    if (fEqual)
                        stack.pop_back();
                    else
                        pc = pend;
                }
            }
            break;

            //
            // Numeric
            //
            case OP_1ADD:
            case OP_1SUB:
            case OP_2MUL:
            case OP_2DIV:
            case OP_NEGATE:
            case OP_ABS:
            case OP_NOT:
            case OP_0NOTEQUAL:
            {
                // (in -- out)
                if (stack.size() < 1)
                    return false;
                CBigNum bn(stacktop(-1));
                switch (opcode)
                {
                case OP_1ADD:       bn += bnOne; break;
                case OP_1SUB:       bn -= bnOne; break;
                case OP_2MUL:       bn <<= 1; break;
                case OP_2DIV:       bn >>= 1; break;
                case OP_NEGATE:     bn = -bn; break;
                case OP_ABS:        if (bn < bnZero) bn = -bn; break;
                case OP_NOT:        bn = (bn == bnZero); break;
                case OP_0NOTEQUAL:  bn = (bn != bnZero); break;
                }
                stack.pop_back();
                stack.push_back(bn.getvch());
            }
            break;

            case OP_ADD:
            case OP_SUB:
            case OP_MUL:
            case OP_DIV:
            case OP_MOD:
            case OP_LSHIFT:
            case OP_RSHIFT:
            case OP_BOOLAND:
            case OP_BOOLOR:
            case OP_NUMEQUAL:
            case OP_NUMEQUALVERIFY:
            case OP_NUMNOTEQUAL:
            case OP_LESSTHAN:
            case OP_GREATERTHAN:
            case OP_LESSTHANOREQUAL:
            case OP_GREATERTHANOREQUAL:
            case OP_MIN:
            case OP_MAX:
            {
                // (x1 x2 -- out)
                if (stack.size() < 2)
                    return false;
                CBigNum bn1(stacktop(-2));
                CBigNum bn2(stacktop(-1));
                CBigNum bn;
                switch (opcode)
                {
                case OP_ADD:
                    bn = bn1 + bn2;
                    break;

                case OP_SUB:
                    bn = bn1 - bn2;
                    break;

                case OP_MUL:
                    if (!BN_mul(&bn, &bn1, &bn2, pctx))
                        return false;
                    break;

                case OP_DIV:
                    if (!BN_div(&bn, NULL, &bn1, &bn2, pctx))
                        return false;
                    break;

                case OP_MOD:
                    if (!BN_mod(&bn, &bn1, &bn2, pctx))
                        return false;
                    break;

                case OP_LSHIFT:
                    if (bn2 < bnZero)
                        return false;
                    bn = bn1 << bn2.getulong();
                    break;

                case OP_RSHIFT:
                    if (bn2 < bnZero)
                        return false;
                    bn = bn1 >> bn2.getulong();
                    break;

                case OP_BOOLAND:             bn = (bn1 != bnZero && bn2 != bnZero); break;
                case OP_BOOLOR:              bn = (bn1 != bnZero || bn2 != bnZero); break;
                case OP_NUMEQUAL:            bn = (bn1 == bn2); break;
                case OP_NUMEQUALVERIFY:      bn = (bn1 == bn2); break;
                case OP_NUMNOTEQUAL:         bn = (bn1 != bn2); break;
                case OP_LESSTHAN:            bn = (bn1 < bn2); break;
                case OP_GREATERTHAN:         bn = (bn1 > bn2); break;
                case OP_LESSTHANOREQUAL:     bn = (bn1 <= bn2); break;
                case OP_GREATERTHANOREQUAL:  bn = (bn1 >= bn2); break;
                case OP_MIN:                 bn = (bn1 < bn2 ? bn1 : bn2); break;
                case OP_MAX:                 bn = (bn1 > bn2 ? bn1 : bn2); break;
                }
                stack.pop_back();
                stack.pop_back();
                stack.push_back(bn.getvch());

                if (opcode == OP_NUMEQUALVERIFY)
                {
                    if (CastToBool(stacktop(-1)))
                        stack.pop_back();
                    else
                        pc = pend;
                }
            }
            break;

            case OP_WITHIN:
            {
                // (x min max -- out)
                if (stack.size() < 3)
                    return false;
                CBigNum bn1(stacktop(-3));
                CBigNum bn2(stacktop(-2));
                CBigNum bn3(stacktop(-1));
                bool fValue = (bn2 <= bn1 && bn1 < bn3);
                stack.pop_back();
                stack.pop_back();
                stack.pop_back();
                stack.push_back(fValue ? vchTrue : vchFalse);
            }
            break;

            //
            // Crypto
            //
            case OP_RIPEMD160:
            case OP_SHA1:
            case OP_SHA256:
            case OP_HASH160:
            case OP_HASH256:
            {
                // (in -- hash)
                if (stack.size() < 1)
                    return false;
                valtype& vch = stacktop(-1);
                valtype vchHash(opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160 ? 20 : 32);
                if (opcode == OP_RIPEMD160)
                    RIPEMD160(&vch[0], vch.size(), &vchHash[0]);
                else if (opcode == OP_SHA1)
                    SHA1(&vch[0], vch.size(), &vchHash[0]);
                else if (opcode == OP_SHA256)
                    SHA256(&vch[0], vch.size(), &vchHash[0]);
                else if (opcode == OP_HASH160)
                {
                    uint160 hash160 = Hash160(vch);
                    memcpy(&vchHash[0], &hash160, sizeof(hash160));
                }
                else if (opcode == OP_HASH256)
                {
                    uint256 hash = Hash(vch.begin(), vch.end());
                    memcpy(&vchHash[0], &hash, sizeof(hash));
                }
                stack.pop_back();
                stack.push_back(vchHash);
            }
            break;

            case OP_CODESEPARATOR:
            {
                // Hash starts after the code separator
                pbegincodehash = pc;
            }
            break;

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

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

                ////// debug print
                //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n");
                //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");

                // Subset of script starting at the most recent codeseparator
                CScript scriptCode(pbegincodehash, pend);

                // Drop the signature, since there's no way for a signature to sign itself
                scriptCode.FindAndDelete(CScript(vchSig));

                bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);

                stack.pop_back();
                stack.pop_back();
                stack.push_back(fSuccess ? vchTrue : vchFalse);
                if (opcode == OP_CHECKSIGVERIFY)
                {
                    if (fSuccess)
                        stack.pop_back();
                    else
                        pc = pend;
                }
            }
            break;

            case OP_CHECKMULTISIG:
            case OP_CHECKMULTISIGVERIFY:
            {
                // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)

                int i = 1;
                if (stack.size() < i)
                    return false;

                int nKeysCount = CBigNum(stacktop(-i)).getint();
                if (nKeysCount < 0)
                    return false;
                int ikey = ++i;
                i += nKeysCount;
                if (stack.size() < i)
                    return false;

                int nSigsCount = CBigNum(stacktop(-i)).getint();
                if (nSigsCount < 0 || nSigsCount > nKeysCount)
                    return false;
                int isig = ++i;
                i += nSigsCount;
                if (stack.size() < i)
                    return false;

                // Subset of script starting at the most recent codeseparator
                CScript scriptCode(pbegincodehash, pend);

                // Drop the signatures, since there's no way for a signature to sign itself
                for (int i = 0; i < nSigsCount; i++)
                {
                    valtype& vchSig = stacktop(-isig-i);
                    scriptCode.FindAndDelete(CScript(vchSig));
                }

                bool fSuccess = true;
                while (fSuccess && nSigsCount > 0)
                {
                    valtype& vchSig    = stacktop(-isig);
                    valtype& vchPubKey = stacktop(-ikey);

                    // Check signature
                    if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType))
                    {
                        isig++;
                        nSigsCount--;
                    }
                    ikey++;
                    nKeysCount--;

                    // If there are more signatures left than keys left,
                    // then too many signatures have failed
                    if (nSigsCount > nKeysCount)
                        fSuccess = false;
                }

                while (i-- > 0)
                    stack.pop_back();
                stack.push_back(fSuccess ? vchTrue : vchFalse);

                if (opcode == OP_CHECKMULTISIGVERIFY)
                {
                    if (fSuccess)
                        stack.pop_back();
                    else
                        pc = pend;
                }
            }
            break;

            default:
                return false;
        }
    }

    if (pvStackRet)
        *pvStackRet = stack;
    return (stack.empty() ? false : CastToBool(stack.back()));
}

以下是关于该函数的一些重要部分:

  1. 迭代器pc总是指向下一个将要读取的字节。
  2. stack是该函数用于操作的主栈。所有从输入脚本中提取的操作数将被压入这个栈当中。所有的通过存放在该栈里的操作数上应用操作符产生的结果也将被压回这个栈。
  3. 如果script.GetOp(pc, opcode, vchPushValue)返回为true(第21行),opcode将包含提取出的操作符,而vchPushValue将包括操作数,如果有的话。
  4. 在第23行,如果opcode小于或等于78(OP_PUSHDATA4的代码值),则vchPushValue中的数据被压入stack,位于第24行。(现在不要管布尔变量fExec,我将会在后面提到)。所以,如果opcode是位于1与75之间的值,即UNKNOWN_OPCODE,vchPushValue中的数据将被压入stack。对于opcode值0,76,77,78同样如此。所以,比特币脚本语言对短操作数进行了优化:对于小于或等于75字节的操作数,你只需要首先压入数据的大小,接着数据本身至CScript中。解释器将正确处理它。对于长的操作数,你必须首先压入一个OP_PUSHDATA#操作符。
  5. 操作符OP_1NEGATE和OP_1与OP_16之间的操作符首先将它们所对应的数据值压入stack(第51行)。因此,OP_1NEGATE(代码值79)将-1压入stack,+OP_2(代码值82)将2压入stack,以此类推。这与push_int64()处理输入数据的方式是一致的。
EvalScript()的结构简单而清晰:script.GetOP()从script(第21行)提取一个指令。取决于所提取的操作符opcode,每种情况分别在switch(opcode)语句(第26行)中的某一分支执行。只有含有变量fExec的代码(第23与25行)令人有些疑惑。下面让我们更进一步了解它们。

嵌套OP_IF

变量fExec在每次循环的开始被赋予一个新值(第15行)。如果容器vfExec包含至少一个false,它的值为false。否则,如果vfExec的每一个条目均为true,则它的值为true。
容器vfExec是一个用来记录在一个嵌套OP_IF语句中当前指令位置的栈。这个位置决定当前指令是否被执行。
在任何时候,vfExec中的条目表示当前指令在嵌套OP_IF语句当中的位置。例如,[true, false]意味着当前指令位于内层语句的false分支,同时位于外层语句的true分支。这便市为什么当vfExec中包含至少一个false时,整个便为false。因为任何vfExec的false表示当前指令在嵌套OP_IF语句当中的某个false分支上,因此不会被执行。

若干switch分支

  1. OP_EQUALVERIFY分支非常简单(第106至107行),它从stack中弹出两个值并进行比较(第113至114行)。
  2. 哈希操作符被放在了一起(第137-141行)。操作符OP_RIPEMD160、OP_SHA1与OP_HASH160的输出大小为160位,OP_SHA256和OP_HASH256为256位。将要哈希的数据从stack顶部取出(第146行),结果被放回stack(第165行)。
  3. OP_CHECKSIG分支更有意思(第174至175行)。该分支检查交易txTo(第二个参数)的签名部分。我现在用我在第一章所提到的例子中的脚本A和B解释一下该操作符是怎样被执行验证签名的。

执行OP_CHECKSIG

现在我用《比特币源码学习笔记(一)》中的验证案例A与B说明:
  1. 验证案例A:<你的签名_vchSig> <你的公钥_vchPubKey> OP_CODESEPARATOR OP_DUP OP_HASH160 <你的地址_hash160> OP_EQUALVERIFY OP__CHECKSIG,例如签名A + OP_CODESEPARATOR + 脚本A。
  2. 验证案例B:<你的签名_vchSig> OP_CODESEPARATOR <你的公钥_vchPubKey> OP__CHECKSIG,例如签名B + OP_CODESEPARATOR + 脚本B。
我将会讲解验证案例B,你可以自己推出验证案例A。
  1. 首先,注意签名B + OP_CODESEPARATOR + 脚本B被作为第一个参数script传入至函数EvalScript()。
  2. 第一个被读入的指令是 <你的公钥_vchPubKey> ,它是一个类型为vector <unsigned char>的签名。因此,它将被压入stack,位于第24行。
  3. 第二个被读入的指令是OP_CODESEPARATOR。在第171行,一个本地变量pegincodehash被赋予值pc,指向下一个将被读取的指令,即 <你的公钥_vchPubKey>。
  4. 第三个被读入的指令是 <你的公钥_vchPubKey>,它是一个256位的公钥,并将会被压入stack,位于第24行。
  5. 第四个被读入的指令是OP__CHECKSIG。执行将进入OP__CHECKSIG分支(第174至175行)。
  6. 在第180行,<你的签名_vchSig>从stack中被提出,即vchSig = stacktop(-2)。
  7. 在第181行,<你的公钥_vchPubKey>从stack中被提出,即vchPubKey = stacktop(-1)。
  8. 第188行对scriptCode没有任何影响,因为它不包含任何签名。
  9. 第189行,函数CheckSig()以参数CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)被调用。下面是该函数的源代码。
bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode,
              const CTransaction& txTo, unsigned int nIn, int nHashType)
{
    CKey key;
    if (!key.SetPubKey(vchPubKey))
        return false;

    // Hash type is one byte tacked on to the end of the signature
    if (vchSig.empty())
        return false;
    if (nHashType == 0)
        nHashType = vchSig.back();
    else if (nHashType != vchSig.back())
        return false;
    vchSig.pop_back();

    if (key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig))
        return true;

    return false;
}

我们来研究下这个函数:

  1. 它的第一个参数vchSIg是<你的签名_vchSig>。
  2. 第二个参数vchPubKey是<你的公钥_vchPubKey>。
  3. 第三个参数scriptCode是<你的公钥_vchPubKey> OP_CHECKSIG。
函数CkeckSig()调用函数SignatureHash(scriptCode,txTo,nIn,nHashType)位于第15行。如果你比较我在第一章里介绍过的函数SignatureHash(),你会发现二者的输入参数完全相同。所以你会得到完全相同的哈希码。
接着回到继续执行验证案例B:
  1. 位于第190至191行,stack最顶端的两个项目,例如<你的签名_vchSig>和<你的公钥_vchPubKey>将被弹出。
  2. 位于第192行,CheckSig()的返回值,为true,将会被压入stack。因此stack现在仅包含一个true,意味着脚本执行成功。

CBigNum

CBigNum是openssl库中定义的BIGNUM的包装类。公钥密码学需要能够处理非常大的整数。标准的数据类型无法满足要求。BIGNUM可以存放任意长度的整型。
CBigNum类的结构并不复杂。它是由一堆不同类型构造BIGNUM的构造器组成,包括char,short,int,long,int64,int256,它们unsigned版本和vector<unsigned char>等。它同样重构操作符,例如加、减、乘、除、位操作等。所有的实际工作代理给了BIGNUM类行。大部分CBigNum的代码仅仅是为BIGNUM的函数准备输入数据。
发文时比特币价格 ¥18710
作者:g2com
BTC地址:39a7MDX3chm8B9Y9Yc6FgFzSiEcw3EV5qM
稿源:巴比特资讯(http://www.8btc.com/bitcoin-code-4)
版权声明: by nc" sa 作者保留权利。文章为作者独立观点,不代表巴比特立场。

评论:10

您需要登录后才可以回复 登录|注册
    +1
    +1
    我要点评
      Author Image
      g2com 132 天前

      收到,谢谢!

      +1
      +1
      我要点评
    Author Image
    KingFxcker 151 天前

    欸,楼主和我写的一样的呢,我也是在研究比特币0.1版本的源码,不过我是写在知乎专栏里面的:
    https://zhuanlan.zhihu.com/p/27512347
    这里也贴出来和大家分享一下

    +1
    +1
    我要点评
    Author Image
    Fish 152 天前

    您好!可以相互认识一下吗?有些问题想相互探讨一下

    +1
    +1
    我要点评
    Author Image
    impressiver 154 天前

    脚本 if else endif 那段代码我没读懂,能不能再详细点?谢谢了

    +1
    +1
    我要点评
    Author Image
    姜家志 167 天前

    您好!能相互认识下吗?一起聊聊技术。

    +1
    +1
    我要点评
    Author Image
    沙漠中的猴 168 天前

    您好,麻烦请教一下,这个版本的源代码去哪里下载?

    +1
    +1
    我要点评
    巴比特资讯
    巴比特资讯 168 天前

    【比特币源码学习笔记(四)】比特币带有一个基于栈的脚本语言。每笔交易的输出部分都嵌有一段脚本。要想花费一笔交易中所携带的币,接收方必须提供他的公钥,以使脚本能够成功执行。本篇将为大家介绍比特币的脚本语言。http://t.cn/RSjLgYG 作者:g2com ​

    +1
    +1
    我要点评