交易的产生(一)–生成地址
交易的产生(二)–创建交易
交易的产生(三)–承诺交易
交易的产生(四)–脚本和签名
/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);//1000聪
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;//0.1*coin
CTxMemPool mempool(::minRelayTxFee);
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
设置费率最低是1000聪,这是进入内存池的最低要求,是以这个值初始化内存池。
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中,这个函数很长,一段段来看
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();
AssertLockHeld(cs_main);
if (pfMissingInputs)
*pfMissingInputs = false;
if (!CheckTransaction(tx, state))
return false; // state filled in by CheckTransaction
函数的前面部分是各种检查
bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{
// Basic checks that don't depend on any context 基本检验,与上下文无关的
if (tx.vin.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
if (tx.vout.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)不计算隔离见证的数据
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values检查负输出值或者值溢出
CAmount nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
}
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
vInOutPoints.insert(txin.prevout);
}
if (tx.IsCoinBase())//coinbase交易的检查
{
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
}
else
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
}
return true;
}
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.对于每一个输入,引用的输出是必须存在的,并且没有被花费。
然后回到代码,后面也是一些检查
// 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
##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;
}