Bitcoin比特币源码解读——交易的生命周期

1、创建交易。

      前提是钱包有比特币,可以启动一个测试结点,自己挖矿。通过RPC `getnewaddress获取个地址`,`sendtoaddress`来创建交易。如下:

      > bitcoin-cli getnewaddress

      1LXVDTasMALkmkLRw7J35pzSdN38VG1Fvp

       >bitcoin-cli sendtoaddress 1LXVDTasMALkmkLRw7J35pzSdN38VG1Fvp 1

       txid

sendtoaddress会调用到SendMoney,然后调用到CWallet::CreateTransaction。CWallet::CreateTransaction会做如下事情:

       调用AvailableCoins获取钱包可用的币(UTXO)。

       初始化CScript scriptChange找零脚本。

       然后在while (true)的循环找到合适的币(交易的输入)使其满足输出和交易费。

       接下来对输入selected_coins逐一签名ProduceSignature(先跳过签名,后面再讲)。CreateTransaction完成。

然后调用CWallet::CommitTransaction将交易放进内存池。其中AcceptToMemoryPool会调用到AcceptToMemoryPoolWorker。该Worker会检查交易的输出值范围是否合理,输入是否重复,是否存在(未花费),手续费是否合理,CheckInputs检查输入的签名是否正确。accepttomemorypool后调用CWalletTx::RelayWalletTransaction。再调用

    void relayTransaction(const uint256& txid) override
    {
        CInv inv(MSG_TX, txid);
        g_connman->ForEachNode([&inv](CNode* node) { node->PushInventory(inv); });
    }

通过MSG_TX将该交易的id广播到邻居结点。

2、网络广播交易。

    上面PushInventory只是将交易id放到集合setInventoryTxToSend里,然后在net_processing.cpp中

            // Determine transactions to relay
            if (fSendTrickle) {
                // Produce a vector with all candidates for sending
                 ....
                    if (vInv.size() == MAX_INV_SZ) {
                        connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
                        vInv.clear();
                    }
                 ....

满足发送时间间隔时将交易id以数组的形式发送出去。邻居结点接收到在net_processing.cpp中处理

bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic& interruptMsgProc, bool enable_bip61)
{
...
    if (strCommand == NetMsgType::INV) {
...
        for (CInv &inv : vInv)
        {
...
            else
            {
                pfrom->AddInventoryKnown(inv);
                if (fBlocksOnly) {
                    LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
                } else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) {
                    RequestTx(State(pfrom->GetId()), inv.hash, nNow);
                }
            }
}

调用到RequestTx,将交易id放到    peer_download_state.m_tx_announced.insert(txid);和peer_download_state.m_tx_process_time.emplace(process_time, txid);里。

满足处理时间后,调用connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));来向广播该交易id的邻居结点请求交易的具体数据。

邻居结点在处理GETDATA时,将交易通过NetMsgType::TX回复请求方。

结点在接收到NetMsgType::TX时,处理逻辑在net_processing.cpp

bool static ProcessMessage(...
...
    if (strCommand == NetMsgType::TX) {

主要是AcceptToMemoryPool(这个过程和上面一样),只是发现输入不存在,会先把交易放到独儿池,等所有输入到齐后再从重AcceptToMemoryPool。

3.打包进块。

    通过RPC命令创建区块

    >bitcoin-cli generatetoaddress 1LXVDTasMALkmkLRw7J35pzSdN38VG1Fvp

    然后执行到BlockAssembler::CreateNewBlock,负责区块的初始化和创建coinbase交费。

    然后执行BlockAssembler::addPackageTxs,负责将内存池中的交易放进区块。

     到此,交易完成,并被网络记录。

   

你可能感兴趣的:(Bitcoin比特币源码解读——交易的生命周期)