mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 04:22:55 +01:00
5a6b8b6b1f
14b4921a91920df25b19ff420bfe2bff8c56f71e wallet: reuse change dest when recreating TX with avoidpartialspends (Matthew Zipkin) Pull request description: Closes https://github.com/bitcoin/bitcoin/issues/27051 When the wallet creates a transaction internally, it will also create an alternative that spends using destination groups and see if the fee difference is negligible. If it costs the user the same to send the grouped version, we send it (even if the user has `avoidpartialspends` set to `false` which is default). This patch ensures that the second transaction creation attempt re-uses the change destination selected by the first attempt. Otherwise, the first change address remains reserved, will not be used in the second attempt, and then will never be used by the wallet, leaving gaps in the BIP44 chain. If the user had `avoidpartialspends` set to true, there is no second version of the created transaction and the change addresses are not affected. I believe this behavior was introduced in https://github.com/bitcoin/bitcoin/pull/14582 ACKs for top commit: achow101: ACK 14b4921a91920df25b19ff420bfe2bff8c56f71e Tree-SHA512: a3d56f251ff4b333fc11325f30d05513e34ab0a2eb703fadd0ad98d167ae074493df1a24068298336c6ed2da6b31aa2befa490bc790bbc260ed357c8f2397659
74 lines
2.6 KiB
Python
Executable File
74 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2023 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
"""Test wallet change address selection"""
|
|
|
|
import re
|
|
|
|
from test_framework.blocktools import COINBASE_MATURITY
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.util import (
|
|
assert_equal,
|
|
)
|
|
|
|
|
|
class WalletChangeAddressTest(BitcoinTestFramework):
|
|
def set_test_params(self):
|
|
self.setup_clean_chain = True
|
|
self.num_nodes = 3
|
|
# discardfee is used to make change outputs less likely in the change_pos test
|
|
self.extra_args = [
|
|
[],
|
|
["-discardfee=1"],
|
|
["-avoidpartialspends", "-discardfee=1"]
|
|
]
|
|
|
|
def skip_test_if_missing_module(self):
|
|
self.skip_if_no_wallet()
|
|
|
|
def assert_change_index(self, node, tx, index):
|
|
change_index = None
|
|
for vout in tx["vout"]:
|
|
info = node.getaddressinfo(vout["scriptPubKey"]["addresses"][0])
|
|
if (info["ismine"] and info["ischange"]):
|
|
change_index = int(re.findall(r'\d+', info["hdkeypath"])[-1])
|
|
break
|
|
assert_equal(change_index, index)
|
|
|
|
def assert_change_pos(self, wallet, tx, pos):
|
|
change_pos = None
|
|
for index, output in enumerate(tx["vout"]):
|
|
info = wallet.getaddressinfo(output["scriptPubKey"]["address"])
|
|
if (info["ismine"] and info["ischange"]):
|
|
change_pos = index
|
|
break
|
|
assert_equal(change_pos, pos)
|
|
|
|
def run_test(self):
|
|
self.log.info("Setting up")
|
|
# Mine some coins
|
|
self.nodes[0].generate(COINBASE_MATURITY + 1)
|
|
|
|
# Get some addresses from the two nodes
|
|
addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]
|
|
addr2 = [self.nodes[2].getnewaddress() for _ in range(3)]
|
|
addrs = addr1 + addr2
|
|
|
|
# Send 1 + 0.5 coin to each address
|
|
[self.nodes[0].sendtoaddress(addr, 10) for addr in addrs]
|
|
[self.nodes[0].sendtoaddress(addr, 5) for addr in addrs]
|
|
self.nodes[0].generate(1)
|
|
|
|
for i in range(20):
|
|
for n in [1, 2]:
|
|
self.log.debug(f"Send transaction from node {n}: expected change index {i}")
|
|
txid = self.nodes[n].sendtoaddress(self.nodes[0].getnewaddress(), 2)
|
|
tx = self.nodes[n].getrawtransaction(txid, True)
|
|
# find the change output and ensure that expected change index was used
|
|
self.assert_change_index(self.nodes[n], tx, i)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
WalletChangeAddressTest().main()
|