比特币探究之交易签名

在比特币探究之交易创建这篇文章里,可以看到在交易创建的最后,需要进行一个交易签名操作。它其实就是交易发起方要提供一个证据,证明自己可以花费这项交易的每一笔输入,也就是说提供的签名scriptSig,能够跟prevOut的scriptPubKey运算,只要最终返回结果是TRUE,就能证明交易的合法性。比特币网络上的其他节点,也是通过这样一个过程来进行交易验证,确认OK之后,交易才会放入交易池,等待打包。

比特币交易创建函数CreateTransaction的最后,调用了如下代码来对交易进行签名:

const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(*this, 
                      MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL),
                      scriptPubKey, sigdata)) 
{
    strFailReason = _("Signing transaction failed");
    return false;
} else {
    UpdateInput(txNew.vin.at(nIn), sigdata);
}

这里的ProduceSignature函数,内部流程也比较复杂。当然它的复杂,来源于签名机制的复杂。为了帮助理解,我画了一个简要流程图。
交易签名流程图

先从末端起,自底向上,理解下面几个函数。

首先是MutableTransactionSignatureCreator.CreateSig函数,顾名思义就是对CMutableTransaction进行签名。它定义在src/script/sign.cpp中。源码如下:

bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, 
    std::vector& vchSig, const CKeyID& address, 
    const CScript& scriptCode, SigVersion sigversion) const
{
    CKey key;
    if (!provider.GetKey(address, key))
        return false;
    //见证脚本必须是压缩版
    if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
        return false;
    //生成交易哈希,用于签名
    uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
    if (!key.Sign(hash, vchSig))  //使用ECDSA椭圆曲线加密算法进行签名
        return false;
    vchSig.push_back((unsigned char)nHashType);
    return true;
}

CKey.Sign函数逻辑很简单,这里就不贴了。SignatureHash函数是根据交易信息生成哈希值,主要源码如下(涉及隔离见证部分可参见比特币探究之隔离见证那篇文章):

template 
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, 
    const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
    if (sigversion == SigVersion::WITNESS_V0) {  //如果是隔离见证,根据BIP-143的规定简化签名内容
        uint256 hashPrevouts, hashSequence, hashOutputs;
        const bool cacheready = cache && cache->ready;
        //非任何人可付,为所有PrevOut的哈希,否则全0
        if (!(nHashType & SIGHASH_ANYONECANPAY)) {
            hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
        }
        //非任何人可付,不是SINGLE或NONE,为所有序列号哈希,否则全0
        if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE 
                                                && (nHashType & 0x1f) != SIGHASH_NONE) {
            hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
        }
        if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
            //非SINGLE和NONE,为所有输出的哈希
            hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
        } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
            //如果是SINGLE,为同一序列号输出的哈希。其他情况全0
            CHashWriter ss(SER_GETHASH, 0);
            ss << txTo.vout[nIn];
            hashOutputs = ss.GetHash();
        }

        CHashWriter ss(SER_GETHASH, 0);
        ss << txTo.nVersion;
        ss << hashPrevouts;
        ss << hashSequence;
        ss << txTo.vin[nIn].prevout;
        ss << scriptCode;
        ss << amount;
        ss << txTo.vin[nIn].nSequence;
        ss << hashOutputs;
        ss << txTo.nLockTime;
        ss << nHashType;

        return ss.GetHash();
    }

    //如果不是隔离见证,调用Serializer输出哈希,根据所有的输入输出计算得出,其复杂度高于隔离见证版
    CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType);
    CHashWriter ss(SER_GETHASH, 0);
    ss << txTmp << nHashType;
    return ss.GetHash();
}

接着看Solver函数。它根据传入的scriptPubKey,判断交易输出类型(P2PKH、P2SH、P2WPKH或P2WSH),并返回相应的数据(参见交易签名流程图)。该函数定义在src/script/standard.cpp中。源码如下:

bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, 
            std::vector >& vSolutionsRet)
{
    vSolutionsRet.clear();

    //P2SH类型,格式为 OP_HASH160 20 [20 byte hash] OP_EQUAL
    if (scriptPubKey.IsPayToScriptHash())
    {
        typeRet = TX_SCRIPTHASH;
        std::vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
        //返回20字节的Redeem Script(赎回脚本)的Hash
        vSolutionsRet.push_back(hashBytes);
        return true;
    }

    int witnessversion;
    std::vector witnessprogram;
    //如果采用了隔离见证,那应该是[见证版本] [见证程序]的格式
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
        //长度20,说明是P2WPKH
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
            typeRet = TX_WITNESS_V0_KEYHASH;
            vSolutionsRet.push_back(witnessprogram);    //返回20字节PubKey Hash
            return true;
        }
        //长度32,说明是P2WSH
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
            typeRet = TX_WITNESS_V0_SCRIPTHASH;
            vSolutionsRet.push_back(witnessprogram); //返回见证脚本
            return true;
        }
        if (witnessversion != 0) {  //向前兼容,当前隔离见证版本号只有0
            typeRet = TX_WITNESS_UNKNOWN;
            vSolutionsRet.push_back(std::vector{(unsigned char)witnessversion});
            vSolutionsRet.push_back(std::move(witnessprogram));
            return true;
        }
        typeRet = TX_NONSTANDARD;  //其他情况就是非标准交易了
        return false;
    }

    //用OP_RETURN带的一堆直推数据
    if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN 
                                 && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
        typeRet = TX_NULL_DATA;
        return true;
    }

    std::vector data;
    //65/33 [65/33字节公钥] OP_CHECKSIG,33为压缩版
    if (MatchPayToPubkey(scriptPubKey, data)) {
        typeRet = TX_PUBKEY;
        vSolutionsRet.push_back(std::move(data));
        return true;
    }

    //OP_DUP OP_HASH160 20 [20字节公钥哈希] OP_EQUALVERIFY OP_CHECKSIG
    if (MatchPayToPubkeyHash(scriptPubKey, data)) {
        typeRet = TX_PUBKEYHASH;
        vSolutionsRet.push_back(std::move(data));
        return true;
    }

    unsigned int required;
    std::vector> keys;
    //多重签名:  [B pubkey] [C pubkey...]  OP_CHECKMULTISIG
    if (MatchMultisig(scriptPubKey, required, keys)) {
        typeRet = TX_MULTISIG;
        vSolutionsRet.push_back({static_cast(required)});
        vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
        vSolutionsRet.push_back({static_cast(keys.size())});
        return true;
    }

    vSolutionsRet.clear();
    typeRet = TX_NONSTANDARD;  //以上都不是,那就是非标准交易了
    return false;
}

Solver函数是被SignStep函数调用的。函数名意思是分步签名,如果是P2SH,或者是隔离见证的P2WPHK/P2WSH,那么SignStep函数会调用两次,第一次返回赎回脚本或见证脚本,第二次才能签名。它定义在src/script/sigh.cpp里。源码如下:

static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, 
                     const CScript& scriptPubKey, std::vector& ret, txnouttype& whichTypeRet, 
                     SigVersion sigversion, SignatureData& sigdata)
{
    CScript scriptRet;
    uint160 h160;
    ret.clear();
    std::vector sig;

    std::vector vSolutions;
    if (!Solver(scriptPubKey, whichTypeRet, vSolutions))    //判断scriptPubKey类型
        return false;

    switch (whichTypeRet)
    {
    case TX_NONSTANDARD:
    case TX_NULL_DATA:
    case TX_WITNESS_UNKNOWN:
        return false;
    case TX_PUBKEY:  //公钥,调用CreateSig生成签名
        if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) 
            return false;
        ret.push_back(std::move(sig));
        return true;
    case TX_PUBKEYHASH: {  //公钥哈希,调用CreateSig生成签名,连同公钥一并返回
        CKeyID keyID = CKeyID(uint160(vSolutions[0]));
        if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
        ret.push_back(std::move(sig));
        CPubKey pubkey;
        GetPubKey(provider, sigdata, keyID, pubkey);
        ret.push_back(ToByteVector(pubkey));
        return true;
    }
    case TX_SCRIPTHASH:  //脚本哈希,取出赎回脚本redeem script
        if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
            ret.push_back(std::vector(scriptRet.begin(), scriptRet.end()));
            return true;
        }
        return false;
    case TX_MULTISIG: {  //多重签名
        size_t required = vSolutions.front()[0];
        ret.push_back(valtype());
        for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
            CPubKey pubkey = CPubKey(vSolutions[i]);
            if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, 
                                                       pubkey.GetID(), scriptPubKey, sigversion)) {
                ret.push_back(std::move(sig));
            }
        }
        bool ok = ret.size() == required + 1; //签名数量够不够?
        for (size_t i = 0; i + ret.size() < required + 1; ++i) {
            ret.push_back(valtype());
        }
        return ok;
    }
    case TX_WITNESS_V0_KEYHASH:  //P2WPKH,直接返回20字节Key Hash
        ret.push_back(vSolutions[0]);
        return true;
    case TX_WITNESS_V0_SCRIPTHASH:  //P2WSH,返回见证脚本(m keys n)
        CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
        if (GetCScript(provider, sigdata, h160, scriptRet)) {
            ret.push_back(std::vector(scriptRet.begin(), scriptRet.end()));
            return true;
        }
        return false;
    default:
        return false;
    }
}

现在可以看ProduceSignature函数了,它的源代码如下:

bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, 
                      const CScript& fromPubKey, SignatureData& sigdata)
{
    if (sigdata.complete) return true;

    std::vector result;
    txnouttype whichType;
    bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
    bool P2SH = false;
    CScript subscript;
    sigdata.scriptWitness.stack.clear();

    if (solved && whichType == TX_SCRIPTHASH)
    {   //P2SH,对子脚本(赎回脚本)二次签名
        subscript = CScript(result[0].begin(), result[0].end());
        sigdata.redeem_script = subscript;
        solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata)
                        && whichType != TX_SCRIPTHASH;
        P2SH = true;
    }

    if (solved && whichType == TX_WITNESS_V0_KEYHASH)
    {   //P2WPKH,先组建P2PKH,二次签名
        CScript witnessscript;
        witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
        txnouttype subType;
        solved = solved && SignStep(provider, creator, witnessscript, result, subType,
                                    SigVersion::WITNESS_V0, sigdata);
        sigdata.scriptWitness.stack = result;
        sigdata.witness = true;
        result.clear();
    }
    else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
    {   //P2WSH,m keys n再调SignStep完成多重签名
        CScript witnessscript(result[0].begin(), result[0].end());
        sigdata.witness_script = witnessscript;
        txnouttype subType;
        solved = solved 
                 && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) 
                 && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH 
                 && subType != TX_WITNESS_V0_KEYHASH;
        result.push_back(std::vector(witnessscript.begin(), witnessscript.end()));
        sigdata.scriptWitness.stack = result;
        sigdata.witness = true;
        result.clear();
    } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
        sigdata.witness = true;
    }

    if (P2SH) {  //子脚本加上去
        result.push_back(std::vector(subscript.begin(), subscript.end()));
    }
    sigdata.scriptSig = PushAll(result);  //填入scriptSig,注意如果是隔离见证,此前已经clear

    //最后还要验证一下
    sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, 
                                              STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
    return sigdata.complete;
}

关于交易验证,还是待下次开发间隙再去探究吧。


你的支持,我的动力!

你可能感兴趣的:(比特币探究之交易签名)