mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-08-28 21:51:59 +02:00
Merge bitcoin/bitcoin#24649: wallet: do not count wallet utxos as external
7832e9438f
test: fundrawtransaction preset input weight calculation (S3RK)c3981e379f
wallet: do not count wallet utxos as external (S3RK) Pull request description: Correctly differentiating between external vs non-external utxos in coin control produces more accurate weight and fee estimations. Weight for external utxos is estimated based on the maximum signature size, while for the wallet utxos we expect minimal signature due to signature grinding. ACKs for top commit: achow101: re-ACK7832e9438f
Xekyo: re-ACK7832e9438f
furszy: ACK7832e943
Tree-SHA512: bb5635b0bd85fa9a76922a53ad3fa062286424c06a695a0e87407c665713e80a33555b644fbb13bcc1ab503dcd7f53aacbdc368d69ac0ecff8005603623ac94f
This commit is contained in:
@@ -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<COutPoint, Coin> 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)) {
|
||||
|
@@ -1023,14 +1023,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<COutPoint, Coin> 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<CreatedTransactionResult> txr = CreateTransaction(wallet, vecSend, nChangePosInOut, error, coinControl, fee_calc_out, false);
|
||||
if (!txr) return false;
|
||||
|
@@ -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")
|
||||
|
||||
|
Reference in New Issue
Block a user