LCOV - code coverage report
Current view: top level - src/wallet - feebumper.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 118 146 80.8 %
Date: 2020-09-26 01:30:44 Functions: 6 7 85.7 %

          Line data    Source code
       1             : // Copyright (c) 2017-2020 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <interfaces/chain.h>
       6             : #include <policy/fees.h>
       7             : #include <policy/policy.h>
       8             : #include <util/moneystr.h>
       9             : #include <util/rbf.h>
      10             : #include <util/system.h>
      11             : #include <util/translation.h>
      12             : #include <wallet/coincontrol.h>
      13             : #include <wallet/feebumper.h>
      14             : #include <wallet/fees.h>
      15             : #include <wallet/wallet.h>
      16             : 
      17             : //! Check whether transaction has descendant in wallet or mempool, or has been
      18             : //! mined, or conflicts with a mined transaction. Return a feebumper::Result.
      19         183 : static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
      20             : {
      21         183 :     if (wallet.HasWalletSpend(wtx.GetHash())) {
      22           1 :         errors.push_back(Untranslated("Transaction has descendants in the wallet"));
      23           1 :         return feebumper::Result::INVALID_PARAMETER;
      24             :     }
      25             : 
      26             :     {
      27         182 :         if (wallet.chain().hasDescendantsInMempool(wtx.GetHash())) {
      28           0 :             errors.push_back(Untranslated("Transaction has descendants in the mempool"));
      29           0 :             return feebumper::Result::INVALID_PARAMETER;
      30             :         }
      31             :     }
      32             : 
      33         182 :     if (wtx.GetDepthInMainChain() != 0) {
      34           0 :         errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));
      35           0 :         return feebumper::Result::WALLET_ERROR;
      36             :     }
      37             : 
      38         182 :     if (!SignalsOptInRBF(*wtx.tx)) {
      39           2 :         errors.push_back(Untranslated("Transaction is not BIP 125 replaceable"));
      40           2 :         return feebumper::Result::WALLET_ERROR;
      41             :     }
      42             : 
      43         180 :     if (wtx.mapValue.count("replaced_by_txid")) {
      44           1 :         errors.push_back(strprintf(Untranslated("Cannot bump transaction %s which was already bumped by transaction %s"), wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid")));
      45           1 :         return feebumper::Result::WALLET_ERROR;
      46             :     }
      47             : 
      48             :     // check that original tx consists entirely of our inputs
      49             :     // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
      50         179 :     isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
      51         179 :     if (!wallet.IsAllFromMe(*wtx.tx, filter)) {
      52           1 :         errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
      53           1 :         return feebumper::Result::WALLET_ERROR;
      54             :     }
      55             : 
      56             : 
      57         178 :     return feebumper::Result::OK;
      58         183 : }
      59             : 
      60             : //! Check if the user provided a valid feeRate
      61          10 : static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector<bilingual_str>& errors)
      62             : {
      63             :     // check that fee rate is higher than mempool's minimum fee
      64             :     // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
      65             :     // This may occur if the user set fee_rate or paytxfee too low, if fallbackfee is too low, or, perhaps,
      66             :     // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
      67             :     // moment earlier. In this case, we report an error to the user, who may adjust the fee.
      68          10 :     CFeeRate minMempoolFeeRate = wallet.chain().mempoolMinFee();
      69             : 
      70          10 :     if (newFeerate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
      71           0 :         errors.push_back(strprintf(
      72           0 :             Untranslated("New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "),
      73           0 :             FormatMoney(newFeerate.GetFeePerK()),
      74           0 :             FormatMoney(minMempoolFeeRate.GetFeePerK())));
      75           0 :         return feebumper::Result::WALLET_ERROR;
      76             :     }
      77             : 
      78          10 :     CAmount new_total_fee = newFeerate.GetFee(maxTxSize);
      79             : 
      80          10 :     CFeeRate incrementalRelayFee = std::max(wallet.chain().relayIncrementalFee(), CFeeRate(WALLET_INCREMENTAL_RELAY_FEE));
      81             : 
      82             :     // Given old total fee and transaction size, calculate the old feeRate
      83          10 :     isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
      84          10 :     CAmount old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut();
      85          10 :     const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
      86          10 :     CFeeRate nOldFeeRate(old_fee, txSize);
      87             :     // Min total fee is old fee + relay fee
      88          10 :     CAmount minTotalFee = nOldFeeRate.GetFee(maxTxSize) + incrementalRelayFee.GetFee(maxTxSize);
      89             : 
      90          10 :     if (new_total_fee < minTotalFee) {
      91           2 :         errors.push_back(strprintf(Untranslated("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)"),
      92           1 :             FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxTxSize)), FormatMoney(incrementalRelayFee.GetFee(maxTxSize))));
      93           1 :         return feebumper::Result::INVALID_PARAMETER;
      94             :     }
      95             : 
      96           9 :     CAmount requiredFee = GetRequiredFee(wallet, maxTxSize);
      97           9 :     if (new_total_fee < requiredFee) {
      98           0 :         errors.push_back(strprintf(Untranslated("Insufficient total fee (cannot be less than required fee %s)"),
      99           0 :             FormatMoney(requiredFee)));
     100           0 :         return feebumper::Result::INVALID_PARAMETER;
     101             :     }
     102             : 
     103             :     // Check that in all cases the new fee doesn't violate maxTxFee
     104           9 :     const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
     105           9 :     if (new_total_fee > max_tx_fee) {
     106           2 :         errors.push_back(strprintf(Untranslated("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)"),
     107           1 :             FormatMoney(new_total_fee), FormatMoney(max_tx_fee)));
     108           1 :         return feebumper::Result::WALLET_ERROR;
     109             :     }
     110             : 
     111           8 :     return feebumper::Result::OK;
     112          10 : }
     113             : 
     114          83 : static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, CCoinControl& coin_control)
     115             : {
     116             :     // Get the fee rate of the original transaction. This is calculated from
     117             :     // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
     118             :     // result.
     119          83 :     int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
     120          83 :     CFeeRate feerate(old_fee, txSize);
     121          83 :     feerate += CFeeRate(1);
     122             : 
     123             :     // The node has a configurable incremental relay fee. Increment the fee by
     124             :     // the minimum of that and the wallet's conservative
     125             :     // WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
     126             :     // network wide policy for incremental relay fee that our node may not be
     127             :     // aware of. This ensures we're over the required relay fee rate
     128             :     // (BIP 125 rule 4).  The replacement tx will be at least as large as the
     129             :     // original tx, so the total fee will be greater (BIP 125 rule 3)
     130          83 :     CFeeRate node_incremental_relay_fee = wallet.chain().relayIncrementalFee();
     131          83 :     CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
     132          83 :     feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);
     133             : 
     134             :     // Fee rate must also be at least the wallet's GetMinimumFeeRate
     135          83 :     CFeeRate min_feerate(GetMinimumFeeRate(wallet, coin_control, /* feeCalc */ nullptr));
     136             : 
     137             :     // Set the required fee rate for the replacement transaction in coin control.
     138          83 :     return std::max(feerate, min_feerate);
     139          83 : }
     140             : 
     141             : namespace feebumper {
     142             : 
     143           0 : bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
     144             : {
     145           0 :     LOCK(wallet.cs_wallet);
     146           0 :     const CWalletTx* wtx = wallet.GetWalletTx(txid);
     147           0 :     if (wtx == nullptr) return false;
     148             : 
     149           0 :     std::vector<bilingual_str> errors_dummy;
     150           0 :     feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
     151           0 :     return res == feebumper::Result::OK;
     152           0 : }
     153             : 
     154          98 : Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
     155             :                                  CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
     156             : {
     157             :     // We are going to modify coin control later, copy to re-use
     158          98 :     CCoinControl new_coin_control(coin_control);
     159             : 
     160          98 :     LOCK(wallet.cs_wallet);
     161          98 :     errors.clear();
     162          98 :     auto it = wallet.mapWallet.find(txid);
     163          98 :     if (it == wallet.mapWallet.end()) {
     164           0 :         errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
     165           0 :         return Result::INVALID_ADDRESS_OR_KEY;
     166             :     }
     167          98 :     const CWalletTx& wtx = it->second;
     168             : 
     169          98 :     Result result = PreconditionChecks(wallet, wtx, errors);
     170          98 :     if (result != Result::OK) {
     171           5 :         return result;
     172             :     }
     173             : 
     174             :     // Fill in recipients(and preserve a single change key if there is one)
     175          93 :     std::vector<CRecipient> recipients;
     176         277 :     for (const auto& output : wtx.tx->vout) {
     177         184 :         if (!wallet.IsChange(output)) {
     178          93 :             CRecipient recipient = {output.scriptPubKey, output.nValue, false};
     179          93 :             recipients.push_back(recipient);
     180          93 :         } else {
     181          91 :             CTxDestination change_dest;
     182          91 :             ExtractDestination(output.scriptPubKey, change_dest);
     183          91 :             new_coin_control.destChange = change_dest;
     184          91 :         }
     185             :     }
     186             : 
     187          93 :     isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
     188          93 :     old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut();
     189             : 
     190          93 :     if (coin_control.m_feerate) {
     191             :         // The user provided a feeRate argument.
     192             :         // We calculate this here to avoid compiler warning on the cs_wallet lock
     193          10 :         const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet);
     194          10 :         Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors);
     195          10 :         if (res != Result::OK) {
     196           2 :             return res;
     197             :         }
     198           8 :     } else {
     199             :         // The user did not provide a feeRate argument
     200          83 :         new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, old_fee, new_coin_control);
     201             :     }
     202             : 
     203             :     // Fill in required inputs we are double-spending(all of them)
     204             :     // N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
     205             :     // used in the replacement transaction, but it's very important for wallets to make
     206             :     // sure that happens. If not, it would be possible to bump a transaction A twice to
     207             :     // A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
     208             :     // to A3 where A and A3 don't conflict). If both later get confirmed then the sender
     209             :     // has accidentally double paid.
     210         317 :     for (const auto& inputs : wtx.tx->vin) {
     211         226 :         new_coin_control.Select(COutPoint(inputs.prevout));
     212             :     }
     213          91 :     new_coin_control.fAllowOtherInputs = true;
     214             : 
     215             :     // We cannot source new unconfirmed inputs(bip125 rule 2)
     216          91 :     new_coin_control.m_min_depth = 1;
     217             : 
     218          91 :     CTransactionRef tx_new = MakeTransactionRef();
     219          91 :     CAmount fee_ret;
     220          91 :     int change_pos_in_out = -1; // No requested location for change
     221          91 :     bilingual_str fail_reason;
     222          91 :     if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
     223           2 :         errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
     224           2 :         return Result::WALLET_ERROR;
     225             :     }
     226             : 
     227             :     // Write back new fee if successful
     228          89 :     new_fee = fee_ret;
     229             : 
     230             :     // Write back transaction
     231          89 :     mtx = CMutableTransaction(*tx_new);
     232             :     // Mark new tx not replaceable, if requested.
     233          89 :     if (!coin_control.m_signal_bip125_rbf.get_value_or(wallet.m_signal_rbf)) {
     234           2 :         for (auto& input : mtx.vin) {
     235           1 :             if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
     236             :         }
     237           1 :     }
     238             : 
     239          89 :     return Result::OK;
     240         191 : }
     241             : 
     242          85 : bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) {
     243          85 :     LOCK(wallet.cs_wallet);
     244          85 :     return wallet.SignTransaction(mtx);
     245          85 : }
     246             : 
     247          85 : Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<bilingual_str>& errors, uint256& bumped_txid)
     248             : {
     249          85 :     LOCK(wallet.cs_wallet);
     250          85 :     if (!errors.empty()) {
     251           0 :         return Result::MISC_ERROR;
     252             :     }
     253          85 :     auto it = txid.IsNull() ? wallet.mapWallet.end() : wallet.mapWallet.find(txid);
     254          85 :     if (it == wallet.mapWallet.end()) {
     255           0 :         errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
     256           0 :         return Result::MISC_ERROR;
     257             :     }
     258          85 :     CWalletTx& oldWtx = it->second;
     259             : 
     260             :     // make sure the transaction still has no descendants and hasn't been mined in the meantime
     261          85 :     Result result = PreconditionChecks(wallet, oldWtx, errors);
     262          85 :     if (result != Result::OK) {
     263           0 :         return result;
     264             :     }
     265             : 
     266             :     // commit/broadcast the tx
     267          85 :     CTransactionRef tx = MakeTransactionRef(std::move(mtx));
     268          85 :     mapValue_t mapValue = oldWtx.mapValue;
     269          85 :     mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
     270             : 
     271          85 :     wallet.CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm);
     272             : 
     273             :     // mark the original tx as bumped
     274          85 :     bumped_txid = tx->GetHash();
     275          85 :     if (!wallet.MarkReplaced(oldWtx.GetHash(), bumped_txid)) {
     276             :         // TODO: see if JSON-RPC has a standard way of returning a response
     277             :         // along with an exception. It would be good to return information about
     278             :         // wtxBumped to the caller even if marking the original transaction
     279             :         // replaced does not succeed for some reason.
     280           0 :         errors.push_back(Untranslated("Created new bumpfee transaction but could not mark the original transaction as replaced"));
     281           0 :     }
     282             :     return Result::OK;
     283         170 : }
     284             : 
     285             : } // namespace feebumper

Generated by: LCOV version 1.15