Closed thunderbiscuit closed 2 years ago
I am now able to reproduce the error consistently. I can get the bug to "work" on my device and in the emulator. Here are two ways I can do it. The second one (Test 2) is really the purest form I am able to make it so far.
12856 (the actual 5856 plus our initial 7000)
(Electrum(Protocol(String("sendrawtransaction RPC error: {\"code\":-25,\"message\":\"bad-txns-inputs-missingorspent\"}"))))
)Tried to reproduce on the cli but I don't get the same behaviour, and the balance shows correctly.
Potential bug is on step 3 of test 2 above.
If the sync function of step 3 is executed (correct balance displayed) but not flushed to disk at that time, it would make sense that when we kill the app abruptly the db doesn't know about the tx yet. In step 5, an attempt to build a new psbt but without any sync makes the library use the only utxo it knows about, the initial received 7000 sats. Somehow when that fails to broadcast then that's where it's unclear to me what happens but in any case the new balance (the change from the transaction in step 2) and the original utxo are added together and you get a total balance of 12856 sats.
This is great to have steps to reproduce. I'm pretty sure the DB is only updated during sync, so this supports your theory that the sync and immediately closing the app is the problem. If you always sync before trying to create a spending TX does that fix the issue? It's only a workaround, but would help confirm. Probably next step is try try to flush the DB after sync and see if that does what we need it to, though I suspect there's always a window where if you close fast enough the sync won't be committed to the DB.
If you always sync before trying to create a spending TX does that fix the issue?
Thanks for the suggestion, I just assumed it would but in fact we've narrowed down our bug! The balance is wrong right on second startup without even trying to generate a new transaction. So the newest version of the steps to reproduce the bug are:
I think this supports the idea that the required flush after sync is our issue.
It looks like most additions to the DB are flushed correctly, but deletions are not: all the issues you've described involve spending a UTXO (which a later sync removes from the DB) and then reloading the app.
A quick interesting test you could do is:
If this ends up working, I think this issue might have already been (implicitly) fixed by https://github.com/bitcoindevkit/bdk/pull/515 because with that PR instead of removing spent UTXOs we issue updates to mark them as spent.
Thanks for the idea! I didn't know about PR #515.
Unfortunately using the workflow/test you proposed didn't solve the issue (does that help narrowing down where the problem might be?). Balance is still over by the extra amount of the first utxo (7000) after steps 4-5-6, even though the correct balance is displayed on sync in step 4. But you're right it appears to be a problem of deleting spent utxos, or recognizing them as spent at least.
Would the issue of confirmation/pending be a part of this? I could try to test to sync/kill app only after the transaction is confirmed.
This is fixed by #116 and using sqlite DB instead of Sled. See also https://github.com/bitcoindevkit/bdk-kotlin/pull/19
I assume this is related to the
flush()
method Ricardo implemented a little while back.I cannot seem to reproduce the problem consistently which doesn't help, but the gist of it is that at some point the sats sent out are not recognized as having been spent, and no amount of
sync
can fix the issue. The only way to fix is to delete and reload/resync the wallet from scratch.I think we should try to flush every time we sync, but I haven't been able to find examples of that, and my attempts currently don't compile.