elements/test/functional/wallet_create_tx.py
MarcoFalke a76dc401d6 Merge bitcoin/bitcoin#24502: wallet: don't create long chains by default
da2bc865d6 [wallet] don't create long chains by default (glozow)

Additional functional test for too-long-chains rejected.

Pull request description:

  Default mempool policy doesn't let you have chains longer than 25 transactions. This is locally configurable of course, but it's not really safe to assume that a chain longer than 25 transactions will propagate. Thus, the wallet should probably avoid creating such transactions by default; set `DEFAULT_WALLET_REJECT_LONG_CHAINS` to true.

  Closes #9752
  Closes #10004

ACKs for top commit:
  MarcoFalke:
    re-ACK da2bc865d6 only change is fixing typos in tests 🎏

Tree-SHA512: 65d8e4ec437fe928adf554aa7e819a52e0599b403d5310895f4e371e99bbc838219b3097c4d2f775bc870ac617ef6b4227b94291f2b376f824f14e8f2b152f31
2026-03-09 14:59:53 +00:00

111 lines
4.5 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright (c) 2018-2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
from test_framework.blocktools import (
TIME_GENESIS_BLOCK,
)
class CreateTxWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
self.log.info('Create some old blocks')
self.nodes[0].setmocktime(TIME_GENESIS_BLOCK)
self.generate(self.nodes[0], 200)
self.nodes[0].setmocktime(0)
self.test_anti_fee_sniping()
self.test_tx_size_too_large()
self.test_create_too_long_mempool_chain()
def test_anti_fee_sniping(self):
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
assert_equal(tx['locktime'], 0)
self.log.info('Check that anti-fee-sniping is enabled when we mine a recent block')
self.generate(self.nodes[0], 1)
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
assert 0 < tx['locktime'] <= 201
def test_tx_size_too_large(self):
# More than 10kB of outputs, so that we hit -maxtxfee with a high feerate
outputs = [{self.nodes[0].getnewaddress(address_type='bech32'): 0.000025} for _ in range(400)]
raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)
output_dict = {}
for output in outputs:
output_dict.update(output)
for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
self.restart_node(0, extra_args=[fee_setting])
assert_raises_rpc_error(
-6,
"Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].sendmany(dummy="", amounts=output_dict),
)
assert_raises_rpc_error(
-4,
"Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
)
self.log.info('Check maxtxfee in combination with settxfee')
self.restart_node(0)
self.nodes[0].settxfee(0.01)
assert_raises_rpc_error(
-6,
"Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].sendmany(dummy="", amounts=output_dict),
)
assert_raises_rpc_error(
-4,
"Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
)
self.nodes[0].settxfee(0)
def test_create_too_long_mempool_chain(self):
self.log.info('Check too-long mempool chain error')
df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
self.nodes[0].createwallet("too_long")
test_wallet = self.nodes[0].get_wallet_rpc("too_long")
tx_data = df_wallet.send(outputs=[{test_wallet.getnewaddress(): 25}], options={"change_position": 0})
txid = tx_data['txid']
vout = 1
options = {"change_position": 0, "add_inputs": False}
for i in range(1, 25):
options['inputs'] = [{'txid': txid, 'vout': vout}]
tx_data = test_wallet.send(outputs=[{test_wallet.getnewaddress(): 25 - i}], options=options)
txid = tx_data['txid']
options = {"include_unsafe": True, 'add_inputs': True}
# Sending one more chained transaction will fail
assert_raises_rpc_error(-4, "Insufficient funds",
test_wallet.send, outputs=[{test_wallet.getnewaddress(): 0.3}], options=options)
test_wallet.unloadwallet()
if __name__ == '__main__':
CreateTxWalletTest().main()