/**
* Call after CreateTransaction unless you want to abort
*/
在创建交易后需要提交这个交易除非你想抛弃这个交易。
位于src/wallet/wallet.cpp,属于类wallet
bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
{
LOCK2(cs_main, cs_wallet);
LogPrintf("CommitTransaction:\n%s", wtxNew.ToString());
{
// This is only to keep the database open to defeat the auto-flush for the
// duration of this scope. This is the only place where this optimization
// maybe makes sense; please don't do it anywhere else.
CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r+") : NULL;
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
AddToWallet(wtxNew, false, pwalletdb);
// Notify that old coins are spent
set<CWalletTx*> setCoins;
BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
coin.BindWallet(this);
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
}
if (fFileBacked)
delete pwalletdb;
}
// Track how many getdata requests our transaction gets
mapRequestCount[wtxNew.GetHash()] = 0;
if (fBroadcastTransactions)
{
CValidationState state;//捕获交易的验证信息
// Broadcast
if (!wtxNew.AcceptToMemoryPool(false, maxTxFee, state)) {
LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason());
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
wtxNew.RelayWalletTransaction();
}
}
}
return true;
}
步骤的注释很清晰,用过的密钥对就从密钥池中移除(主要是指用于找零的密钥对),这样保证不会重复使用,导致信息泄漏。将交易加入到钱包中,因为找零也是我们的或者用作历史记录。通知钱包中在这笔交易中花费的币。最后是广播,判断这个交易是否被加入到交易池(mempool)中。
maxTxFee
是一笔交易的最大交易费,设置为0.1比特币,位于mian.h
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee, CValidationState& state)
{ return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee); }
调用全局函数AcceptToMemoryPool,
::
:直接用在全局函数前,表示是全局函数
mempool不是CMerkleTx或其父类的成员,是一个全局对象。
//main.cpp
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;//0.1*coin
CTxMemPool mempool(::minRelayTxFee);
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
设置费率最低是1000聪,这是进入内存池的最低要求,是以这个值初始化内存池。
CTxMemPool
的介绍在数据结构中有写到https://blog.csdn.net/m0_37847176/article/details/81699796#ctxmempool
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
std::vector<uint256> vHashTxToUncache;
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
if (!res) {
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
pcoinsTip->Uncache(hashTx);
}
return res;
}
这里又调用了AcceptToMemoryPoolWorker
这个函数,在main.cpp中,这个函数很长,一段段来看
CheckTransaction
1.交易的语法和数据结构必须正确。
2.输入与输出列表都不能为空。
3.交易的字节大小是小于MAX_BLOCK_BASE_SIZE的
/* The maximum allowed size for a block excluding witness data, in bytes (network rule) /
static const unsigned int MAX_BLOCK_BASE_SIZE = 1000000;//1M大小
4.每一个输出值,以及总量,必须在规定值的范围内 (小于 2,100 万个币,大于
0)。
5.对于每一个输入,引用的输出是必须存在的,并且没有被花费。
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
std::vector<uint256>& vHashTxnToUncache)
{
const uint256 hash = tx.GetHash();//获取这笔交易的hash
AssertLockHeld(cs_main);
if (pfMissingInputs)
*pfMissingInputs = false;
if (!CheckTransaction(tx, state))//与上下文无关的有效性检查,如果与上下文无关则无效交易
return false; // state filled in by CheckTransaction
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())//Coinbase是特殊的交易,不是普通交易处理,不会放到池中,在块中有效
return state.DoS(100, false, REJECT_INVALID, "coinbase");
// Don't relay version 2 transactions until CSV is active, and we can be
// sure that such transactions will be mined (unless we're on
// -testnet/-regtest).
/*在 const CChainParams& chainparams = Params(); if (fRequireStandard && tx.nVersion >= 2 && VersionBitsTipState(chainparams.GetConsensus(), Consensus::DEPLOYMENT_CSV) != THRESHOLD_ACTIVE) { return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx"); } // Reject transactions with witness before segregated witness activates (override with -prematurewitness) /*在隔离见证激活前拒绝使用见证的交易*/
bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !witnessEnabled) {
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
}
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
//不支持费标准交易,除非是测试网
string reason;
if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
/*对于使用nLockTime限定的交易只接收能进入下一个区块的交易,不希望池中充满不能被包含在下一个区块的交易*/
if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");
// is it already in the memory pool?
if (pool.exists(hash))
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool");
省略判断部分的代码
// Check for conflicts with in-memory transactions
// do we already have it?
// do all inputs exist?
// are the actual inputs available?
// Check for non-standard pay-to-script-hash in inputs
// Check for non-standard witness in P2WSH
// Keep track of transactions that spend a coinbase
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
// Check that the transaction doesn't have an excessive number of sigops, making it impossible to mine
// Continuously rate-limit free (really, very-low-fee) transactions
// A transaction that spends outputs that would be replaced by it is invalid.
//Don't allow the replacement to reduce the feerate of the mempool.
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
另外还有关于交易替换的逻辑代码
BOOST_FOREACH(const uint256 &hashConflicting, setConflicts)
{
针对有冲突的交易,用高交易费的交易替换低交易费的交易。
如果提交到内存池失败的化,提示不能马上被广播,接着调用函数RelayWalletTransaction
中继钱包交易,如果这个交易已经在池中或者再次提交成功的话,那么中继这个交易成功,否则失败。
bool CWalletTx::RelayWalletTransaction()
{
assert(pwallet->GetBroadcastTransactions());
if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0)
{
CValidationState state;
/* GetDepthInMainChain already catches known conflicts. */
if (InMempool() || AcceptToMemoryPool(false, maxTxFee, state)) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
RelayTransaction((CTransaction)*this);
return true;
}
}
return false;
}