Skip to content

Commit bce58bb

Browse files
committed
Merge bitcoin/bitcoin#22929: wallet: Automatically add receiving destinations to the address book
3d71d16 test: listtranscations with externally generated addresses (S3RK) d045664 Add to spends only transcations from me (S3RK) 9f3a622 Automatically add labels to detected receiving addresses (S3RK) c1b99c0 Return used destinations from ScriptPubKeyMan::MarkUnusedAddresses (S3RK) 03840c2 Add CWallet::IsInternalScriptPubKeyMan (S3RK) 456e350 wallet: resolve ambiguity of two ScriptPubKey managers providing same script (S3RK) Pull request description: This PR fixes certain use-cases when **send-to-self** transactions are missing from `listtransactions` output. 1. When a receiving address is generated externally to the wallet (e.g. same wallet running on two nodes, or by 3rd party from xpub) 2. When restoring backup with lost metadata, but keypool gap is not exceeded yet When the block is connected or tx added to mempool we already mark used keys. This PR extends this logic to determine whether the destination is a receiving one and if yes add it to the address book with empty label. Works both for legacy and descriptors wallets. - For legacy it uses the internal flag from the keypool entry. Caveat: because we don't know which script type would be used we add all possible destinations for such keys. - For descriptor wallets it uses internal flag for the script pub key manager. Caveat: it only works for active descriptors. fixes #19856 fixes #20293 ACKs for top commit: laanwj: Code review ACK 3d71d16 Tree-SHA512: 03fafd5548ead0c4ffe9ebcc9eb2849f1d2fa7270fda4166419b86877d4e57dcf04460e465fbb9c90b42031f3c05d1b83f1b67a9f82c2a42980825ed1e7b52e6
2 parents 26a1147 + 3d71d16 commit bce58bb

File tree

8 files changed

+202
-59
lines changed

8 files changed

+202
-59
lines changed

src/wallet/rpcdump.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,11 +1808,10 @@ RPCHelpMan listdescriptors()
18081808
}
18091809
spk.pushKV("desc", descriptor);
18101810
spk.pushKV("timestamp", wallet_descriptor.creation_time);
1811-
const bool active = active_spk_mans.count(desc_spk_man) != 0;
1812-
spk.pushKV("active", active);
1813-
const auto& type = wallet_descriptor.descriptor->GetOutputType();
1814-
if (active && type) {
1815-
spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man);
1811+
spk.pushKV("active", active_spk_mans.count(desc_spk_man) != 0);
1812+
const auto internal = wallet->IsInternalScriptPubKeyMan(desc_spk_man);
1813+
if (internal.has_value()) {
1814+
spk.pushKV("internal", *internal);
18161815
}
18171816
if (wallet_descriptor.descriptor->IsRange()) {
18181817
UniValue range(UniValue::VARR);

src/wallet/rpcwallet.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,8 +3940,12 @@ RPCHelpMan getaddressinfo()
39403940
ret.pushKV("solvable", false);
39413941
}
39423942

3943+
const auto& spk_mans = pwallet->GetScriptPubKeyMans(scriptPubKey);
3944+
// In most cases there is only one matching ScriptPubKey manager and we can't resolve ambiguity in a better way
3945+
ScriptPubKeyMan* spk_man{nullptr};
3946+
if (spk_mans.size()) spk_man = *spk_mans.begin();
39433947

3944-
DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey));
3948+
DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
39453949
if (desc_spk_man) {
39463950
std::string desc_str;
39473951
if (desc_spk_man->GetDescriptorString(desc_str, /* priv */ false)) {
@@ -3956,7 +3960,6 @@ RPCHelpMan getaddressinfo()
39563960

39573961
ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
39583962

3959-
ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
39603963
if (spk_man) {
39613964
if (const std::unique_ptr<CKeyMetadata> meta = spk_man->GetMetadata(dest)) {
39623965
ret.pushKV("timestamp", meta->nCreateTime);

src/wallet/scriptpubkeyman.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,15 +354,22 @@ bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t i
354354
return true;
355355
}
356356

357-
void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
357+
std::vector<WalletDestination> LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
358358
{
359359
LOCK(cs_KeyStore);
360+
std::vector<WalletDestination> result;
360361
// extract addresses and check if they match with an unused keypool key
361362
for (const auto& keyid : GetAffectedKeys(script, *this)) {
362363
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
363364
if (mi != m_pool_key_to_index.end()) {
364365
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__);
365-
MarkReserveKeysAsUsed(mi->second);
366+
for (const auto& keypool : MarkReserveKeysAsUsed(mi->second)) {
367+
// derive all possible destinations as any of them could have been used
368+
for (const auto& type : LEGACY_OUTPUT_TYPES) {
369+
const auto& dest = GetDestinationForKey(keypool.vchPubKey, type);
370+
result.push_back({dest, keypool.fInternal});
371+
}
372+
}
366373

367374
if (!TopUp()) {
368375
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
@@ -384,6 +391,8 @@ void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
384391
}
385392
}
386393
}
394+
395+
return result;
387396
}
388397

389398
void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
@@ -1427,14 +1436,15 @@ void LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey& key)
14271436
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
14281437
}
14291438

1430-
void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id)
1439+
std::vector<CKeyPool> LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id)
14311440
{
14321441
AssertLockHeld(cs_KeyStore);
14331442
bool internal = setInternalKeyPool.count(keypool_id);
14341443
if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id));
14351444
std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool);
14361445
auto it = setKeyPool->begin();
14371446

1447+
std::vector<CKeyPool> result;
14381448
WalletBatch batch(m_storage.GetDatabase());
14391449
while (it != std::end(*setKeyPool)) {
14401450
const int64_t& index = *(it);
@@ -1448,7 +1458,10 @@ void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id)
14481458
batch.ErasePool(index);
14491459
WalletLogPrintf("keypool index %d removed\n", index);
14501460
it = setKeyPool->erase(it);
1461+
result.push_back(std::move(keypool));
14511462
}
1463+
1464+
return result;
14521465
}
14531466

14541467
std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider)
@@ -1820,19 +1833,32 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
18201833
return true;
18211834
}
18221835

1823-
void DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
1836+
std::vector<WalletDestination> DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
18241837
{
18251838
LOCK(cs_desc_man);
1839+
std::vector<WalletDestination> result;
18261840
if (IsMine(script)) {
18271841
int32_t index = m_map_script_pub_keys[script];
18281842
if (index >= m_wallet_descriptor.next_index) {
18291843
WalletLogPrintf("%s: Detected a used keypool item at index %d, mark all keypool items up to this item as used\n", __func__, index);
1830-
m_wallet_descriptor.next_index = index + 1;
1844+
auto out_keys = std::make_unique<FlatSigningProvider>();
1845+
std::vector<CScript> scripts_temp;
1846+
while (index >= m_wallet_descriptor.next_index) {
1847+
if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) {
1848+
throw std::runtime_error(std::string(__func__) + ": Unable to expand descriptor from cache");
1849+
}
1850+
CTxDestination dest;
1851+
ExtractDestination(scripts_temp[0], dest);
1852+
result.push_back({dest, std::nullopt});
1853+
m_wallet_descriptor.next_index++;
1854+
}
18311855
}
18321856
if (!TopUp()) {
18331857
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
18341858
}
18351859
}
1860+
1861+
return result;
18361862
}
18371863

18381864
void DescriptorScriptPubKeyMan::AddDescriptorKey(const CKey& key, const CPubKey &pubkey)

src/wallet/scriptpubkeyman.h

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ class CKeyPool
149149
}
150150
};
151151

152+
struct WalletDestination
153+
{
154+
CTxDestination dest;
155+
std::optional<bool> internal;
156+
};
157+
152158
/*
153159
* A class implementing ScriptPubKeyMan manages some (or all) scriptPubKeys used in a wallet.
154160
* It contains the scripts and keys related to the scriptPubKeys it manages.
@@ -181,8 +187,14 @@ class ScriptPubKeyMan
181187
*/
182188
virtual bool TopUp(unsigned int size = 0) { return false; }
183189

184-
//! Mark unused addresses as being used
185-
virtual void MarkUnusedAddresses(const CScript& script) {}
190+
/** Mark unused addresses as being used
191+
* Affects all keys up to and including the one determined by provided script.
192+
*
193+
* @param script determines the last key to mark as used
194+
*
195+
* @return All of the addresses affected
196+
*/
197+
virtual std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) { return {}; }
186198

187199
/** Sets up the key generation stuff, i.e. generates new HD seeds and sets them as active.
188200
* Returns false if already setup or setup fails, true if setup is successful
@@ -357,7 +369,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
357369

358370
bool TopUp(unsigned int size = 0) override;
359371

360-
void MarkUnusedAddresses(const CScript& script) override;
372+
std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) override;
361373

362374
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
363375
void UpgradeKeyMetadata();
@@ -482,9 +494,13 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
482494
void LearnAllRelatedScripts(const CPubKey& key);
483495

484496
/**
485-
* Marks all keys in the keypool up to and including reserve_key as used.
497+
* Marks all keys in the keypool up to and including the provided key as used.
498+
*
499+
* @param keypool_id determines the last key to mark as used
500+
*
501+
* @return All affected keys
486502
*/
487-
void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
503+
std::vector<CKeyPool> MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
488504
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
489505

490506
std::set<CKeyID> GetKeys() const override;
@@ -564,7 +580,7 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
564580
// (with or without private keys), the "keypool" is a single xpub.
565581
bool TopUp(unsigned int size = 0) override;
566582

567-
void MarkUnusedAddresses(const CScript& script) override;
583+
std::vector<WalletDestination> MarkUnusedAddresses(const CScript& script) override;
568584

569585
bool IsHDEnabled() const override;
570586

src/wallet/test/wallet_tests.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -816,30 +816,35 @@ BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
816816
context.args = &gArgs;
817817
context.chain = m_node.chain.get();
818818
auto wallet = TestLoadWallet(context);
819-
CKey key;
820-
key.MakeNewKey(true);
821-
AddKey(*wallet, key);
819+
AddKey(*wallet, coinbaseKey);
822820

823-
std::string error;
821+
// rescan to ensure coinbase transactions from test fixture are picked up by the wallet
822+
{
823+
WalletRescanReserver reserver(*wallet);
824+
reserver.reserve();
825+
wallet->ScanForWalletTransactions(m_node.chain->getBlockHash(0), 0, /* max height= */ {}, reserver, /* update= */ true);
826+
}
827+
// create one more block to get the first block coinbase to maturity
824828
m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
825-
auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
826-
CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
829+
// spend first coinbase tx
830+
auto spend_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
831+
CreateAndProcessBlock({spend_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
827832

828833
SyncWithValidationInterfaceQueue();
829834

830835
{
831-
auto block_hash = block_tx.GetHash();
836+
auto spend_tx_hash = spend_tx.GetHash();
832837
auto prev_hash = m_coinbase_txns[0]->GetHash();
833838

834839
LOCK(wallet->cs_wallet);
835840
BOOST_CHECK(wallet->HasWalletSpend(prev_hash));
836-
BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u);
841+
BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 1u);
837842

838-
std::vector<uint256> vHashIn{ block_hash }, vHashOut;
843+
std::vector<uint256> vHashIn{spend_tx_hash}, vHashOut;
839844
BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK);
840845

841846
BOOST_CHECK(!wallet->HasWalletSpend(prev_hash));
842-
BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u);
847+
BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 0u);
843848
}
844849

845850
TestUnloadWallet(std::move(wallet));

src/wallet/wallet.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,9 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
919919
wtx.nOrderPos = IncOrderPosNext(&batch);
920920
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
921921
wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
922-
AddToSpends(hash, &batch);
922+
if (IsFromMe(*tx.get())) {
923+
AddToSpends(hash);
924+
}
923925
}
924926

925927
if (!fInsertedNew)
@@ -1061,8 +1063,23 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxS
10611063

10621064
// loop though all outputs
10631065
for (const CTxOut& txout: tx.vout) {
1064-
for (const auto& spk_man_pair : m_spk_managers) {
1065-
spk_man_pair.second->MarkUnusedAddresses(txout.scriptPubKey);
1066+
for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) {
1067+
for (auto &dest : spk_man->MarkUnusedAddresses(txout.scriptPubKey)) {
1068+
// If internal flag is not defined try to infer it from the ScriptPubKeyMan
1069+
if (!dest.internal.has_value()) {
1070+
dest.internal = IsInternalScriptPubKeyMan(spk_man);
1071+
}
1072+
1073+
// skip if can't determine whether it's a receiving address or not
1074+
if (!dest.internal.has_value()) continue;
1075+
1076+
// If this is a receiving address and it's not in the address book yet
1077+
// (e.g. it wasn't generated on this node or we're restoring from backup)
1078+
// add it to the address book for proper transaction accounting
1079+
if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) {
1080+
SetAddressBook(dest.dest, "", "receive");
1081+
}
1082+
}
10661083
}
10671084
}
10681085

@@ -2253,16 +2270,15 @@ void ReserveDestination::ReturnDestination()
22532270
bool CWallet::DisplayAddress(const CTxDestination& dest)
22542271
{
22552272
CScript scriptPubKey = GetScriptForDestination(dest);
2256-
const auto spk_man = GetScriptPubKeyMan(scriptPubKey);
2257-
if (spk_man == nullptr) {
2258-
return false;
2259-
}
2260-
auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan*>(spk_man);
2261-
if (signer_spk_man == nullptr) {
2262-
return false;
2273+
for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) {
2274+
auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan *>(spk_man);
2275+
if (signer_spk_man == nullptr) {
2276+
continue;
2277+
}
2278+
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
2279+
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
22632280
}
2264-
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
2265-
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
2281+
return false;
22662282
}
22672283

22682284
bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch)
@@ -3050,9 +3066,10 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern
30503066
return it->second;
30513067
}
30523068

3053-
std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const
3069+
std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const
30543070
{
30553071
std::set<ScriptPubKeyMan*> spk_mans;
3072+
SignatureData sigdata;
30563073
for (const auto& spk_man_pair : m_spk_managers) {
30573074
if (spk_man_pair.second->CanProvide(script, sigdata)) {
30583075
spk_mans.insert(spk_man_pair.second.get());
@@ -3061,17 +3078,6 @@ std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, S
30613078
return spk_mans;
30623079
}
30633080

3064-
ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
3065-
{
3066-
SignatureData sigdata;
3067-
for (const auto& spk_man_pair : m_spk_managers) {
3068-
if (spk_man_pair.second->CanProvide(script, sigdata)) {
3069-
return spk_man_pair.second.get();
3070-
}
3071-
}
3072-
return nullptr;
3073-
}
3074-
30753081
ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const
30763082
{
30773083
if (m_spk_managers.count(id) > 0) {
@@ -3287,6 +3293,30 @@ DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDes
32873293
return nullptr;
32883294
}
32893295

3296+
std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const
3297+
{
3298+
// Legacy script pubkey man can't be either external or internal
3299+
if (IsLegacy()) {
3300+
return std::nullopt;
3301+
}
3302+
3303+
// only active ScriptPubKeyMan can be internal
3304+
if (!GetActiveScriptPubKeyMans().count(spk_man)) {
3305+
return std::nullopt;
3306+
}
3307+
3308+
const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
3309+
if (!desc_spk_man) {
3310+
throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type.");
3311+
}
3312+
3313+
LOCK(desc_spk_man->cs_desc_man);
3314+
const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType();
3315+
assert(type.has_value());
3316+
3317+
return GetScriptPubKeyMan(*type, /* internal= */ true) == desc_spk_man;
3318+
}
3319+
32903320
ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal)
32913321
{
32923322
AssertLockHeld(cs_wallet);

src/wallet/wallet.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -809,14 +809,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
809809
//! Get the ScriptPubKeyMan for the given OutputType and internal/external chain.
810810
ScriptPubKeyMan* GetScriptPubKeyMan(const OutputType& type, bool internal) const;
811811

812-
//! Get the ScriptPubKeyMan for a script
813-
ScriptPubKeyMan* GetScriptPubKeyMan(const CScript& script) const;
812+
//! Get all the ScriptPubKeyMans for a script
813+
std::set<ScriptPubKeyMan*> GetScriptPubKeyMans(const CScript& script) const;
814814
//! Get the ScriptPubKeyMan by id
815815
ScriptPubKeyMan* GetScriptPubKeyMan(const uint256& id) const;
816816

817-
//! Get all of the ScriptPubKeyMans for a script given additional information in sigdata (populated by e.g. a psbt)
818-
std::set<ScriptPubKeyMan*> GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const;
819-
820817
//! Get the SigningProvider for a script
821818
std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const;
822819
std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script, SignatureData& sigdata) const;
@@ -882,6 +879,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
882879
//! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet
883880
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;
884881

882+
//! Returns whether the provided ScriptPubKeyMan is internal
883+
//! @param[in] spk_man The ScriptPubKeyMan to test
884+
//! @return contains value only for active DescriptorScriptPubKeyMan, otherwise undefined
885+
std::optional<bool> IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const;
886+
885887
//! Add a descriptor to the wallet, return a ScriptPubKeyMan & associated output type
886888
ScriptPubKeyMan* AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
887889
};

0 commit comments

Comments
 (0)