比特币源码解读之交易发起
(本文使用的是比特币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());
else
strError = "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 included
if (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 change
if (nValueIn > nValue)
{
// Use the same key as one of the coins
vector<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 ourself
CScript 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 open
CRITICAL_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 spent
set<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 transactions
CTransaction* 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 transaction
if (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链网原创,严禁修改。转载请注明作者和原文链接
作者:雨后的蚊子