This commit is contained in:
Ava Chow 2024-04-29 04:29:59 +02:00 committed by GitHub
commit c203ecf16f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 163 additions and 179 deletions

View File

@ -18,7 +18,8 @@
#include <vector>
namespace wallet {
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::unique_ptr<Descriptor> desc)
ExternalSignerScriptPubKeyMan::ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr<Descriptor> desc)
: DescriptorScriptPubKeyMan(storage, keypool_size)
{
LOCK(cs_desc_man);
assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
@ -39,7 +40,6 @@ bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::uni
TopUpWithDB(batch);
m_storage.UnsetBlankWalletFlag(batch);
return true;
}
ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {

View File

@ -14,18 +14,13 @@ struct bilingual_str;
namespace wallet {
class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
{
public:
ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size)
: DescriptorScriptPubKeyMan(storage, descriptor, keypool_size)
public:
//! Create an ExternalSPKM from existing wallet data
ExternalSignerScriptPubKeyMan(WalletStorage& storage, const uint256& id, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys)
: DescriptorScriptPubKeyMan(storage, id, descriptor, keypool_size, keys, ckeys)
{}
ExternalSignerScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size)
: DescriptorScriptPubKeyMan(storage, keypool_size)
{}
/** Provide a descriptor at setup time
* Returns false if already setup or setup fails, true if setup is successful
*/
bool SetupDescriptor(WalletBatch& batch, std::unique_ptr<Descriptor>desc);
//! Create a new ExternalSPKM from just a descriptor
ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr<Descriptor> desc);
static ExternalSigner GetExternalSigner();

View File

@ -1812,9 +1812,8 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
desc_spk_man->AddDescriptorKey(key, key.GetPubKey());
desc_spk_man->TopUp();
keys.keys.emplace(key.GetPubKey().GetID(), key);
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size, keys));
auto desc_spks = desc_spk_man->GetScriptPubKeys();
// Remove the scriptPubKeys from our current set
@ -1857,9 +1856,8 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
WalletDescriptor w_desc(std::move(desc), 0, 0, chain_counter, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
desc_spk_man->AddDescriptorKey(master_key.key, master_key.key.GetPubKey());
desc_spk_man->TopUp();
keys.keys.emplace(master_key.key.GetPubKey().GetID(), master_key.key);
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size, keys));
auto desc_spks = desc_spk_man->GetScriptPubKeys();
// Remove the scriptPubKeys from our current set
@ -1918,16 +1916,15 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
desc->Expand(0, provider, desc_spks, provider);
} else {
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
for (const auto& keyid : privkeyids) {
CKey key;
if (!GetKey(keyid, key)) {
continue;
}
desc_spk_man->AddDescriptorKey(key, key.GetPubKey());
keys.keys.emplace(key.GetPubKey().GetID(), key);
}
desc_spk_man->TopUp();
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size, keys));
auto desc_spks_set = desc_spk_man->GetScriptPubKeys();
desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end());
@ -2004,6 +2001,37 @@ bool LegacyScriptPubKeyMan::DeleteRecords()
return batch.EraseRecords(DBKeys::LEGACY_TYPES);
}
DescriptorScriptPubKeyMan::DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider)
: ScriptPubKeyMan(storage),
m_keypool_size(keypool_size),
m_wallet_descriptor(descriptor)
{
UpdateWithSigningProvider(provider);
}
DescriptorScriptPubKeyMan::DescriptorScriptPubKeyMan(WalletStorage& storage, const uint256& id, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys)
: ScriptPubKeyMan(storage),
m_map_keys(keys),
m_map_crypted_keys(ckeys),
m_keypool_size(keypool_size),
m_wallet_descriptor(descriptor)
{
if (!m_map_keys.empty() && !m_map_crypted_keys.empty()) {
throw std::runtime_error("Error: Wallet contains both unencrypted and encrypted keys");
}
if (id != GetID()) {
throw std::runtime_error("The descriptor ID calculated by the wallet differs from the one in DB");
}
SetCache(m_wallet_descriptor.cache);
}
DescriptorScriptPubKeyMan::DescriptorScriptPubKeyMan(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, const CExtKey& master_key, OutputType addr_type, bool internal)
: ScriptPubKeyMan(storage),
m_keypool_size(keypool_size)
{
SetupDescriptorGeneration(batch, master_key, addr_type, internal);
}
util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type)
{
// Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later
@ -2641,24 +2669,6 @@ void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache)
m_storage.TopUpCallback(new_spks, this);
}
bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key)
{
LOCK(cs_desc_man);
m_map_keys[key_id] = key;
return true;
}
bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key)
{
LOCK(cs_desc_man);
if (!m_map_keys.empty()) {
return false;
}
m_map_crypted_keys[key_id] = make_pair(pubkey, crypted_key);
return true;
}
bool DescriptorScriptPubKeyMan::HasWalletDescriptor(const WalletDescriptor& desc) const
{
LOCK(cs_desc_man);
@ -2747,7 +2757,7 @@ void DescriptorScriptPubKeyMan::UpgradeDescriptorCache()
}
}
void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor)
void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider)
{
LOCK(cs_desc_man);
std::string error;
@ -2760,9 +2770,24 @@ void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descrip
m_max_cached_index = -1;
m_wallet_descriptor = descriptor;
UpdateWithSigningProvider(provider);
NotifyFirstKeyTimeChanged(this, m_wallet_descriptor.creation_time);
}
void DescriptorScriptPubKeyMan::UpdateWithSigningProvider(const FlatSigningProvider& signing_provider)
{
// Add the private keys to the descriptor
for (const auto& entry : signing_provider.keys) {
const CKey& key = entry.second;
AddDescriptorKey(key, key.GetPubKey());
}
// Top up key pool, to generate scriptPubKeys
if (!TopUp()) {
throw std::runtime_error("Could not top up scriptPubKeys");
}
}
bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error)
{
LOCK(cs_desc_man);

View File

@ -203,12 +203,6 @@ public:
*/
virtual std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) { return {}; }
/** Sets up the key generation stuff, i.e. generates new HD seeds and sets them as active.
* Returns false if already setup or setup fails, true if setup is successful
* Set force=true to make it re-setup if already setup, used for upgrades
*/
virtual bool SetupGeneration(bool force = false) { return false; }
/* Returns true if HD is enabled */
virtual bool IsHDEnabled() const { return false; }
@ -276,18 +270,22 @@ static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES {
class DescriptorScriptPubKeyMan;
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
using KeyMap = std::map<CKeyID, CKey>;
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
using ScriptPubKeyMap = std::map<CScript, int32_t>; // Map of scripts to descriptor range index
using PubKeyMap = std::map<CPubKey, int32_t>; // Map of pubkeys involved in scripts to descriptor range index
class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
{
private:
//! keeps track of whether Unlock has run a thorough check before
bool fDecryptionThoroughlyChecked = true;
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr;
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
@ -397,7 +395,11 @@ public:
bool IsHDEnabled() const override;
bool SetupGeneration(bool force = false) override;
/** Sets up the key generation stuff, i.e. generates new HD seeds and sets them as active.
* Returns false if already setup or setup fails, true if setup is successful
* Set force=true to make it re-setup if already setup, used for upgrades
*/
bool SetupGeneration(bool force = false);
bool Upgrade(int prev_version, int new_version, bilingual_str& error) override;
@ -559,11 +561,6 @@ public:
class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
{
private:
using ScriptPubKeyMap = std::map<CScript, int32_t>; // Map of scripts to descriptor range index
using PubKeyMap = std::map<CPubKey, int32_t>; // Map of pubkeys involved in scripts to descriptor range index
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
using KeyMap = std::map<CKeyID, CKey>;
ScriptPubKeyMap m_map_script_pub_keys GUARDED_BY(cs_desc_man);
PubKeyMap m_map_pubkeys GUARDED_BY(cs_desc_man);
int32_t m_max_cached_index = -1;
@ -590,22 +587,32 @@ private:
// Fetch the SigningProvider for a given index and optionally include private keys. Called by the above functions.
std::unique_ptr<FlatSigningProvider> GetSigningProvider(int32_t index, bool include_private = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
void SetCache(const DescriptorCache& cache);
void AddDescriptorKey(const CKey& key, const CPubKey &pubkey);
void UpdateWithSigningProvider(const FlatSigningProvider& signing_provider);
//! Setup descriptors based on the given CExtkey
bool SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal);
protected:
DescriptorScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size)
: ScriptPubKeyMan(storage),
m_keypool_size(keypool_size)
{}
WalletDescriptor m_wallet_descriptor GUARDED_BY(cs_desc_man);
//! Same as 'TopUp' but designed for use within a batch transaction context
bool TopUpWithDB(WalletBatch& batch, unsigned int size = 0);
public:
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size)
: ScriptPubKeyMan(storage),
m_keypool_size(keypool_size),
m_wallet_descriptor(descriptor)
{}
DescriptorScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size)
: ScriptPubKeyMan(storage),
m_keypool_size(keypool_size)
{}
//! Create a new DescriptorScriptPubKeyMan from an existing descriptor (i.e. from an import)
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider);
//! Create a DescriptorScriptPubKeyMan from existing data (i.e. during loading)
DescriptorScriptPubKeyMan(WalletStorage& storage, const uint256& id, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys);
//! Create an automatically generated DescriptorScriptPubKeyMan
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, const CExtKey& master_key, OutputType addr_type, bool internal);
mutable RecursiveMutex cs_desc_man;
@ -628,9 +635,6 @@ public:
bool IsHDEnabled() const override;
//! Setup descriptors based on the given CExtkey
bool SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal);
bool HavePrivateKeys() const override;
bool HasPrivKey(const CKeyID& keyid) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
//! Retrieve the particular key if it is available. Returns nullopt if the key is not in the wallet, or if the wallet is locked.
@ -655,15 +659,9 @@ public:
uint256 GetID() const override;
void SetCache(const DescriptorCache& cache);
bool AddKey(const CKeyID& key_id, const CKey& key);
bool AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key);
bool HasWalletDescriptor(const WalletDescriptor& desc) const;
void UpdateWalletDescriptor(WalletDescriptor& descriptor);
void UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider);
bool CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error);
void AddDescriptorKey(const CKey& key, const CPubKey &pubkey);
void WriteDescriptor();
WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);

View File

@ -72,6 +72,11 @@ static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWal
std::unique_ptr<Descriptor> parsed_desc{Parse(desc_str.value(), keys, error, false)};
if (!parsed_desc) return std::nullopt;
FlatSigningProvider out_keys;
std::vector<CScript> scripts_temp;
DescriptorCache temp_cache;
if (!parsed_desc->Expand(0, keys, scripts_temp, out_keys, &temp_cache)) return std::nullopt;
WalletDescriptor w_desc{std::move(parsed_desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1};
return std::make_pair(w_desc, keys);
}
@ -140,15 +145,6 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)
}
}
},
[&] {
CKey key{ConsumePrivateKey(fuzzed_data_provider, /*compressed=*/fuzzed_data_provider.ConsumeBool())};
if (!key.IsValid()) {
good_data = false;
return;
}
spk_manager->AddDescriptorKey(key, key.GetPubKey());
spk_manager->TopUp();
},
[&] {
std::string descriptor;
(void)spk_manager->GetDescriptorString(descriptor, /*priv=*/fuzzed_data_provider.ConsumeBool());

View File

@ -423,6 +423,11 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
return nullptr;
}
// Unset the blank flag if not specified by the user
if (!create_blank) {
wallet->UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
@ -430,33 +435,6 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
status = DatabaseStatus::FAILED_ENCRYPT;
return nullptr;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
status = DatabaseStatus::FAILED_ENCRYPT;
return nullptr;
}
// Set a seed for the wallet
{
LOCK(wallet->cs_wallet);
if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
wallet->SetupDescriptorScriptPubKeyMans();
} else {
for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
error = Untranslated("Unable to generate initial keys");
status = DatabaseStatus::FAILED_CREATE;
return nullptr;
}
}
}
}
// Relock the wallet
wallet->Lock();
}
}
NotifyWalletLoaded(context, wallet);
@ -861,16 +839,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
Lock();
Unlock(strWalletPassphrase);
// If we are using descriptors, make new descriptors with a new seed
if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
SetupDescriptorScriptPubKeyMans();
} else if (auto spk_man = GetLegacyScriptPubKeyMan()) {
// if we are using HD, replace the HD seed with a new one
if (spk_man->IsHDEnabled()) {
if (!spk_man->SetupGeneration(true)) {
return false;
}
}
// Generate new descriptors or seed if not blank or disable private keys
if (!SetupWalletGeneration()) {
return false;
}
Lock();
@ -3013,25 +2984,9 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
walletInstance->InitWalletFlags(wallet_creation_flags);
// Only create LegacyScriptPubKeyMan when not descriptor wallet
if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
walletInstance->SetupLegacyScriptPubKeyMan();
}
if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
LOCK(walletInstance->cs_wallet);
if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
walletInstance->SetupDescriptorScriptPubKeyMans();
// SetupDescriptorScriptPubKeyMans already calls SetupGeneration for us so we don't need to call SetupGeneration separately
} else {
// Legacy wallets need SetupGeneration here.
for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
error = _("Unable to generate initial keys");
return nullptr;
}
}
}
if (!walletInstance->SetupWalletGeneration()) {
error = _("Unable to generate initial keys");
return nullptr;
}
if (chain) {
@ -3650,22 +3605,21 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
}
}
DescriptorScriptPubKeyMan& CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc, const KeyMap& keys, const CryptedKeyMap& ckeys)
{
DescriptorScriptPubKeyMan* spk_manager;
if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
spk_manager = new ExternalSignerScriptPubKeyMan(*this, desc, m_keypool_size);
spk_manager = new ExternalSignerScriptPubKeyMan(*this, id, desc, m_keypool_size, keys, ckeys);
} else {
spk_manager = new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size);
spk_manager = new DescriptorScriptPubKeyMan(*this, id, desc, m_keypool_size, keys, ckeys);
}
AddScriptPubKeyMan(id, std::unique_ptr<ScriptPubKeyMan>(spk_manager));
return *spk_manager;
}
DescriptorScriptPubKeyMan& CWallet::SetupDescriptorScriptPubKeyMan(WalletBatch& batch, const CExtKey& master_key, const OutputType& output_type, bool internal)
{
AssertLockHeld(cs_wallet);
auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, m_keypool_size));
auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, batch, m_keypool_size, master_key, output_type, internal));
if (IsCrypted()) {
if (IsLocked()) {
throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
@ -3674,7 +3628,6 @@ DescriptorScriptPubKeyMan& CWallet::SetupDescriptorScriptPubKeyMan(WalletBatch&
throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors");
}
}
spk_manager->SetupDescriptorGeneration(batch, master_key, output_type, internal);
DescriptorScriptPubKeyMan* out = spk_manager.get();
uint256 id = spk_manager->GetID();
AddScriptPubKeyMan(id, std::move(spk_manager));
@ -3742,8 +3695,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
continue;
}
OutputType t = *desc->GetOutputType();
auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, m_keypool_size));
spk_manager->SetupDescriptor(batch, std::move(desc));
auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, batch, m_keypool_size, std::move(desc)));
uint256 id = spk_manager->GetID();
AddScriptPubKeyMan(id, std::move(spk_manager));
AddActiveScriptPubKeyManWithDb(batch, id, t, internal);
@ -3755,6 +3707,33 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
}
}
bool CWallet::SetupWalletGeneration()
{
LOCK(cs_wallet);
// Skip if blank or no privkeys
if (!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER) &&
(IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET) || IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS))) {
// Just make sure that there's a legacy spkm if this wallet is legacy
if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
SetupLegacyScriptPubKeyMan();
}
return true;
}
// If we are using descriptors, make new descriptors with a new seed
if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
SetupDescriptorScriptPubKeyMans();
} else if (auto spk_man = GetOrCreateLegacyScriptPubKeyMan()) {
// if we are using HD, set or replace the HD seed with a new one
// non-HD always has key generation enabled by default
if (CanSupportFeature(FEATURE_HD)) {
if (!spk_man->SetupGeneration(true)) {
return false;
}
}
}
return true;
}
void CWallet::AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
{
WalletBatch batch(GetDatabase());
@ -3864,9 +3843,9 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
auto spk_man = GetDescriptorScriptPubKeyMan(desc);
if (spk_man) {
WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
spk_man->UpdateWalletDescriptor(desc);
spk_man->UpdateWalletDescriptor(desc, signing_provider);
} else {
auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size));
auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size, signing_provider));
spk_man = new_spk_man.get();
// Save the descriptor to memory
@ -3874,18 +3853,6 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
AddScriptPubKeyMan(id, std::move(new_spk_man));
}
// Add the private keys to the descriptor
for (const auto& entry : signing_provider.keys) {
const CKey& key = entry.second;
spk_man->AddDescriptorKey(key, key.GetPubKey());
}
// Top up key pool, the manager will generate new scriptPubKeys internally
if (!spk_man->TopUp()) {
WalletLogPrintf("Could not top up scriptPubKeys\n");
return nullptr;
}
// Apply the label if necessary
// Note: we disable labels for ranged descriptors
if (!desc.descriptor->IsRange()) {

View File

@ -994,7 +994,7 @@ public:
void ConnectScriptPubKeyManNotifiers();
//! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it
DescriptorScriptPubKeyMan& LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc);
void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc, const KeyMap& keys, const CryptedKeyMap& ckeys);
//! Adds the active ScriptPubKeyMan for the specified type and internal. Writes it to the wallet file
//! @param[in] id The unique id for the ScriptPubKeyMan
@ -1020,6 +1020,9 @@ public:
void SetupDescriptorScriptPubKeyMans(const CExtKey& master_key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SetupDescriptorScriptPubKeyMans() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Setup new descriptors or seed for new address generation
bool SetupWalletGeneration();
//! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;

View File

@ -808,13 +808,6 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
strErr = strprintf("%s\nDetails: %s", strErr, e.what());
return DBErrors::UNKNOWN_DESCRIPTOR;
}
DescriptorScriptPubKeyMan& spkm = pwallet->LoadDescriptorScriptPubKeyMan(id, desc);
// Prior to doing anything with this spkm, verify ID compatibility
if (id != spkm.GetID()) {
strErr = "The descriptor ID calculated by the wallet differs from the one in DB";
return DBErrors::CORRUPT;
}
DescriptorCache cache;
@ -870,15 +863,14 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
});
result = std::max(result, lh_cache_res.m_result);
// Set the cache for this descriptor
auto spk_man = (DescriptorScriptPubKeyMan*)pwallet->GetScriptPubKeyMan(id);
assert(spk_man);
spk_man->SetCache(cache);
// Set the cache to the WalletDescriptor
desc.cache = cache;
// Get unencrypted keys
std::map<CKeyID, CKey> keys;
prefix = PrefixStream(DBKeys::WALLETDESCRIPTORKEY, id);
LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORKEY, prefix,
[&id, &spk_man] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
[&id, &keys] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
uint256 desc_id;
CPubKey pubkey;
key >> desc_id;
@ -913,16 +905,17 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
strErr = "Error reading wallet database: descriptor unencrypted key CPrivKey corrupt";
return DBErrors::CORRUPT;
}
spk_man->AddKey(pubkey.GetID(), privkey);
keys[pubkey.GetID()] = privkey;
return DBErrors::LOAD_OK;
});
result = std::max(result, key_res.m_result);
num_keys = key_res.m_records;
// Get encrypted keys
std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>> ckeys;
prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCKEY, id);
LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCKEY, prefix,
[&id, &spk_man] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
[&id, &ckeys] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
uint256 desc_id;
CPubKey pubkey;
key >> desc_id;
@ -936,12 +929,19 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
std::vector<unsigned char> privkey;
value >> privkey;
spk_man->AddCryptedKey(pubkey.GetID(), pubkey, privkey);
ckeys[pubkey.GetID()] = make_pair(pubkey, privkey);
return DBErrors::LOAD_OK;
});
result = std::max(result, ckey_res.m_result);
num_ckeys = ckey_res.m_records;
try {
pwallet->LoadDescriptorScriptPubKeyMan(id, desc, keys, ckeys);
} catch (std::runtime_error& e) {
strErr = e.what();
return DBErrors::CORRUPT;
}
return result;
});