比特币-一个交易的产生(二)-提交交易

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37847176/article/details/82351978

/**
* Call after CreateTransaction unless you want to abort
*/
在创建交易后需要提交这个交易除非你想抛弃这个交易。

CommitTransaction

位于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

AcceptToMemoryPool

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

这里又调用了AcceptToMemoryPoolWorker这个函数,在main.cpp中,这个函数很长,一段段来看

1)条件判断

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");

2)条件判断

省略判断部分的代码

 // 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;
}
阅读更多 登录后自动展开

更多精彩内容