Merge #13723: PSBT key path cleanups
917353c8b0
Make SignPSBTInput operate on a private SignatureData object (Pieter Wuille)cad5dd2368
Pass HD path data through SignatureData (Pieter Wuille)03a99586a3
Implement key origin lookup in CWallet (Pieter Wuille)3b01efa0d1
[MOVEONLY] Move ParseHDKeypath to utilstrencodings (Pieter Wuille)81e1dd5ce1
Generalize PublicOnlySigningProvider into HidingSigningProvider (Pieter Wuille)84f1f1bfdf
Make SigningProvider expose key origin information (Pieter Wuille)611ab307fb
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
This commit is contained in:
commit
aa39ca7645
9 changed files with 160 additions and 134 deletions
|
@ -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);
|
||||
|
|
|
@ -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(address, pubkey);
|
||||
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
|
||||
{
|
||||
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); }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
std::vector<uint32_t> keypath;
|
||||
if (!meta.hdKeypath.empty()) {
|
||||
if (!ParseHDKeypath(meta.hdKeypath, keypath)) {
|
||||
KeyOriginInfo info;
|
||||
if (!pwallet->GetKeyOrigin(keyID, info)) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in a new issue