descriptors: Increment key_exp_index in ParsePubkey(Inner)
What changed, and why it matters
This is a small internal code cleanup in Bitcoin Core's descriptor parser. It moves the counting of key expressions (used to label parsed public keys) into a helper function so callers don't have to do it manually. There is no visible change in behavior for users, no bug fix, and no security-relevant change.
No security action required. Treat as normal code refactoring/review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors descriptor parsing in src/script/descriptor.cpp. ParsePubkeyInner now takes key_exp_index by reference and increments it internally after each successfully parsed public key. Callers in ParsePubkey, ParseScript, and KeyParser have their explicit ++key_exp_index / key_exp_index++ statements removed. The MuSig path also increments the index for the combined MuSig provider. The final OriginPubkeyProvider now uses the already-assigned provider expression index (prov->m_expr_index) rather than the outer key_exp_index. Net effect is purely structural: the same indices are produced with fewer manual increments at call sites.
Changed components
src/script/descriptor.cppDescriptor parser (ParsePubkeyInner, ParsePubkey, ParseScript, KeyParser)Inspect captured patch +7 / −11
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index d1780592..f761757f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1873,7 +1873,7 @@ static DeriveType ParseDeriveType(std::vector<std::span<const char>>& split, boo
}
/** Parse a public key that excludes origin information. */
-std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_index, const std::span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, bool& apostrophe, std::string& error)
+std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t& key_exp_index, const std::span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, bool& apostrophe, std::string& error)
{
std::vector<std::unique_ptr<PubkeyProvider>> ret;
bool permit_uncompressed = ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH;
@@ -1898,6 +1898,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
if (pubkey.IsFullyValid()) {
if (permit_uncompressed || pubkey.IsCompressed()) {
ret.emplace_back(std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false));
+ ++key_exp_index;
return ret;
} else {
error = "Uncompressed keys are not allowed";
@@ -1909,6 +1910,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
pubkey.Set(std::begin(fullkey), std::end(fullkey));
if (pubkey.IsFullyValid()) {
ret.emplace_back(std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true));
+ ++key_exp_index;
return ret;
}
}
@@ -1921,6 +1923,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
CPubKey pubkey = key.GetPubKey();
out.keys.emplace(pubkey.GetID(), key);
ret.emplace_back(std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, ctx == ParseScriptContext::P2TR));
+ ++key_exp_index;
return ret;
} else {
error = "Uncompressed keys are not allowed";
@@ -1944,6 +1947,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
for (auto& path : paths) {
ret.emplace_back(std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type, apostrophe));
}
+ ++key_exp_index;
return ret;
}
@@ -2001,7 +2005,6 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t& key_exp_index
max_multipath_len = std::max(max_multipath_len, pk.size());
providers.emplace_back(std::move(pk));
- key_exp_index++;
}
if (!any_key_parsed) {
error = "musig(): Must contain key expressions";
@@ -2093,6 +2096,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t& key_exp_index
// No multipath derivation, MuSigPubkeyProvider uses the first (and only) participant pubkey providers, and the first (and only) path
emplace_final_provider(0, 0);
}
+ ++key_exp_index; // Increment key expression index for the MuSigPubkeyProvider too
return ret;
}
@@ -2133,7 +2137,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t& key_exp_index
if (providers.empty()) return {};
ret.reserve(providers.size());
for (auto& prov : providers) {
- ret.emplace_back(std::make_unique<OriginPubkeyProvider>(key_exp_index, info, std::move(prov), apostrophe));
+ ret.emplace_back(std::make_unique<OriginPubkeyProvider>(prov->m_expr_index, info, std::move(prov), apostrophe));
}
return ret;
}
@@ -2207,7 +2211,6 @@ struct KeyParser {
assert(m_out);
Key key = m_keys.size();
auto pk = ParsePubkey(m_expr_index, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error);
- ++m_expr_index;
if (pk.empty()) return {};
m_keys.emplace_back(std::move(pk));
return key;
@@ -2279,7 +2282,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
error = strprintf("pk(): %s", error);
return {};
}
- ++key_exp_index;
for (auto& pubkey : pubkeys) {
ret.emplace_back(std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR));
}
@@ -2291,7 +2293,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
error = strprintf("pkh(): %s", error);
return {};
}
- ++key_exp_index;
for (auto& pubkey : pubkeys) {
ret.emplace_back(std::make_unique<PKHDescriptor>(std::move(pubkey)));
}
@@ -2303,7 +2304,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
error = strprintf("combo(): %s", error);
return {};
}
- ++key_exp_index;
for (auto& pubkey : pubkeys) {
ret.emplace_back(std::make_unique<ComboDescriptor>(std::move(pubkey)));
}
@@ -2343,7 +2343,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
script_size += pks.at(0)->GetSize() + 1;
max_providers_len = std::max(max_providers_len, pks.size());
providers.emplace_back(std::move(pks));
- key_exp_index++;
}
if ((multi || sortedmulti) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG)) {
error = strprintf("Cannot have %u keys in multisig; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTISIG);
@@ -2413,7 +2412,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
error = strprintf("wpkh(): %s", error);
return {};
}
- key_exp_index++;
for (auto& pubkey : pubkeys) {
ret.emplace_back(std::make_unique<WPKHDescriptor>(std::move(pubkey)));
}
@@ -2466,7 +2464,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
return {};
}
size_t max_providers_len = internal_keys.size();
- ++key_exp_index;
std::vector<std::vector<std::unique_ptr<DescriptorImpl>>> subscripts; //!< list of multipath expanded script subexpressions
std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
if (expr.size()) {
@@ -2570,7 +2567,6 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
error = strprintf("rawtr(): %s", error);
return {};
}
- ++key_exp_index;
for (auto& pubkey : output_keys) {
ret.emplace_back(std::make_unique<RawTRDescriptor>(std::move(pubkey)));
}
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.