diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1504,6 +1504,22 @@ //! Connect the signals from ScriptPubKeyMans to the signals in CWallet void ConnectScriptPubKeyManNotifiers(); + + //! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and + //! load it + void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor &desc); + + //! Sets the active ScriptPubKeyMan for the specified type and internal + //! @param[in] id The unique id for the ScriptPubKeyMan + //! @param[in] type The OutputType this ScriptPubKeyMan provides addresses + //! for + //! @param[in] internal Whether this ScriptPubKeyMan provides change + //! addresses + //! @param[in] memonly Whether to record this update to the database. Set to + //! true for wallet loading, normally false when actually + //! updating the wallet. + void SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, + bool memonly = false); }; /** diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4911,3 +4911,30 @@ NotifyCanGetAddressesChanged); } } + +void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, + WalletDescriptor &desc) { + auto spk_manager = std::unique_ptr( + new DescriptorScriptPubKeyMan(*this, desc)); + m_spk_managers[id] = std::move(spk_manager); +} + +void CWallet::SetActiveScriptPubKeyMan(uint256 id, OutputType type, + bool internal, bool memonly) { + auto &spk_mans = + internal ? m_internal_spk_managers : m_external_spk_managers; + auto spk_man = m_spk_managers.at(id).get(); + spk_man->SetType(type, internal); + spk_mans[type] = spk_man; + + if (!memonly) { + WalletBatch batch(*database); + if (!batch.WriteActiveScriptPubKeyMan(static_cast(type), id, + internal)) { + throw std::runtime_error( + std::string(__func__) + + ": writing active ScriptPubKeyMan id failed"); + } + } + NotifyCanGetAddressesChanged(); +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -220,6 +220,9 @@ /// Erase destination data tuple from wallet database. bool EraseDestData(const CTxDestination &address, const std::string &key); + bool WriteActiveScriptPubKeyMan(uint8_t type, const uint256 &id, + bool internal); + DBErrors LoadWallet(CWallet *pwallet); DBErrors FindWalletTx(std::vector &txIds, std::vector &vWtx); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -20,6 +20,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"}; @@ -40,6 +42,7 @@ const std::string SETTINGS{"settings"}; const std::string TX{"tx"}; const std::string VERSION{"version"}; +const std::string WALLETDESCRIPTOR{"walletdescriptor"}; const std::string WATCHMETA{"watchmeta"}; const std::string WATCHS{"watchs"}; } // namespace DBKeys @@ -192,6 +195,13 @@ 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); +} + class CWalletScanState { public: unsigned int nKeys{0}; @@ -202,6 +212,8 @@ bool fIsEncrypted{false}; bool fAnyUnordered{false}; std::vector vWalletUpgrade; + std::map m_active_external_spks; + std::map m_active_internal_spks; CWalletScanState() {} }; @@ -436,6 +448,28 @@ strErr = "Found unsupported 'wkey' record, try loading with " "version 0.20"; 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(type)) > 0) { + strErr = + "Multiple ScriptPubKeyMans specified for a single type"; + return false; + } + spk_mans[static_cast(type)] = id; + } else if (strType == DBKeys::WALLETDESCRIPTOR) { + uint256 id; + ssKey >> id; + WalletDescriptor desc; + ssValue >> desc; + pwallet->LoadDescriptorScriptPubKeyMan(id, desc); } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE && strType != DBKeys::MINVERSION && @@ -532,6 +566,18 @@ 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); + } + if (fNoncriticalErrors && result == DBErrors::LOAD_OK) { result = DBErrors::NONCRITICAL_ERROR; }