fix(poller): only update last used index for affected keychain

This commit is contained in:
Michael Mallan 2025-07-09 10:48:18 +01:00
parent a6e41ab471
commit cb60089ace
No known key found for this signature in database
GPG Key ID: 5177CDCEDB0EABEB
3 changed files with 30 additions and 35 deletions

View File

@ -73,10 +73,9 @@ fn update_coins(
};
// First of if we are receiving coins that are beyond our next derivation index,
// adjust it.
if derivation_index > db_conn.receive_index() {
if !is_change && derivation_index > db_conn.receive_index() {
db_conn.set_receive_index(derivation_index, secp);
}
if derivation_index > db_conn.change_index() {
} else if is_change && derivation_index > db_conn.change_index() {
db_conn.set_change_index(derivation_index, secp);
}

View File

@ -406,27 +406,18 @@ def test_listrevealedaddresses(lianad, bitcoind):
assert list_rec["continue_from"] == 7 # same as starting index
assert len(list_rec["addresses"]) == 0
# The poller currently sets the change index to match the receive index.
# See https://github.com/wizardsardine/liana/issues/1333.
# The change index has index 0 as "revealed".
assert lianad.rpc.getinfo()["receive_index"] == 7
assert lianad.rpc.getinfo()["change_index"] == 7
assert lianad.rpc.getinfo()["change_index"] == 0
# We can get change addresses:
# We can get the single revealed change address:
list_cha = lianad.rpc.listrevealedaddresses(True, False, 3)
assert list_cha["continue_from"] == 4
assert len(list_cha["addresses"]) == 3
assert list_cha["addresses"][0]["index"] == 7
assert list_cha["addresses"][0]["address"] == addresses[7]["change"]
assert list_cha["continue_from"] is None
assert len(list_cha["addresses"]) == 1
assert list_cha["addresses"][0]["index"] == 0
assert list_cha["addresses"][0]["address"] == addresses[0]["change"]
assert list_cha["addresses"][0]["used_count"] == 0
assert list_cha["addresses"][0]["label"] is None
assert list_cha["addresses"][1]["index"] == 6
assert list_cha["addresses"][1]["address"] == addresses[6]["change"]
assert list_cha["addresses"][1]["used_count"] == 0
assert list_cha["addresses"][1]["label"] is None
assert list_cha["addresses"][2]["index"] == 5
assert list_cha["addresses"][2]["address"] == addresses[5]["change"]
assert list_cha["addresses"][2]["used_count"] == 0
assert list_cha["addresses"][2]["label"] is None
def test_listcoins(lianad, bitcoind):
@ -771,10 +762,8 @@ def test_create_spend(lianad, bitcoind):
# 15 new receive addresses have been generated (starting at index 1),
# so last used value is 15:
assert lianad.rpc.getinfo()["receive_index"] == 15
# For each received coin, the change index has also been updated by the poller
# (see https://github.com/wizardsardine/liana/issues/1333), so is also 15.
# Then `createspend` will use the next index for change and update the DB value accordingly:
assert lianad.rpc.getinfo()["change_index"] == 16
# `createspend` will use the next index for change and update the DB value accordingly:
assert lianad.rpc.getinfo()["change_index"] == 1
# The transaction must contain the spent transaction for each input for P2WSH. But not for Taproot.
# We don't make assumptions about the ordering of PSBT inputs.

View File

@ -242,9 +242,7 @@ def test_send_to_self(lianad, bitcoind):
info = lianad.rpc.getinfo()
assert info["receive_index"] == 3
# Change index has been updated by poller, even though none used
# (see https://github.com/wizardsardine/liana/issues/1333):
assert info["change_index"] == 3
assert info["change_index"] == 0
# Then create a send-to-self transaction (by not providing any destination) that
# sweeps them all.
@ -258,7 +256,7 @@ def test_send_to_self(lianad, bitcoind):
# Send to self didn't use any receive addresses...
assert info["receive_index"] == 3
# ... but it did use a new change address:
assert info["change_index"] == 4
assert info["change_index"] == 1
# Note they may ask for an impossible send-to-self. In this case we'll report missing amount.
huge_feerate = 50_000 if USE_TAPROOT else 40_500
@ -285,23 +283,32 @@ def test_send_to_self(lianad, bitcoind):
wait_for(lambda: len(list(unspent_coins())) == 1)
info = lianad.rpc.getinfo()
# The poller has updated the receive index based on the change index
# (see https://github.com/wizardsardine/liana/issues/1333):
assert info["receive_index"] == 4
assert info["change_index"] == 4
# The indices have not changed:
assert info["receive_index"] == 3
assert info["change_index"] == 1
# Create a new spend to the receive address with index 3.
recv_addr = lianad.rpc.listaddresses(3, 1)["addresses"][0]["receive"]
res = lianad.rpc.createspend(
{recv_addr: 11_965_000 if USE_TAPROOT else 11_955_000}, [], 2
)
assert "psbt" in res
# Max(receive_index, change_index) is now 4, so we return addresses 0, 1, 2, 3, 4:
assert len(lianad.rpc.listaddresses()["addresses"]) == 5
# Max(receive_index, change_index) is 3, so we return addresses 0, 1, 2, 3:
assert len(lianad.rpc.listaddresses()["addresses"]) == 4
# But the spend has no change:
psbt = PSBT.from_base64(res["psbt"])
assert len(psbt.o) == 1
# As the spend has no change, only the receive index was incremented.
# Therefore, the DB receive index is now 4.
# Now sign and broadcast the spend:
signed_psbt = lianad.signer.sign_psbt(psbt)
lianad.rpc.updatespend(signed_psbt.to_base64())
spend_txid = signed_psbt.tx.txid().hex()
lianad.rpc.broadcastspend(spend_txid)
# Wait for coin to be detected by poller:
wait_for(lambda: len(lianad.rpc.listcoins([], [f"{spend_txid}:0"])["coins"]) == 1)
# The indices have not changed:
info = lianad.rpc.getinfo()
assert info["receive_index"] == 3
assert info["change_index"] == 1
def test_coin_selection(lianad, bitcoind):