欧美成人午夜免费全部完,亚洲午夜福利精品久久,а√最新版在线天堂,另类亚洲综合区图片小说区,亚洲欧美日韩精品色xxx

扣丁學(xué)堂區(qū)塊鏈開發(fā)技術(shù)之探究比特幣交易簽名

2018-08-14 10:48:46 1562瀏覽

隨著區(qū)塊鏈概念理論的不斷成熟以及強(qiáng)勁技術(shù)的不斷深耕,區(qū)塊鏈已經(jīng)成為投資圈中備受關(guān)注的熱點,從區(qū)塊鏈1.0時代落地數(shù)字貨幣比特幣、萊特幣等,打開了區(qū)塊鏈通向新彎道的高速路口,到區(qū)塊鏈2.0時代開始通過智能合約來解決貨幣支付難題,再到3.0時代區(qū)塊鏈嘗試向各傳統(tǒng)行業(yè)發(fā)力,今天扣丁學(xué)堂區(qū)塊鏈培訓(xùn)技術(shù)給大家介紹一下關(guān)于比特幣探究之交易創(chuàng)建詳解,下面我們一起來看一下吧。

首先在交易創(chuàng)建的最后,需要進(jìn)行一個交易簽名操作。它其實就是交易發(fā)起方要提供一個證據(jù),證明自己可以花費(fèi)這項交易的每一筆輸入,也就是說提供的簽名scriptSig,能夠跟prevOut的scriptPubKey運(yùn)算,只要最終返回結(jié)果是TRUE,就能證明交易的合法性。比特幣網(wǎng)絡(luò)上的其他節(jié)點,也是通過這樣一個過程來進(jìn)行交易驗證,確認(rèn)OK之后,交易才會放入交易池,等待打包。

比特幣交易創(chuàng)建函數(shù)CreateTransaction的最后,調(diào)用了如下代碼來對交易進(jìn)行簽名:

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函數(shù),內(nèi)部流程也比較復(fù)雜。當(dāng)然它的復(fù)雜,來源于簽名機(jī)制的復(fù)雜。為了幫助理解,我畫了一個簡要流程圖。



交易簽名流程圖

先從末端起,自底向上,理解下面幾個函數(shù)。

首先是MutableTransactionSignatureCreator.CreateSig函數(shù),顧名思義就是對CMutableTransaction進(jìn)行簽名。它定義在src/script/sign.cpp中。源碼如下:

bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, 
    std::vector<unsigned char>& 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橢圓曲線加密算法進(jìn)行簽名
        return false;
    vchSig.push_back((unsigned char)nHashType);
    return true;
}

CKey.Sign函數(shù)邏輯很簡單,這里就不貼了。SignatureHash函數(shù)是根據(jù)交易信息生成哈希值,主要源碼如下(涉及隔離見證部分可參見比特幣探究之隔離見證那篇文章):

template <class T>
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) {  //如果是隔離見證,根據(jù)BIP-143的規(guī)定簡化簽名內(nèi)容
        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();
    }

    //如果不是隔離見證,調(diào)用Serializer輸出哈希,根據(jù)所有的輸入輸出計算得出,其復(fù)雜度高于隔離見證版
    CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);
    CHashWriter ss(SER_GETHASH, 0);
    ss << txTmp << nHashType;
    return ss.GetHash();
}

接著看Solver函數(shù)。它根據(jù)傳入的scriptPubKey,判斷交易輸出類型(P2PKH、P2SH、P2WPKH或P2WSH),并返回相應(yīng)的數(shù)據(jù)(參見交易簽名流程圖)。該函數(shù)定義在src/script/standard.cpp中。源碼如下:

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

    //P2SH類型,格式為 OP_HASH160 20 [20 byte hash] OP_EQUAL
    if (scriptPubKey.IsPayToScriptHash())
    {
        typeRet = TX_SCRIPTHASH;
        std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
        //返回20字節(jié)的Redeem Script(贖回腳本)的Hash
        vSolutionsRet.push_back(hashBytes);
        return true;
    }

    int witnessversion;
    std::vector<unsigned char> witnessprogram;
    //如果采用了隔離見證,那應(yīng)該是[見證版本] [見證程序]的格式
    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字節(jié)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) {  //向前兼容,當(dāng)前隔離見證版本號只有0
            typeRet = TX_WITNESS_UNKNOWN;
            vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
            vSolutionsRet.push_back(std::move(witnessprogram));
            return true;
        }
        typeRet = TX_NONSTANDARD;  //其他情況就是非標(biāo)準(zhǔn)交易了
        return false;
    }

    //用OP_RETURN帶的一堆直推數(shù)據(jù)
    if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN 
                                 && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
        typeRet = TX_NULL_DATA;
        return true;
    }

    std::vector<unsigned char> data;
    //65/33 [65/33字節(jié)公鑰] OP_CHECKSIG,33為壓縮版
    if (MatchPayToPubkey(scriptPubKey, data)) {
        typeRet = TX_PUBKEY;
        vSolutionsRet.push_back(std::move(data));
        return true;
    }

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

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

    vSolutionsRet.clear();
    typeRet = TX_NONSTANDARD;  //以上都不是,那就是非標(biāo)準(zhǔn)交易了
    return false;
}

Solver函數(shù)是被SignStep函數(shù)調(diào)用的。函數(shù)名意思是分步簽名,如果是P2SH,或者是隔離見證的P2WPHK/P2WSH,那么SignStep函數(shù)會調(diào)用兩次,第一次返回贖回腳本或見證腳本,第二次才能簽名。它定義在src/script/sigh.cpp里。源碼如下:

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

    std::vector<valtype> 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:  //公鑰,調(diào)用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: {  //公鑰哈希,調(diào)用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<unsigned char>(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; //簽名數(shù)量夠不夠?
        for (size_t i = 0; i + ret.size() < required + 1; ++i) {
            ret.push_back(valtype());
        }
        return ok;
    }
    case TX_WITNESS_V0_KEYHASH:  //P2WPKH,直接返回20字節(jié)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<unsigned char>(scriptRet.begin(), scriptRet.end()));
            return true;
        }
        return false;
    default:
        return false;
    }
}

現(xiàn)在可以看ProduceSignature函數(shù)了,它的源代碼如下:

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

    std::vector<valtype> 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再調(diào)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<unsigned char>(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<unsigned char>(subscript.begin(), subscript.end()));
    }
    sigdata.scriptSig = PushAll(result);  //填入scriptSig,注意如果是隔離見證,此前已經(jīng)clear

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


以上就是扣丁學(xué)堂區(qū)塊鏈開發(fā)技術(shù)之探究比特幣交易簽名的詳細(xì)介紹,想要了解更多請關(guān)注扣丁學(xué)堂官網(wǎng)或微信公眾號,此外扣丁學(xué)堂區(qū)塊鏈視頻教程80%供學(xué)員免費(fèi)在線觀看,想要學(xué)習(xí)區(qū)塊鏈的小伙伴快到扣丁學(xué)堂報名吧??鄱W(xué)堂區(qū)塊鏈交流群:850351616。


【關(guān)注微信公眾號獲取更多學(xué)習(xí)資料】

 

查看更多關(guān)于區(qū)塊鏈培訓(xùn)技術(shù)資訊的相關(guān)文章>>

標(biāo)簽: 區(qū)塊鏈培訓(xùn) 區(qū)塊鏈技術(shù) 區(qū)塊鏈開發(fā) 區(qū)塊鏈視頻教程 比特幣 以太坊

熱門專區(qū)

暫無熱門資訊

課程推薦

微信
微博
15311698296

全國免費(fèi)咨詢熱線

郵箱:codingke@1000phone.com

官方群:148715490

北京千鋒互聯(lián)科技有限公司版權(quán)所有   北京市海淀區(qū)寶盛北里西區(qū)28號中關(guān)村智誠科創(chuàng)大廈4層
京ICP備2021002079號-2   Copyright ? 2017 - 2022
返回頂部 返回頂部