Skip to content

Commit 58f54b6

Browse files
committed
Add DescriptorCache* read_cache and DescriptorCache* write_cache to Expand and GetPubKey
Have Expand, ExpandFromCache, and ExpandHelper take additional DescriptorCache parameters. These are then passed into PubkeyProvider::GetPubKey which also takes them as arguments. Reading and writing to the cache is pushed down into GetPubKey. The old cache where pubkeys are serialized to a vector is completely removed and instead xpubs are being cached in DescriptorCache.
1 parent 66c2cad commit 58f54b6

File tree

3 files changed

+73
-60
lines changed

3 files changed

+73
-60
lines changed

src/script/descriptor.cpp

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,12 @@ struct PubkeyProvider
160160

161161
virtual ~PubkeyProvider() = default;
162162

163-
/** Derive a public key. If key==nullptr, only info is desired. */
164-
virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const = 0;
163+
/** Derive a public key.
164+
* read_cache is the cache to read keys from (if not nullptr)
165+
* write_cache is the cache to write keys to (if not nullptr)
166+
* Caches are not exclusive but this is not tested. Currently we use them exclusively
167+
*/
168+
virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const = 0;
165169

166170
/** Whether this represent multiple public keys at different positions. */
167171
virtual bool IsRange() const = 0;
@@ -191,9 +195,9 @@ class OriginPubkeyProvider final : public PubkeyProvider
191195

192196
public:
193197
OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : PubkeyProvider(exp_index), m_origin(std::move(info)), m_provider(std::move(provider)) {}
194-
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
198+
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
195199
{
196-
if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
200+
if (!m_provider->GetPubKey(pos, arg, key, info, read_cache, write_cache)) return false;
197201
std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint);
198202
info.path.insert(info.path.begin(), m_origin.path.begin(), m_origin.path.end());
199203
return true;
@@ -221,9 +225,9 @@ class ConstPubkeyProvider final : public PubkeyProvider
221225

222226
public:
223227
ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey) : PubkeyProvider(exp_index), m_pubkey(pubkey) {}
224-
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
228+
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
225229
{
226-
if (key) *key = m_pubkey;
230+
key = m_pubkey;
227231
info.path.clear();
228232
CKeyID keyid = m_pubkey.GetID();
229233
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
@@ -271,6 +275,16 @@ class BIP32PubkeyProvider final : public PubkeyProvider
271275
return true;
272276
}
273277

278+
// Derives the last xprv
279+
bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv) const
280+
{
281+
if (!GetExtKey(arg, xprv)) return false;
282+
for (auto entry : m_path) {
283+
xprv.Derive(xprv, entry);
284+
}
285+
return true;
286+
}
287+
274288
bool IsHardened() const
275289
{
276290
if (m_derive == DeriveType::HARDENED) return true;
@@ -284,29 +298,47 @@ class BIP32PubkeyProvider final : public PubkeyProvider
284298
BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
285299
bool IsRange() const override { return m_derive != DeriveType::NO; }
286300
size_t GetSize() const override { return 33; }
287-
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey* key, KeyOriginInfo& info) const override
301+
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key_out, KeyOriginInfo& final_info_out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override
288302
{
289-
if (key) {
290-
if (IsHardened()) {
291-
CKey priv_key;
292-
if (!GetPrivKey(pos, arg, priv_key)) return false;
293-
*key = priv_key.GetPubKey();
294-
} else {
295-
// TODO: optimize by caching
296-
CExtPubKey extkey = m_root_extkey;
297-
for (auto entry : m_path) {
298-
extkey.Derive(extkey, entry);
299-
}
300-
if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
301-
assert(m_derive != DeriveType::HARDENED);
302-
*key = extkey.pubkey;
303+
// Info of parent of the to be derived pubkey
304+
KeyOriginInfo parent_info;
305+
CKeyID keyid = m_root_extkey.pubkey.GetID();
306+
std::copy(keyid.begin(), keyid.begin() + sizeof(parent_info.fingerprint), parent_info.fingerprint);
307+
parent_info.path = m_path;
308+
309+
// Info of the derived key itself which is copied out upon successful completion
310+
KeyOriginInfo final_info_out_tmp = parent_info;
311+
if (m_derive == DeriveType::UNHARDENED) final_info_out_tmp.path.push_back((uint32_t)pos);
312+
if (m_derive == DeriveType::HARDENED) final_info_out_tmp.path.push_back(((uint32_t)pos) | 0x80000000L);
313+
314+
// Derive keys or fetch them from cache
315+
CExtPubKey final_extkey = m_root_extkey;
316+
bool der = true;
317+
if (read_cache) {
318+
if (!read_cache->GetCachedDerivedExtPubKey(m_expr_index, pos, final_extkey)) return false;
319+
} else if (IsHardened()) {
320+
CExtKey xprv;
321+
if (!GetDerivedExtKey(arg, xprv)) return false;
322+
if (m_derive == DeriveType::UNHARDENED) der = xprv.Derive(xprv, pos);
323+
if (m_derive == DeriveType::HARDENED) der = xprv.Derive(xprv, pos | 0x80000000UL);
324+
final_extkey = xprv.Neuter();
325+
} else {
326+
for (auto entry : m_path) {
327+
der = final_extkey.Derive(final_extkey, entry);
328+
assert(der);
303329
}
330+
if (m_derive == DeriveType::UNHARDENED) der = final_extkey.Derive(final_extkey, pos);
331+
assert(m_derive != DeriveType::HARDENED);
304332
}
305-
CKeyID keyid = m_root_extkey.pubkey.GetID();
306-
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
307-
info.path = m_path;
308-
if (m_derive == DeriveType::UNHARDENED) info.path.push_back((uint32_t)pos);
309-
if (m_derive == DeriveType::HARDENED) info.path.push_back(((uint32_t)pos) | 0x80000000L);
333+
assert(der);
334+
335+
final_info_out = final_info_out_tmp;
336+
key_out = final_extkey.pubkey;
337+
338+
if (write_cache) {
339+
write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey);
340+
}
341+
310342
return true;
311343
}
312344
std::string ToString() const override
@@ -332,10 +364,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
332364
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
333365
{
334366
CExtKey extkey;
335-
if (!GetExtKey(arg, extkey)) return false;
336-
for (auto entry : m_path) {
337-
extkey.Derive(extkey, entry);
338-
}
367+
if (!GetDerivedExtKey(arg, extkey)) return false;
339368
if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
340369
if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
341370
key = extkey.key;
@@ -434,35 +463,20 @@ class DescriptorImpl : public Descriptor
434463
return ret;
435464
}
436465

437-
bool ExpandHelper(int pos, const SigningProvider& arg, Span<const unsigned char>* cache_read, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache_write) const
466+
bool ExpandHelper(int pos, const SigningProvider& arg, const DescriptorCache* read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache) const
438467
{
439468
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
440469
entries.reserve(m_pubkey_args.size());
441470

442471
// Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
443472
for (const auto& p : m_pubkey_args) {
444473
entries.emplace_back();
445-
// If we have a cache, we don't need GetPubKey to compute the public key.
446-
// Pass in nullptr to signify only origin info is desired.
447-
if (!p->GetPubKey(pos, arg, cache_read ? nullptr : &entries.back().first, entries.back().second)) return false;
448-
if (cache_read) {
449-
// Cached expanded public key exists, use it.
450-
if (cache_read->size() == 0) return false;
451-
bool compressed = ((*cache_read)[0] == 0x02 || (*cache_read)[0] == 0x03) && cache_read->size() >= 33;
452-
bool uncompressed = ((*cache_read)[0] == 0x04) && cache_read->size() >= 65;
453-
if (!(compressed || uncompressed)) return false;
454-
CPubKey pubkey(cache_read->begin(), cache_read->begin() + (compressed ? 33 : 65));
455-
entries.back().first = pubkey;
456-
*cache_read = cache_read->subspan(compressed ? 33 : 65);
457-
}
458-
if (cache_write) {
459-
cache_write->insert(cache_write->end(), entries.back().first.begin(), entries.back().first.end());
460-
}
474+
if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second, read_cache, write_cache)) return false;
461475
}
462476
std::vector<CScript> subscripts;
463477
if (m_subdescriptor_arg) {
464478
FlatSigningProvider subprovider;
465-
if (!m_subdescriptor_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
479+
if (!m_subdescriptor_arg->ExpandHelper(pos, arg, read_cache, subscripts, subprovider, write_cache)) return false;
466480
out = Merge(out, subprovider);
467481
}
468482

@@ -486,15 +500,14 @@ class DescriptorImpl : public Descriptor
486500
return true;
487501
}
488502

489-
bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const final
503+
bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const final
490504
{
491-
return ExpandHelper(pos, provider, nullptr, output_scripts, out, cache);
505+
return ExpandHelper(pos, provider, nullptr, output_scripts, out, write_cache);
492506
}
493507

494-
bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const final
508+
bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const final
495509
{
496-
Span<const unsigned char> span = MakeSpan(cache);
497-
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
510+
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &read_cache, output_scripts, out, nullptr);
498511
}
499512

500513
void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const final

src/script/descriptor.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,18 @@ struct Descriptor {
9696
* @param[in] provider The provider to query for private keys in case of hardened derivation.
9797
* @param[out] output_scripts The expanded scriptPubKeys.
9898
* @param[out] out Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
99-
* @param[out] cache Cache data necessary to evaluate the descriptor at this point without access to private keys.
99+
* @param[out] write_cache Cache data necessary to evaluate the descriptor at this point without access to private keys.
100100
*/
101-
virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const = 0;
101+
virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const = 0;
102102

103103
/** Expand a descriptor at a specified position using cached expansion data.
104104
*
105105
* @param[in] pos The position at which to expand the descriptor. If IsRange() is false, this is ignored.
106-
* @param[in] cache Cached expansion data.
106+
* @param[in] read_cache Cached expansion data.
107107
* @param[out] output_scripts The expanded scriptPubKeys.
108108
* @param[out] out Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
109109
*/
110-
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
110+
virtual bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
111111

112112
/** Expand the private key for a descriptor at a specified position, if possible.
113113
*

src/test/descriptor_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,14 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
135135
// Evaluate the descriptor selected by `t` in poisition `i`.
136136
FlatSigningProvider script_provider, script_provider_cached;
137137
std::vector<CScript> spks, spks_cached;
138-
std::vector<unsigned char> cache;
139-
BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider, &cache));
138+
DescriptorCache desc_cache;
139+
BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider, &desc_cache));
140140

141141
// Compare the output with the expected result.
142142
BOOST_CHECK_EQUAL(spks.size(), ref.size());
143143

144144
// Try to expand again using cached data, and compare.
145-
BOOST_CHECK(parse_pub->ExpandFromCache(i, cache, spks_cached, script_provider_cached));
145+
BOOST_CHECK(parse_pub->ExpandFromCache(i, desc_cache, spks_cached, script_provider_cached));
146146
BOOST_CHECK(spks == spks_cached);
147147
BOOST_CHECK(script_provider.pubkeys == script_provider_cached.pubkeys);
148148
BOOST_CHECK(script_provider.scripts == script_provider_cached.scripts);

0 commit comments

Comments
 (0)