比特币源码--交易的产生(二)--创建交易

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

文章目录

##CreateTransaction

/**
* Create a new transaction paying the recipients with a set of coins
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/

这里的 SelectCoins()是选择一组币使得nValueRet >= nTargetValue,大于或等于目标金额
鉴于这段代码很长,分段来讲
###1)计算总支出金额
参数

类型 名称 说明
vector<CRecipient> vecSend struct CRecipient{CScript scriptPubKey;CAmount nAmount;bool fSubtractFeeFromAmount;}
CWalletTx& wtxNew 包含附加信息的交易信息,这个类只关注本钱包发起(可能包含接收)的交易
CReserveKey& reservekey 密钥池分配的密钥
CAmount& nFeeRet CAmount nFeeRequired;未赋值
int& nChangePosRet 改变标志位?
std::string& strFailReason 失败的原因
CCoinControl* coinControl 币控制功能
bool sign 默认为true
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
                                int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
{
    CAmount nValue = 0;//初始化为0
    int nChangePosRequest = nChangePosInOut;//赋值
    unsigned int nSubtractFeeFromAmount = 0;
    BOOST_FOREACH (const CRecipient& recipient, vecSend)//解析接收者的信息
    {
        if (nValue < 0 || recipient.nAmount < 0)
        {//交易金额不能为负
            strFailReason = _("Transaction amounts must be positive");
            return false;
        }
        nValue += recipient.nAmount;//计算总共要支付的

        if (recipient.fSubtractFeeFromAmount)
            nSubtractFeeFromAmount++;//如果要从交易金额中减去交易费,则计数增加
    }
    if (vecSend.empty() || nValue < 0)
    {
        strFailReason = _("Transaction amounts must be positive");
        return false;
    }

    wtxNew.fTimeReceivedIsTxTime = true;
    wtxNew.BindWallet(this);//绑定钱包
    CMutableTransaction txNew;//A mutable version of CTransaction.

###2)nLockTime
nLockTime是交易类的成员变量,参考这篇
https://blog.csdn.net/m0_37847176/article/details/81624052#ctransaction
锁定时间也称为 nLocktime,是来自于 Bitcoin Core 代码库中使用的变量名称。在 大多数交易中将其设置为零,以指示即时传播和执行。如果 nLocktime 不为零, 低于 5 亿,则将其解释为块高度,这意味着交易无效,并且在指定的块高度之前 未被中继或包含在块链中。
如果超过 5 亿,它被解释为 Unix 纪元时间戳(自 Jan-1-1970 之后的秒数),并且 交易在指定时间之前无效。指定未来块或时间的 nLocktime 的交易必须由始发系 统持有,并且只有在有效后才被发送到比特币网络。如果交易在指定的 nLocktime之前传输到网络,那么第一个节点就会拒绝该交易,并且不会被中继到其他节点。使用 nLocktime 等同于一张延期支票。

Discourage fee sniping.针对费用狙击
For a large miner the value of the transactions in the best block and the mempool can exceed the cost of deliberately attempting to mine two blocks to orphan the current best block. By setting nLockTime such that only the next block can include the transaction, we discourage this practice as the height restricted and limited blocksize gives miners considering fee sniping fewer options for pulling off this attack.
A simple way to think about this is from the wallet’s point of view we always want the blockchain to move forward. By setting nLockTime this way we’re basically making the statement that we only want this transaction to appear in the next block; we don’t want to potentially encourage reorgs by allowing transactions to appear at lower heights than the next block in forks of the best chain.
Of course, the subsidy is high enough, and transaction volume low enough, that fee sniping isn’t a problem yet, but by implementing a fix now we ensure code won’t be written that makes assumptions about nLockTime that preclude a fix later.
对于大型矿工而言,最佳区块和mempool中的交易价值可能超过尝试故意挖掘两个区块以孤立当前最佳区块的成本。通过设置nLockTime使得只有下一个区块可以包括交易,我们不鼓励这种做法,因为高度限制和有限的区块大小给矿工考虑费用狙击更少的选项来解除这种攻击。
考虑这个问题的一个简单方法是从钱包的角度来看,我们总是希望区块链能够向前发展。通过以这种方式设置nLockTime,我们基本上是在声明我们只希望此交易出现在下一个块中;我们不希望通过允许交易出现在比最佳链的下一个分叉块中更低的高度来促进重新排序。
当然,补贴足够高,交易量足够低,费用狙击就不是问题,但是现在通过实现一个修复,我们确保代码不会被编写对nLockTime进行假设,以防止以后修复。

以下解说引用自《精通比特币()》

费用狙击是一种理论攻击情形,矿工试图从将来的块(挑选手续费较高的交易)重写过去的块,实现“狙击”更高费用的交易,以最大限度地提高盈利能力。
例如,假设存在的最高块是块#100,000。如果不是试图把#100,001 号的矿区扩 大到区块链,那么一些矿工们会试图重新挖矿#100,000。这些矿工可以选择在候 选块#100,000 中包括任何有效的交易(尚未开采)。他们不必使用相同的交易 来恢复块。事实上,他们有动力选择最有利可图(最高每 kBB)的交易来包含在 其中。它们可以包括处于“旧”#100,000 中的任何交易,以及来自当前内存池的 任何交易。当他们重新创建块#100,000 时,他们本质上可以将交易从“现在”提取 到重写的“过去”中。
今天,这种袭击并不是非常有利可图,因为回报奖励(因为包括 一定数量的比特币奖励)远远高于每个区块的总费用。但在未来的某个时候,交 易费将是奖励的大部分(甚至是奖励的整体)。那时候这种情况变得不可避免了。
为了防止“费用狙击”,当 Bitcoin Core /钱包 创建交易时,默认情况下,它使用 nLocktime 将它们限制为“下一个块”。在我们的环境中,Bitcoin Core /钱包将在任 何创建的交易上将 nLocktime 设置为 100,001。在正常情况下,这个 nLocktime 没 有任何效果 - 交易只能包含在#100,001 块中,这是下一个区块。 但是在区块链 分叉攻击的情况下,由于所有这些交易都将被时间锁阻止在#100,001,所以矿工 们无法从筹码中提取高额交易。他们只能在当时有效的任何交易中重新挖矿 #100,000,这导致实质上不会获得新的费用。 为了实现这一点,Bitcoin Core/钱 包将所有新交易的 nLocktime 设置为,并将所有输入上的 nSequence 设置为 0xFFFFFFFE 以启用 nLocktime。

txNew.nLockTime = chainActive.Height();//当前有效区块的高度

Secondly occasionally randomly pick a nLockTime even further back, so that transactions that are delayed after signing for whatever reason, e.g. high-latency mix networks and some CoinJoin implementations, have better privacy.
接着偶尔(0.1的概率)随机获取一个甚至可能更早的nLockTime,以便签名后的交易因任意原因延迟,比如高延迟混合网络和一些CoinJoin实现,有更好的隐私性。

if (GetRandInt(10) == 0)
        txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));

    assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
    assert(txNew.nLockTime < LOCKTIME_THRESHOLD);

###3)vouts

{
        LOCK2(cs_main, cs_wallet);
        {
            std::vector<COutput> vAvailableCoins;
            //用可用的交易输出填充vAvailableCoins,vAvailableCoins就是可用的交易输出
            AvailableCoins(vAvailableCoins, true, coinControl);

            nFeeRet = 0;//从交易费是0开始
            // Start with no fee and loop until there is enough fee
            //循环直到有足够的交易金额
            while (true)
            {
            //初始化工作,清零
                nChangePosInOut = nChangePosRequest;
                txNew.vin.clear();
                txNew.vout.clear();
                txNew.wit.SetNull();
                wtxNew.fFromMe = true;
                bool fFirst = true;

                CAmount nValueToSelect = nValue;//需要选择的金额,初始为之前统计的所有接受者的金额
                if (nSubtractFeeFromAmount == 0)
                    nValueToSelect += nFeeRet;//加上交易费
                double dPriority = 0;

对每个接收者的处理,对每个接收者创建一个CTxOut,关于这个类可以参考
https://blog.csdn.net/m0_37847176/article/details/81624052#ctxout

// vouts to the payees
                BOOST_FOREACH (const CRecipient& recipient, vecSend)
                {
                    CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
//如果设置从交易金额中减去交易费,那么需要分摊到每个接收者,减去平均交易费
                    if (recipient.fSubtractFeeFromAmount)
                    {
                        txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient


//第一个接收者,还需要支出不能被整除的需要的交易费的剩余部分
                        if (fFirst) // first receiver pays the remainder not divisible by output count
                        {
                            fFirst = false;
                            txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
                        }
                    }
//如果这个输出是Dust输出,也就是交易输出太小,称为灰尘交易
                    if (txout.IsDust(::minRelayTxFee))
                    {
                        if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
                        {
                            if (txout.nValue < 0)
                                strFailReason = _("The transaction amount is too small to pay the fee");
                            else
                                strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
                        }
                        else
                            strFailReason = _("Transaction amount too small");
                        return false;
                    }
                    txNew.vout.push_back(txout);//写入交易的输出部分
                }

###4)

扫描二维码关注公众号,回复: 3254636 查看本文章
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
//Shuffle and select coins until nTargetValue is reached while avoiding small change;
//打乱重排并选择可用的coins直到达到nTargetValue同时避免小的找零,这里是指到达nValueToSelect 
//setCoins包含支付给你本人地址的交易,即你所拥有的币     
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))
{
  strFailReason = _("Insufficient funds");
  return false;
}
//对选择好的这一组coins的每个来源计算优先级
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
	CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
	//The coin age after the next block (depth+1) is used instead of the current,
	//reflecting an assumption the user would accept a bit more delay for
	//a chance at a free transaction.
	//But mempool inputs might still be in the mempool, so their age stays 0
	/*用下一个块(深度+ 1)之后的硬币年龄而不是当前,这反映了一个假设,即用户可以接受更多延迟以获得免费交易的机会。但是mempool输入可能仍然在mempool中, 所以他们的年龄保持在0*/
	int age = pcoin.first->GetDepthInMainChain();
    assert(age >= 0);
    if (age != 0)
       age += 1;
    dPriority += (double)nCredit * age;//增加优先级
}

###5)找零

const CAmount nChange = nValueIn - nValueToSelect;//超出所需支出,那么需要找零
if (nChange > 0)
 {
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
/*为自己填写一个vout
*TODO:传入scriptChange而不是reservekey,因此找零交易并不总是付费到比特币地址*/
	CScript scriptChange;

    // coin control: send change to custom address找零支付回习惯地址,如果设置的话
     if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
           scriptChange = GetScriptForDestination(coinControl->destChange);

     // no coin control: send change to newly generated address
     //没有coin control,默认没有设置,那么找零到一个新创建的地址
      else
      {
       // Note: We use a new key here to keep it from being obvious which side is the change.
       //  The drawback is that by not reusing a previous key, the change may be lost if a
       //  backup is restored, if the backup doesn't have the new private key for the change.
       //  If we reused the old key, it would be possible to add code to look for and
       //  rediscover unknown transactions that were written with keys of ours to recover
       //  post-backup change.
      /*我们使用一个新的密钥避免找零地址变得明显。缺点是不重复使用之前的密钥,如果没有备份用于找零的新私钥,
      *则在恢复备份时可能会丢失找零。如果我们重复使用老的密钥,那么有可能通过添加代码去查找和重新发现
      *用我们的密钥编写的未明确的交易以恢复备份后的更改*/
      // Reserve a new key pair from key pool 从密钥池中预约一个新的密钥
	      CPubKey vchPubKey;
          bool ret;
          ret = reservekey.GetReservedKey(vchPubKey);
          if (!ret)
          {
                strFailReason = _("Keypool ran out, please call keypoolrefill first");
                 return false;
            }

           scriptChange = GetScriptForDestination(vchPubKey.GetID());
                    }
       CTxOut newTxOut(nChange, scriptChange);//新建一笔找零交易

上述代码调用reservekeyGetReservedKeyreservekey是传入的参数

bool CReserveKey::GetReservedKey(CPubKey& pubkey)
{
    if (nIndex == -1)
    {
        CKeyPool keypool;
        pwallet->ReserveKeyFromKeyPool(nIndex, keypool);
        if (nIndex != -1)
            vchPubKey = keypool.vchPubKey;
        else {
            return false;
        }
    }
    assert(vchPubKey.IsValid());
    pubkey = vchPubKey;
    return true;
}

GetReservedKey调用CWallet类的ReserveKeyFromKeyPool

void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
{
    nIndex = -1;
    keypool.vchPubKey = CPubKey();//构造一个无效的PubKey
    {
        LOCK(cs_wallet);

        if (!IsLocked())
            TopUpKeyPool();//充值密钥池,这个函数里一个循环,默认创建(最少)100个新的密钥添加到池中

        // Get the oldest key 
        if(setKeyPool.empty())
            return;

        CWalletDB walletdb(strWalletFile);

        nIndex = *(setKeyPool.begin());//返回容器指向的第一个元素
        setKeyPool.erase(setKeyPool.begin());//擦除这个指针指向的元素
        if (!walletdb.ReadPool(nIndex, keypool))
            throw runtime_error(std::string(__func__) + ": read failed");
        if (!HaveKey(keypool.vchPubKey.GetID()))
            throw runtime_error(std::string(__func__) + ": unknown key in key pool");
        assert(keypool.vchPubKey.IsValid());
        LogPrintf("keypool reserve %d\n", nIndex);
    }
}

需要注意这里的setKeyPool是signd long long的set容器类型
在日志信息中可以看到,创建了101把密钥,从1到101,也就是原本这个池中一把都没有,这个池应该是专门用于找零的池子,虽然密钥都是保存在键值对中,但是找零专用的密钥对写在池中“pool“,调用WritePool函数,另一种是写在‘keymate‘中
这里写图片描述
发送测试币回2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF
使用rpc指令
这里写图片描述
创建两笔交易

parallels@parallels-vm:~$ bitcoin-cli walletpassphrase aser6789dfgb 300
parallels@parallels-vm:~$ bitcoin-cli sendtoaddress "2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF" 0.2
441bb6516409b37f0b2da928cf4691ff0508f99f5481add15d608ee39ee59b04
parallels@parallels-vm:~$ bitcoin-cli getbalance
1.09896799
parallels@parallels-vm:~$ bitcoin-cli sendtoaddress "2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF" 0.2
aceeee8102eb3a922521edb784bf890e29c26e3336ab8e99b5430fc71345b641
parallels@parallels-vm:~$ 

这里写图片描述
第一笔交易输出找零到mpCjnRXL2mVbFBU77ixWsCJ88JyqfU2g1c,作为第二笔交易的输入
这里写图片描述

这里写图片描述
在创建三笔交易,创建一个地址,创建一个交易
这里写图片描述


###6)dust output

// Never create dust outputs; if we would, just
                    // add the dust to the fee.
                    if (newTxOut.IsDust(::minRelayTxFee))
                    {
                        nChangePosInOut = -1;
                        nFeeRet += nChange;
                        reservekey.ReturnKey();
                    }
                    else
                    {
                        if (nChangePosInOut == -1)
                        {
                            // Insert change txn at random position:
                            nChangePosInOut = GetRandInt(txNew.vout.size()+1);
                        }
                        else if ((unsigned int)nChangePosInOut > txNew.vout.size())
                        {
                            strFailReason = _("Change index out of range");
                            return false;
                        }

                        vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
                        txNew.vout.insert(position, newTxOut);
                    }
                }
                else
                    reservekey.ReturnKey();

###7)vin & sign
涉及vin的序列号,这个类写在这里https://blog.csdn.net/m0_37847176/article/details/81624052#ctxin

// Fill vin
//
// Note how the sequence number is set to max()-1 so that the
// nLockTime set above actually works.
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
    txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),std::numeric_limits<unsigned int>::max()-1));

这里设置输入交易容器vin,对于每一笔setCoins中的交易coin,构造CTxIn对象,使用的构造函数CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);第1、2个参数用于构造COutPoint,第3、4个参数是CTxIn的成员变量,这里设置序列号为max()-1,不是SEQUENCE_FINAL(=max()),因此nLockTime实际是有效的。最后将CTxIn逐个加入容器的底部。
接下来看签名

// Sign
int nIn = 0;
CTransaction txNewConst(txNew);
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
	{
		bool signSuccess;
        const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
        SignatureData sigdata;
        if (sign)
        //非隔离见证的交易
	        signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
         else
         //使用隔离见证的交易,这里使用空的签名,在交易本身之外包含一个隔离见证
             signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);

           if (!signSuccess)
	           {
                   strFailReason = _("Signing transaction failed");
                   return false;
                } else {
                //从交易中抽取签名数据然后插入
                   UpdateTransaction(txNew, nIn, sigdata);
                }

           nIn++;
          }

对于setCoins的每一笔交易coin,coin.first是CWalletTx的指针,CWalletTx没有成员变量,不过他的父类CMerkleTx的父类CTransaction有vout成员变量,通过序号获取对应的那一笔交易的锁定脚本scriptPubKey。
调用函数ProduceSignature使用通用签名创建者生成脚本签名

//sign.h
/** Produce a script signature using a generic signature creator. */
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);

###8)判断是否满足

 unsigned int nBytes = GetVirtualTransactionSize(txNew);

                // Remove scriptSigs if we used dummy signatures for fee calculation
                if (!sign) {
                    BOOST_FOREACH (CTxIn& vin, txNew.vin)
                        vin.scriptSig = CScript();
                    txNew.wit.SetNull();
                }

                // Embed the constructed transaction data in wtxNew.
                *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);

                // Limit size限制大小
                if (GetTransactionWeight(txNew) >= MAX_STANDARD_TX_WEIGHT)
                {
                    strFailReason = _("Transaction too large");
                    return false;
                }

                dPriority = wtxNew.ComputePriority(dPriority, nBytes);

                // Can we complete this as a free transaction?
                //构造一个免费的交易,费用不够的话用优先级来凑
                if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)//默认值分别为false、1000
                {
                    // Not enough fee: enough priority?
                    double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget);
                    // Require at least hard-coded AllowFree.
                    if (dPriority >= dPriorityNeeded && AllowFree(dPriority))
                        break;
                }
//获取最小交易费
                CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
                //默认coinControl为空,这段逻辑跳过,以后懂了再说
                if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
                    nFeeNeeded = coinControl->nMinimumTotalFee;
                }
                if (coinControl && coinControl->fOverrideFeeRate)
                    nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);

                // If we made it here and we aren't even able to meet the relay fee on the next pass, give up
                // because we must be at the maximum allowed fee.
                if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
                {
                    strFailReason = _("Transaction too large for fee policy");
                    return false;
                }

                if (nFeeRet >= nFeeNeeded)//直到有足够的费用
                    break; // Done, enough fee included.

                // Include more fee and try again.
                nFeeRet = nFeeNeeded;//
                continue;
            }
        }
    }

到这里是循环结束,满足条件则跳出循环,否则再次循环或报错
这里来关注下GetMinimumFee的实现,用来计算最小交易费用,和交易的字节相关,但是交易的字节是在交易构造后才能计算,所以用nFeeRet保存预估费用,在此基础上构建新的交易,如果得到的真实交易费小于预估,则需要要替换交易费,再次构建。

CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
{
////! -mintxfee default
//static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
//CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
    return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
    //static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
    //CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
}

CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
{
    // payTxFee is user-set "I want to pay this much"
    CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
    // User didn't set: use -txconfirmtarget to estimate...
    if (nFeeNeeded == 0) {
        int estimateFoundTarget = nConfirmTarget;
        nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);
        // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
        if (nFeeNeeded == 0)//没有足够的信息计算,那么使用这个费用
            nFeeNeeded = fallbackFee.GetFee(nTxBytes);
    ////! -fallbackfee default
//static const CAmount DEFAULT_FALLBACK_FEE = 20,000;
//CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
    }
    // prevent user from paying a fee below minRelayTxFee or minTxFee
    //阻止用户支付低于minRelayTxFee或minTxFee的费用
    nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes));
    // But always obey the maximum
    if (nFeeNeeded > maxTxFee)//大于最大费用
        nFeeNeeded = maxTxFee;
    return nFeeNeeded;
}

payTxfee是类CFeeRate的对象,全局变量,表示每千字节的费用,初始化0

/**

  • Fee rate in satoshis per kilobyte: CAmount / kB
    */

首先调用该类的成员函数GetFee(),计算字节对应的费用,用到的nSatoshisPerK是CFeeRate的私有成员变量

CAmount CFeeRate::GetFee(size_t nBytes_) const
{
    assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
    int64_t nSize = int64_t(nBytes_);

    CAmount nFee = nSatoshisPerK * nSize / 1000;

    if (nFee == 0 && nSize != 0) {
        if (nSatoshisPerK > 0)
            nFee = CAmount(1);
        if (nSatoshisPerK < 0)
            nFee = CAmount(-1);
    }

    return nFee;
}

可以看到,按默认值计算得到的nFeeNeeded就是0,所以有第二段,针对nFeeNeeded==0的操作语句,注释也表明用户没有设置-txconfirmtarget参数来估算会这样

//wallet.cpp
/** Transaction fee set by the user */
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);//0
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;//2

用到的nTxConfirmTarget是默认设置为2 satoshis/kb,变量pool是传入的参数,类CTxMemPool,调用函数estimateSmartFee这个函数展开又比较大了
大致看了下,里面调用EstimateMedianVal,会统计在这个目标金额下确认的交易数,总交易数和未确认的交易数,统计平均最佳值,然后返回。大概是这个意思,等我想看了再写


###9)参数读取

if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
        // Lastly, ensure this tx will pass the mempool's chain limits
        LockPoints lp;
        CTxMemPoolEntry entry(txNew, 0, 0, 0, 0, false, 0, false, 0, lp);
        CTxMemPool::setEntries setAncestors;
        size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
        size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
        size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
        size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
        std::string errString;
        if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
            strFailReason = _("Transaction has too long of a mempool chain");
            return false;
        }
    }
    return true;
    }

那么到这里就创建完交易了,接下来提交交易

附:支付给自己
在这里插入图片描述
mwtXCZEe8Wz9TkLHd4L8NAAjzdK8QrxaNw是我的比特币地址

parallels@parallels-vm:~$ bitcoin-cli sendtoaddress "mwtXCZEe8Wz9TkLHd4L8NAAjzdK8QrxaNw" 0.03
ae1d05e0443736379b0184c3c1b9017ed074f232ae8def8fd208848538cb8904

我给自己转了0.03,以及一大笔交易费

猜你喜欢

转载自blog.csdn.net/m0_37847176/article/details/82493420
今日推荐