比特币-一个交易的产生(三)-承诺交易

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

交易的产生(一)–生成地址
交易的产生(二)–创建交易
交易的产生(三)–承诺交易
交易的产生(四)–脚本和签名


/Call after CreateTransaction unless you want to abort/


在创建交易后需要承诺或者说约束这个交易除非你想抛弃这个交易。所以这个函数里面就是对交易个各种检查

1.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

1.1AddToWallet



2.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);//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;
}

3.AcceptToMemoryPoolWorker

这里又调用了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

3.1条件判断

函数的前面部分是各种检查

3.1.1CheckTransaction

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

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;
}

没有更多推荐了,返回首页