wallet: include keys when constructing DescriptorSPKM during import

When importing a descriptor, all of the descriptor data should be
provided at the same time in the constructor.
This commit is contained in:
Ava Chow
2024-02-20 11:48:42 -05:00
parent 6538f69135
commit aa4f7823aa
3 changed files with 63 additions and 36 deletions

View File

@@ -596,16 +596,15 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
// Construct the combo descriptor
std::string desc_str = "combo(" + origin_str + HexStr(key.GetPubKey()) + ")";
FlatSigningProvider keys;
FlatSigningProvider provider;
std::string error;
std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, error, false);
std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, provider, error, false);
CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor
WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey()));
desc_spk_man->TopUpWithDB(batch);
provider.keys.emplace(key.GetPubKey().GetID(), key);
auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider);
auto desc_spks = desc_spk_man->GetScriptPubKeys();
// Remove the scriptPubKeys from our current set
@@ -644,17 +643,16 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
// Make the combo descriptor
std::string xpub = EncodeExtPubKey(master_key.Neuter());
std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)";
FlatSigningProvider keys;
FlatSigningProvider provider;
std::string error;
std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, error, false);
std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, provider, error, false);
CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor
uint32_t chain_counter = std::max((i == 1 ? chain.nInternalChainCounter : chain.nExternalChainCounter), (uint32_t)0);
WalletDescriptor w_desc(std::move(descs.at(0)), 0, 0, chain_counter, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey()));
desc_spk_man->TopUpWithDB(batch);
provider.keys.emplace(master_key.key.GetPubKey().GetID(), master_key.key);
auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider);
auto desc_spks = desc_spk_man->GetScriptPubKeys();
// Remove the scriptPubKeys from our current set
@@ -727,16 +725,15 @@ std::optional<MigrationData> LegacyDataSPKM::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::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
for (const auto& keyid : privkeyids) {
CKey key;
if (!GetKey(keyid, key)) {
continue;
}
WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey()));
keys.keys.emplace(key.GetPubKey().GetID(), key);
}
desc_spk_man->TopUpWithDB(batch);
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, keys);
auto desc_spks_set = desc_spk_man->GetScriptPubKeys();
desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end());
@@ -821,6 +818,23 @@ bool LegacyDataSPKM::DeleteRecordsWithDB(WalletBatch& batch)
return batch.EraseRecords(DBKeys::LEGACY_TYPES);
}
std::unique_ptr<DescriptorScriptPubKeyMan> DescriptorScriptPubKeyMan::CreateFromImport(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider)
{
auto spkm = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size));
LOCK(spkm->cs_desc_man);
WalletBatch batch(storage.GetDatabase());
spkm->UpdateWithSigningProvider(batch, provider);
return spkm;
}
std::unique_ptr<DescriptorScriptPubKeyMan> DescriptorScriptPubKeyMan::CreateFromMigration(WalletStorage& storage, WalletBatch& batch, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider)
{
auto spkm = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size));
LOCK(spkm->cs_desc_man);
spkm->UpdateWithSigningProvider(batch, provider);
return spkm;
}
DescriptorScriptPubKeyMan::DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys)
: ScriptPubKeyMan(storage),
m_map_keys(keys),
@@ -1565,7 +1579,7 @@ void DescriptorScriptPubKeyMan::UpgradeDescriptorCache()
}
}
util::Result<void> DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor)
util::Result<void> DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider)
{
LOCK(cs_desc_man);
std::string error;
@@ -1578,10 +1592,29 @@ util::Result<void> DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescr
m_max_cached_index = -1;
m_wallet_descriptor = descriptor;
WalletBatch batch(m_storage.GetDatabase());
UpdateWithSigningProvider(batch, provider);
NotifyFirstKeyTimeChanged(this, m_wallet_descriptor.creation_time);
return {};
}
void DescriptorScriptPubKeyMan::UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider)
{
AssertLockHeld(cs_desc_man);
// Add the private keys to the descriptor
for (const auto& entry : signing_provider.keys) {
const CKey& key = entry.second;
if (!AddDescriptorKeyWithDB(batch, key, key.GetPubKey())) {
throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed");
}
}
// Top up key pool, to generate scriptPubKeys
if (!TopUpWithDB(batch)) {
throw std::runtime_error("Could not top up scriptPubKeys");
}
}
bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error)
{
LOCK(cs_desc_man);

View File

@@ -303,6 +303,13 @@ private:
*/
mutable std::map<uint256, MuSig2SecNonce> m_musig2_secnonces;
//! Create a new DescriptorScriptPubKeyMan from an existing descriptor (i.e. from an import)
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size)
: ScriptPubKeyMan(storage),
m_keypool_size(keypool_size),
m_wallet_descriptor(descriptor)
{}
bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
@@ -316,6 +323,9 @@ private:
void Load();
void AddDescriptorKey(const CKey& key, const CPubKey &pubkey);
void UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
protected:
//! Create a DescriptorScriptPubKeyMan from existing data (i.e. during loading)
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys);
@@ -326,18 +336,14 @@ protected:
bool TopUpWithDB(WalletBatch& batch, unsigned int size = 0);
public:
//! Create a new DescriptorScriptPubKeyMan from an existing descriptor (i.e. from an import)
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)
{}
static std::unique_ptr<DescriptorScriptPubKeyMan> LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys);
static std::unique_ptr<DescriptorScriptPubKeyMan> CreateFromImport(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider);
static std::unique_ptr<DescriptorScriptPubKeyMan> CreateFromMigration(WalletStorage& storage, WalletBatch& batch, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider);
mutable RecursiveMutex cs_desc_man;
@@ -391,9 +397,8 @@ public:
uint256 GetID() const override;
bool HasWalletDescriptor(const WalletDescriptor& desc) const;
util::Result<void> UpdateWalletDescriptor(WalletDescriptor& descriptor);
util::Result<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

@@ -3753,11 +3753,11 @@ util::Result<std::reference_wrapper<DescriptorScriptPubKeyMan>> CWallet::AddWall
auto spk_man = GetDescriptorScriptPubKeyMan(desc);
if (spk_man) {
WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
if (auto spkm_res = spk_man->UpdateWalletDescriptor(desc); !spkm_res) {
if (auto spkm_res = spk_man->UpdateWalletDescriptor(desc, signing_provider); !spkm_res) {
return util::Error{util::ErrorString(spkm_res)};
}
} else {
auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size));
auto new_spk_man = DescriptorScriptPubKeyMan::CreateFromImport(*this, desc, m_keypool_size, signing_provider);
spk_man = new_spk_man.get();
// Save the descriptor to memory
@@ -3765,17 +3765,6 @@ util::Result<std::reference_wrapper<DescriptorScriptPubKeyMan>> CWallet::AddWall
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()) {
return util::Error{_("Could not top up scriptPubKeys")};
}
// Apply the label if necessary
// Note: we disable labels for ranged descriptors
if (!desc.descriptor->IsRange()) {