diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2018-08-28 16:25:00 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2018-08-28 16:25:04 +0200 |
commit | aa39ca764578a9017e03796b554955796022eb0d (patch) | |
tree | 14d07c3ad418b799d89f02ae53433ab30148ca40 | |
parent | ee9e6e7c5f762d86bfaf46c28ebb489420ae40ff (diff) | |
parent | 917353c8b0eff4cd95f9a5f7719f6756bb8338b1 (diff) |
Merge #13723: PSBT key path cleanups
917353c8b0eff4cd95f9a5f7719f6756bb8338b1 Make SignPSBTInput operate on a private SignatureData object (Pieter Wuille)
cad5dd2368109ec398a3b79c8b9e94dfd23f0845 Pass HD path data through SignatureData (Pieter Wuille)
03a99586a398ee38f40c3b72d24c6a2ba4b88579 Implement key origin lookup in CWallet (Pieter Wuille)
3b01efa0d1bf3d23d1b7b7e518849f1fc26314f9 [MOVEONLY] Move ParseHDKeypath to utilstrencodings (Pieter Wuille)
81e1dd5ce1a32114a38691ec6b55e72ab04dbbb1 Generalize PublicOnlySigningProvider into HidingSigningProvider (Pieter Wuille)
84f1f1bfdf900cd28099e428441aa42f9d11a0ed Make SigningProvider expose key origin information (Pieter Wuille)
611ab307fbd8b6f8f7ffc1d569bb86d1f9cb4e92 Introduce KeyOriginInfo for fingerprint + path (Pieter Wuille)
Pull request description:
This PR adds "key origin" (master fingeprint + key path) information to what is exposed from `SigningProvider`s, allowing this information to be used by the generic PSBT code instead of having the RPC pull it directly from the wallet.
This is also a preparation to having PSBT interact with output descriptors, which can then directly expose key origin information for the scripts they generate.
Tree-SHA512: c718382ba8ba2d6fc9a32c062bd4cff08b6f39b133838aa03115c39aeca0f654c7cc3ec72d87005bf8306e550824cd8eb9d60f0bd41784a3e22e17b2afcfe833
-rw-r--r-- | src/rpc/rawtransaction.cpp | 18 | ||||
-rw-r--r-- | src/script/sign.cpp | 55 | ||||
-rw-r--r-- | src/script/sign.h | 49 | ||||
-rw-r--r-- | src/utilstrencodings.cpp | 40 | ||||
-rw-r--r-- | src/utilstrencodings.h | 3 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 99 | ||||
-rw-r--r-- | src/wallet/test/psbt_wallet_tests.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 26 | ||||
-rw-r--r-- | src/wallet/wallet.h | 2 |
9 files changed, 160 insertions, 134 deletions
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7ca097f8cf..248b963c0b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1458,11 +1458,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) UniValue keypath(UniValue::VOBJ); keypath.pushKV("pubkey", HexStr(entry.first)); - uint32_t fingerprint = entry.second.at(0); - keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint))); - - entry.second.erase(entry.second.begin()); - keypath.pushKV("path", WriteHDKeypath(entry.second)); + keypath.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(entry.second.fingerprint))); + keypath.pushKV("path", WriteHDKeypath(entry.second.path)); keypaths.push_back(keypath); } in.pushKV("bip32_derivs", keypaths); @@ -1520,12 +1517,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) for (auto entry : output.hd_keypaths) { UniValue keypath(UniValue::VOBJ); keypath.pushKV("pubkey", HexStr(entry.first)); - - uint32_t fingerprint = entry.second.at(0); - keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint))); - - entry.second.erase(entry.second.begin()); - keypath.pushKV("path", WriteHDKeypath(entry.second)); + keypath.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(entry.second.fingerprint))); + keypath.pushKV("path", WriteHDKeypath(entry.second.path)); keypaths.push_back(keypath); } out.pushKV("bip32_derivs", keypaths); @@ -1646,8 +1639,7 @@ UniValue finalizepsbt(const JSONRPCRequest& request) for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { PSBTInput& input = psbtx.inputs.at(i); - SignatureData sigdata; - complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, sigdata, i, 1); + complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, i, 1); } UniValue result(UniValue::VOBJ); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 23af1bd97f..d779910425 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -50,10 +50,6 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) { - if (provider.GetPubKey(address, pubkey)) { - sigdata.misc_pubkeys.emplace(pubkey.GetID(), pubkey); - return true; - } // Look for pubkey in all partial sigs const auto it = sigdata.signatures.find(address); if (it != sigdata.signatures.end()) { @@ -63,7 +59,15 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c // Look for pubkey in pubkey list const auto& pk_it = sigdata.misc_pubkeys.find(address); if (pk_it != sigdata.misc_pubkeys.end()) { - pubkey = pk_it->second; + pubkey = pk_it->second.first; + return true; + } + // Query the underlying provider + if (provider.GetPubKey(address, pubkey)) { + KeyOriginInfo info; + if (provider.GetKeyOrigin(address, info)) { + sigdata.misc_pubkeys.emplace(address, std::make_pair(pubkey, std::move(info))); + } return true; } return false; @@ -232,7 +236,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato return sigdata.complete; } -bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash) +bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash) { // if this input has a final scriptsig or scriptwitness, don't do anything with it if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) { @@ -240,6 +244,7 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t } // Fill SignatureData with input info + SignatureData sigdata; input.FillSignatureData(sigdata); // Get UTXO @@ -271,6 +276,16 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t // Verify that a witness signature was produced in case one was required. if (require_witness_sig && !sigdata.witness) return false; input.FromSignatureData(sigdata); + + // If both UTXO types are present, drop the unnecessary one. + if (input.non_witness_utxo && !input.witness_utxo.IsNull()) { + if (sigdata.witness) { + input.non_witness_utxo = nullptr; + } else { + input.witness_utxo.SetNull(); + } + } + return sig_complete; } @@ -541,7 +556,7 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const sigdata.witness_script = witness_script; } for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first); + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } } @@ -569,6 +584,9 @@ void PSBTInput::FromSignatureData(const SignatureData& sigdata) if (witness_script.empty() && !sigdata.witness_script.empty()) { witness_script = sigdata.witness_script; } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } } void PSBTInput::Merge(const PSBTInput& input) @@ -610,7 +628,7 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const sigdata.witness_script = witness_script; } for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first); + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } } @@ -622,6 +640,9 @@ void PSBTOutput::FromSignatureData(const SignatureData& sigdata) if (witness_script.empty() && !sigdata.witness_script.empty()) { witness_script = sigdata.witness_script; } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } } bool PSBTOutput::IsNull() const @@ -638,14 +659,26 @@ void PSBTOutput::Merge(const PSBTOutput& output) if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script; } -bool PublicOnlySigningProvider::GetCScript(const CScriptID &scriptid, CScript& script) const +bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return m_provider->GetCScript(scriptid, script); } -bool PublicOnlySigningProvider::GetPubKey(const CKeyID &address, CPubKey& pubkey) const +bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const +{ + return m_provider->GetPubKey(keyid, pubkey); +} + +bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const +{ + if (m_hide_secret) return false; + return m_provider->GetKey(keyid, key); +} + +bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { - return m_provider->GetPubKey(address, pubkey); + if (m_hide_origin) return false; + return m_provider->GetKeyOrigin(keyid, info); } bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); } diff --git a/src/script/sign.h b/src/script/sign.h index 7ade715ee2..18b7320998 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -20,6 +20,12 @@ class CTransaction; struct CMutableTransaction; +struct KeyOriginInfo +{ + unsigned char fingerprint[4]; + std::vector<uint32_t> path; +}; + /** An interface to be implemented by keystores that support signing. */ class SigningProvider { @@ -28,19 +34,24 @@ public: virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; } virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; } virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; } + virtual bool GetKeyOrigin(const CKeyID& id, KeyOriginInfo& info) const { return false; } }; extern const SigningProvider& DUMMY_SIGNING_PROVIDER; -class PublicOnlySigningProvider : public SigningProvider +class HidingSigningProvider : public SigningProvider { private: + const bool m_hide_secret; + const bool m_hide_origin; const SigningProvider* m_provider; public: - PublicOnlySigningProvider(const SigningProvider* provider) : m_provider(provider) {} - bool GetCScript(const CScriptID &scriptid, CScript& script) const; - bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const; + HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {} + bool GetCScript(const CScriptID& scriptid, CScript& script) const override; + bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; + bool GetKey(const CKeyID& keyid, CKey& key) const override; + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; }; struct FlatSigningProvider final : public SigningProvider @@ -98,7 +109,7 @@ struct SignatureData { CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. - std::map<CKeyID, CPubKey> misc_pubkeys; + std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys; SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} @@ -155,7 +166,7 @@ void UnserializeFromVector(Stream& s, X&... args) // Deserialize HD keypaths into a map template<typename Stream> -void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths) +void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths) { // Make sure that the key is the size of pubkey + 1 if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) { @@ -172,25 +183,31 @@ void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std // Read in key path uint64_t value_len = ReadCompactSize(s); - std::vector<uint32_t> keypath; - for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) { + if (value_len % 4 || value_len == 0) { + throw std::ios_base::failure("Invalid length for HD key path"); + } + + KeyOriginInfo keypath; + s >> keypath.fingerprint; + for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) { uint32_t index; s >> index; - keypath.push_back(index); + keypath.path.push_back(index); } // Add to map - hd_keypaths.emplace(pubkey, keypath); + hd_keypaths.emplace(pubkey, std::move(keypath)); } // Serialize HD keypaths to a stream from a map template<typename Stream> -void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths, uint8_t type) +void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type) { for (auto keypath_pair : hd_keypaths) { SerializeToVector(s, type, MakeSpan(keypath_pair.first)); - WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t)); - for (auto& path : keypath_pair.second) { + WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t)); + s << keypath_pair.second.fingerprint; + for (const auto& path : keypath_pair.second.path) { s << path; } } @@ -205,7 +222,7 @@ struct PSBTInput CScript witness_script; CScript final_script_sig; CScriptWitness final_script_witness; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<CKeyID, SigPair> partial_sigs; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; int sighash_type = 0; @@ -418,7 +435,7 @@ struct PSBTOutput { CScript redeem_script; CScript witness_script; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; bool IsNull() const; @@ -687,7 +704,7 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); /** Signs a PSBTInput, verifying that all provided data matches what is being signed. */ -bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1); +bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash = SIGHASH_ALL); /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index a06d88cb19..e66e2c238c 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -544,3 +544,43 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) return true; } +bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath) +{ + std::stringstream ss(keypath_str); + std::string item; + bool first = true; + while (std::getline(ss, item, '/')) { + if (item.compare("m") == 0) { + if (first) { + first = false; + continue; + } + return false; + } + // Finds whether it is hardened + uint32_t path = 0; + size_t pos = item.find("'"); + if (pos != std::string::npos) { + // The hardened tick can only be in the last index of the string + if (pos != item.size() - 1) { + return false; + } + path |= 0x80000000; + item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick + } + + // Ensure this is only numbers + if (item.find_first_not_of( "0123456789" ) != std::string::npos) { + return false; + } + uint32_t number; + if (!ParseUInt32(item, &number)) { + return false; + } + path |= number; + + keypath.push_back(path); + first = false; + } + return true; +} diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 5f2211b5dc..8f29f8f322 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -183,4 +183,7 @@ bool ConvertBits(const O& outfn, I it, I end) { return true; } +/** Parse an HD keypaths like "m/7/0'/2000". */ +bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath); + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c82c914e1e..0dd854bf9f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3842,74 +3842,17 @@ UniValue sethdseed(const JSONRPCRequest& request) return NullUniValue; } -bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath) -{ - std::stringstream ss(keypath_str); - std::string item; - bool first = true; - while (std::getline(ss, item, '/')) { - if (item.compare("m") == 0) { - if (first) { - first = false; - continue; - } - return false; - } - // Finds whether it is hardened - uint32_t path = 0; - size_t pos = item.find("'"); - if (pos != std::string::npos) { - // The hardened tick can only be in the last index of the string - if (pos != item.size() - 1) { - return false; - } - path |= 0x80000000; - item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick - } - - // Ensure this is only numbers - if (item.find_first_not_of( "0123456789" ) != std::string::npos) { - return false; - } - uint32_t number; - if (!ParseUInt32(item, &number)) { - return false; - } - path |= number; - - keypath.push_back(path); - first = false; - } - return true; -} - -void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths) +void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, KeyOriginInfo>& hd_keypaths) { CPubKey vchPubKey; if (!pwallet->GetPubKey(keyID, vchPubKey)) { return; } - CKeyMetadata meta; - auto it = pwallet->mapKeyMetadata.find(keyID); - if (it != pwallet->mapKeyMetadata.end()) { - meta = it->second; + KeyOriginInfo info; + if (!pwallet->GetKeyOrigin(keyID, info)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken"); } - std::vector<uint32_t> keypath; - if (!meta.hdKeypath.empty()) { - if (!ParseHDKeypath(meta.hdKeypath, keypath)) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken"); - } - // Get the proper master key id - CKey key; - pwallet->GetKey(meta.hd_seed_id, key); - CExtKey masterKey; - masterKey.SetSeed(key.begin(), key.size()); - // Add to map - keypath.insert(keypath.begin(), ReadLE32(masterKey.key.GetPubKey().GetID().begin())); - } else { // Single pubkeys get the master fingerprint of themselves - keypath.insert(keypath.begin(), ReadLE32(vchPubKey.GetID().begin())); - } - hd_keypaths.emplace(vchPubKey, keypath); + hd_keypaths.emplace(vchPubKey, std::move(info)); } bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type, bool sign, bool bip32derivs) @@ -3937,28 +3880,7 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match."); } - SignatureData sigdata; - if (sign) { - complete &= SignPSBTInput(*pwallet, *psbtx.tx, input, sigdata, i, sighash_type); - } else { - complete &= SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx, input, sigdata, i, sighash_type); - } - - if (it != pwallet->mapWallet.end()) { - // Drop the unnecessary UTXO if we added both from the wallet. - if (sigdata.witness) { - input.non_witness_utxo = nullptr; - } else { - input.witness_utxo.SetNull(); - } - } - - // Get public key paths - if (bip32derivs) { - for (const auto& pubkey_it : sigdata.misc_pubkeys) { - AddKeypathToMap(pwallet, pubkey_it.first, input.hd_keypaths); - } - } + complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), *psbtx.tx, input, i, sighash_type); } // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change @@ -3971,15 +3893,8 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C psbt_out.FillSignatureData(sigdata); MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1); - ProduceSignature(*pwallet, creator, out.scriptPubKey, sigdata); + ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata); psbt_out.FromSignatureData(sigdata); - - // Get public key paths - if (bip32derivs) { - for (const auto& pubkey_it : sigdata.misc_pubkeys) { - AddKeypathToMap(pwallet, pubkey_it.first, psbt_out.hd_keypaths); - } - } } return complete; } diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index 61c3fa94a6..526f2d983f 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -13,8 +13,6 @@ #include <test/test_bitcoin.h> #include <wallet/test/wallet_test_fixture.h> -extern bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath); - BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup) BOOST_AUTO_TEST_CASE(psbt_updater_test) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d0857bcb72..913334a767 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4469,3 +4469,29 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu } return groups; } + +bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const +{ + CKeyMetadata meta; + { + LOCK(cs_wallet); + auto it = mapKeyMetadata.find(keyID); + if (it != mapKeyMetadata.end()) { + meta = it->second; + } + } + if (!meta.hdKeypath.empty()) { + if (!ParseHDKeypath(meta.hdKeypath, info.path)) return false; + // Get the proper master key id + CKey key; + GetKey(meta.hd_seed_id, key); + CExtKey masterKey; + masterKey.SetSeed(key.begin(), key.size()); + // Compute identifier + CKeyID masterid = masterKey.key.GetPubKey().GetID(); + std::copy(masterid.begin(), masterid.begin() + 4, info.fingerprint); + } else { // Single pubkeys get the master fingerprint of themselves + std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint); + } + return true; +} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 15062680de..5f550319ed 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1218,6 +1218,8 @@ public: LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...); }; + /** Implement lookup of key origin information through wallet key metadata. */ + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; }; /** A key allocated from the key pool. */ |