{
 "number": 34193,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/34193",
 "title": "wallet: make migration more robust against failures",
 "author": "furszy",
 "author_association": "MEMBER",
 "created_at": "2026-01-02T20:59:40Z",
 "updated_at": "2026-09-14T13:38:32Z",
 "age_days": 257,
 "draft": false,
 "labels": [
  "Wallet",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "5c77af06234a7bf1a71ec2953ff266c56bde5d1a",
 "head_ref": "2026_wallet_safer_MigrateToSQLite",
 "head_repo": "furszy/bitcoin-core",
 "head_history": [
  {
   "t": "2026-01-02T22:50:16Z",
   "sha": "3e59872437d0c7a94d6b4d9c6874c8beb4a82f01"
  },
  {
   "t": "2026-01-04T00:30:25Z",
   "sha": "ac13657de4b584bbe88fd824f5f4f53da137c882"
  },
  {
   "t": "2026-01-04T23:02:48Z",
   "sha": "0fc9bddf447243a485aefc4beaa54ec418d5c5b1"
  },
  {
   "t": "2026-01-05T01:20:15Z",
   "sha": "1d7324850a5e560e005646bf2d927d610eb1450f"
  },
  {
   "t": "2026-01-07T21:14:44Z",
   "sha": "396998be7f25966ba96aefd86d1a482a50f5db7e"
  },
  {
   "t": "2026-01-08T03:56:09Z",
   "sha": "ad3942500bc4568add0a2c8d94ab84857e504db3"
  },
  {
   "t": "2026-01-19T14:44:57Z",
   "sha": "16661d66b4652cad9316c408362cddee5402583a"
  },
  {
   "t": "2026-01-19T16:54:50Z",
   "sha": "d710878d6d0934154c79d02e63c025329368dc29"
  },
  {
   "t": "2026-01-26T19:42:47Z",
   "sha": "e1886ab9b1abfa485bfce25b51f2dd9b566cb26a"
  },
  {
   "t": "2026-01-26T22:43:54Z",
   "sha": "cfdf666a61bd1bbdc103b832dd49269882bccc16"
  },
  {
   "t": "2026-01-26T23:04:59Z",
   "sha": "adaed8db08b82fb48fe7697bfeeeff441aa84319"
  },
  {
   "t": "2026-01-27T00:58:59Z",
   "sha": "cd70da43e87e0ab53e2d918979b6708cf2bbbbd3"
  },
  {
   "t": "2026-01-27T02:08:17Z",
   "sha": "8fa1ecf3b7dd8d68c8ef49541741d7ebe1a3e95b"
  },
  {
   "t": "2026-02-04T20:53:26Z",
   "sha": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2"
  },
  {
   "t": "2026-09-02T20:49:06Z",
   "sha": "b63adca402217f20e5c6471558c3e201f0a13237"
  },
  {
   "t": "2026-09-02T20:59:45Z",
   "sha": "9fec57b6962a3ab3b250cbcc8b7fa9aa224785ad"
  },
  {
   "t": "2026-09-02T21:05:47Z",
   "sha": "5c77af06234a7bf1a71ec2953ff266c56bde5d1a"
  }
 ],
 "additions": 187,
 "deletions": 106,
 "changed_files": 5,
 "commit_count": 8,
 "size_bucket": "M",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "stale_ack": [
     {
      "login": "ryanofsky",
      "url": "https://github.com/bitcoin/bitcoin/pull/34193#pullrequestreview-4566894731"
     }
    ]
   },
   "conflicts": [
    {
     "number": 35436,
     "title": "wallet: Add addHDkey interface",
     "author": "pseudoramdom"
    },
    {
     "number": 34909,
     "title": "wallet, refactor: modularise wallet by extracting out legacy wallet migration",
     "author": "rkrux"
    }
   ]
  }
 },
 "acks_parsed": {
  "ryanofsky": {
   "kind": "ack",
   "hash": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "t": "2026-06-25T01:59:40Z",
   "stale": true
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 1,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 1,
  "changes_requested": 0,
  "distinct_reviewers": [
   "achow101",
   "hebasto",
   "maflcko",
   "ryanofsky",
   "sedited"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-09-02T21:18:18Z",
  "last_reviewer_activity": "2026-08-27T11:03:59Z",
  "last_reviewer": "sedited",
  "author_silent_days": 14,
  "waiting_on_author_days": 0,
  "days_since_update": 3
 },
 "refs": {
  "mentioned": [
   34156,
   34176
  ],
  "depends_on": [
   34156
  ],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 34156,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-01-07",
    "title": "wallet: fix unnamed legacy wallet migration failure"
   },
   {
    "number": 34176,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-04-29",
    "title": "wallet: crash fix, handle non-writable db directories"
   }
  ],
  "conflicts": [
   35436,
   34909
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/util/fs_helpers.cpp",
  "src/wallet/wallet.cpp"
 ],
 "body": "This is just me finding a few more edge cases after #34156 and #34176\n\nThe goal of the PR is to handle failures in a controlled way. Just so the process can automatically restore the original wallet without requiring user manual intervention.\n\nThe covered cases are:\n\n1. During `DoMigration()`: There are methods that can throw exceptions and abruptly abort the process.\nInstead of crashing (GUI) or returning a generic exception, we now will catch and return the error gracefully. This lets the process restore the original wallet automatically.\n\n2. Trying to migrate a wallet in a read-only directory throws a filesystem exception and skips cleanup.\n\u2028Now the process will fail gracefully with a clear error msg, and automatically restore the original wallet.\n\n3. Any failure during `MigrateToSQLite` requires user manual intervention.\nNow the original wallet db will remain untouched, and only be updated once the sqlite db creation fully succeeds.",
 "commits": [
  {
   "sha": "172e49728e20e7228c49b1548852faada73670e8",
   "date": "2026-09-02T20:38:38Z",
   "message": "wallet: migration, unify watchonly and solvables wallets creation\n\nNo need to repeat the exact same code twice.\nThis will let us introduce new safety checks and improve error\nhandling for both wallets without the risk of forgetting to\nupdate one of them."
  },
  {
   "sha": "1bfdd9c67836ba6bde64a199a510e93438bddcb2",
   "date": "2026-09-02T20:38:39Z",
   "message": "wallet: migration, handle exceptions during wallet creation\n\nExceptions can be thrown internally by 'MakeWalletDatabase',\n'CWallet::Create' and 'AddWalletDescriptor'. Instead of\nabruptly interrupting the process, catch them and return\nerror gracefully.\n\nThis ensures migration can be cleaned up and the original\nwallet is restored from the backup if something goes wrong."
  },
  {
   "sha": "1b49ad0fc6a8b54408b0e74b71c8956a889a8408",
   "date": "2026-09-02T20:38:39Z",
   "message": "wallet: fail migration gracefully on non-writable wallets\n\nCurrently, attempting a wallet migration when the database\ndirectory or file is not writable results in a filesystem\nexception that abruptly aborts the process, skipping the\npost-failure cleanup logic and returning a not particularly\nhelpful error message."
  },
  {
   "sha": "a7570c9051e2f08ebbc4b3bc488272a98f0a6ec8",
   "date": "2026-09-02T20:38:39Z",
   "message": "test: add coverage for migrating a non-writable wallet"
  },
  {
   "sha": "eb5fef61260ee5c4f7edccd0dde11a82815a795e",
   "date": "2026-09-02T20:38:39Z",
   "message": "wallet: use RunWithinTxn within MigrateToSQlite\n\nUnifying db txn assertion failures into one single spot. This will\nbe useful in the next commit, when we return an error message\ninstead of crashing."
  },
  {
   "sha": "20a4aa4ad27459589543dc813e65e5b13837b0b8",
   "date": "2026-09-02T20:48:19Z",
   "message": "wallet: migration, make sqlite creation robust against failures\n\nCurrently, MigrateToSQLite loads all BDB records into memory,\nthen deletes the original database just to create the sqlite\ndb in the same place, and finally writes all cached records\nto it.\n\nThis is not really the best because it removes the original db\nbefore making sure the sqlite one is fully built. If creation\nfails, the wallet ends up partly in sqlite format with legacy\nrecords, and the user has to restore from a backup manually.\n\nThis fixes all that by creating the sqlite db in a temporary\ndirectory first. Then, the original BDB files are removed only\nonce the sqlite db is fully constructed, and the new db is\nmoved into the original location.\n\nThe result is that no user manual intervention is needed if\nMigrateToSQLite fails as the original db stays untouched."
  },
  {
   "sha": "2dc201e1f9164180a9b4e7e5c9e6cdc6451124d5",
   "date": "2026-09-02T21:02:58Z",
   "message": "test: migrate wallet located on a different filesystem"
  },
  {
   "sha": "5c77af06234a7bf1a71ec2953ff266c56bde5d1a",
   "date": "2026-09-02T21:05:34Z",
   "message": "wallet: safety-belt, catch exceptions during migration\n\nThis allows the process to restore the previous wallet\nfrom the backup instead of bubbling-up the exception."
  }
 ],
 "timeline": [
  {
   "t": "2026-01-02T22:50:16Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "3e59872437d0c7a94d6b4d9c6874c8beb4a82f01"
  },
  {
   "t": "2026-01-04T00:30:25Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "ac13657de4b584bbe88fd824f5f4f53da137c882"
  },
  {
   "t": "2026-01-04T23:02:48Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "0fc9bddf447243a485aefc4beaa54ec418d5c5b1"
  },
  {
   "t": "2026-01-05T01:20:15Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "1d7324850a5e560e005646bf2d927d610eb1450f"
  },
  {
   "t": "2026-01-05T10:16:53Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Looks like the CI fails for msvcrt, but not for ucrt:\n\n```\nRun ./bin/bench_bitcoin.exe -sanity-check\nRunning with -sanity-check option, output is being suppressed as benchmark results will be useless.\nError: filesystem error: cannot copy: File exists [C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\test_common bitcoin\\WalletMigration\\bb3603a0a694b3c4c79a\\regtest\\tmp_sqlite_13776532190043757581] [C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\test_common bitcoin\\WalletMigration\\bb3603a0a694b3c4c79a\\regtest]\n```\n\nhttps://github.com/bitcoin/bitcoin/actions/runs/20702313540/job/59427203126?pr=34193#step:9:200"
  },
  {
   "t": "2026-01-05T12:08:01Z",
   "kind": "comment",
   "who": "hebasto",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nSpecifically, the `WalletMigration` benchmark fails."
  },
  {
   "t": "2026-01-07T15:18:53Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/fs_helpers.cpp",
   "commit": "eb5e076b189aa5dadb9572f3ce7b33eb609d7b69",
   "in_reply_to": null,
   "text": "In commit \"wallet: fail migration gracefully on non-writable wallets\" (eb5e076b189aa5dadb9572f3ce7b33eb609d7b69)\n\nWas skimming this PR and the first few commits seem reasonable, but I\u2019d advise against the approach in the last two commits of checking whether paths are writable before writing, rather than handling write failures directly. A few concerns:\n\n1. It masks other problems. Write failures should surface through the normal error-reporting paths with clear messages, regardless of where they originate. Pre-checks bypass those paths and can obscure or regress error reporting, especially if lower-level errors improve over time.\n\n2. It adds unnecessary complexity. The write still has to be attempted, so the pre-check duplicates logic without simplifying the code.\n\n3. It introduces TOCTOU risks. A path that is writable at check time may not be writable at write time.\n\nIn general, attempting the write and handling failures directly is simpler, more robust, and leads to better error reporting."
  },
  {
   "t": "2026-01-07T20:42:50Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/fs_helpers.cpp",
   "commit": "eb5e076b189aa5dadb9572f3ce7b33eb609d7b69",
   "in_reply_to": 2668881126,
   "text": "The issue here is that we are doing low-level operations. Basically, we are:\n1) Loading all BDB records into memory.\n2) Deleting the BDB file.\n3) Creating a new sqlite db.\n4) Writing all records to the new db.\n\nThe added checks are meant to avoid failing with a generic filesystem exception at step (2), during the BDB deletion, which can be located in a different directory than the sqlite one we are about to create\n\nBut.. thinking further, we could handle this differently by catching the exception directly from the `fs::remove` call instead. Let me see why I didn't do it in that way before, there must be a reason.\n\nAlso, the last commit is about not removing the original wallet until we are sure the sqlite one has at least been created successfully. If we are not able to write to disk at that point, the migration will fail anyway. This function is just the early \"move everything from BDB to sqlite\" step; we still need to create and store the descriptors, remove the old records, and so on."
  },
  {
   "t": "2026-01-07T21:14:44Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "396998be7f25966ba96aefd86d1a482a50f5db7e"
  },
  {
   "t": "2026-01-08T03:56:09Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "ad3942500bc4568add0a2c8d94ab84857e504db3"
  },
  {
   "t": "2026-01-19T12:50:59Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Maybe turn into draft while the CI is red?"
  },
  {
   "t": "2026-01-19T14:44:57Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "16661d66b4652cad9316c408362cddee5402583a"
  },
  {
   "t": "2026-01-19T14:45:00Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nthanks for the ping, wasn't aware of the failure."
  },
  {
   "t": "2026-01-19T16:54:50Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "d710878d6d0934154c79d02e63c025329368dc29"
  },
  {
   "t": "2026-01-26T19:42:47Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "e1886ab9b1abfa485bfce25b51f2dd9b566cb26a"
  },
  {
   "t": "2026-01-26T21:21:10Z",
   "kind": "review_comment",
   "who": "achow101",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "e1886ab9b1abfa485bfce25b51f2dd9b566cb26a",
   "in_reply_to": null,
   "text": "In e1886ab9b1abfa485bfce25b51f2dd9b566cb26a \"wallet: migration, make sqlite creation robust against failures\"\n\nCould use a name that is a more informative if there's an issue with moving the wallet back, maybe `tmp_sqlite_<wallet name>_<timestamp>`"
  },
  {
   "t": "2026-01-26T21:24:34Z",
   "kind": "review_comment",
   "who": "achow101",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "e1886ab9b1abfa485bfce25b51f2dd9b566cb26a",
   "in_reply_to": null,
   "text": "In e1886ab9b1abfa485bfce25b51f2dd9b566cb26a \"wallet: migration, make sqlite creation robust against failures\"\n\nWhy throw instead of returning an error? `MigrateToSQLite` already bypasses failed migration cleanup."
  },
  {
   "t": "2026-01-26T22:43:54Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "cfdf666a61bd1bbdc103b832dd49269882bccc16"
  },
  {
   "t": "2026-01-26T22:44:04Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "e1886ab9b1abfa485bfce25b51f2dd9b566cb26a",
   "in_reply_to": 2729297888,
   "text": "Sure. Done as suggested."
  },
  {
   "t": "2026-01-26T22:46:42Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "e1886ab9b1abfa485bfce25b51f2dd9b566cb26a",
   "in_reply_to": 2729311871,
   "text": "I coded it with half of my brain. Done. Changed it to return an error instead."
  },
  {
   "t": "2026-01-26T23:04:59Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "adaed8db08b82fb48fe7697bfeeeff441aa84319"
  },
  {
   "t": "2026-01-27T00:58:59Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "cd70da43e87e0ab53e2d918979b6708cf2bbbbd3"
  },
  {
   "t": "2026-01-27T02:08:17Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "8fa1ecf3b7dd8d68c8ef49541741d7ebe1a3e95b"
  },
  {
   "t": "2026-02-04T20:53:26Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2"
  },
  {
   "t": "2026-06-25T00:12:44Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "09a09e48b53dcf7c10f9f4b89a7d3697589751e1",
   "in_reply_to": null,
   "text": "In commit \"wallet: fail migration gracefully on non-writable wallets\" (09a09e48b53dcf7c10f9f4b89a7d3697589751e1)\n\nWould be good to include more details in the message like `strprintf(_(\"Error: Wallet db cannot be updated. Path: %s, Error: %s.%s\"), db_path, err_db.message(), err_help);`. Error messages saying something failed without saying why it failed can be frustrating to deal with."
  },
  {
   "t": "2026-06-25T00:55:14Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\nThis function returns util::Result but contains a lot of filesystem calls that can throw exceptions (directory_iterator constructor, fs::exists, fs::rename). It could be good to call non throwing version of these function and return error messages if these calls fail, or wrap the function body in a try/catch. Or keep current code but document that this can throw in addition to return a Result error so callers know to handle this."
  },
  {
   "t": "2026-06-25T00:58:04Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\nfs::exists will follow symlinks which is probably not what we want here. Also this can return false if target is a broken symlink. Could use `if (fs::is_symlink(target) || fs::exists(target))` or similar to prevent these things"
  },
  {
   "t": "2026-06-25T00:59:21Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\ns/exists as a backup/is untouched/ here as well"
  },
  {
   "t": "2026-06-25T01:05:52Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\nNow that original database is kept in place at this point, instead of being deleted, it would seem nice to replace these asserts with simple failures. Previously it (sort of) made sense to crash the process at this point since the database was in half-written incomplete state. But now the original database is unchanged, and there isn't actually a critical error anymore."
  },
  {
   "t": "2026-06-25T01:09:06Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\nCould delete tmp_wallet_path here"
  },
  {
   "t": "2026-06-25T01:12:09Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\nCould delete tmp_wallet_path here. Also not sure if of the code populating this database will throw, but it would also seem good to delete it in that case too."
  },
  {
   "t": "2026-06-25T01:53:30Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "5c77af06234a7bf1a71ec2953ff266c56bde5d1a",
   "in_reply_to": null,
   "text": "In commit \"wallet: migration, make sqlite creation robust against failures\" (406c14db5b16c2e1befdef76d6debd0b0ca4f6c2)\n\nIf after anything after this fs::remove fails (like the renames below or the removes or the MakeDatabase call) or throws, then the wallet directory will be an a bad state and the user will have to manually restore from a backup. This was also the case with the previous fs::remove call, but that code has had more testing while this code is new. So it might be more likely to have problems. Might be possible to address this by having MigrateLegacyToDescriptor call RestoreWallet in this case."
  },
  {
   "t": "2026-06-25T01:59:40Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "text": "Code review ACK 406c14db5b16c2e1befdef76d6debd0b0ca4f6c2. I think this looks good, and all the changes here seem safe, but given complexity of the code would want to have another pass of review to have more confidence there are no bugs.\n\nOn the approach: first 4 commits look great and straightforwardly improve error handling. Initial concern i had earlier about trying to predict whether paths were writable is resolved and well handled.\n\nThe last commit should also be a net improvement, but is more of a mixed bag. The last commit provides a potentially significant benefit because it delays deleting the original wallet database until the new sqlite database is fully created, so if there are errors creating it, the old wallet will be left in place, and it it can be migrated again later (or dumped or opened in an old version of bitcoin core) without the user manually needing to restore from the backup file.\n\nThe downside of the last commit is it adds more complexity to code that is already pretty complex, and leaves filesystem in a slightly messier state because if creating and populating the sqlite database fails there will now be two copies of the original bdb database instead of one.\n\nMore ideally the code would get rid of all these deletes and renames and backups and restores and just leave the original data exactly where it is during the migration, writing the new data to temporary directory. Then do simple renames if the migration succeeds, renaming the old wallet to have a migrated_<timestamp> suffix and renaming the new wallet directories in its place."
  },
  {
   "t": "2026-08-27T11:03:59Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@furszy can you rebase and address @ryanofsky's comments?"
  },
  {
   "t": "2026-09-02T20:49:06Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "b63adca402217f20e5c6471558c3e201f0a13237"
  },
  {
   "t": "2026-09-02T20:59:45Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "9fec57b6962a3ab3b250cbcc8b7fa9aa224785ad"
  },
  {
   "t": "2026-09-02T21:05:47Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "5c77af06234a7bf1a71ec2953ff266c56bde5d1a"
  },
  {
   "t": "2026-09-02T21:13:08Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Updated per feedback. Thanks @ryanofsky! sorry for the delay.\n\n[quoted text omitted]\nI had a branch for this ~2.5 years ago (it should still be in my repo fork), creating a new wallet and importing descriptors, txs, metadata there, etc. which was letting us remove `MigrateToSQLite` entirely, but I wasn't convinced it was ever going to be merged due to all the bugs we were continually finding within the process back then, and some other refactoring that was needed. We are much better now to retry to implement it but I hardly see such a big change landing at this point in time."
  },
  {
   "t": "2026-09-02T21:13:29Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "09a09e48b53dcf7c10f9f4b89a7d3697589751e1",
   "in_reply_to": 3471073269,
   "text": "done as suggested. Thanks!"
  },
  {
   "t": "2026-09-02T21:16:47Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": 3471206152,
   "text": "Ended up dropping the function entirely. We don't need such machinery. The db is just a single .dat sqlite file and can be moved in isolation. I probably was way too worried about the wallets/ deletion bug back then and overthought this."
  },
  {
   "t": "2026-09-02T21:17:42Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": 3471236148,
   "text": "Sure, done as suggested. Thanks!"
  },
  {
   "t": "2026-09-02T21:17:52Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "406c14db5b16c2e1befdef76d6debd0b0ca4f6c2",
   "in_reply_to": 3471252807,
   "text": "Sure, done as suggested. Thanks!"
  },
  {
   "t": "2026-09-02T21:18:18Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "5c77af06234a7bf1a71ec2953ff266c56bde5d1a",
   "in_reply_to": 3471389845,
   "text": "All cases should be covered now, thanks!"
  }
 ],
 "labels_log": [
  {
   "t": "2026-01-02T20:59:43Z",
   "action": "labeled",
   "label": "Wallet",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-02T22:50:59Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-07T12:47:22Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-07T21:47:46Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-19T19:03:05Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-26T17:57:06Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-26T20:05:09Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-26T20:39:06Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-26T21:58:58Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-26T23:05:32Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-27T03:04:04Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-04T20:23:10Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-04T22:46:05Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-28T20:16:29Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-02T20:59:38Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-02T21:00:58Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-02T22:14:29Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-14T13:38:31Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-01-02T20:59:55Z",
   "kind": "closed",
   "who": "furszy"
  },
  {
   "t": "2026-01-02T21:28:46Z",
   "kind": "renamed",
   "who": "furszy",
   "from": "wallet: make migration more robust",
   "to": "wallet: make migration more robust against failures"
  },
  {
   "t": "2026-01-02T22:49:36Z",
   "kind": "reopened",
   "who": "furszy"
  }
 ],
 "text_chars": 12232,
 "text_tokens_estimate": 3058,
 "changed_paths": [],
 "files": [],
 "test_lines": null,
 "git": null,
 "input_hash": "7d61f282b76b6128",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}