aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/walletdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/walletdb.cpp')
-rw-r--r--src/wallet/walletdb.cpp187
1 files changed, 185 insertions, 2 deletions
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 568b21ed00..79316ca3e7 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2019 The Bitcoin Core developers
+// Copyright (c) 2009-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -21,6 +21,8 @@
namespace DBKeys {
const std::string ACENTRY{"acentry"};
+const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
+const std::string ACTIVEINTERNALSPK{"activeinternalspk"};
const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
const std::string BESTBLOCK{"bestblock"};
const std::string CRYPTED_KEY{"ckey"};
@@ -41,6 +43,10 @@ const std::string PURPOSE{"purpose"};
const std::string SETTINGS{"settings"};
const std::string TX{"tx"};
const std::string VERSION{"version"};
+const std::string WALLETDESCRIPTOR{"walletdescriptor"};
+const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"};
+const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"};
+const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"};
const std::string WATCHMETA{"watchmeta"};
const std::string WATCHS{"watchs"};
} // namespace DBKeys
@@ -179,6 +185,51 @@ bool WalletBatch::WriteMinVersion(int nVersion)
return WriteIC(DBKeys::MINVERSION, nVersion);
}
+bool WalletBatch::WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal)
+{
+ std::string key = internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK;
+ return WriteIC(make_pair(key, type), id);
+}
+
+bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
+{
+ // hash pubkey/privkey to accelerate wallet load
+ std::vector<unsigned char> key;
+ key.reserve(pubkey.size() + privkey.size());
+ key.insert(key.end(), pubkey.begin(), pubkey.end());
+ key.insert(key.end(), privkey.begin(), privkey.end());
+
+ return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(privkey, Hash(key.begin(), key.end())), false);
+}
+
+bool WalletBatch::WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret)
+{
+ if (!WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORCKEY, std::make_pair(desc_id, pubkey)), secret, false)) {
+ return false;
+ }
+ EraseIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)));
+ return true;
+}
+
+bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor)
+{
+ return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
+}
+
+bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
+{
+ std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
+ xpub.Encode(ser_xpub.data());
+ return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
+}
+
+bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
+{
+ std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
+ xpub.Encode(ser_xpub.data());
+ return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
+}
+
class CWalletScanState {
public:
unsigned int nKeys{0};
@@ -189,6 +240,11 @@ public:
bool fIsEncrypted{false};
bool fAnyUnordered{false};
std::vector<uint256> vWalletUpgrade;
+ std::map<OutputType, uint256> m_active_external_spks;
+ std::map<OutputType, uint256> m_active_internal_spks;
+ std::map<uint256, DescriptorCache> m_descriptor_caches;
+ std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys;
+ std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys;
CWalletScanState() {
}
@@ -404,6 +460,108 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
} else if (strType == DBKeys::OLD_KEY) {
strErr = "Found unsupported 'wkey' record, try loading with version 0.18";
return false;
+ } else if (strType == DBKeys::ACTIVEEXTERNALSPK || strType == DBKeys::ACTIVEINTERNALSPK) {
+ uint8_t type;
+ ssKey >> type;
+ uint256 id;
+ ssValue >> id;
+
+ bool internal = strType == DBKeys::ACTIVEINTERNALSPK;
+ auto& spk_mans = internal ? wss.m_active_internal_spks : wss.m_active_external_spks;
+ if (spk_mans.count(static_cast<OutputType>(type)) > 0) {
+ strErr = "Multiple ScriptPubKeyMans specified for a single type";
+ return false;
+ }
+ spk_mans[static_cast<OutputType>(type)] = id;
+ } else if (strType == DBKeys::WALLETDESCRIPTOR) {
+ uint256 id;
+ ssKey >> id;
+ WalletDescriptor desc;
+ ssValue >> desc;
+ if (wss.m_descriptor_caches.count(id) == 0) {
+ wss.m_descriptor_caches[id] = DescriptorCache();
+ }
+ pwallet->LoadDescriptorScriptPubKeyMan(id, desc);
+ } else if (strType == DBKeys::WALLETDESCRIPTORCACHE) {
+ bool parent = true;
+ uint256 desc_id;
+ uint32_t key_exp_index;
+ uint32_t der_index;
+ ssKey >> desc_id;
+ ssKey >> key_exp_index;
+
+ // if the der_index exists, it's a derived xpub
+ try
+ {
+ ssKey >> der_index;
+ parent = false;
+ }
+ catch (...) {}
+
+ std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
+ ssValue >> ser_xpub;
+ CExtPubKey xpub;
+ xpub.Decode(ser_xpub.data());
+ if (wss.m_descriptor_caches.count(desc_id)) {
+ wss.m_descriptor_caches[desc_id] = DescriptorCache();
+ }
+ if (parent) {
+ wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub);
+ } else {
+ wss.m_descriptor_caches[desc_id].CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
+ }
+ } else if (strType == DBKeys::WALLETDESCRIPTORKEY) {
+ uint256 desc_id;
+ CPubKey pubkey;
+ ssKey >> desc_id;
+ ssKey >> pubkey;
+ if (!pubkey.IsValid())
+ {
+ strErr = "Error reading wallet database: CPubKey corrupt";
+ return false;
+ }
+ CKey key;
+ CPrivKey pkey;
+ uint256 hash;
+
+ wss.nKeys++;
+ ssValue >> pkey;
+ ssValue >> hash;
+
+ // hash pubkey/privkey to accelerate wallet load
+ std::vector<unsigned char> to_hash;
+ to_hash.reserve(pubkey.size() + pkey.size());
+ to_hash.insert(to_hash.end(), pubkey.begin(), pubkey.end());
+ to_hash.insert(to_hash.end(), pkey.begin(), pkey.end());
+
+ if (Hash(to_hash.begin(), to_hash.end()) != hash)
+ {
+ strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
+ return false;
+ }
+
+ if (!key.Load(pkey, pubkey, true))
+ {
+ strErr = "Error reading wallet database: CPrivKey corrupt";
+ return false;
+ }
+ wss.m_descriptor_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), key));
+ } else if (strType == DBKeys::WALLETDESCRIPTORCKEY) {
+ uint256 desc_id;
+ CPubKey pubkey;
+ ssKey >> desc_id;
+ ssKey >> pubkey;
+ if (!pubkey.IsValid())
+ {
+ strErr = "Error reading wallet database: CPubKey corrupt";
+ return false;
+ }
+ std::vector<unsigned char> privkey;
+ ssValue >> privkey;
+ wss.nCKeys++;
+
+ wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey)));
+ wss.fIsEncrypted = true;
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
@@ -497,6 +655,31 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
result = DBErrors::CORRUPT;
}
+ // Set the active ScriptPubKeyMans
+ for (auto spk_man_pair : wss.m_active_external_spks) {
+ pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ false, /* memonly */ true);
+ }
+ for (auto spk_man_pair : wss.m_active_internal_spks) {
+ pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ true, /* memonly */ true);
+ }
+
+ // Set the descriptor caches
+ for (auto desc_cache_pair : wss.m_descriptor_caches) {
+ auto spk_man = pwallet->GetScriptPubKeyMan(desc_cache_pair.first);
+ assert(spk_man);
+ ((DescriptorScriptPubKeyMan*)spk_man)->SetCache(desc_cache_pair.second);
+ }
+
+ // Set the descriptor keys
+ for (auto desc_key_pair : wss.m_descriptor_keys) {
+ auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first);
+ ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second);
+ }
+ for (auto desc_key_pair : wss.m_descriptor_crypt_keys) {
+ auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first);
+ ((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second);
+ }
+
if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
result = DBErrors::NONCRITICAL_ERROR;
@@ -516,7 +699,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
// nTimeFirstKey is only reliable if all keys have metadata
- if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) {
+ if (pwallet->IsLegacy() && (wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) {
auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
if (spk_man) {
LOCK(spk_man->cs_KeyStore);