mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
merge bitcoin#15024: Allow specific private keys to be derived from descriptor
This commit is contained in:
parent
94bcbf588d
commit
5df05449be
@ -166,6 +166,9 @@ struct PubkeyProvider
|
||||
|
||||
/** Get the descriptor string form including private data (if available in arg). */
|
||||
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
|
||||
|
||||
/** Derive a private key, if private data is available in arg. */
|
||||
virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
|
||||
};
|
||||
|
||||
class OriginPubkeyProvider final : public PubkeyProvider
|
||||
@ -197,6 +200,10 @@ public:
|
||||
ret = "[" + OriginString() + "]" + std::move(sub);
|
||||
return true;
|
||||
}
|
||||
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||
{
|
||||
return m_provider->GetPrivKey(pos, arg, key);
|
||||
}
|
||||
};
|
||||
|
||||
/** An object representing a parsed constant public key in a descriptor. */
|
||||
@ -224,6 +231,10 @@ public:
|
||||
ret = EncodeSecret(key);
|
||||
return true;
|
||||
}
|
||||
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||
{
|
||||
return arg.GetKey(m_pubkey.GetID(), key);
|
||||
}
|
||||
};
|
||||
|
||||
enum class DeriveType {
|
||||
@ -268,14 +279,9 @@ public:
|
||||
{
|
||||
if (key) {
|
||||
if (IsHardened()) {
|
||||
CExtKey extkey;
|
||||
if (!GetExtKey(arg, extkey)) return false;
|
||||
for (auto entry : m_path) {
|
||||
extkey.Derive(extkey, entry);
|
||||
}
|
||||
if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
|
||||
if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
|
||||
*key = extkey.Neuter().pubkey;
|
||||
CKey priv_key;
|
||||
if (!GetPrivKey(pos, arg, priv_key)) return false;
|
||||
*key = priv_key.GetPubKey();
|
||||
} else {
|
||||
// TODO: optimize by caching
|
||||
CExtPubKey extkey = m_extkey;
|
||||
@ -314,6 +320,18 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||
{
|
||||
CExtKey extkey;
|
||||
if (!GetExtKey(arg, extkey)) return false;
|
||||
for (auto entry : m_path) {
|
||||
extkey.Derive(extkey, entry);
|
||||
}
|
||||
if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
|
||||
if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
|
||||
key = extkey.key;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** Base class for all Descriptor implementations. */
|
||||
@ -468,6 +486,20 @@ public:
|
||||
Span<const unsigned char> span = MakeSpan(cache);
|
||||
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
|
||||
}
|
||||
|
||||
void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const final
|
||||
{
|
||||
for (const auto& p : m_pubkey_args) {
|
||||
CKey key;
|
||||
if (!p->GetPrivKey(pos, provider, key)) continue;
|
||||
out.keys.emplace(key.GetPubKey().GetID(), key);
|
||||
}
|
||||
if (m_subdescriptor_arg) {
|
||||
FlatSigningProvider subprovider;
|
||||
m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider);
|
||||
out = Merge(out, subprovider);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Construct a vector with one element, which is moved into it. */
|
||||
|
@ -63,6 +63,14 @@ struct Descriptor {
|
||||
* @param[out] out: Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
|
||||
*/
|
||||
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
|
||||
|
||||
/** Expand the private key for a descriptor at a specified position, if possible.
|
||||
*
|
||||
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
|
||||
* provider: the provider to query for the private keys.
|
||||
* out: any private keys available for the specified pos will be placed here.
|
||||
*/
|
||||
virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0;
|
||||
};
|
||||
|
||||
/** Parse a `descriptor` string. Included private keys are put in `out`.
|
||||
|
@ -1258,8 +1258,7 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
|
||||
|
||||
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
|
||||
|
||||
// Expand all descriptors to get public keys and scripts.
|
||||
// TODO: get private keys from descriptors too
|
||||
// Expand all descriptors to get public keys and scripts, and private keys if available.
|
||||
for (int i = range_start; i <= range_end; ++i) {
|
||||
FlatSigningProvider out_keys;
|
||||
std::vector<CScript> scripts_temp;
|
||||
@ -1273,7 +1272,10 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
|
||||
import_data.import_scripts.emplace(x.second);
|
||||
}
|
||||
|
||||
parsed_desc->ExpandPrivate(i, keys, out_keys);
|
||||
|
||||
std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
|
||||
std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
|
||||
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
|
||||
}
|
||||
|
||||
|
@ -448,13 +448,12 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
error_code=-8,
|
||||
error_message='Descriptor is ranged, please specify the range')
|
||||
|
||||
# Test importing of a ranged descriptor without keys
|
||||
# Test importing of a ranged descriptor with xpriv
|
||||
self.log.info("Should import the ranged descriptor with specified range as solvable")
|
||||
self.test_importmulti({"desc": descsum_create(desc),
|
||||
"timestamp": "now",
|
||||
"range": 1},
|
||||
success=True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
success=True)
|
||||
|
||||
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
|
||||
success=False, error_code=-8, error_message='End of range is too high')
|
||||
@ -471,6 +470,23 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
|
||||
success=False, error_code=-8, error_message='Range is too large')
|
||||
|
||||
# Test importing a descriptor containing a WIF private key
|
||||
wif_priv = "cTT3BvHnd51YJf8fkdr2XvZTQRRUZruWhRvRyQY1raVFg5Lvam2A"
|
||||
address = "ySWABbcNKyHUgBb1ffhpuETuis9jsdR3aq"
|
||||
desc = "sh(pkh(" + wif_priv + "))"
|
||||
self.log.info("Should import a descriptor with a WIF private key as spendable")
|
||||
self.test_importmulti({"desc": descsum_create(desc),
|
||||
"timestamp": "now"},
|
||||
success=True)
|
||||
test_address(self.nodes[1],
|
||||
address,
|
||||
solvable=True,
|
||||
ismine=True)
|
||||
|
||||
# dump the private key to ensure it matches what was imported
|
||||
privkey = self.nodes[1].dumpprivkey(address)
|
||||
assert_equal(privkey, wif_priv)
|
||||
|
||||
# Test importing of a P2PKH address via descriptor
|
||||
key = get_key(self.nodes[0])
|
||||
self.log.info("Should import a p2pkh address from descriptor")
|
||||
|
Loading…
Reference in New Issue
Block a user