Skip to content
Browse files

Merge #15749: Fix: importmulti only imports origin info for PKH outputs

b5d3987 Take non-importing keys into account for spendability warning in descriptor import (Pieter Wuille)
6e59700 Import all origin info in importmulti; even for non-importing pubkeys (Pieter Wuille)
9a93c91 Keep full pubkeys in FlatSigningProvider::origins (Pieter Wuille)

Pull request description:

  This fixes #15743 and #15742.

  Since #15263, pubkeys are no longer imported for non-PKH (or WPKH, or any wrapped form of those) outputs, as that would incorrectly mark outputs to single-key versions of multisig policies as watched.

  As a side effect, this change also caused origin info not to be imported anymore for multisig policies.

  Fix this by plumbing through the full pubkey information for origins in FlatSigningProvider, and then importing all origin info we have in `importmulti` (knowing more never hurts, and additional origin information has no negative consequences like importing the pubkeys themselves).

ACKs for commit b5d398:
    utACK b5d3987

Tree-SHA512: 37caa2be8d01b8baa12f70a58eaa7c583f5f0afbe012e02936dd8790dc5dc852f880b77258b34ddb68cae30c029585f2d1c4f5d00015380557a1e8b471e500f3
  • Loading branch information...
meshcollider committed Apr 9, 2019
2 parents db29856 + b5d3987 commit 54798c3a31b0fe3dbe3b0c2dd957ecce75e29797
@@ -436,7 +436,7 @@ class DescriptorImpl : public Descriptor
for (auto& entry : entries) {
pubkeys.push_back(entry.first);, std::move(entry.second));, std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
if (m_script_arg) {
for (const auto& subscript : subscripts) {
@@ -483,7 +483,13 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf

bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
std::pair<CPubKey, KeyOriginInfo> out;
bool ret = LookupHelper(origins, keyid, out);
if (ret) info = std::move(out.second);
return ret;
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }

FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
@@ -77,7 +77,7 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, KeyOriginInfo> origins;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;

bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
@@ -154,8 +154,8 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
// and then remove it from that set.
for (const auto& origin : {
BOOST_CHECK_MESSAGE(paths.count(origin.second.path), "Unexpected key path: " + prv);
BOOST_CHECK_MESSAGE(paths.count(origin.second.second.path), "Unexpected key path: " + prv);
@@ -900,7 +900,7 @@ struct ImportData
// Output data
std::set<CScript> import_scripts;
std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
std::map<CKeyID, KeyOriginInfo> key_origins;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;

enum class ScriptContext
@@ -1197,6 +1197,9 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
[&](const std::pair<CKeyID, CPubKey>& used_key) {
return privkey_map.count(used_key.first) > 0;
}) && std::all_of(import_data.key_origins.begin(), import_data.key_origins.end(),
[&](const std::pair<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& entry) {
return privkey_map.count(entry.first) > 0;
if (!watch_only && !spendable) {
warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
@@ -1274,7 +1277,10 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
for (const auto& entry : import_data.key_origins) {
pwallet->AddKeyOrigin(entry.second.first, entry.second.second);
for (const CKeyID& id : ordered_pubkeys) {
auto entry = pubkey_map.find(id);
if (entry == pubkey_map.end()) {
@@ -1285,10 +1291,6 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
const auto& key_orig_it = import_data.key_origins.find(id);
if (key_orig_it != import_data.key_origins.end()) {
pwallet->AddKeyOrigin(pubkey, key_orig_it->second);
pwallet->mapKeyMetadata[id].nCreateTime = timestamp;

// Add to keypool only works with pubkeys
@@ -629,7 +629,8 @@ def run_test(self):"Should import a 1-of-2 bare multisig from descriptor")
self.test_importmulti({"desc": descsum_create("multi(1," + key1.pubkey + "," + key2.pubkey + ")"),
"timestamp": "now"},
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])"Should not treat individual keys from the imported bare multisig as watchonly")

0 comments on commit 54798c3

Please sign in to comment.
You can’t perform that action at this time.