Changeset View
Changeset View
Standalone View
Standalone View
src/script/descriptor.cpp
Show First 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | protected: | ||||
//! m_expr_index = 0 | //! m_expr_index = 0 | ||||
uint32_t m_expr_index; | uint32_t m_expr_index; | ||||
public: | public: | ||||
PubkeyProvider(uint32_t exp_index) : m_expr_index(exp_index) {} | PubkeyProvider(uint32_t exp_index) : m_expr_index(exp_index) {} | ||||
virtual ~PubkeyProvider() = default; | virtual ~PubkeyProvider() = default; | ||||
/** Derive a public key. If key==nullptr, only info is desired. */ | /** | ||||
virtual bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key, | * Derive a public key. | ||||
KeyOriginInfo &info) const = 0; | * read_cache is the cache to read keys from (if not nullptr) | ||||
* write_cache is the cache to write keys to (if not nullptr) | |||||
* Caches are not exclusive but this is not tested. Currently we use them | |||||
* exclusively | |||||
*/ | |||||
virtual bool GetPubKey(int pos, const SigningProvider &arg, CPubKey &key, | |||||
KeyOriginInfo &info, | |||||
const DescriptorCache *read_cache = nullptr, | |||||
DescriptorCache *write_cache = nullptr) const = 0; | |||||
/** Whether this represent multiple public keys at different positions. */ | /** Whether this represent multiple public keys at different positions. */ | ||||
virtual bool IsRange() const = 0; | virtual bool IsRange() const = 0; | ||||
/** Get the size of the generated public key(s) in bytes (33 or 65). */ | /** Get the size of the generated public key(s) in bytes (33 or 65). */ | ||||
virtual size_t GetSize() const = 0; | virtual size_t GetSize() const = 0; | ||||
/** Get the descriptor string form. */ | /** Get the descriptor string form. */ | ||||
Show All 21 Lines | std::string OriginString() const { | ||||
FormatHDKeypath(m_origin.path); | FormatHDKeypath(m_origin.path); | ||||
} | } | ||||
public: | public: | ||||
OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, | OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, | ||||
std::unique_ptr<PubkeyProvider> provider) | std::unique_ptr<PubkeyProvider> provider) | ||||
: PubkeyProvider(exp_index), m_origin(std::move(info)), | : PubkeyProvider(exp_index), m_origin(std::move(info)), | ||||
m_provider(std::move(provider)) {} | m_provider(std::move(provider)) {} | ||||
bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key, | bool GetPubKey(int pos, const SigningProvider &arg, CPubKey &key, | ||||
KeyOriginInfo &info) const override { | KeyOriginInfo &info, | ||||
if (!m_provider->GetPubKey(pos, arg, key, info)) { | const DescriptorCache *read_cache = nullptr, | ||||
DescriptorCache *write_cache = nullptr) const override { | |||||
if (!m_provider->GetPubKey(pos, arg, key, info, read_cache, | |||||
write_cache)) { | |||||
return false; | return false; | ||||
} | } | ||||
std::copy(std::begin(m_origin.fingerprint), | std::copy(std::begin(m_origin.fingerprint), | ||||
std::end(m_origin.fingerprint), info.fingerprint); | std::end(m_origin.fingerprint), info.fingerprint); | ||||
info.path.insert(info.path.begin(), m_origin.path.begin(), | info.path.insert(info.path.begin(), m_origin.path.begin(), | ||||
m_origin.path.end()); | m_origin.path.end()); | ||||
return true; | return true; | ||||
} | } | ||||
Show All 19 Lines | |||||
/** An object representing a parsed constant public key in a descriptor. */ | /** An object representing a parsed constant public key in a descriptor. */ | ||||
class ConstPubkeyProvider final : public PubkeyProvider { | class ConstPubkeyProvider final : public PubkeyProvider { | ||||
CPubKey m_pubkey; | CPubKey m_pubkey; | ||||
public: | public: | ||||
ConstPubkeyProvider(uint32_t exp_index, const CPubKey &pubkey) | ConstPubkeyProvider(uint32_t exp_index, const CPubKey &pubkey) | ||||
: PubkeyProvider(exp_index), m_pubkey(pubkey) {} | : PubkeyProvider(exp_index), m_pubkey(pubkey) {} | ||||
bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key, | bool GetPubKey(int pos, const SigningProvider &arg, CPubKey &key, | ||||
KeyOriginInfo &info) const override { | KeyOriginInfo &info, | ||||
if (key) { | const DescriptorCache *read_cache = nullptr, | ||||
*key = m_pubkey; | DescriptorCache *write_cache = nullptr) const override { | ||||
} | key = m_pubkey; | ||||
info.path.clear(); | info.path.clear(); | ||||
CKeyID keyid = m_pubkey.GetID(); | CKeyID keyid = m_pubkey.GetID(); | ||||
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), | std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), | ||||
info.fingerprint); | info.fingerprint); | ||||
return true; | return true; | ||||
} | } | ||||
bool IsRange() const override { return false; } | bool IsRange() const override { return false; } | ||||
size_t GetSize() const override { return m_pubkey.size(); } | size_t GetSize() const override { return m_pubkey.size(); } | ||||
Show All 38 Lines | bool GetExtKey(const SigningProvider &arg, CExtKey &ret) const { | ||||
m_root_extkey.vchFingerprint + sizeof(ret.vchFingerprint), | m_root_extkey.vchFingerprint + sizeof(ret.vchFingerprint), | ||||
ret.vchFingerprint); | ret.vchFingerprint); | ||||
ret.nChild = m_root_extkey.nChild; | ret.nChild = m_root_extkey.nChild; | ||||
ret.chaincode = m_root_extkey.chaincode; | ret.chaincode = m_root_extkey.chaincode; | ||||
ret.key = key; | ret.key = key; | ||||
return true; | return true; | ||||
} | } | ||||
// Derives the last xprv | |||||
bool GetDerivedExtKey(const SigningProvider &arg, CExtKey &xprv) const { | |||||
if (!GetExtKey(arg, xprv)) { | |||||
return false; | |||||
} | |||||
for (auto entry : m_path) { | |||||
xprv.Derive(xprv, entry); | |||||
} | |||||
return true; | |||||
} | |||||
bool IsHardened() const { | bool IsHardened() const { | ||||
if (m_derive == DeriveType::HARDENED) { | if (m_derive == DeriveType::HARDENED) { | ||||
return true; | return true; | ||||
} | } | ||||
for (auto entry : m_path) { | for (auto entry : m_path) { | ||||
if (entry >> 31) { | if (entry >> 31) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
public: | public: | ||||
BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey &extkey, | BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey &extkey, | ||||
KeyPath path, DeriveType derive) | KeyPath path, DeriveType derive) | ||||
: PubkeyProvider(exp_index), m_root_extkey(extkey), | : PubkeyProvider(exp_index), m_root_extkey(extkey), | ||||
m_path(std::move(path)), m_derive(derive) {} | m_path(std::move(path)), m_derive(derive) {} | ||||
bool IsRange() const override { return m_derive != DeriveType::NO; } | bool IsRange() const override { return m_derive != DeriveType::NO; } | ||||
size_t GetSize() const override { return 33; } | size_t GetSize() const override { return 33; } | ||||
bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key, | bool GetPubKey(int pos, const SigningProvider &arg, CPubKey &key_out, | ||||
KeyOriginInfo &info) const override { | KeyOriginInfo &final_info_out, | ||||
if (key) { | const DescriptorCache *read_cache = nullptr, | ||||
if (IsHardened()) { | DescriptorCache *write_cache = nullptr) const override { | ||||
CKey priv_key; | // Info of parent of the to be derived pubkey | ||||
if (!GetPrivKey(pos, arg, priv_key)) { | KeyOriginInfo parent_info; | ||||
CKeyID keyid = m_root_extkey.pubkey.GetID(); | |||||
std::copy(keyid.begin(), | |||||
keyid.begin() + sizeof(parent_info.fingerprint), | |||||
parent_info.fingerprint); | |||||
parent_info.path = m_path; | |||||
// Info of the derived key itself which is copied out upon successful | |||||
// completion | |||||
KeyOriginInfo final_info_out_tmp = parent_info; | |||||
if (m_derive == DeriveType::UNHARDENED) { | |||||
final_info_out_tmp.path.push_back((uint32_t)pos); | |||||
} | |||||
if (m_derive == DeriveType::HARDENED) { | |||||
final_info_out_tmp.path.push_back(((uint32_t)pos) | 0x80000000L); | |||||
} | |||||
// Derive keys or fetch them from cache | |||||
CExtPubKey final_extkey = m_root_extkey; | |||||
bool der = true; | |||||
if (read_cache) { | |||||
if (!read_cache->GetCachedDerivedExtPubKey(m_expr_index, pos, | |||||
final_extkey)) { | |||||
return false; | return false; | ||||
} | } | ||||
*key = priv_key.GetPubKey(); | } else if (IsHardened()) { | ||||
} else { | CExtKey xprv; | ||||
// TODO: optimize by caching | if (!GetDerivedExtKey(arg, xprv)) { | ||||
CExtPubKey extkey = m_root_extkey; | return false; | ||||
for (auto entry : m_path) { | |||||
extkey.Derive(extkey, entry); | |||||
} | } | ||||
if (m_derive == DeriveType::UNHARDENED) { | if (m_derive == DeriveType::UNHARDENED) { | ||||
extkey.Derive(extkey, pos); | der = xprv.Derive(xprv, pos); | ||||
} | } | ||||
assert(m_derive != DeriveType::HARDENED); | if (m_derive == DeriveType::HARDENED) { | ||||
*key = extkey.pubkey; | der = xprv.Derive(xprv, pos | 0x80000000UL); | ||||
} | } | ||||
final_extkey = xprv.Neuter(); | |||||
} else { | |||||
for (auto entry : m_path) { | |||||
der = final_extkey.Derive(final_extkey, entry); | |||||
assert(der); | |||||
} | } | ||||
CKeyID keyid = m_root_extkey.pubkey.GetID(); | |||||
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), | |||||
info.fingerprint); | |||||
info.path = m_path; | |||||
if (m_derive == DeriveType::UNHARDENED) { | if (m_derive == DeriveType::UNHARDENED) { | ||||
info.path.push_back(uint32_t(pos)); | der = final_extkey.Derive(final_extkey, pos); | ||||
} | } | ||||
if (m_derive == DeriveType::HARDENED) { | assert(m_derive != DeriveType::HARDENED); | ||||
info.path.push_back(uint32_t(pos) | 0x80000000L); | } | ||||
assert(der); | |||||
final_info_out = final_info_out_tmp; | |||||
key_out = final_extkey.pubkey; | |||||
if (write_cache) { | |||||
write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
std::string ToString() const override { | std::string ToString() const override { | ||||
std::string ret = | std::string ret = | ||||
EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path); | EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path); | ||||
if (IsRange()) { | if (IsRange()) { | ||||
ret += "/*"; | ret += "/*"; | ||||
if (m_derive == DeriveType::HARDENED) { | if (m_derive == DeriveType::HARDENED) { | ||||
Show All 15 Lines | bool ToPrivateString(const SigningProvider &arg, | ||||
out += '\''; | out += '\''; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool GetPrivKey(int pos, const SigningProvider &arg, | bool GetPrivKey(int pos, const SigningProvider &arg, | ||||
CKey &key) const override { | CKey &key) const override { | ||||
CExtKey extkey; | CExtKey extkey; | ||||
if (!GetExtKey(arg, extkey)) { | if (!GetDerivedExtKey(arg, extkey)) { | ||||
return false; | return false; | ||||
} | } | ||||
for (auto entry : m_path) { | |||||
extkey.Derive(extkey, entry); | |||||
} | |||||
if (m_derive == DeriveType::UNHARDENED) { | if (m_derive == DeriveType::UNHARDENED) { | ||||
extkey.Derive(extkey, pos); | extkey.Derive(extkey, pos); | ||||
} | } | ||||
if (m_derive == DeriveType::HARDENED) { | if (m_derive == DeriveType::HARDENED) { | ||||
extkey.Derive(extkey, pos | 0x80000000UL); | extkey.Derive(extkey, pos | 0x80000000UL); | ||||
} | } | ||||
key = extkey.key; | key = extkey.key; | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | public: | ||||
bool ToPrivateString(const SigningProvider &arg, | bool ToPrivateString(const SigningProvider &arg, | ||||
std::string &out) const override final { | std::string &out) const override final { | ||||
bool ret = ToStringHelper(&arg, out, true); | bool ret = ToStringHelper(&arg, out, true); | ||||
out = AddChecksum(out); | out = AddChecksum(out); | ||||
return ret; | return ret; | ||||
} | } | ||||
bool ExpandHelper(int pos, const SigningProvider &arg, | bool ExpandHelper(int pos, const SigningProvider &arg, | ||||
Span<const uint8_t> *cache_read, | const DescriptorCache *read_cache, | ||||
std::vector<CScript> &output_scripts, | std::vector<CScript> &output_scripts, | ||||
FlatSigningProvider &out, | FlatSigningProvider &out, | ||||
std::vector<uint8_t> *cache_write) const { | DescriptorCache *write_cache) const { | ||||
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries; | std::vector<std::pair<CPubKey, KeyOriginInfo>> entries; | ||||
entries.reserve(m_pubkey_args.size()); | entries.reserve(m_pubkey_args.size()); | ||||
// Construct temporary data in `entries` and `subscripts`, to avoid | // Construct temporary data in `entries` and `subscripts`, to avoid | ||||
// producing output in case of failure. | // producing output in case of failure. | ||||
for (const auto &p : m_pubkey_args) { | for (const auto &p : m_pubkey_args) { | ||||
entries.emplace_back(); | entries.emplace_back(); | ||||
// If we have a cache, we don't need GetPubKey to compute the public | if (!p->GetPubKey(pos, arg, entries.back().first, | ||||
// key. Pass in nullptr to signify only origin info is desired. | entries.back().second, read_cache, write_cache)) { | ||||
if (!p->GetPubKey(pos, arg, | return false; | ||||
cache_read ? nullptr : &entries.back().first, | |||||
entries.back().second)) { | |||||
return false; | |||||
} | |||||
if (cache_read) { | |||||
// Cached expanded public key exists, use it. | |||||
if (cache_read->size() == 0) { | |||||
return false; | |||||
} | |||||
bool compressed = | |||||
((*cache_read)[0] == 0x02 || (*cache_read)[0] == 0x03) && | |||||
cache_read->size() >= 33; | |||||
bool uncompressed = | |||||
((*cache_read)[0] == 0x04) && cache_read->size() >= 65; | |||||
if (!(compressed || uncompressed)) { | |||||
return false; | |||||
} | |||||
CPubKey pubkey(cache_read->begin(), | |||||
cache_read->begin() + (compressed ? 33 : 65)); | |||||
entries.back().first = pubkey; | |||||
*cache_read = cache_read->subspan(compressed ? 33 : 65); | |||||
} | |||||
if (cache_write) { | |||||
cache_write->insert(cache_write->end(), | |||||
entries.back().first.begin(), | |||||
entries.back().first.end()); | |||||
} | } | ||||
} | } | ||||
std::vector<CScript> subscripts; | std::vector<CScript> subscripts; | ||||
if (m_subdescriptor_arg) { | if (m_subdescriptor_arg) { | ||||
FlatSigningProvider subprovider; | FlatSigningProvider subprovider; | ||||
if (!m_subdescriptor_arg->ExpandHelper(pos, arg, cache_read, | if (!m_subdescriptor_arg->ExpandHelper(pos, arg, read_cache, | ||||
subscripts, subprovider, | subscripts, subprovider, | ||||
cache_write)) { | write_cache)) { | ||||
return false; | return false; | ||||
} | } | ||||
out = Merge(out, subprovider); | out = Merge(out, subprovider); | ||||
} | } | ||||
std::vector<CPubKey> pubkeys; | std::vector<CPubKey> pubkeys; | ||||
pubkeys.reserve(entries.size()); | pubkeys.reserve(entries.size()); | ||||
for (auto &entry : entries) { | for (auto &entry : entries) { | ||||
Show All 15 Lines | bool ExpandHelper(int pos, const SigningProvider &arg, | ||||
} else { | } else { | ||||
output_scripts = MakeScripts(pubkeys, nullptr, out); | output_scripts = MakeScripts(pubkeys, nullptr, out); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool Expand(int pos, const SigningProvider &provider, | bool Expand(int pos, const SigningProvider &provider, | ||||
std::vector<CScript> &output_scripts, FlatSigningProvider &out, | std::vector<CScript> &output_scripts, FlatSigningProvider &out, | ||||
std::vector<uint8_t> *cache = nullptr) const final { | DescriptorCache *write_cache = nullptr) const final { | ||||
return ExpandHelper(pos, provider, nullptr, output_scripts, out, cache); | return ExpandHelper(pos, provider, nullptr, output_scripts, out, | ||||
write_cache); | |||||
} | } | ||||
bool ExpandFromCache(int pos, const std::vector<uint8_t> &cache, | bool ExpandFromCache(int pos, const DescriptorCache &read_cache, | ||||
std::vector<CScript> &output_scripts, | std::vector<CScript> &output_scripts, | ||||
FlatSigningProvider &out) const final { | FlatSigningProvider &out) const final { | ||||
Span<const uint8_t> span = MakeSpan(cache); | return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &read_cache, | ||||
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, | output_scripts, out, nullptr); | ||||
out, nullptr) && | |||||
span.size() == 0; | |||||
} | } | ||||
void ExpandPrivate(int pos, const SigningProvider &provider, | void ExpandPrivate(int pos, const SigningProvider &provider, | ||||
FlatSigningProvider &out) const final { | FlatSigningProvider &out) const final { | ||||
for (const auto &p : m_pubkey_args) { | for (const auto &p : m_pubkey_args) { | ||||
CKey key; | CKey key; | ||||
if (!p->GetPrivKey(pos, provider, key)) { | if (!p->GetPrivKey(pos, provider, key)) { | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 615 Lines • Show Last 20 Lines |