mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 12:32: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). */
|
/** Get the descriptor string form including private data (if available in arg). */
|
||||||
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
|
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
|
class OriginPubkeyProvider final : public PubkeyProvider
|
||||||
@ -197,6 +200,10 @@ public:
|
|||||||
ret = "[" + OriginString() + "]" + std::move(sub);
|
ret = "[" + OriginString() + "]" + std::move(sub);
|
||||||
return true;
|
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. */
|
/** An object representing a parsed constant public key in a descriptor. */
|
||||||
@ -224,6 +231,10 @@ public:
|
|||||||
ret = EncodeSecret(key);
|
ret = EncodeSecret(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
|
||||||
|
{
|
||||||
|
return arg.GetKey(m_pubkey.GetID(), key);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DeriveType {
|
enum class DeriveType {
|
||||||
@ -268,14 +279,9 @@ public:
|
|||||||
{
|
{
|
||||||
if (key) {
|
if (key) {
|
||||||
if (IsHardened()) {
|
if (IsHardened()) {
|
||||||
CExtKey extkey;
|
CKey priv_key;
|
||||||
if (!GetExtKey(arg, extkey)) return false;
|
if (!GetPrivKey(pos, arg, priv_key)) return false;
|
||||||
for (auto entry : m_path) {
|
*key = priv_key.GetPubKey();
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
// TODO: optimize by caching
|
// TODO: optimize by caching
|
||||||
CExtPubKey extkey = m_extkey;
|
CExtPubKey extkey = m_extkey;
|
||||||
@ -314,6 +320,18 @@ public:
|
|||||||
}
|
}
|
||||||
return true;
|
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. */
|
/** Base class for all Descriptor implementations. */
|
||||||
@ -468,6 +486,20 @@ public:
|
|||||||
Span<const unsigned char> span = MakeSpan(cache);
|
Span<const unsigned char> span = MakeSpan(cache);
|
||||||
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
|
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. */
|
/** 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`).
|
* @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;
|
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`.
|
/** 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();
|
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
|
||||||
|
|
||||||
// Expand all descriptors to get public keys and scripts.
|
// Expand all descriptors to get public keys and scripts, and private keys if available.
|
||||||
// TODO: get private keys from descriptors too
|
|
||||||
for (int i = range_start; i <= range_end; ++i) {
|
for (int i = range_start; i <= range_end; ++i) {
|
||||||
FlatSigningProvider out_keys;
|
FlatSigningProvider out_keys;
|
||||||
std::vector<CScript> scripts_temp;
|
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);
|
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.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());
|
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,13 +448,12 @@ class ImportMultiTest(BitcoinTestFramework):
|
|||||||
error_code=-8,
|
error_code=-8,
|
||||||
error_message='Descriptor is ranged, please specify the range')
|
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.log.info("Should import the ranged descriptor with specified range as solvable")
|
||||||
self.test_importmulti({"desc": descsum_create(desc),
|
self.test_importmulti({"desc": descsum_create(desc),
|
||||||
"timestamp": "now",
|
"timestamp": "now",
|
||||||
"range": 1},
|
"range": 1},
|
||||||
success=True,
|
success=True)
|
||||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
|
||||||
|
|
||||||
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
|
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
|
||||||
success=False, error_code=-8, error_message='End of range is too high')
|
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]},
|
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
|
||||||
success=False, error_code=-8, error_message='Range is too large')
|
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
|
# Test importing of a P2PKH address via descriptor
|
||||||
key = get_key(self.nodes[0])
|
key = get_key(self.nodes[0])
|
||||||
self.log.info("Should import a p2pkh address from descriptor")
|
self.log.info("Should import a p2pkh address from descriptor")
|
||||||
|
Loading…
Reference in New Issue
Block a user