Skip to content

Commit 22a5ccf

Browse files
committed
Merge bitcoin/bitcoin#29510: wallet: getrawchangeaddress and getnewaddress failures should not affect keypools for descriptor wallets
e073f1d test: make sure keypool sizes do not change on `getrawchangeaddress`/`getnewaddress` failures (UdjinM6) 367bb7a wallet: Avoid updating `ReserveDestination::nIndex` when `GetReservedDestination` fails (UdjinM6) Pull request description: I think the expected behaviour of `getrawchangeaddress` and `getnewaddress` RPCs is that their failure should not affect keypool in any way. At least that's how legacy wallets work, you can confirm this behaviour by running `wallet_keypool.py --legacy-wallet` on master with e073f1d applied on top. However running `wallet_keypool.py --descriptors` on the same commit results in the following failure: ``` File "/path/to/bitcoin/test/functional/test_framework/test_framework.py", line 131, in main self.run_test() File "/path/to/bitcoin/test/functional/wallet_keypool.py", line 114, in run_test assert_equal(kp_size_before, kp_size_after) File "/path/to/bitcoin/test/functional/test_framework/util.py", line 57, in assert_equal raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args)) AssertionError: not([18, 24] == [19, 24]) ``` This happens because we pass `nIndex` (which is a class member) into `GetReservedDestination` and since it's passed by reference we get an updated value back, so `nIndex` won't be equal `-1` anymore, no matter if the function failed or succeeded. This means that `ReturnDestination` (called by dtor of `ReserveDestination`) will try to return something we did not actually reserve. The fix is to simply use a temporary variable instead of a class member and only update `nIndex` when `op_address` actually has value, basically do it the same way we do for other class members (`address` and `fInternal`) already. ACKs for top commit: achow101: ACK e073f1d josibake: ACK bitcoin/bitcoin@e073f1d Tree-SHA512: 1128288a60dd4d8f306ef6f7ac66cdfeae3c9cc35c66ecada2d78fa61ac759f2a757b70fc3976ba8b5081200942b58dfabc184c01ccf911af40ba8c145344651
2 parents 61aa981 + e073f1d commit 22a5ccf

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/wallet/wallet.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2607,8 +2607,10 @@ util::Result<CTxDestination> ReserveDestination::GetReservedDestination(bool int
26072607

26082608
if (nIndex == -1) {
26092609
CKeyPool keypool;
2610-
auto op_address = m_spk_man->GetReservedDestination(type, internal, nIndex, keypool);
2610+
int64_t index;
2611+
auto op_address = m_spk_man->GetReservedDestination(type, internal, index, keypool);
26112612
if (!op_address) return op_address;
2613+
nIndex = index;
26122614
address = *op_address;
26132615
fInternal = keypool.fInternal;
26142616
}

test/functional/wallet_keypool.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,34 @@ def run_test(self):
103103
nodes[0].getrawchangeaddress()
104104
nodes[0].getrawchangeaddress()
105105
nodes[0].getrawchangeaddress()
106-
addr = set()
106+
# remember keypool sizes
107+
wi = nodes[0].getwalletinfo()
108+
kp_size_before = [wi['keypoolsize_hd_internal'], wi['keypoolsize']]
107109
# the next one should fail
108110
assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getrawchangeaddress)
111+
# check that keypool sizes did not change
112+
wi = nodes[0].getwalletinfo()
113+
kp_size_after = [wi['keypoolsize_hd_internal'], wi['keypoolsize']]
114+
assert_equal(kp_size_before, kp_size_after)
109115

110116
# drain the external keys
117+
addr = set()
111118
addr.add(nodes[0].getnewaddress(address_type="bech32"))
112119
addr.add(nodes[0].getnewaddress(address_type="bech32"))
113120
addr.add(nodes[0].getnewaddress(address_type="bech32"))
114121
addr.add(nodes[0].getnewaddress(address_type="bech32"))
115122
addr.add(nodes[0].getnewaddress(address_type="bech32"))
116123
addr.add(nodes[0].getnewaddress(address_type="bech32"))
117124
assert len(addr) == 6
125+
# remember keypool sizes
126+
wi = nodes[0].getwalletinfo()
127+
kp_size_before = [wi['keypoolsize_hd_internal'], wi['keypoolsize']]
118128
# the next one should fail
119129
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
130+
# check that keypool sizes did not change
131+
wi = nodes[0].getwalletinfo()
132+
kp_size_after = [wi['keypoolsize_hd_internal'], wi['keypoolsize']]
133+
assert_equal(kp_size_before, kp_size_after)
120134

121135
# refill keypool with three new addresses
122136
nodes[0].walletpassphrase('test', 1)

0 commit comments

Comments
 (0)