Skip to content

Commit 1d00601

Browse files
committed
Merge bitcoin/bitcoin#30309: wallet: notify when preset + automatic inputs exceed max weight
72b2268 wallet: notify when preset + automatic inputs exceed max weight (furszy) Pull request description: Small change. Found it while finishing my review on #29523. This does not interfere with it. Basically, we are erroring out early when the automatic coin selection process exceeds the maximum weight, but we are not doing so when the user-preselected inputs combined with the wallet-selected inputs exceed the maximum weight. This change avoids signing all inputs before erroring out and introduces test coverage for `fundrawtransaction`. ACKs for top commit: achow101: ACK 72b2268 tdb3: re ACK for 72b2268 rkrux: tACK [72b2268](bitcoin/bitcoin@72b2268) ismaelsadeeq: utACK 72b2268 Tree-SHA512: d77be19231023383a9c79a5d66b642dcbc6ebfc31a363e0b9f063c44898720a7859ec211cdbc0914ac7a3bfdf15e52fb8fc20d97f171431f70492c0f159dbc36
2 parents d3d2bbf + 72b2268 commit 1d00601

File tree

3 files changed

+74
-0
lines changed

3 files changed

+74
-0
lines changed

src/wallet/spend.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,13 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av
799799
op_selection_result->RecalculateWaste(coin_selection_params.min_viable_change,
800800
coin_selection_params.m_cost_of_change,
801801
coin_selection_params.m_change_fee);
802+
803+
// Verify we haven't exceeded the maximum allowed weight
804+
int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
805+
if (op_selection_result->GetWeight() > max_inputs_weight) {
806+
return util::Error{_("The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. "
807+
"Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
808+
}
802809
}
803810
return op_selection_result;
804811
}

test/functional/wallet_fundrawtransaction.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def run_test(self):
114114
self.test_add_inputs_default_value()
115115
self.test_preset_inputs_selection()
116116
self.test_weight_calculation()
117+
self.test_weight_limits()
117118
self.test_change_position()
118119
self.test_simple()
119120
self.test_simple_two_coins()
@@ -1312,6 +1313,38 @@ def test_weight_calculation(self):
13121313

13131314
self.nodes[2].unloadwallet("test_weight_calculation")
13141315

1316+
def test_weight_limits(self):
1317+
self.log.info("Test weight limits")
1318+
1319+
self.nodes[2].createwallet("test_weight_limits")
1320+
wallet = self.nodes[2].get_wallet_rpc("test_weight_limits")
1321+
1322+
outputs = []
1323+
for _ in range(1472):
1324+
outputs.append({wallet.getnewaddress(address_type="legacy"): 0.1})
1325+
txid = self.nodes[0].send(outputs=outputs)["txid"]
1326+
self.generate(self.nodes[0], 1)
1327+
1328+
# 272 WU per input (273 when high-s); picking 1471 inputs will exceed the max standard tx weight.
1329+
rawtx = wallet.createrawtransaction([], [{wallet.getnewaddress(): 0.1 * 1471}])
1330+
1331+
# 1) Try to fund transaction only using the preset inputs
1332+
input_weights = []
1333+
for i in range(1471):
1334+
input_weights.append({"txid": txid, "vout": i, "weight": 273})
1335+
assert_raises_rpc_error(-4, "Transaction too large", wallet.fundrawtransaction, hexstring=rawtx, input_weights=input_weights)
1336+
1337+
# 2) Let the wallet fund the transaction
1338+
assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
1339+
wallet.fundrawtransaction, hexstring=rawtx)
1340+
1341+
# 3) Pre-select some inputs and let the wallet fill-up the remaining amount
1342+
inputs = input_weights[0:1000]
1343+
assert_raises_rpc_error(-4, "The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
1344+
wallet.fundrawtransaction, hexstring=rawtx, input_weights=inputs)
1345+
1346+
self.nodes[2].unloadwallet("test_weight_limits")
1347+
13151348
def test_include_unsafe(self):
13161349
self.log.info("Test fundrawtxn with unsafe inputs")
13171350

test/functional/wallet_send.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,5 +577,39 @@ def run_test(self):
577577
# but rounded to nearest integer, it should be the same as the target fee rate
578578
assert_equal(round(actual_fee_rate_sat_vb), target_fee_rate_sat_vb)
579579

580+
# Check tx creation size limits
581+
self.test_weight_limits()
582+
583+
def test_weight_limits(self):
584+
self.log.info("Test weight limits")
585+
586+
self.nodes[1].createwallet("test_weight_limits")
587+
wallet = self.nodes[1].get_wallet_rpc("test_weight_limits")
588+
589+
# Generate future inputs; 272 WU per input (273 when high-s).
590+
# Picking 1471 inputs will exceed the max standard tx weight.
591+
outputs = []
592+
for _ in range(1472):
593+
outputs.append({wallet.getnewaddress(address_type="legacy"): 0.1})
594+
self.nodes[0].send(outputs=outputs)
595+
self.generate(self.nodes[0], 1)
596+
597+
# 1) Try to fund transaction only using the preset inputs
598+
inputs = wallet.listunspent()
599+
assert_raises_rpc_error(-4, "Transaction too large",
600+
wallet.send, outputs=[{wallet.getnewaddress(): 0.1 * 1471}], options={"inputs": inputs, "add_inputs": False})
601+
602+
# 2) Let the wallet fund the transaction
603+
assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
604+
wallet.send, outputs=[{wallet.getnewaddress(): 0.1 * 1471}])
605+
606+
# 3) Pre-select some inputs and let the wallet fill-up the remaining amount
607+
inputs = inputs[0:1000]
608+
assert_raises_rpc_error(-4, "The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
609+
wallet.send, outputs=[{wallet.getnewaddress(): 0.1 * 1471}], options={"inputs": inputs, "add_inputs": True})
610+
611+
self.nodes[1].unloadwallet("test_weight_limits")
612+
613+
580614
if __name__ == '__main__':
581615
WalletSendTest().main()

0 commit comments

Comments
 (0)