(本文使用的是比特币v0.1.0版本 点击下载源码)
本文主要描述交易是如何发起中,其过程包含交易的新建(包含交易的选择,交易费的计算、签名);提交交易请求;本节点接受交易(验证、检查并保存交易等);最后广播交易到其他节点中。
流程图如下所示:
新建交易
if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired)){string strError;if (nValue + nFeeRequired > GetBalance())strError = strprintf("Error: This is an oversized transaction that requires a transaction fee of %s ", FormatMoney(nFeeRequired).c_str());elsestrError = "Error: Transaction creation failed ";wxMessageBox(strError, "Sending...");return error("SendMoney() : %s\n", strError.c_str());}
选取交易
具体选择交易使用的“随机逼近算法”在后续《比特币源码解读之选币》一文中介绍
set<CWalletTx*> setCoins;if (!SelectCoins(nValue, setCoins))return false;
计算交易费
如果默认的交易费小于当前计算的交易费用,则需要根据当前计算的交易费重新填充交易
// Check that enough fee is includedif (nFee < wtxNew.GetMinFee(true)){nFee = nFeeRequiredRet = wtxNew.GetMinFee(true);continue;}
填充输入和输出
填充输出
wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));// Fill vout[1] back to self with any changeif (nValueIn > nValue){// Use the same key as one of the coinsvector<unsigned char> vchPubKey;CTransaction& txFirst = *(*setCoins.begin());foreach(const CTxOut& txout, txFirst.vout)if (txout.IsMine())if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))break;if (vchPubKey.empty())return false;// Fill vout[1] to ourselfCScript scriptPubKey;scriptPubKey << vchPubKey << OP_CHECKSIG;wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey));}
填充输入
foreach(CWalletTx* pcoin, setCoins)for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)if (pcoin->vout[nOut].IsMine())wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut));签名int nIn = 0;foreach(CWalletTx* pcoin, setCoins)for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)if (pcoin->vout[nOut].IsMine())SignSignature(*pcoin, wtxNew, nIn++);
计算Merkle值
wtxNew.AddSupportingTransactions(txdb);void CWalletTx::AddSupportingTransactions(CTxDB& txdb){vtxPrev.clear();const int COPY_DEPTH = 3;if (SetMerkleBranch() < COPY_DEPTH){vector<uint256> vWorkQueue;foreach(const CTxIn& txin, vin)vWorkQueue.push_back(txin.prevout.hash);// This critsect is OK because txdb is already openCRITICAL_BLOCK(cs_mapWallet){map<uint256, const CMerkleTx*> mapWalletPrev;set<uint256> setAlreadyDone;for (int i = 0; i < vWorkQueue.size(); i++){uint256 hash = vWorkQueue[i];if (setAlreadyDone.count(hash))continue;setAlreadyDone.insert(hash);CMerkleTx tx;if (mapWallet.count(hash)){tx = mapWallet[hash];foreach(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev)mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;}else if (mapWalletPrev.count(hash)){tx = *mapWalletPrev[hash];}else if (!fClient && txdb.ReadDiskTx(hash, tx)){;}else{printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");continue;}int nDepth = tx.SetMerkleBranch();vtxPrev.push_back(tx);if (nDepth < COPY_DEPTH)foreach(const CTxIn& txin, tx.vin)vWorkQueue.push_back(txin.prevout.hash);}}}reverse(vtxPrev.begin(), vtxPrev.end());}
提交交易请求
if (!CommitTransactionSpent(wtxNew)){wxMessageBox("Error finalizing transaction", "Sending...");return error("SendMoney() : Error finalizing transaction");}
增加交易到钱包中
增加交易到钱包中并保存在mapWallet变量中,通过将选择交易写入磁盘
bool CommitTransactionSpent(const CWalletTx& wtxNew){... ...AddToWallet(wtxNew);// Mark old coins as spentset<CWalletTx*> setCoins;foreach(const CTxIn& txin, wtxNew.vin)setCoins.insert(&mapWallet[txin.prevout.hash]);foreach(CWalletTx* pcoin, setCoins){pcoin->fSpent = true;pcoin->WriteToDisk();vWalletUpdated.push_back(make_pair(pcoin->GetHash(), false));}}MainFrameRepaint();return true;}
接受交易
if (!wtxNew.AcceptTransaction()){// This must not fail. The transaction has already been signed and recorded.throw runtime_error("SendMoney() : wtxNew.AcceptTransaction() failed\n");wxMessageBox("Error: Transaction not valid", "Sending...");return error("SendMoney() : Error: Transaction not valid");}
检查交易是否有效
if (IsCoinBase())return error("AcceptTransaction() : coinbase as individual tx");if (!CheckTransaction())return error("AcceptTransaction() : CheckTransaction failed");
检查交易是否创建成功
uint256 hash = GetHash();CRITICAL_BLOCK(cs_mapTransactions)if (mapTransactions.count(hash))return false;if (fCheckInputs)if (txdb.ContainsTx(hash))return false;
检查交易是否冲突
uint256 hash = GetHash();CRITICAL_BLOCK(cs_mapTransactions)if (mapTransactions.count(hash))return false;if (fCheckInputs)if (txdb.ContainsTx(hash))return false;// Check for conflicts with in-memory transactionsCTransaction* ptxOld = NULL;for (int i = 0; i < vin.size(); i++){COutPoint outpoint = vin[i].prevout;if (mapNextTx.count(outpoint)){// Allow replacing with a newer version of the same transactionif (i != 0)return false;ptxOld = mapNextTx[outpoint].ptx;if (!IsNewerThan(*ptxOld))return false;for (int i = 0; i < vin.size(); i++){COutPoint outpoint = vin[i].prevout;if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)return false;}break;}}
检查是否与已有交易冲突
map<uint256, CTxIndex> mapUnused;int64 nFees = 0;if (fCheckInputs && !ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), 0, nFees, false, false)){if (pfMissingInputs)*pfMissingInputs = true;return error("AcceptTransaction() : ConnectInputs failed %s", hash.ToString().substr(0,6).c_str());}
交易保存到内存中
CRITICAL_BLOCK(cs_mapTransactions){if (ptxOld){printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str());mapTransactions.erase(ptxOld->GetHash());}AddToMemoryPool();}
从钱包移除旧的交易
if (ptxOld)EraseFromWallet(ptxOld->GetHash());
广播钱包中的交易
wtxNew.RelayWalletTransaction();
发送INV消息
void CWalletTx::RelayWalletTransaction(CTxDB& txdb){foreach(const CMerkleTx& tx, vtxPrev){if (!tx.IsCoinBase()){uint256 hash = tx.GetHash();if (!txdb.ContainsTx(hash))RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);}}if (!IsCoinBase()){uint256 hash = GetHash();if (!txdb.ContainsTx(hash)){printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str());RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);}}}
上一篇: 比特币源码解读之创世块的产生
下一篇:比特币源码解读之选币
版权声明:B链网原创,严禁修改。转载请注明作者和原文链接
作者:雨后的蚊子
原文链接:http://www.360bchain.com/article/89.html