#26606 introduced BerkeleyRODatabase which is an independent parser for BDB files. This PR uses this in legacy wallet migration so that migration will continue to work once the legacy wallet and BDB are removed. LegacyDataSPKM is introduced to have the minimum data and functions necessary for a legacy wallet to be loaded for migration.
wallet: Migrate legacy wallets to descriptor wallets without requiring BDB #26596
pull achow101 wants to merge 6 commits into bitcoin:master from achow101:indep-desc-migrate2 changing 6 files +221 −125-
achow101 commented at 11:46 PM on November 28, 2022: member
-
DrahtBot commented at 11:46 PM on November 28, 2022: contributor
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--006a51241073e994b41acfe9ec718e94-->
Code Coverage
For detailed information about the code coverage, see the test coverage report.
<!--021abf342d371248e50ceaed478a90ca-->
Reviews
See the guideline for information on the review process.
Type Reviewers ACK cbergqvist, theStack, furszy If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
<!--174a7506f384e20aa4161008e828411d-->
Conflicts
Reviewers, this pull request conflicts with the following ones:
- #29124 (wallet: Automatically repair corrupted metadata with doubled derivation path by achow101)
- #28574 (wallet: optimize migration process, batch db transactions by furszy)
- #28333 (wallet: Construct ScriptPubKeyMans with all data rather than loaded progressively by achow101)
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
- DrahtBot added the label Wallet on Nov 28, 2022
- achow101 force-pushed on Nov 28, 2022
- achow101 force-pushed on Nov 29, 2022
-
Sjors commented at 2:41 PM on November 29, 2022: member
I think it would be easier to review if you made a separate PR for
BerkeleyRODatabaseand have it work as a regular, albeit read-only, wallet database backend.I'm pleasantly surprised at how small the implementation is.
- achow101 force-pushed on Nov 29, 2022
-
luke-jr commented at 9:20 PM on November 29, 2022: member
I don't see how reimplementing BDB is better than just continuing to use BDB.
- achow101 force-pushed on Nov 29, 2022
-
achow101 commented at 9:27 PM on November 29, 2022: member
I don't see how reimplementing BDB is better than just continuing to use BDB.
The reimplementation is quite compact because it is for only reading BDB files, only the features that we use, and none of the actual DB system stuff. BDB itself contains a ton of code, a lot of which we don't use. Maintaining it as a dependency will eventually be more burdensome - we already need to patch it in order to compile in the depends system. This reimplementation does not carry those issues - it implements a format that isn't going to change, and it only depends on things that are already being used elsewhere in the codebase.
- achow101 force-pushed on Nov 30, 2022
- achow101 force-pushed on Nov 30, 2022
- achow101 force-pushed on Nov 30, 2022
- DrahtBot added the label Needs rebase on Dec 1, 2022
- achow101 force-pushed on Dec 1, 2022
- DrahtBot removed the label Needs rebase on Dec 1, 2022
- achow101 force-pushed on Dec 2, 2022
- achow101 force-pushed on Dec 2, 2022
- DrahtBot added the label Needs rebase on Feb 1, 2023
- achow101 force-pushed on Feb 1, 2023
- achow101 force-pushed on Feb 2, 2023
- DrahtBot removed the label Needs rebase on Feb 2, 2023
- DrahtBot added the label Needs rebase on Feb 17, 2023
- achow101 force-pushed on Feb 17, 2023
- DrahtBot removed the label Needs rebase on Feb 17, 2023
- DrahtBot added the label Needs rebase on Feb 22, 2023
- achow101 force-pushed on Mar 1, 2023
- DrahtBot removed the label Needs rebase on Mar 1, 2023
- DrahtBot added the label CI failed on May 2, 2023
- achow101 force-pushed on May 2, 2023
- achow101 force-pushed on May 2, 2023
- DrahtBot removed the label CI failed on May 2, 2023
- achow101 force-pushed on May 27, 2023
- DrahtBot added the label CI failed on Jun 7, 2023
- achow101 force-pushed on Jun 8, 2023
- DrahtBot removed the label CI failed on Jun 8, 2023
- DrahtBot added the label Needs rebase on Jun 28, 2023
- achow101 force-pushed on Jun 28, 2023
- DrahtBot removed the label Needs rebase on Jun 28, 2023
- DrahtBot added the label CI failed on Jun 29, 2023
- achow101 force-pushed on Jun 29, 2023
- DrahtBot removed the label CI failed on Jul 6, 2023
- DrahtBot added the label CI failed on Jul 23, 2023
- DrahtBot added the label Needs rebase on Aug 1, 2023
- achow101 force-pushed on Aug 1, 2023
- DrahtBot removed the label Needs rebase on Aug 1, 2023
- achow101 force-pushed on Sep 15, 2023
- DrahtBot added the label Needs rebase on Sep 19, 2023
- achow101 force-pushed on Sep 27, 2023
- DrahtBot removed the label Needs rebase on Sep 27, 2023
- achow101 force-pushed on Sep 27, 2023
- DrahtBot removed the label CI failed on Sep 28, 2023
- DrahtBot added the label Needs rebase on Oct 23, 2023
- achow101 force-pushed on Oct 23, 2023
- DrahtBot removed the label Needs rebase on Oct 24, 2023
- DrahtBot added the label CI failed on Oct 24, 2023
- achow101 force-pushed on Oct 24, 2023
- achow101 force-pushed on Nov 28, 2023
- DrahtBot removed the label CI failed on Nov 28, 2023
- DrahtBot added the label Needs rebase on Dec 14, 2023
- achow101 force-pushed on Dec 19, 2023
- DrahtBot removed the label Needs rebase on Dec 19, 2023
- DrahtBot added the label Needs rebase on Jan 5, 2024
- achow101 force-pushed on Jan 5, 2024
- DrahtBot removed the label Needs rebase on Jan 6, 2024
- DrahtBot added the label CI failed on Jan 13, 2024
- DrahtBot added the label Needs rebase on Jan 31, 2024
- achow101 force-pushed on Feb 1, 2024
- achow101 force-pushed on Feb 1, 2024
- DrahtBot removed the label Needs rebase on Feb 1, 2024
- achow101 force-pushed on Feb 1, 2024
- achow101 force-pushed on Feb 1, 2024
- DrahtBot removed the label CI failed on Feb 2, 2024
- DrahtBot added the label Needs rebase on Feb 3, 2024
- achow101 force-pushed on Feb 3, 2024
- DrahtBot removed the label Needs rebase on Feb 3, 2024
- DrahtBot added the label Needs rebase on Feb 6, 2024
- achow101 force-pushed on Feb 8, 2024
- DrahtBot removed the label Needs rebase on Feb 8, 2024
- DrahtBot added the label CI failed on Feb 18, 2024
- achow101 force-pushed on Feb 20, 2024
- DrahtBot removed the label CI failed on Feb 20, 2024
- DrahtBot added the label CI failed on Feb 28, 2024
-
DrahtBot commented at 6:51 AM on February 28, 2024: contributor
<!--85328a0da195eb286784d51f73fa0af9-->
🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.
Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
Leave a comment here, if you need help tracking down a confusing failure.
<sub>Debug: https://github.com/bitcoin/bitcoin/runs/21780750809</sub>
- achow101 force-pushed on Feb 29, 2024
- achow101 force-pushed on Mar 11, 2024
- achow101 force-pushed on Apr 1, 2024
- DrahtBot removed the label CI failed on Apr 5, 2024
- achow101 force-pushed on Apr 25, 2024
- achow101 force-pushed on Apr 25, 2024
- achow101 force-pushed on Apr 26, 2024
- fanquake referenced this in commit 5acdc2b97d on May 21, 2024
- DrahtBot added the label Needs rebase on May 21, 2024
- achow101 force-pushed on May 21, 2024
- achow101 marked this as ready for review on May 21, 2024
- DrahtBot removed the label Needs rebase on May 21, 2024
-
in src/wallet/wallet.cpp:4484 in 37df50fffa outdated
4464 | @@ -4438,6 +4465,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle 4465 | // both before and after reloading. This ensures the set is complete even if one of the wallets 4466 | // fails to reload. 4467 | std::set<fs::path> wallet_dirs; 4468 | + options.require_format = std::nullopt;
cbergqvist commented at 7:22 PM on May 30, 2024:Why reset
require_formathere? Seemsoptionsis not going to be read by anything after this line and none of the database types is holding a reference to it.Avoiding that would mean that you could introduce more immutability around line 4386:
const DatabaseOptions options{ .require_existing = true, .require_format = DatabaseFormat::BERKELEY_RO, };
achow101 commented at 12:51 AM on June 6, 2024:It's used by the
reload_walletcalls which capture it.
cbergqvist commented at 9:32 AM on June 6, 2024:Thanks, sorry for missing that. Worth an explaining comment like
"Reset require_format since reload_wallet will only be called on non-Berkeley DB sub-wallets from here on."or something more correct?
achow101 commented at 4:12 PM on June 6, 2024:Added a comment.
in src/wallet/wallet.cpp:3659 in 20c8fd5dfc outdated
3646 | + // Only create base LegacyDataSPKM if using BERKELEY_RO 3647 | + if (m_database->Format() == "bdb_ro") { 3648 | + spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyDataSPKM(*this)); 3649 | + } else { 3650 | + spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this, m_keypool_size)); 3651 | + }
cbergqvist commented at 8:42 PM on May 30, 2024:// Only create base LegacyDataSPKM if using BERKELEY_RO std::unique_ptr<ScriptPubKeyMan> spk_manager = m_database->Format() == "bdb_ro" ? std::make_unique<LegacyDataSPKM>(*this) : std::make_unique<LegacyScriptPubKeyMan>(*this, m_keypool_size);
achow101 commented at 1:09 AM on June 6, 2024:Done as suggested
in src/wallet/wallet.cpp:4379 in 37df50fffa outdated
4374 | + 4375 | + if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { 4376 | + return util::Error{_("Error: This wallet is already a descriptor wallet")}; 4377 | + } 4378 | + // Unload 4379 | + wallet.reset();
furszy commented at 2:59 PM on June 3, 2024:Loading the entire wallet just to obtain the descriptors flag shouldn't be needed. Could just check the file type instead. We want a bdb file here. So, something like https://github.com/furszy/bitcoin-core/commit/7a03a039ae99e69504fe9df95b04f5e326c45a70 would work well. Yet, the code isn't the best but it includes a test that verifies the behavior.
achow101 commented at 11:34 PM on June 4, 2024:Good point, I think the original intention was to be able to handle the legacy-sqlite case, but that's moot since we require BDB_RO later. I've changed this to check for just look for BDB files, and also added your test.
achow101 force-pushed on Jun 4, 2024in src/wallet/scriptpubkeyman.h:281 in e11fa45247 outdated
275 | @@ -276,31 +276,106 @@ static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES { 276 | 277 | class DescriptorScriptPubKeyMan; 278 | 279 | -class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider 280 | +class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider
cbergqvist commented at 7:04 PM on June 5, 2024:This class and possibly
LegacyScriptPubKeyManbelow could do with comments summarizing their responsibilities since breaking them apart and naming oneLegacyDataSPKMmakes things less clear.
achow101 commented at 1:09 AM on June 6, 2024:Add a comment to each
in src/wallet/scriptpubkeyman.h:386 in e11fa45247 outdated
386 | - bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey); 387 | - bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); 388 | + bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) override; 389 | 390 | /** 391 | * Private version of AddWatchOnly method which does not accept a
cbergqvist commented at 8:04 PM on June 5, 2024:This comment goes on to say
AddWatchOnly(const CScript&)is an inherited virtual method which appears untrue. Maybe add an extra commit ahead of the others that tidies up that comment?
achow101 commented at 12:52 AM on June 6, 2024:I don't think it's necessary to add changes that correct this documentation when it's all going to be deleted soon anyways.
in src/wallet/walletdb.cpp:359 in e11fa45247 outdated
353 | @@ -354,7 +354,7 @@ bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::stri 354 | strErr = "Error reading wallet database: CPrivKey corrupt"; 355 | return false; 356 | } 357 | - if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey)) 358 | + if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadKey(key, vchPubKey)) 359 | { 360 | strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
cbergqvist commented at 8:10 PM on June 5, 2024:*
"Error reading wallet database: LegacyDataSPKM::LoadKey failed", same forLoadCryptedKeyandLoadCScriptbelow.
achow101 commented at 1:09 AM on June 6, 2024:Done as suggested
in src/wallet/wallet.h:967 in e11fa45247 outdated
960 | @@ -961,6 +961,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati 961 | //! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external. 962 | LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const; 963 | LegacyScriptPubKeyMan* GetOrCreateLegacyScriptPubKeyMan(); 964 | + LegacyDataSPKM* GetLegacyDataSPKM() const; 965 | + LegacyDataSPKM* GetOrCreateLegacyDataSPKM(); 966 | 967 | //! Make a LegacyScriptPubKeyMan and set it for all types, internal, and external.
cbergqvist commented at 8:25 PM on June 5, 2024:(
"Make a *LegacyDataSPKM and set it...". Similar need for update in wallet.cpp around line 3019 before call toSetupLegacyScriptPubKeyMan().)
achow101 commented at 12:53 AM on June 6, 2024:The function still creates a
LegacyScriptPubKeyManin normal operation.
cbergqvist commented at 9:40 AM on June 6, 2024:LegacyScriptPubKeyManis aLegacyDataSPKM, so the comment would be more correct, but it could be corrected later when removingLegacyScriptPubKeyMan- unless you plan to renameLegacyDataSPKMback intoLegacyScriptPubKeyManonce the deletion of the latter is complete.
achow101 commented at 3:57 PM on June 6, 2024:I think the meaning is well understood, both before and after
LegacyScriptPubKeyManis deleted.SPKMis an abbreviation forScriptPubKeyMananyways.Changed it to
Legacy(Data)SPKM.in src/wallet/wallet.cpp:2926 in e11fa45247 outdated
2922 | @@ -2923,7 +2923,7 @@ bool CWallet::EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestinatio 2923 | return true; 2924 | } 2925 | 2926 | -std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string) 2927 | +util::Result<fs::path> GetWalletPath(const std::string& name)
cbergqvist commented at 8:42 PM on June 5, 2024:Could be marked
static?
achow101 commented at 1:09 AM on June 6, 2024:Done
in src/wallet/scriptpubkeyman.cpp:1869 in 46520aacc4 outdated
1885 | @@ -1887,7 +1886,6 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor() 1886 | for (const CScript& spk : desc_spks) { 1887 | size_t erased = spks.erase(spk); 1888 | assert(erased == 1); 1889 | - assert(IsMine(spk) == ISMINE_SPENDABLE);
cbergqvist commented at 9:07 PM on June 5, 2024:In commit 46520aacc43dd42a7968587ab9ece488bbe9503d: Should this line have been removed in the following commit (31e918afc3920a2f8aa367ad323f590b4856aa26)?
achow101 commented at 12:55 AM on June 6, 2024:No.
LegacyDataSPKMdoes not have anIsMine()function, so this call needs to be removed otherwise it will not compile.
furszy commented at 9:21 PM on June 11, 2024:No.
LegacyDataSPKMdoes not have anIsMine()function, so this call needs to be removed otherwise it will not compile.I think the outcome would be worst. It would compile using the base
ScriptPubKeyMan::IsMinefunction. ReturningISMINE_NOfor allIsMinecalls.Still, I think we should state why this assertion removal is ok in the commit description too.
in src/wallet/scriptpubkeyman.h:455 in e11fa45247 outdated
499 | @@ -500,7 +500,6 @@ class LegacyScriptPubKeyMan : public LegacyDataSPKM 500 | 501 | /* Set the HD chain model (chain child index counters) and writes it to the database */ 502 | void AddHDChain(const CHDChain& chain); 503 | - const CHDChain& GetHDChain() const { return m_hd_chain; }
cbergqvist commented at 9:28 PM on June 5, 2024:This was removed in e11fa4524723aebc0d879ee083c2d6d46849d89a but probably shouldn't have been included in 84a5fb96382960300ebbd2cf2fe8756142bea572 (verified through compiling on that commit + with it removed).
achow101 commented at 1:09 AM on June 6, 2024:Done
in src/wallet/scriptpubkeyman.cpp:1956 in e11fa45247 outdated
1952 | @@ -1812,7 +1953,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor() 1953 | WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); 1954 | 1955 | // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys 1956 | - auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size)); 1957 | + auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, /*keypool_size=*/0));
cbergqvist commented at 10:04 PM on June 5, 2024:Worth adding a comment about the consequences of setting
keypool_size=0? I'm guessing there are still cases where we generate some pubkeys despite having a zero pool size, otherwise you would have removed some of the code and the for-loop below?
cbergqvist commented at 10:13 PM on June 5, 2024:nit: Might as well use
make_uniqueon the lines you modify:auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
achow101 commented at 12:58 AM on June 6, 2024:I don't think this needs to have any additional explanation.
keypool_sizeonly matters for ranged descriptors. Non-HD keys do not make ranged descriptors, so the value here is ignored. The loop below is for an entirely different set of descriptors and is unrelated to this.
achow101 commented at 1:09 AM on June 6, 2024:Done as suggested
cbergqvist commented at 10:59 AM on June 6, 2024:Haven't fully grokked the code yet, so I understand if you'd rather not answer my questions, but if you want:
I don't think this needs to have any additional explanation. keypool_size only matters for ranged descriptors. Non-HD keys do not make ranged descriptors, so the value here is ignored.
We're in an HD chain
for-loop, how is this not involving HD keys? Surely legacy wallets could be HD wallets.The loop below is for an entirely different set of descriptors and is unrelated to this.
I'm referring to the loop over
desc_spkswhich is using the pubkeys from theDescriptorScriptPubKeyManthat's just been created and comes fromDescriptorScriptPubKeyMan::m_map_script_pub_keyswhich is filled out inDescriptorScriptPubKeyMan::TopUpWithDB()which is affected bym_keypool_size.Would
m_keypool_sizealready always be0before this change?
achow101 commented at 4:07 PM on June 6, 2024:The line this comment is on is in the one that goes over the non-HD keys. However, I see that you're confused about the similar line in the loop below that iterates the hd chains.
The
keypool_sizehere is a runtime value that depends on the startup options. It's is set when theDescriptorScriptPubKeymanis instantiated on loading, and the value is never directly persisted to disk. It ends up written as part ofrange_endwhenrange_endis updated duringTopUp(), butrange_endcan also be set directly.The value that does matter is the
range_endstored inWalletDescriptor. When we are going over the hd chains, we setrange_endto match the chain counter stored in theCHDChainthat is being migrated. This results in a descriptor that will produce exactly the same scriptPubKeys as the original hd chain, including it's keypool. We are enforcing that the migrated descriptors produce exactly the same scriptPubKeys (no more, no less). Settingkeypool_sizecould mean thatTopUp()would generate more scriptPubKeys than we need/expect, which would cause migration to assert and crash.
cbergqvist commented at 6:57 PM on June 6, 2024:Thanks for taking the time to clarify this matter!
in src/wallet/walletdb.cpp:766 in e11fa45247 outdated
762 | @@ -763,7 +763,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, 763 | 764 | // nTimeFirstKey is only reliable if all keys have metadata 765 | if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) { 766 | - auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan(); 767 | + auto spk_man = pwallet->GetLegacyScriptPubKeyMan();
cbergqvist commented at 10:25 PM on June 5, 2024:Why not
OrCreatehere any longer?
achow101 commented at 1:07 AM on June 6, 2024:It's redundant since
IsLegacy()ensures the existence of aLegacyScriptPubKeyMan, so there's no need to try to create one.cbergqvist commented at 10:36 PM on June 5, 2024: contributorCode reviewed e11fa4524723aebc0d879ee083c2d6d46849d89a.
Overall concept: Not fully convinced about the benefit of breaking out
LegacyDataSPKM, seems to add complexity. Is the goal to deleteLegacyScriptPubKeyManin the future when ripping out BDB and only keepLegacyDataSPKM?Why perform the inlining of
IsMine()in 31e918afc3920a2f8aa367ad323f590b4856aa26 before the function is removed? Seems risky in case of other PRs modifying it prior to removal etc.LegacyScriptPubKeyMan::NewKeyPool()only touches data fromLegacyDataSPKM, should it be moved to that class as public (or protected and then exposed in the subclass?). (IfLegacyScriptPubKeyManis slated for removal I can understand you want to signal that method will no longer be needed). (Have not checked if there are other methods along the same lines).Assuming
LegacyDataSPKMname is kept, comment insrc/script/signingprovider.hcould be updated to refer toLegacyDataSPKM::setWatchOnlyinstead.achow101 commented at 12:36 AM on June 6, 2024: memberIs the goal to delete
LegacyScriptPubKeyManin the future when ripping out BDB and only keepLegacyDataSPKM?Yes.
We are trying to remove the legacy wallet in its entirety, leaving only behind the absolute minimum necessary to perform migration.
Why perform the inlining of
IsMine()in 31e918a before the function is removed? Seems risky in case of other PRs modifying it prior to removal etc.IsMine()is unlikely to change. Besides the fact that any changes to the legacy wallet are likely to be closed instead of seriously considered,IsMine()has not had any material changes in the last several years. It could be included in #28710 but that PR is big enough as it is.LegacyScriptPubKeyMan::NewKeyPool()only touches data fromLegacyDataSPKM, should it be moved to that class as public (or protected and then exposed in the subclass?)No. The separation exists now to indicate which things can be removed at the end.
LegacyDataSPKMis the bare minimum that must stick around for migration, everything else remains inLegacyScriptPubKeyManand will be removed at the same time.achow101 force-pushed on Jun 6, 2024in src/wallet/scriptpubkeyman.h:372 in 31632d18ea outdated
373 | + /** Delete all the records ofthis LegacyScriptPubKeyMan from disk*/ 374 | + bool DeleteRecords(); 375 | +}; 376 | + 377 | +// Implements the full legacy wallet behavior 378 | +class LegacyScriptPubKeyMan : public LegacyDataSPKM
cbergqvist commented at 9:48 AM on June 6, 2024:// Implements the full legacy wallet behavior // Slated for deletion once Berkeley DB library dependency is removed. class LegacyScriptPubKeyMan : public LegacyDataSPKM
achow101 commented at 4:13 PM on June 6, 2024:I don't think it's necessary to have a comment saying such.
cbergqvist commented at 6:48 PM on June 6, 2024:If the code is unable to communicate why it is like it is, it's good to explain the otherwise surprising state of it. It would hopefully limit the amount of questions like my prior one from #26596#pullrequestreview-2089208116:
LegacyScriptPubKeyMan::NewKeyPool()only touches data fromLegacyDataSPKM, should it be moved to that class as public (or protected and then exposed in the subclass?).But I guess you're fairly confident it will be deleted in time for the next release anyway.
achow101 force-pushed on Jun 6, 2024cbergqvist approvedcbergqvist commented at 7:24 PM on June 6, 2024: contributorACK a8f8a2d5d0e2328cbe2e569c7e12f653d18f456d
(Ran tests on 31632d18ea122def82820770f14d9aa009726114 but only comments changed since then). Functional tests including
--extendedpassed (a few automatically skipped).make checkpassed.in src/wallet/wallet.cpp:4394 in f732495191 outdated
4370 | + if (!wallet_path) { 4371 | + return util::Error{util::ErrorString(wallet_path)}; 4372 | + } 4373 | + if (!IsBDBFile(BDBDataFile(*wallet_path))) { 4374 | + return util::Error{_("Error: This wallet is already a descriptor wallet")}; 4375 | + }
theStack commented at 12:31 PM on June 7, 2024:in commit f732495191d346b877ffe8402ec68761713eaaf7: now that the legacy-wallet-check is done without loading the wallet, is the check for the descriptor flag below still needed? (can a BDB wallet ever have this flag set?)
// Before anything else, check if there is something to migrate. if (local_wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { if (was_loaded) { reload_wallet(local_wallet); } return util::Error{_("Error: This wallet is already a descriptor wallet")}; }
achow101 commented at 4:24 PM on June 7, 2024:It is theoretically possible to have a descriptor wallet using BDB under 2 scenarios, however this is still an unsupported configuration:
theStack commented at 12:35 PM on June 7, 2024: contributorConcept ACK
in src/wallet/scriptpubkeyman.cpp:1833 in c653f4fdbf outdated
1832 | @@ -1833,7 +1833,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor() 1833 | WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); 1834 | 1835 | // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys 1836 | - auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size)); 1837 | + auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
furszy commented at 1:46 PM on June 18, 2024:Not really an issue but this works because
TopUpsets the descriptor range_end to 1 on the first run when the descriptor is not ranged. As this would fail if we ever change that, what if we set the range properly inWalletDescriptorconstructor? E.g.WalletDescriptor w_desc(std::move(desc), creation_time, /*range_start=*/0, /*range_end=*/1, /*next_index=*/0)Also, this change doesn't seem to be related to c653f4fdbfe06 description?--> nvm about this last point, theStack just made me notice that this is becausem_keypool_sizeis not accesible fromLegacyDataSPKM.furszy commented at 10:26 PM on June 18, 2024: memberJust a small comment; you don't have to take it.
I believe this PR could get merged quite fast if you leave 89503710386f52d9162f67fcd707eceffa954faa for a follow-up and make
IsMinecompatible withLegacyDataSPKMin this PR.I'm suggesting this because this PR starts testing the newly introduced BDB reader class and allows people to use it, which is great since we want it polished for the next release. The
IsMineremoval is also nice but I wouldn't miss a deadline for it.theStack commented at 3:38 PM on June 21, 2024: contributorStill trying to fully grasp the
IsMineinlining commit 89503710386f52d9162f67fcd707eceffa954faa, meanwhile a few comments:- The PR description is outdated, as it still mentions adding
BerkeleyRODatabasewhich was already merged in #26606 - in c653f4fdbfe0607ad9be27e80ad22c9aee314bb7: typo in commit subject (s/MigrateToLegacy/MigrateToDescriptor/)
- To my understanding, the class
LegacyDataSPKMis supposed to never write to the database (asBerkeleyRODatabaseobviously doesn't support that), butLegacyDataSPKM::CheckDecryptionKeycontains a code-path for that (for a necessary rewrite): https://github.com/achow101/bitcoin/blob/a8f8a2d5d0e2328cbe2e569c7e12f653d18f456d/src/wallet/scriptpubkeyman.cpp#L254-L255 ShouldCheckDecryptionKeystay inLegacyScriptPubKeyMan?
furszy commented at 9:27 PM on June 21, 2024: member- To my understanding, the class
LegacyDataSPKMis supposed to never write to the database (asBerkeleyRODatabaseobviously doesn't support that), butLegacyDataSPKM::CheckDecryptionKeycontains a code-path for that (for a necessary rewrite): https://github.com/achow101/bitcoin/blob/a8f8a2d5d0e2328cbe2e569c7e12f653d18f456d/src/wallet/scriptpubkeyman.cpp#L254-L255 ShouldCheckDecryptionKeystay inLegacyScriptPubKeyMan?
It isn't really an issue because
CheckDecryptionKeydoes not verify the outcome of the write call (which would be "successful" because the read-only bdb class return true for all write operations). But.. for the sake of correctness I see two possible paths to follow:As this write is done exclusively on wallets with no checksum on crypted keys, we could move the upgrade code to the loading code in
walletdb.cpp; removingfDecryptionThoroughlyCheckedentirely and requiring such ancient wallets to provide the passphrase during load to perform the upgrade (adding a "passphrase" arg to theloadwalletRPC command).Introduce a new
WalletBatch::IsReadOnly()method to avoid calling the write insideCheckDecryptionKey.
The first approach will make the legacy wallet code simpler while the second one is a minimal change.
achow101 force-pushed on Jun 24, 2024achow101 force-pushed on Jun 24, 2024achow101 commented at 4:47 PM on June 24, 2024: memberI believe this PR could get merged quite fast if you leave 8950371 for a follow-up and make
IsMinecompatible withLegacyDataSPKMin this PR.Moved to #30328
The
IsMineremoval is also nice but I wouldn't miss a deadline for it.I think it needs to be in the next release as it is a critical part of migration. I would prefer to have it in a release that people can use before deleting the legacy wallet.
- The PR description is outdated, as it still mentions adding
BerkeleyRODatabasewhich was already merged in wallet: Implement independent BDB parser [#26606](/bitcoin-bitcoin/26606/)
Updated
- in c653f4f: typo in commit subject (s/MigrateToLegacy/MigrateToDescriptor/)
Fixed
- To my understanding, the class
LegacyDataSPKMis supposed to never write to the database (asBerkeleyRODatabaseobviously doesn't support that), butLegacyDataSPKM::CheckDecryptionKeycontains a code-path for that (for a necessary rewrite): https://github.com/achow101/bitcoin/blob/a8f8a2d5d0e2328cbe2e569c7e12f653d18f456d/src/wallet/scriptpubkeyman.cpp#L254-L255 ShouldCheckDecryptionKeystay inLegacyScriptPubKeyMan?
No, we should not be migrating a wallet if we cannot decrypt the keys.
BerkeleyRODatabasealways returns true for all write operations so it doesn't matter. Also, I don't think it is useful to invert that and have to have specific bypasses forBerkeleyRODatabasein all functions that attempts to write to it.furszy commented at 1:35 PM on June 26, 2024: memberStrange CI https://github.com/bitcoin/bitcoin/actions/runs/9649050504/job/26611651920?pr=26596 error.. it doesn't seem to be related. The "salvage" command opens the wallet in
DatabaseFormat::BERKELEYformat.in src/wallet/wallet.cpp:4465 in f732495191 outdated
4460 | @@ -4438,6 +4461,8 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle 4461 | // both before and after reloading. This ensures the set is complete even if one of the wallets 4462 | // fails to reload. 4463 | std::set<fs::path> wallet_dirs; 4464 | + // Reset options.require_format as options are used by reload_wallet which may reload wallets of any format. 4465 | + options.require_format = std::nullopt;
furszy commented at 7:49 PM on June 26, 2024:In f732495191d346b8:
The reset call needs to be moved above the
reload_walletfunction declaration. Otherwise, the wallet will be reloaded in read-only mode for any failure occurring in-between thereload_walletdeclaration and this line, such as failures during backup creation or the decryption procedure.I have written a test to trigger this misbehavior: furszy@c24b673 (feel free to pull it here). The test passes when the fix is cherry-picked on top: furszy@2d5d9a4.
achow101 commented at 8:39 PM on June 26, 2024:Fixed and pulled the test. I decided to just move this into
reload_walletdirectly, it doesn't need to live outside of it.517e204bacChange MigrateLegacyToDescriptor to reopen wallet as BERKELEY_RO
When we reopen the wallet to do the migration, instead of opening using BDB, open it using the BerkeleyRO implementation.
achow101 force-pushed on Jun 26, 2024in src/wallet/scriptpubkeyman.cpp:813 in 5fee29f541 outdated
810 | + LegacyDataSPKM::LoadScriptMetadata(script_id, meta); 811 | UpdateTimeFirstKey(meta.nCreateTime); 812 | - m_script_metadata[script_id] = meta; 813 | +} 814 | + 815 | +bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
furszy commented at 10:09 PM on June 26, 2024:nano nit:
bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey)
cbergqvist commented at 10:16 PM on June 27, 2024:(In response to #26596 (review))
This made me cringe when first looking at the code too, but I guess retaining the inconsistency makes it easier to search for
AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)and find all 4 places it exists. Changing it in this PR would arguably add noise (on the other hand - if not now, then when?).
achow101 commented at 6:26 PM on July 1, 2024:Done
cbergqvist commented at 9:38 PM on July 1, 2024:(Now grepping "AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey)" gives 1 result. grepping "AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)" gives 3 results.
It would be nice to align style within the same class between .CPP/.H).
achow101 commented at 9:56 PM on July 1, 2024:Not going to change existing code, especially code that will be deleted, to match style
cbergqvist commented at 10:01 PM on July 1, 2024:(Agreed, but then I would skip doing the suggested change in the .CPP file).
in src/wallet/scriptpubkeyman.cpp:818 in 5fee29f541 outdated
815 | +bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) 816 | +{ 817 | + // This function should only be called during loading of a legacy to be migrated. 818 | + // As such, the wallet should not be encrypted if this is called. 819 | + LOCK(cs_KeyStore); 820 | + assert(!m_storage.HasEncryptionKeys());
furszy commented at 10:12 PM on June 26, 2024:I'm not sure this is asserting what you expect. In the loading process, we load the encryption keys after the key records. So the encryption keys map should always be empty and
HasEncryptionKeysshould always return false but not for the reasons stated here.
achow101 commented at 11:30 PM on June 26, 2024:Yes, that's what the comment is saying. It is because this function is only called during loading, and because legacy wallet records are loaded first, that this assertion makes sense. After all, to be migrated legacy wallets can still be encrypted.
furszy commented at 8:11 PM on June 27, 2024:I think calling
IsLocked()would be more precise and make more sense then. It seems confusing to say that the wallet can still be encrypted but expect no encryption keys (we understand it because we know the loading procedure but others might not understand it).Other than that, what if instead of creating this new
LegacyDataSPKM::AddKeyPubKeyInnermethod you place this three lines inside theLegacyDataSPKM::LoadKeywhich is only called during load?
achow101 commented at 6:23 PM on July 1, 2024:Other than that, what if instead of creating this new
LegacyDataSPKM::AddKeyPubKeyInnermethod you place this three lines inside theLegacyDataSPKM::LoadKeywhich is only called during load?We can't do that because
LoadKeyneeds to callLegacyScriptPubKeyMan::AddKeyPubKeyInnerfor legacy wallets that are being loaded normally. Since migration cannot do all of the things inLegacyScriptPubKeyMan::AddKeyPubKeyInner, we need to haveLegacyDataSPKM::AddKeyPubKeyInner.I think calling
IsLocked()would be more precise and make more sense then. It seems confusing to say that the wallet can still be encrypted but expect no encryption keys (we understand it because we know the loading procedure but others might not understand it).Actually, looking at this more closely, your original comment was correct. This function is only reached during legacy migration wallet migration loading for unencrypted keys only. Since the encryption keys are loaded later, this check doesn't actually do anything useful.
achow101 commented at 6:26 PM on July 1, 2024:Removed the assert and the comment.
DrahtBot added the label CI failed on Jun 27, 2024furszy commented at 8:33 PM on June 27, 2024: memberNow that the
IsMinechanges were moved to another PR, could reorder the commits just so 0cce574 is introduced before c3373a39a1acc94dbf644103fab2e477d49da671. This will let us keep the IsMine assertions insideMigrateToDescriptorfor now.Other than that, code review ACK 9700c21.
DrahtBot requested review from theStack on Jun 27, 2024DrahtBot requested review from cbergqvist on Jun 27, 2024cbergqvist commented at 10:27 PM on June 27, 2024: contributorgit range-diff master a8f8a2d 9700c217Agree with @furszy on reordering the commits to make it possible to retain the 3 asserts in
MigrateToDescriptor().DrahtBot requested review from cbergqvist on Jun 27, 20247461d0c006wallet: Move LegacySPKM data storage and handling to LegacyDataSPKM
In order to load the necessary data for migrating a legacy wallet without the full LegacyScriptPubKeyMan, move the data storage and loading components to LegacyDataSPKM. LegacyScriptPubKeyMan now subclasses that.
b231f4d556wallet: Move LegacyScriptPubKeyMan::IsMine to LegacyDataSPKM
IsMine is necessary for migration. It should be inlined with migration when the legacy wallet is removed.
wallet: Move MigrateToDescriptor and DeleteRecords to LegacyDataSPKM 61d872f1b3771bc60f13wallet: Use LegacyDataSPKM when loading
In SetupLegacyScriptPubKeyMan, a base LegacyDataSPKM will be created if the database has the format "bdb_ro" (i.e. the wallet was opened only for migration purposes). All of the loading functions are now called with a LegacyDataSPKM object instead of LegacyScriptPubKeyMan.
8ce3739edbtest: verify wallet is still active post-migration failure
The migration process reloads the wallet after all failures. This commit tests the behavior by trying to obtain a new address after a decryption failure during migration.
achow101 force-pushed on Jul 1, 2024achow101 commented at 6:26 PM on July 1, 2024: memberReordered the commits and preserved the
IsMineasserts.cbergqvist approvedcbergqvist commented at 10:03 PM on July 1, 2024: contributorACK 8ce3739edbcf6437bf2695087e0ebe8c633df19b
git range-diff 9700c217~5..9700c217 8ce3739~5..8ce3739- not the easiest read, also used GH compare.Passed
make check&test/functional/test_runner.pyincludingp2p_handshake.pywhich failed on CI.Only one non-critical reservation: #26596 (review)
DrahtBot requested review from furszy on Jul 1, 2024DrahtBot removed the label CI failed on Jul 1, 2024in src/wallet/wallet.cpp:3622 in 771bc60f13 outdated
3617 | + return nullptr; 3618 | + } 3619 | + auto it = m_internal_spk_managers.find(OutputType::LEGACY); 3620 | + if (it == m_internal_spk_managers.end()) return nullptr; 3621 | + return dynamic_cast<LegacyDataSPKM*>(it->second); 3622 | +}
theStack commented at 3:13 PM on July 3, 2024:nit: was about to suggest to deduplicate shared code between this method and
::GetLegacyScriptPubKeyManabove, but since the latter is removed soon anyway (in PR #28710, commit https://github.com/bitcoin/bitcoin/pull/28710/commits/5652a9c59f9ec793886eff00126c382003e4ce02#diff-1f2db0e4d5c12d109c7f0962333c245b49b696cb39ff432da048e9d6c08944d8), it's probably not worth it.
furszy commented at 7:51 PM on July 3, 2024:If you re-touch it again. Could:
LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const { if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { return nullptr; } return dynamic_cast<LegacyDataSPKM*>(GetScriptPubKeyMan(OutputType::LEGACY, /*internal=*/true)); }theStack approvedtheStack commented at 3:14 PM on July 3, 2024: contributorCode-review ACK 8ce3739edbcf6437bf2695087e0ebe8c633df19b
in src/wallet/scriptpubkeyman.cpp:798 in 7461d0c006 outdated
794 | { 795 | LOCK(cs_KeyStore); 796 | + LegacyDataSPKM::LoadKeyMetadata(keyID, meta); 797 | UpdateTimeFirstKey(meta.nCreateTime); 798 | - mapKeyMetadata[keyID] = meta; 799 | +}
furszy commented at 8:00 PM on July 3, 2024:In 7461d0c006c92:
At this point, just a non-blocking nit but you are locking
cs_KeyStoretwice. One here, and another one insideLegacyDataSPKM::LoadKeyMetadata.Same for
LoadScriptMetadata.in src/wallet/scriptpubkeyman.cpp:816 in 7461d0c006 outdated
813 | +} 814 | + 815 | +bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey) 816 | +{ 817 | + LOCK(cs_KeyStore); 818 | + return FillableSigningProvider::AddKeyPubKey(key, pubkey);
furszy commented at 8:02 PM on July 3, 2024:In https://github.com/bitcoin/bitcoin/commit/7461d0c006c92ede2f2595b79a5509eaf3509fb7:
FillableSigningProvider::AddKeyPubKeyalready lockscs_Keystoreinternally.furszy commented at 8:10 PM on July 3, 2024: memberCode review ACK 8ce3739edbcf6437bf2695087e0ebe8c633df19b
glozow merged this on Jul 11, 2024glozow closed this on Jul 11, 2024bitcoin locked this on Jul 11, 2025
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-02 15:13 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me