Is there an existing issue for this?
- I have searched the existing issues
Current behaviour
After updating bitcoin core, on startup fails to load an existing descriptor wallet containing a Miniscript descriptor. Startup aborts with the wallet DB corruption error: The descriptor ID calculated by the wallet differs from the one in DB
The wallet previously loaded successfully on older bitcoin core versions.
Expected behaviour
Existing descriptor wallets should continue to load after upgrading, as long as the stored descriptor is valid and describes the same scripts. A change in descriptor string serialization/canonicalization should not make an existing wallet appear corrupted.
This patch fixes the issue. But I am unsure about other cases, eg if wallet is a Taproot descriptor with a non-Miniscript internal key plus Miniscript taptree, the old mixed-format ID may be more subtle
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 5ad29970d2..38247b9f7d 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -8,9 +8,11 @@
#include <wallet/walletdb.h>
#include <common/system.h>
+#include <crypto/sha256.h>
#include <key_io.h>
#include <primitives/transaction_identifier.h>
#include <protocol.h>
+#include <script/descriptor.h>
#include <script/script.h>
#include <serialize.h>
#include <sync.h>
@@ -69,6 +71,17 @@ void LogDBInfo()
LogInfo("Using SQLite Version %s", SQLiteDatabaseVersion());
}
+static uint256 LegacyMiniscriptDescriptorID(const Descriptor& desc)
+{
+ // Before [#31734](/bitcoin-bitcoin/31734/), MiniscriptDescriptor::ToStringHelper() did not respect
+ // StringType::COMPAT, so the wallet descriptor id for Miniscript
+ // descriptors was calculated from the non-compat descriptor string.
+ const std::string desc_str{desc.ToString(/*compat_format=*/false)};
+ uint256 id;
+ CSHA256().Write(reinterpret_cast<const unsigned char*>(desc_str.data()), desc_str.size()).Finalize(id.begin());
+ return id;
+}
+
//
// WalletBatch
//
@@ -772,10 +785,11 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
return DBErrors::UNKNOWN_DESCRIPTOR;
}
- if (id != desc.id) {
+ if (id != desc.id && id != LegacyMiniscriptDescriptorID(*desc.descriptor)) {
strErr = "The descriptor ID calculated by the wallet differs from the one in DB";
return DBErrors::CORRUPT;
}
+ desc.id = id;
DescriptorCache cache;
I also don't know if there are side-effects elsewhere due to change in descriptor id
Steps to reproduce
- On an older Bitcoin Core version prior to 53b72372da91c5e90df865bc15961e16feb4a983 #31734, create/import a descriptor wallet containing a Miniscript multisig descriptor, especially one with key origin information containing hardened derivation markers.
- Shut down Bitcoin Core.
- Upgrade to current master.
- Start Bitcoin Core / Bitcoin Qt and load the same wallet.
- Wallet loading fails with a descriptor ID mismatch.
Relevant log output
init message: Loading wallet… The descriptor ID calculated by the wallet differs from the one in DB Error loading */wallet.dat: Wallet corrupted
How did you obtain Bitcoin Core
Compiled from source
What version of Bitcoin Core are you using?
master@fbe628756cc417dd4b6ccd9d3a709ca8e2f6023c
Operating system and version
N/A
Machine specifications
N/A