Original context from 03/16/2023:
If we get a new address with New n times, and then we get a new address with LastUnused, we get back the n+1th address (expected behavior).
However, if we send funds to the n-th address, the next time we get a new address with LastUnused, we get back the 2n-th address
And shedding a bit more light on the internals that folks on the team was able to uncover:
Long version: The BDK wallet maintains a database (sqlite in our case). When the wallet is sync’d or when a new address is requested, the wallet caches a large (100) batch of script pubkeys (addresses). These scriptpubkeys are stored in a table with the schema keychain [internal or external], child number, scriptpubkey. When the wallet pulls down transaction data from the electrum server, gets a list of the scriptpubkeys from that database table, and then joins that list to the list of transaction IDs that it gets from the electrum server. It uses this combined list to tell what the last-used address index is. For example, it might see that there are transactions for scriptpubkey 1, 3, and 5. So it knows that the next address it should generate (to prevent re-use) is 6. The problem is that in the version of BDK that we rely on, there is no uniqueness constraints on the table containing the list of scriptpubkeys. That means that under some circumstances, the list of scriptpubkeys can be duplicated. So instead of having 0,1,2,3,4,..., we’ve observed the table contain 0,0,0,1,1,1,2,2,2,3,3,3,... When the wallet sync happens, the fetch of the scriptpubkey list would get these duplicates and join them to the list of transactions. Meaning that if the “latest” transaction was on child 3, it might show up as the 9th entry in the list, causing the wallet to think that the next address it needs to generate is the 10th address.
BDK: 0.25.0
Original context from 03/16/2023:
If we get a new address with
Newn times, and then we get a new address withLastUnused,we get back the n+1th address (expected behavior).However, if we send funds to the n-th address, the next time we get a new address with
LastUnused, we get back the 2n-th addressAnd shedding a bit more light on the internals that folks on the team was able to uncover:
Long version: The BDK wallet maintains a database (sqlite in our case). When the wallet is sync’d or when a new address is requested, the wallet caches a large (100) batch of script pubkeys (addresses). These
scriptpubkeysare stored in a table with the schemakeychain [internal or external], child number,scriptpubkey. When the wallet pulls down transaction data from the electrum server, gets a list of thescriptpubkeysfrom that database table, and then joins that list to the list of transaction IDs that it gets from the electrum server. It uses this combined list to tell what the last-used address index is. For example, it might see that there are transactions forscriptpubkey1, 3, and 5. So it knows that the next address it should generate (to prevent re-use) is 6. The problem is that in the version of BDK that we rely on, there is no uniqueness constraints on the table containing the list ofscriptpubkeys. That means that under some circumstances, the list ofscriptpubkeyscan be duplicated. So instead of having 0,1,2,3,4,..., we’ve observed the table contain 0,0,0,1,1,1,2,2,2,3,3,3,... When the wallet sync happens, the fetch of thescriptpubkeylist would get these duplicates and join them to the list of transactions. Meaning that if the “latest” transaction was on child 3, it might show up as the 9th entry in the list, causing the wallet to think that the next address it needs to generate is the 10th address.BDK: 0.25.0