比特币源码解读之交易发起

(本文使用的是比特币v0.1.0版本 点击下载源码)

  • 新建交易
    • 选取交易
    • 计算交易费
    • 填充输入和输出
    • 计算Merkle值
  • 提交交易请求
    • 增加交易到钱包中
  • 接受交易
    • 检查交易是否有效
    • 检查交易是否创建成功
    • 检查交易是否冲突
    • 检查是否与已有交易冲突
    • 交易保存到内存中
    • 从钱包移除旧的交易
  • 广播钱包中的交易
    • 发送INV消息

本文主要描述交易是如何发起中,其过程包含交易的新建(包含交易的选择,交易费的计算、签名);提交交易请求;本节点接受交易(验证、检查并保存交易等);最后广播交易到其他节点中。
流程图如下所示:

新建交易


  1. if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired))
  2. {
  3. string strError;
  4. if (nValue + nFeeRequired > GetBalance())
  5. strError = strprintf("Error: This is an oversized transaction that requires a transaction fee of %s ", FormatMoney(nFeeRequired).c_str());
  6. else
  7. strError = "Error: Transaction creation failed ";
  8. wxMessageBox(strError, "Sending...");
  9. return error("SendMoney() : %s\n", strError.c_str());
  10. }

选取交易

具体选择交易使用的“随机逼近算法”在后续《比特币源码解读之选币》一文中介绍


  1. set<CWalletTx*> setCoins;
  2. if (!SelectCoins(nValue, setCoins))
  3. return false;

计算交易费

如果默认的交易费小于当前计算的交易费用,则需要根据当前计算的交易费重新填充交易


  1. // Check that enough fee is included
  2. if (nFee < wtxNew.GetMinFee(true))
  3. {
  4. nFee = nFeeRequiredRet = wtxNew.GetMinFee(true);
  5. continue;
  6. }

填充输入和输出

填充输出


  1. wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
  2. // Fill vout[1] back to self with any change
  3. if (nValueIn > nValue)
  4. {
  5. // Use the same key as one of the coins
  6. vector<unsigned char> vchPubKey;
  7. CTransaction& txFirst = *(*setCoins.begin());
  8. foreach(const CTxOut& txout, txFirst.vout)
  9. if (txout.IsMine())
  10. if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
  11. break;
  12. if (vchPubKey.empty())
  13. return false;
  14. // Fill vout[1] to ourself
  15. CScript scriptPubKey;
  16. scriptPubKey << vchPubKey << OP_CHECKSIG;
  17. wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey));
  18. }

填充输入


  1. foreach(CWalletTx* pcoin, setCoins)
  2. for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
  3. if (pcoin->vout[nOut].IsMine())
  4. wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut));
  5. 签名
  6. int nIn = 0;
  7. foreach(CWalletTx* pcoin, setCoins)
  8. for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
  9. if (pcoin->vout[nOut].IsMine())
  10. SignSignature(*pcoin, wtxNew, nIn++);

计算Merkle值


  1. wtxNew.AddSupportingTransactions(txdb);
  2. void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
  3. {
  4. vtxPrev.clear();
  5. const int COPY_DEPTH = 3;
  6. if (SetMerkleBranch() < COPY_DEPTH)
  7. {
  8. vector vWorkQueue;
  9. foreach(const CTxIn& txin, vin)
  10. vWorkQueue.push_back(txin.prevout.hash);
  11. // This critsect is OK because txdb is already open
  12. CRITICAL_BLOCK(cs_mapWallet)
  13. {
  14. map<uint256, const CMerkleTx*> mapWalletPrev;
  15. set setAlreadyDone;
  16. for (int i = 0; i < vWorkQueue.size(); i++)
  17. {
  18. uint256 hash = vWorkQueue[i];
  19. if (setAlreadyDone.count(hash))
  20. continue;
  21. setAlreadyDone.insert(hash);
  22. CMerkleTx tx;
  23. if (mapWallet.count(hash))
  24. {
  25. tx = mapWallet[hash];
  26. foreach(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev)
  27. mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
  28. }
  29. else if (mapWalletPrev.count(hash))
  30. {
  31. tx = *mapWalletPrev[hash];
  32. }
  33. else if (!fClient && txdb.ReadDiskTx(hash, tx))
  34. {
  35. ;
  36. }
  37. else
  38. {
  39. printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
  40. continue;
  41. }
  42. int nDepth = tx.SetMerkleBranch();
  43. vtxPrev.push_back(tx);
  44. if (nDepth < COPY_DEPTH)
  45. foreach(const CTxIn& txin, tx.vin)
  46. vWorkQueue.push_back(txin.prevout.hash);
  47. }
  48. }
  49. }
  50. reverse(vtxPrev.begin(), vtxPrev.end());
  51. }

提交交易请求


  1. if (!CommitTransactionSpent(wtxNew))
  2. {
  3. wxMessageBox("Error finalizing transaction", "Sending...");
  4. return error("SendMoney() : Error finalizing transaction");
  5. }

增加交易到钱包中

增加交易到钱包中并保存在mapWallet变量中,通过将选择交易写入磁盘


  1. bool CommitTransactionSpent(const CWalletTx& wtxNew)
  2. {
  3. ... ...
  4. AddToWallet(wtxNew);
  5. // Mark old coins as spent
  6. set<CWalletTx*> setCoins;
  7. foreach(const CTxIn& txin, wtxNew.vin)
  8. setCoins.insert(&mapWallet[txin.prevout.hash]);
  9. foreach(CWalletTx* pcoin, setCoins)
  10. {
  11. pcoin->fSpent = true;
  12. pcoin->WriteToDisk();
  13. vWalletUpdated.push_back(make_pair(pcoin->GetHash(), false));
  14. }
  15. }
  16. MainFrameRepaint();
  17. return true;
  18. }

接受交易


  1. if (!wtxNew.AcceptTransaction())
  2. {
  3. // This must not fail. The transaction has already been signed and recorded.
  4. throw runtime_error("SendMoney() : wtxNew.AcceptTransaction() failed\n");
  5. wxMessageBox("Error: Transaction not valid", "Sending...");
  6. return error("SendMoney() : Error: Transaction not valid");
  7. }

检查交易是否有效


  1. if (IsCoinBase())
  2. return error("AcceptTransaction() : coinbase as individual tx");
  3. if (!CheckTransaction())
  4. return error("AcceptTransaction() : CheckTransaction failed");

检查交易是否创建成功


  1. uint256 hash = GetHash();
  2. CRITICAL_BLOCK(cs_mapTransactions)
  3. if (mapTransactions.count(hash))
  4. return false;
  5. if (fCheckInputs)
  6. if (txdb.ContainsTx(hash))
  7. return false;

检查交易是否冲突


  1. uint256 hash = GetHash();
  2. CRITICAL_BLOCK(cs_mapTransactions)
  3. if (mapTransactions.count(hash))
  4. return false;
  5. if (fCheckInputs)
  6. if (txdb.ContainsTx(hash))
  7. return false;
  8. // Check for conflicts with in-memory transactions
  9. CTransaction* ptxOld = NULL;
  10. for (int i = 0; i < vin.size(); i++)
  11. {
  12. COutPoint outpoint = vin[i].prevout;
  13. if (mapNextTx.count(outpoint))
  14. {
  15. // Allow replacing with a newer version of the same transaction
  16. if (i != 0)
  17. return false;
  18. ptxOld = mapNextTx[outpoint].ptx;
  19. if (!IsNewerThan(*ptxOld))
  20. return false;
  21. for (int i = 0; i < vin.size(); i++)
  22. {
  23. COutPoint outpoint = vin[i].prevout;
  24. if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
  25. return false;
  26. }
  27. break;
  28. }
  29. }

检查是否与已有交易冲突


  1. map<uint256, CTxIndex> mapUnused;
  2. int64 nFees = 0;
  3. if (fCheckInputs && !ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), 0, nFees, false, false))
  4. {
  5. if (pfMissingInputs)
  6. *pfMissingInputs = true;
  7. return error("AcceptTransaction() : ConnectInputs failed %s", hash.ToString().substr(0,6).c_str());
  8. }

交易保存到内存中


  1. CRITICAL_BLOCK(cs_mapTransactions)
  2. {
  3. if (ptxOld)
  4. {
  5. printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str());
  6. mapTransactions.erase(ptxOld->GetHash());
  7. }
  8. AddToMemoryPool();
  9. }

从钱包移除旧的交易


  1. if (ptxOld)
  2. EraseFromWallet(ptxOld->GetHash());

广播钱包中的交易


  1. wtxNew.RelayWalletTransaction();

发送INV消息


  1. void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
  2. {
  3. foreach(const CMerkleTx& tx, vtxPrev)
  4. {
  5. if (!tx.IsCoinBase())
  6. {
  7. uint256 hash = tx.GetHash();
  8. if (!txdb.ContainsTx(hash))
  9. RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
  10. }
  11. }
  12. if (!IsCoinBase())
  13. {
  14. uint256 hash = GetHash();
  15. if (!txdb.ContainsTx(hash))
  16. {
  17. printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str());
  18. RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
  19. }
  20. }
  21. }

上一篇: 比特币源码解读之创世块的产生
下一篇:比特币源码解读之选币

版权声明:B链网原创,严禁修改。转载请注明作者和原文链接

作者:雨后的蚊子

原文链接:http://www.360bchain.com/article/89.html

你可能感兴趣的:(区块链)