From c3981e379fa088aa7aa03b2f505342a5b3bc3436 Mon Sep 17 00:00:00 2001 From: S3RK <1466284+S3RK@users.noreply.github.com> Date: Wed, 18 May 2022 08:25:04 +0200 Subject: [PATCH 1/2] wallet: do not count wallet utxos as external --- src/wallet/rpc/spend.cpp | 13 ------------- src/wallet/spend.cpp | 22 ++++++++++++++++++---- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 3d975b54024..9020eade237 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -699,19 +699,6 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, setSubtractFeeFromOutputs.insert(pos); } - // Fetch specified UTXOs from the UTXO set to get the scriptPubKeys and values of the outputs being selected - // and to match with the given solving_data. Only used for non-wallet outputs. - std::map coins; - for (const CTxIn& txin : tx.vin) { - coins[txin.prevout]; // Create empty map entry keyed by prevout. - } - wallet.chain().findCoins(coins); - for (const auto& coin : coins) { - if (!coin.second.out.IsNull()) { - coinControl.SelectExternal(coin.first, coin.second.out); - } - } - bilingual_str error; if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) { diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index e5fd7b0eb4a..162d06e596b 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -1018,14 +1018,28 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, coinControl.fAllowOtherInputs = true; - for (const CTxIn& txin : tx.vin) { - coinControl.Select(txin.prevout); - } - // Acquire the locks to prevent races to the new locked unspents between the // CreateTransaction call and LockCoin calls (when lockUnspents is true). LOCK(wallet.cs_wallet); + // Fetch specified UTXOs from the UTXO set to get the scriptPubKeys and values of the outputs being selected + // and to match with the given solving_data. Only used for non-wallet outputs. + std::map coins; + for (const CTxIn& txin : tx.vin) { + coins[txin.prevout]; // Create empty map entry keyed by prevout. + } + wallet.chain().findCoins(coins); + + for (const CTxIn& txin : tx.vin) { + // if it's not in the wallet and corresponding UTXO is found than select as external output + const auto& outPoint = txin.prevout; + if (wallet.mapWallet.find(outPoint.hash) == wallet.mapWallet.end() && !coins[outPoint].out.IsNull()) { + coinControl.SelectExternal(outPoint, coins[outPoint].out); + } else { + coinControl.Select(outPoint); + } + } + FeeCalculation fee_calc_out; std::optional txr = CreateTransaction(wallet, vecSend, nChangePosInOut, error, coinControl, fee_calc_out, false); if (!txr) return false; From 7832e9438f5c66b88f60676d14e1e11d669eb109 Mon Sep 17 00:00:00 2001 From: S3RK <1466284+S3RK@users.noreply.github.com> Date: Wed, 30 Mar 2022 09:18:33 +0200 Subject: [PATCH 2/2] test: fundrawtransaction preset input weight calculation --- test/functional/rpc_fundrawtransaction.py | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 759e43194bd..948deaaec44 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -11,6 +11,9 @@ from math import ceil from test_framework.descriptors import descsum_create from test_framework.key import ECKey +from test_framework.messages import ( + COIN, +) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_approx, @@ -103,6 +106,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.generate(self.nodes[2], 1) self.generate(self.nodes[0], 121) + self.test_weight_calculation() self.test_change_position() self.test_simple() self.test_simple_two_coins() @@ -1069,6 +1073,27 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[2].unloadwallet("extfund") + def test_weight_calculation(self): + self.log.info("Test weight calculation with external inputs") + + self.nodes[2].createwallet("test_weight_calculation") + wallet = self.nodes[2].get_wallet_rpc("test_weight_calculation") + + addr = wallet.getnewaddress() + txid = self.nodes[0].sendtoaddress(addr, 5) + vout = find_vout_for_address(self.nodes[0], txid, addr) + + self.nodes[0].sendtoaddress(wallet.getnewaddress(), 5) + self.generate(self.nodes[0], 1) + + rawtx = wallet.createrawtransaction([{'txid': txid, 'vout': vout}], [{self.nodes[0].getnewaddress(): 9.999}]) + fundedtx = wallet.fundrawtransaction(rawtx, {'fee_rate': 10}) + # with 71-byte signatures we should expect following tx size + tx_size = 10 + 41*2 + 31*2 + (2 + 107*2)/4 + assert_equal(fundedtx['fee'] * COIN, tx_size * 10) + + self.nodes[2].unloadwallet("test_weight_calculation") + def test_include_unsafe(self): self.log.info("Test fundrawtxn with unsafe inputs")