Skip to content

Commit

Permalink
wallet: notify when preset + automatic inputs exceed max weight
Browse files Browse the repository at this point in the history
This also avoids signing all inputs prior to erroring out.
  • Loading branch information
furszy committed Jun 19, 2024
1 parent 2d21060 commit 3f50276
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/wallet/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,13 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av
op_selection_result->RecalculateWaste(coin_selection_params.min_viable_change,
coin_selection_params.m_cost_of_change,
coin_selection_params.m_change_fee);

// Verify we haven't exceeded the maximum allowed weight
int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
if (op_selection_result->GetWeight() >= max_inputs_weight) {
return util::Error{_("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")};
}
}
return op_selection_result;
}
Expand Down
33 changes: 33 additions & 0 deletions test/functional/wallet_fundrawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def run_test(self):
self.test_add_inputs_default_value()
self.test_preset_inputs_selection()
self.test_weight_calculation()
self.test_weight_limits()
self.test_change_position()
self.test_simple()
self.test_simple_two_coins()
Expand Down Expand Up @@ -1312,6 +1313,38 @@ def test_weight_calculation(self):

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

def test_weight_limits(self):
self.log.info("Test weight limits")

self.nodes[2].createwallet("test_weight_limits")
wallet = self.nodes[2].get_wallet_rpc("test_weight_limits")

outputs = []
for _ in range(1472):
outputs.append({wallet.getnewaddress(address_type="legacy"): 0.1})
txid = self.nodes[0].send(outputs=outputs)["txid"]
self.generate(self.nodes[0], 1)

# 272 WU per input (273 when high-s); picking 1471 inputs will exceed the max standard tx weight.
rawtx = wallet.createrawtransaction([], [{wallet.getnewaddress(): 0.1 * 1471}])

# 1) Try to fund transaction only using the preset inputs
input_weights = []
for i in range(1471):
input_weights.append({"txid": txid, "vout": i, "weight": 273})
assert_raises_rpc_error(-4, "Transaction too large", wallet.fundrawtransaction, hexstring=rawtx, input_weights=input_weights)

# 2) Let the wallet fund the transaction
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",
wallet.fundrawtransaction, hexstring=rawtx)

# 3) Pre-select some inputs and let the wallet fill-up the remaining amount
inputs = input_weights[0:1000]
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",
wallet.fundrawtransaction, hexstring=rawtx, input_weights=inputs)

self.nodes[2].unloadwallet("test_weight_limits")

def test_include_unsafe(self):
self.log.info("Test fundrawtxn with unsafe inputs")

Expand Down

0 comments on commit 3f50276

Please sign in to comment.