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:
Wladimir J. van der Laan
2018-08-28 16:25:00 +02:00
9 changed files with 160 additions and 134 deletions

View File

@@ -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;
}