Skip to content

Commit b878b1f

Browse files
Merge dashpay#6570: feat: mnemonic support for descriptor wallets
42e80f1 refactor: add prefix crypted_ for arguments related to crypted mnemonic (Konstantin Akimov) c89e181 feat: improve wording for errors, RPC and logs (Konstantin Akimov) b3a7475 refactor: removed dead commented code from serialize.h (Konstantin Akimov) c8a6f69 refactor: use args instead gArgs in new mnemonic code (Konstantin Akimov) d093506 fix: compilation errors due to advancing develop branch (Konstantin Akimov) e636d19 test: multiple fixes in wallet_mnemonics to fix review comments (Konstantin Akimov) 3a8ab80 fix: intermittent error in wallet_mnemonic.py due to different order of descriptors (Konstantin Akimov) 4bd214e test: adjust commentary, ensure cb private key is "inactive" descriptor (Konstantin Akimov) 8eb5d4a fix: do not reset seed / mnemonic for descriptor wallets in case of encryption (Konstantin Akimov) e27f95c fix: text message for HD wallets (both descriptors and non-descriptors) (Konstantin Akimov) 4f6be3d test: make functional test wallet_mnemonicbits.py works for descriptor wallets (Konstantin Akimov) 979ed95 feat: implement mnemonic for descriptor wallets (Konstantin Akimov) 267693f fix: expand text commentary for RPC encryptwallet (Konstantin Akimov) d287f09 refactor: drop -usehd=1 from some functional tests so far as it is default option (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented Descriptor wallets doesn't support yet mnemonics (BIP39), which are supported only by legacy (non-descriptor) wallet. ## What was done? This PR adds basic support BIP39 to descriptor wallets: - newly created descriptor wallets will have mnemonic - RPC `upgradetohd` now support descriptor wallets and let to set specifict mnemonic with passphrase to newly created wallet - mnemonic is shown when calling RPC `listdescriptors` if exists ## How Has This Been Tested? Functional test `wallet_mnemonicbits.py` support `--descriptors` now Functional test `wallet_upgradetohd.py` is updated also ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK 42e80f1 kwvg: utACK 42e80f1 Tree-SHA512: 2f55dd291356da8bfca31bd270c1a22056e7d2977e5f91761970cfaa6ccf5038cc930d59b6a572a2fcc6198ac9031ab177ca86d63155a6b3bd86bd92d3dbb4ee
2 parents 4653ea3 + 42e80f1 commit b878b1f

22 files changed

+329
-77
lines changed

src/bench/wallet_balance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
2828
{
2929
LOCK(wallet.cs_wallet);
3030
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
31-
wallet.SetupDescriptorScriptPubKeyMans();
31+
wallet.SetupDescriptorScriptPubKeyMans("", "");
3232
if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
3333
}
3434
auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});

src/qt/test/addressbooktests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
7676
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
7777
{
7878
LOCK(wallet->cs_wallet);
79-
wallet->SetupDescriptorScriptPubKeyMans();
79+
wallet->SetupDescriptorScriptPubKeyMans("", "");
8080
}
8181

8282
auto build_address = [wallet]() {

src/qt/test/wallettests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void TestGUI(interfaces::Node& node)
126126
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
127127
{
128128
LOCK(wallet->cs_wallet);
129-
wallet->SetupDescriptorScriptPubKeyMans();
129+
wallet->SetupDescriptorScriptPubKeyMans("", "");
130130

131131
// Add the coinbase key
132132
FlatSigningProvider provider;

src/serialize.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <utility>
2727
#include <vector>
2828

29+
#include <support/allocators/secure.h>
2930
#include <prevector.h>
3031
#include <span.h>
3132

@@ -820,8 +821,8 @@ struct VectorFormatter
820821
/**
821822
* string
822823
*/
823-
template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string<C>& str);
824-
template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str);
824+
template<typename Stream, typename A, typename B, typename C> void Serialize(Stream& os, const std::basic_string<A, B, C>& str);
825+
template<typename Stream, typename A, typename B, typename C> void Unserialize(Stream& is, std::basic_string<A, B, C>& str);
825826

826827
/**
827828
* prevector
@@ -951,16 +952,16 @@ struct DefaultFormatter
951952
/**
952953
* string
953954
*/
954-
template<typename Stream, typename C>
955-
void Serialize(Stream& os, const std::basic_string<C>& str)
955+
template<typename Stream, typename A, typename B, typename C>
956+
void Serialize(Stream& os, const std::basic_string<A, B, C>& str)
956957
{
957958
WriteCompactSize(os, str.size());
958959
if (!str.empty())
959960
os.write(MakeByteSpan(str));
960961
}
961962

962-
template<typename Stream, typename C>
963-
void Unserialize(Stream& is, std::basic_string<C>& str)
963+
template<typename Stream, typename A, typename B, typename C>
964+
void Unserialize(Stream& is, std::basic_string<A, B, C>& str)
964965
{
965966
unsigned int nSize = ReadCompactSize(is);
966967
str.resize(nSize);

src/wallet/rpc/backup.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -874,9 +874,9 @@ RPCHelpMan dumphdinfo()
874874
RPCResult{
875875
RPCResult::Type::OBJ, "", "",
876876
{
877-
{RPCResult::Type::STR_HEX, "hdseed", "The HD seed (bip32, in hex)"},
878-
{RPCResult::Type::STR, "mnemonic", "The mnemonic for this HD wallet (bip39, english words)"},
879-
{RPCResult::Type::STR, "mnemonicpassphrase", "The mnemonic passphrase for this HD wallet (bip39)"},
877+
{RPCResult::Type::STR_HEX, "hdseed", "The HD seed (BIP32, in hex)"},
878+
{RPCResult::Type::STR, "mnemonic", "The mnemonic for this HD wallet (BIP39, english words)"},
879+
{RPCResult::Type::STR, "mnemonicpassphrase", "The mnemonic passphrase for this HD wallet (BIP39)"},
880880
}
881881
},
882882
RPCExamples{
@@ -1969,6 +1969,8 @@ RPCHelpMan listdescriptors()
19691969
{
19701970
{RPCResult::Type::OBJ, "", "", {
19711971
{RPCResult::Type::STR, "desc", "Descriptor string representation"},
1972+
{RPCResult::Type::STR, "mnemonic", "The mnemonic for this descriptor wallet (BIP39, english words). Presented only if private=true and created with a mnemonic"},
1973+
{RPCResult::Type::STR, "mnemonicpassphrase", "The mnemonic passphrase for this descriptor wallet (BIP39). Presented only if private=true and created with a mnemonic"},
19721974
{RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
19731975
{RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
19741976
{RPCResult::Type::BOOL, "internal", /*optional=*/true, "True if this descriptor is used to generate change addresses. False if this descriptor is used to generate receiving addresses; defined only for active descriptors"},
@@ -2015,6 +2017,14 @@ RPCHelpMan listdescriptors()
20152017
if (!desc_spk_man->GetDescriptorString(descriptor, priv)) {
20162018
throw JSONRPCError(RPC_WALLET_ERROR, "Can't get descriptor string.");
20172019
}
2020+
if (priv) {
2021+
SecureString mnemonic;
2022+
SecureString mnemonic_passphrase;
2023+
if (desc_spk_man->GetMnemonicString(mnemonic, mnemonic_passphrase) && !mnemonic.empty()) {
2024+
spk.pushKV("mnemonic", mnemonic.c_str());
2025+
spk.pushKV("mnemonicpassphrase", mnemonic_passphrase.c_str());
2026+
}
2027+
}
20182028
spk.pushKV("desc", descriptor);
20192029
spk.pushKV("timestamp", wallet_descriptor.creation_time);
20202030
const bool active = active_spk_mans.count(desc_spk_man) != 0;

src/wallet/rpc/encrypt.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,10 @@ RPCHelpMan encryptwallet()
252252
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
253253
}
254254

255-
return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
255+
if (pwallet->IsHDEnabled()) {
256+
return "wallet encrypted; If you forget the passphrase, you will lose access to your funds. Make sure that you have backup of your seed or mnemonic.";
257+
}
258+
return "wallet encrypted; The keypool has been flushed. You need to make a new backup.";
256259
},
257260
};
258261
}

src/wallet/scriptpubkeyman.cpp

Lines changed: 128 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <util/strencodings.h>
1414
#include <util/system.h>
1515
#include <util/translation.h>
16+
#include <wallet/bip39.h>
1617
#include <wallet/scriptpubkeyman.h>
1718

1819
namespace wallet {
@@ -1849,6 +1850,7 @@ bool DescriptorScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master
18491850
keyFail = true;
18501851
break;
18511852
}
1853+
// TODO: test for mnemonics
18521854
keyPass = true;
18531855
if (m_decryption_thoroughly_checked)
18541856
break;
@@ -1875,15 +1877,34 @@ bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, Walle
18751877
{
18761878
const CKey &key = key_in.second;
18771879
CPubKey pubkey = key.GetPubKey();
1880+
assert(pubkey.GetID() == key_in.first);
1881+
const auto mnemonic_in = m_mnemonics.find(key_in.first);
18781882
CKeyingMaterial secret(key.begin(), key.end());
18791883
std::vector<unsigned char> crypted_secret;
18801884
if (!EncryptSecret(master_key, secret, pubkey.GetHash(), crypted_secret)) {
18811885
return false;
18821886
}
1887+
std::vector<unsigned char> crypted_mnemonic;
1888+
std::vector<unsigned char> crypted_mnemonic_passphrase;
1889+
if (mnemonic_in != m_mnemonics.end()) {
1890+
const Mnemonic mnemonic = mnemonic_in->second;
1891+
1892+
CKeyingMaterial mnemonic_secret(mnemonic.first.begin(), mnemonic.first.end());
1893+
CKeyingMaterial mnemonic_passphrase_secret(mnemonic.second.begin(), mnemonic.second.end());
1894+
if (!EncryptSecret(master_key, mnemonic_secret, pubkey.GetHash(), crypted_mnemonic)) {
1895+
return false;
1896+
}
1897+
if (!EncryptSecret(master_key, mnemonic_passphrase_secret, pubkey.GetHash(), crypted_mnemonic_passphrase)) {
1898+
return false;
1899+
}
1900+
}
1901+
18831902
m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret);
1884-
batch->WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret);
1903+
m_crypted_mnemonics[pubkey.GetID()] = make_pair(crypted_mnemonic, crypted_mnemonic_passphrase);
1904+
batch->WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret, crypted_mnemonic, crypted_mnemonic_passphrase);
18851905
}
18861906
m_map_keys.clear();
1907+
m_mnemonics.clear();
18871908
return true;
18881909
}
18891910

@@ -2008,12 +2029,12 @@ void DescriptorScriptPubKeyMan::AddDescriptorKey(const CKey& key, const CPubKey
20082029
{
20092030
LOCK(cs_desc_man);
20102031
WalletBatch batch(m_storage.GetDatabase());
2011-
if (!AddDescriptorKeyWithDB(batch, key, pubkey)) {
2032+
if (!AddDescriptorKeyWithDB(batch, key, pubkey, "", "")) {
20122033
throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed");
20132034
}
20142035
}
20152036

2016-
bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey)
2037+
bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey, const SecureString& mnemonic, const SecureString& mnemonic_passphrase)
20172038
{
20182039
AssertLockHeld(cs_desc_man);
20192040
assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
@@ -2030,22 +2051,37 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
20302051
}
20312052

20322053
std::vector<unsigned char> crypted_secret;
2054+
std::vector<unsigned char> crypted_mnemonic;
2055+
std::vector<unsigned char> crypted_mnemonic_passphrase;
20332056
CKeyingMaterial secret(key.begin(), key.end());
2057+
CKeyingMaterial mnemonic_secret(mnemonic.begin(), mnemonic.end());
2058+
CKeyingMaterial mnemonic_passphrase_secret(mnemonic_passphrase.begin(), mnemonic_passphrase.end());
20342059
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
2035-
return EncryptSecret(encryption_key, secret, pubkey.GetHash(), crypted_secret);
2060+
if (!EncryptSecret(encryption_key, secret, pubkey.GetHash(), crypted_secret)) return false;
2061+
if (!mnemonic.empty()) {
2062+
if (!EncryptSecret(encryption_key, mnemonic_secret, pubkey.GetHash(), crypted_mnemonic)) {
2063+
return false;
2064+
}
2065+
if (!EncryptSecret(encryption_key, mnemonic_passphrase_secret, pubkey.GetHash(), crypted_mnemonic_passphrase)) {
2066+
return false;
2067+
}
2068+
}
2069+
return true;
20362070
})) {
20372071
return false;
20382072
}
20392073

20402074
m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret);
2041-
return batch.WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret);
2075+
m_crypted_mnemonics[pubkey.GetID()] = make_pair(crypted_mnemonic, crypted_mnemonic_passphrase);
2076+
return batch.WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret, crypted_mnemonic, crypted_mnemonic_passphrase);
20422077
} else {
20432078
m_map_keys[pubkey.GetID()] = key;
2044-
return batch.WriteDescriptorKey(GetID(), pubkey, key.GetPrivKey());
2079+
m_mnemonics[pubkey.GetID()] = make_pair(mnemonic, mnemonic_passphrase);
2080+
return batch.WriteDescriptorKey(GetID(), pubkey, key.GetPrivKey(), mnemonic, mnemonic_passphrase);
20452081
}
20462082
}
20472083

2048-
bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, bool internal)
2084+
bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, const SecureString& secure_mnemonic, const SecureString& secure_mnemonic_passphrase, bool internal)
20492085
{
20502086
LOCK(cs_desc_man);
20512087
assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
@@ -2055,6 +2091,16 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
20552091
return false;
20562092
}
20572093

2094+
if (!secure_mnemonic.empty()) {
2095+
// TODO: remove duplicated code with AddKey()
2096+
SecureVector seed_key_tmp;
2097+
CMnemonic::ToSeed(secure_mnemonic, secure_mnemonic_passphrase, seed_key_tmp);
2098+
2099+
CExtKey master_key_tmp;
2100+
master_key_tmp.SetSeed(MakeByteSpan(seed_key_tmp));
2101+
assert(master_key == master_key_tmp);
2102+
}
2103+
20582104
int64_t creation_time = GetTime();
20592105

20602106
std::string xpub = EncodeExtPubKey(master_key.Neuter());
@@ -2075,7 +2121,7 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_
20752121

20762122
// Store the master private key, and descriptor
20772123
WalletBatch batch(m_storage.GetDatabase());
2078-
if (!AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey())) {
2124+
if (!AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey(), secure_mnemonic, secure_mnemonic_passphrase)) {
20792125
throw std::runtime_error(std::string(__func__) + ": writing descriptor master private key failed");
20802126
}
20812127
if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) {
@@ -2356,21 +2402,34 @@ void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache)
23562402
}
23572403
}
23582404

2359-
bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key)
2405+
bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key, const SecureString& mnemonic, const SecureString& mnemonic_passphrase)
23602406
{
23612407
LOCK(cs_desc_man);
2408+
if (!mnemonic.empty()) {
2409+
// TODO: remove duplicated code with AddKey()
2410+
SecureVector seed_key_tmp;
2411+
CMnemonic::ToSeed(mnemonic, mnemonic_passphrase, seed_key_tmp);
2412+
2413+
CExtKey master_key_tmp;
2414+
master_key_tmp.SetSeed(MakeByteSpan(seed_key_tmp));
2415+
assert(key == master_key_tmp.key);
2416+
}
2417+
23622418
m_map_keys[key_id] = key;
2419+
m_mnemonics[key_id] = make_pair(mnemonic, mnemonic_passphrase);
2420+
23632421
return true;
23642422
}
23652423

2366-
bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key)
2424+
bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key, const std::vector<unsigned char>& crypted_mnemonic,const std::vector<unsigned char>& crypted_mnemonic_passphrase)
23672425
{
23682426
LOCK(cs_desc_man);
23692427
if (!m_map_keys.empty()) {
23702428
return false;
23712429
}
23722430

23732431
m_map_crypted_keys[key_id] = make_pair(pubkey, crypted_key);
2432+
m_crypted_mnemonics[key_id] = make_pair(crypted_mnemonic, crypted_mnemonic_passphrase);
23742433
return true;
23752434
}
23762435

@@ -2412,7 +2471,6 @@ bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, const bool
24122471

24132472
FlatSigningProvider provider;
24142473
provider.keys = GetKeys();
2415-
24162474
if (priv) {
24172475
// For the private version, always return the master key to avoid
24182476
// exposing child private keys. The risk implications of exposing child
@@ -2423,6 +2481,65 @@ bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, const bool
24232481
return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, &m_wallet_descriptor.cache);
24242482
}
24252483

2484+
bool DescriptorScriptPubKeyMan::GetMnemonicString(SecureString& mnemonic_out, SecureString& mnemonic_passphrase_out) const
2485+
{
2486+
LOCK(cs_desc_man);
2487+
2488+
mnemonic_out.clear();
2489+
mnemonic_passphrase_out.clear();
2490+
2491+
if (m_mnemonics.empty() && m_crypted_mnemonics.empty()) {
2492+
WalletLogPrintf("%s: Descriptor wallet has no mnemonic defined\n", __func__);
2493+
return false;
2494+
}
2495+
if (m_storage.IsLocked(false)) return false;
2496+
2497+
if (m_mnemonics.size() + m_crypted_mnemonics.size() > 1) {
2498+
WalletLogPrintf("%s: ERROR: One descriptor has multiple mnemonics. Can't match it\n", __func__);
2499+
return false;
2500+
}
2501+
if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked(true)) {
2502+
if (!m_crypted_mnemonics.empty() && m_map_crypted_keys.size() != 1) {
2503+
WalletLogPrintf("%s: ERROR: can't choose encryption key for mnemonic out of %lld\n", __func__, m_map_crypted_keys.size());
2504+
return false;
2505+
}
2506+
const CPubKey& pubkey = m_map_crypted_keys.begin()->second.first;
2507+
const auto mnemonic = m_crypted_mnemonics.begin()->second;
2508+
const std::vector<unsigned char>& crypted_mnemonic = mnemonic.first;
2509+
const std::vector<unsigned char>& crypted_mnemonic_passphrase = mnemonic.second;
2510+
2511+
SecureVector mnemonic_v;
2512+
SecureVector mnemonic_passphrase_v;
2513+
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
2514+
return DecryptSecret(encryption_key, crypted_mnemonic, pubkey.GetHash(), mnemonic_v);
2515+
})) {
2516+
WalletLogPrintf("%s: ERROR: can't decrypt mnemonic pubkey %s crypted: %s\n", __func__, pubkey.GetHash().ToString(), HexStr(crypted_mnemonic));
2517+
return false;
2518+
}
2519+
if (!crypted_mnemonic_passphrase.empty()) {
2520+
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
2521+
return DecryptSecret(encryption_key, crypted_mnemonic_passphrase, pubkey.GetHash(), mnemonic_passphrase_v);
2522+
})) {
2523+
WalletLogPrintf("%s: ERROR: can't decrypt mnemonic passphrase\n", __func__);
2524+
return false;
2525+
}
2526+
}
2527+
2528+
std::copy(mnemonic_v.begin(), mnemonic_v.end(), std::back_inserter(mnemonic_out));
2529+
std::copy(mnemonic_passphrase_v.begin(), mnemonic_passphrase_v.end(), std::back_inserter(mnemonic_passphrase_out));
2530+
2531+
return true;
2532+
}
2533+
if (m_mnemonics.empty()) return false;
2534+
2535+
const auto mnemonic_it = m_mnemonics.begin();
2536+
2537+
mnemonic_out = mnemonic_it->second.first;
2538+
mnemonic_passphrase_out = mnemonic_it->second.second;
2539+
2540+
return true;
2541+
}
2542+
24262543
void DescriptorScriptPubKeyMan::UpgradeDescriptorCache()
24272544
{
24282545
LOCK(cs_desc_man);

0 commit comments

Comments
 (0)