Skip to content

Commit 960e768

Browse files
committed
merge bitcoin#19651: importdescriptors update existing
1 parent 1f31823 commit 960e768

File tree

8 files changed

+242
-60
lines changed

8 files changed

+242
-60
lines changed

src/wallet/rpcdump.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,9 +1750,8 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
17501750
// Check if the wallet already contains the descriptor
17511751
auto existing_spk_manager = pwallet->GetDescriptorScriptPubKeyMan(w_desc);
17521752
if (existing_spk_manager) {
1753-
LOCK(existing_spk_manager->cs_desc_man);
1754-
if (range_start > existing_spk_manager->GetWalletDescriptor().range_start) {
1755-
throw JSONRPCError(RPC_INVALID_PARAMS, strprintf("range_start can only decrease; current range = [%d,%d]", existing_spk_manager->GetWalletDescriptor().range_start, existing_spk_manager->GetWalletDescriptor().range_end));
1753+
if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
1754+
throw JSONRPCError(RPC_INVALID_PARAMETER, error);
17561755
}
17571756
}
17581757

@@ -1769,16 +1768,16 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
17691768
} else {
17701769
pwallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), internal);
17711770
}
1771+
} else {
1772+
if (w_desc.descriptor->GetOutputType()) {
1773+
pwallet->DeactivateScriptPubKeyMan(spk_manager->GetID(), internal);
1774+
}
17721775
}
17731776

17741777
result.pushKV("success", UniValue(true));
17751778
} catch (const UniValue& e) {
17761779
result.pushKV("success", UniValue(false));
17771780
result.pushKV("error", e);
1778-
} catch (...) {
1779-
result.pushKV("success", UniValue(false));
1780-
1781-
result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
17821781
}
17831782
if (warnings.size()) result.pushKV("warnings", warnings);
17841783
return result;

src/wallet/scriptpubkeyman.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,12 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
20072007
AssertLockHeld(cs_desc_man);
20082008
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
20092009

2010+
// Check if provided key already exists
2011+
if (m_map_keys.find(pubkey.GetID()) != m_map_keys.end() ||
2012+
m_map_crypted_keys.find(pubkey.GetID()) != m_map_crypted_keys.end()) {
2013+
return true;
2014+
}
2015+
20102016
if (m_storage.HasEncryptionKeys()) {
20112017
if (m_storage.IsLocked()) {
20122018
return false;
@@ -2442,3 +2448,37 @@ void DescriptorScriptPubKeyMan::UpgradeDescriptorCache()
24422448
throw std::runtime_error(std::string(__func__) + ": writing cache items failed");
24432449
}
24442450
}
2451+
2452+
void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor)
2453+
{
2454+
LOCK(cs_desc_man);
2455+
std::string error;
2456+
if (!CanUpdateToWalletDescriptor(descriptor, error)) {
2457+
throw std::runtime_error(std::string(__func__) + ": " + error);
2458+
}
2459+
2460+
m_map_pubkeys.clear();
2461+
m_map_script_pub_keys.clear();
2462+
m_max_cached_index = -1;
2463+
m_wallet_descriptor = descriptor;
2464+
}
2465+
2466+
bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error)
2467+
{
2468+
LOCK(cs_desc_man);
2469+
if (!HasWalletDescriptor(descriptor)) {
2470+
error = "can only update matching descriptor";
2471+
return false;
2472+
}
2473+
2474+
if (descriptor.range_start > m_wallet_descriptor.range_start ||
2475+
descriptor.range_end < m_wallet_descriptor.range_end) {
2476+
// Use inclusive range for error
2477+
error = strprintf("new range must include current range = [%d,%d]",
2478+
m_wallet_descriptor.range_start,
2479+
m_wallet_descriptor.range_end - 1);
2480+
return false;
2481+
}
2482+
2483+
return true;
2484+
}

src/wallet/scriptpubkeyman.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,8 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
603603
bool AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key);
604604

605605
bool HasWalletDescriptor(const WalletDescriptor& desc) const;
606+
void UpdateWalletDescriptor(WalletDescriptor& descriptor);
607+
bool CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error);
606608
void AddDescriptorKey(const CKey& key, const CPubKey &pubkey);
607609
void WriteDescriptor();
608610

src/wallet/wallet.cpp

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5750,16 +5750,42 @@ void CWallet::AddActiveScriptPubKeyMan(uint256 id, bool internal)
57505750

57515751
void CWallet::LoadActiveScriptPubKeyMan(uint256 id, bool internal)
57525752
{
5753+
// Activating ScriptPubKeyManager for a given output and change type is incompatible with legacy wallets.
5754+
// Legacy wallets have only one ScriptPubKeyManager and it's active for all output and change types.
5755+
Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
5756+
57535757
WalletLogPrintf("Setting spkMan to active: id = %s, type = %d, internal = %d\n", id.ToString(), static_cast<int>(OutputType::LEGACY), static_cast<int>(internal));
57545758
auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
5759+
auto& spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers;
57555760
auto spk_man = m_spk_managers.at(id).get();
57565761
spk_man->SetInternal(internal);
57575762
spk_mans = spk_man;
57585763

5764+
if (spk_mans_other == spk_man) {
5765+
spk_mans_other = nullptr;
5766+
}
5767+
57595768
NotifyCanGetAddressesChanged();
57605769

57615770
}
57625771

5772+
void CWallet::DeactivateScriptPubKeyMan(uint256 id, bool internal)
5773+
{
5774+
auto spk_man = GetScriptPubKeyMan(internal);
5775+
if (spk_man != nullptr && spk_man->GetID() == id) {
5776+
WalletLogPrintf("Deactivate spkMan: id = %s, type = %d, internal = %d\n", id.ToString(), static_cast<int>(OutputType::LEGACY), static_cast<int>(internal));
5777+
WalletBatch batch(GetDatabase());
5778+
if (!batch.EraseActiveScriptPubKeyMan(internal)) {
5779+
throw std::runtime_error(std::string(__func__) + ": erasing active ScriptPubKeyMan id failed");
5780+
}
5781+
5782+
auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
5783+
spk_mans = nullptr;
5784+
}
5785+
5786+
NotifyCanGetAddressesChanged();
5787+
}
5788+
57635789
bool CWallet::IsLegacy() const
57645790
{
57655791
if (m_internal_spk_managers == nullptr) return false;
@@ -5788,52 +5814,34 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
57885814
}
57895815

57905816
LOCK(cs_wallet);
5791-
auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
5792-
5793-
// If we already have this descriptor, remove it from the maps but add the existing cache to desc
5794-
auto old_spk_man = GetDescriptorScriptPubKeyMan(desc);
5795-
if (old_spk_man) {
5817+
auto spk_man = GetDescriptorScriptPubKeyMan(desc);
5818+
if (spk_man) {
57965819
WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
5820+
spk_man->UpdateWalletDescriptor(desc);
5821+
} else {
5822+
auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
5823+
spk_man = new_spk_man.get();
57975824

5798-
{
5799-
LOCK(old_spk_man->cs_desc_man);
5800-
new_spk_man->SetCache(old_spk_man->GetWalletDescriptor().cache);
5801-
}
5802-
5803-
// Remove from maps of active spkMans
5804-
auto old_spk_man_id = old_spk_man->GetID();
5805-
for (bool internal : {false, true}) {
5806-
{ // only one OutputType - LEGACY
5807-
auto active_spk_man = GetScriptPubKeyMan(internal);
5808-
if (active_spk_man && active_spk_man->GetID() == old_spk_man_id) {
5809-
if (internal) {
5810-
m_internal_spk_managers = nullptr;
5811-
} else {
5812-
m_external_spk_managers = nullptr;
5813-
}
5814-
break;
5815-
}
5816-
}
5817-
}
5818-
m_spk_managers.erase(old_spk_man_id);
5825+
// Save the descriptor to memory
5826+
m_spk_managers[new_spk_man->GetID()] = std::move(new_spk_man);
58195827
}
58205828

58215829
// Add the private keys to the descriptor
58225830
for (const auto& entry : signing_provider.keys) {
58235831
const CKey& key = entry.second;
5824-
new_spk_man->AddDescriptorKey(key, key.GetPubKey());
5832+
spk_man->AddDescriptorKey(key, key.GetPubKey());
58255833
}
58265834

58275835
// Top up key pool, the manager will generate new scriptPubKeys internally
5828-
if (!new_spk_man->TopUp()) {
5836+
if (!spk_man->TopUp()) {
58295837
WalletLogPrintf("Could not top up scriptPubKeys\n");
58305838
return nullptr;
58315839
}
58325840

58335841
// Apply the label if necessary
58345842
// Note: we disable labels for ranged descriptors
58355843
if (!desc.descriptor->IsRange()) {
5836-
auto script_pub_keys = new_spk_man->GetScriptPubKeys();
5844+
auto script_pub_keys = spk_man->GetScriptPubKeys();
58375845
if (script_pub_keys.empty()) {
58385846
WalletLogPrintf("Could not generate scriptPubKeys (cache is empty)\n");
58395847
return nullptr;
@@ -5845,12 +5853,8 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
58455853
}
58465854
}
58475855

5848-
// Save the descriptor to memory
5849-
auto ret = new_spk_man.get();
5850-
m_spk_managers[new_spk_man->GetID()] = std::move(new_spk_man);
5851-
58525856
// Save the descriptor to DB
5853-
ret->WriteDescriptor();
5857+
spk_man->WriteDescriptor();
58545858

5855-
return ret;
5859+
return spk_man;
58565860
}

src/wallet/wallet.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,6 +1470,12 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
14701470
//! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
14711471
void LoadActiveScriptPubKeyMan(uint256 id, bool internal);
14721472

1473+
//! Remove specified ScriptPubKeyMan from set of active SPK managers. Writes the change to the wallet file.
1474+
//! @param[in] id The unique id for the ScriptPubKeyMan
1475+
//! @param[in] type The OutputType this ScriptPubKeyMan provides addresses for
1476+
//! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
1477+
void DeactivateScriptPubKeyMan(uint256 id, bool internal);
1478+
14731479
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
14741480
void SetupDescriptorScriptPubKeyMans();
14751481

src/wallet/walletdb.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ bool WalletBatch::WriteActiveScriptPubKeyMan(const uint256& id, bool internal)
234234
return WriteIC(key, id);
235235
}
236236

237+
bool WalletBatch::EraseActiveScriptPubKeyMan(bool internal)
238+
{
239+
const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
240+
return EraseIC(key);
241+
}
242+
237243
bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
238244
{
239245
// hash pubkey/privkey to accelerate wallet load

src/wallet/walletdb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class WalletBatch
226226
bool EraseDestData(const std::string &address, const std::string &key);
227227

228228
bool WriteActiveScriptPubKeyMan(const uint256& id, bool internal);
229+
bool EraseActiveScriptPubKeyMan(bool internal);
229230

230231
DBErrors LoadWallet(CWallet* pwallet);
231232
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);

0 commit comments

Comments
 (0)