Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251std::string prv1;252BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv1));253if (expected_prv) {254BOOST_CHECK_MESSAGE(EqualDescriptor(*expected_prv, prv1), "Private ser: " + prv1 + "Private desc: " + *expected_prv);255} else {256BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv);257}258BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1));259BOOST_CHECK(parse_pub->ToPrivateString(keys_priv, prv1));260if (expected_prv) {261BOOST_CHECK(EqualDescriptor(*expected_prv, prv1));262BOOST_CHECK_MESSAGE(EqualDescriptor(*expected_prv, prv1), "Private ser: " + prv1 + " Private desc: " + *expected_prv);263} else {264BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv);265}266BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1));268// Check that both can ExpandPrivate and get the same SigningProviders269FlatSigningProvider priv_prov;270parse_priv->ExpandPrivate(0, keys_priv, priv_prov);272FlatSigningProvider pub_prov;273parse_pub->ExpandPrivate(0, keys_priv, pub_prov);275BOOST_CHECK_MESSAGE(EqualSigningProviders(priv_prov, pub_prov), "Private desc: " + prv + " Pub desc: " + pub);276} else if (keys_priv.keys.size() > 0) {277// If there is at least one private key, ToPrivateString() should return true and include that key278std::string prv_str;279BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv_str));280size_t checksum_len = 9; // Including the '#' character281BOOST_CHECK_MESSAGE(prv == prv_str.substr(0, prv_str.length() - checksum_len), prv);282}284// Check that private can produce the normalized descriptors285std::string norm1;286BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1));287BOOST_CHECK_MESSAGE(EqualDescriptor(norm1, norm_pub), "priv->ToNormalizedString(): " + norm1 + " Norm. desc: " + norm_pub);288BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1));289BOOST_CHECK_MESSAGE(EqualDescriptor(norm1, norm_pub), "pub->ToNormalizedString(): " + norm1 + " Norm. desc: " + norm_pub);291// Check whether IsRange on both returns the expected result292BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0);293BOOST_CHECK_EQUAL(parse_priv->IsRange(), (flags & RANGE) != 0);295// Check that the highest key expression index matches the number of keys in the descriptor296BOOST_TEST_INFO("Pub desc: " + pub);297uint32_t key_exprs = parse_pub->GetMaxKeyExpr();298BOOST_CHECK_EQUAL(key_exprs + 1, parse_pub->GetKeyCount());299BOOST_TEST_INFO("Priv desc: " + prv);300BOOST_CHECK_EQUAL(key_exprs, parse_priv->GetMaxKeyExpr());301BOOST_CHECK_EQUAL(key_exprs + 1, parse_priv->GetKeyCount());303// * For ranged descriptors, the `scripts` parameter is a list of expected result outputs, for subsequent304// positions to evaluate the descriptors on (so the first element of `scripts` is for evaluating the305// descriptor at 0; the second at 1; and so on). To verify this, we evaluate the descriptors once for306// each element in `scripts`.307// * For non-ranged descriptors, we evaluate the descriptors at positions 0, 1, and 2, but expect the308// same result in each case, namely the first element of `scripts`. Because of that, the size of309// `scripts` must be one in that case.310if (!(flags & RANGE)) assert(scripts.size() == 1);311size_t max = (flags & RANGE) ? scripts.size() : 3;313// Iterate over the position we'll evaluate the descriptors in.314for (size_t i = 0; i < max; ++i) {315// Call the expected result scripts `ref`.316const auto& ref = scripts[(flags & RANGE) ? i : 0];317// When t=0, evaluate the `prv` descriptor; when t=1, evaluate the `pub` descriptor.318for (int t = 0; t < 2; ++t) {319// When the descriptor is hardened, evaluate with access to the private keys inside.320const FlatSigningProvider& key_provider = (flags & HARDENED) ? keys_priv : keys_pub;322// Evaluate the descriptor selected by `t` in position `i`.323FlatSigningProvider script_provider, script_provider_cached;324std::vector<CScript> spks, spks_cached;325DescriptorCache desc_cache;326BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider, &desc_cache));328// Compare the output with the expected result.329BOOST_CHECK_EQUAL(spks.size(), ref.size());331// Try to expand again using cached data, and compare.332BOOST_CHECK(parse_pub->ExpandFromCache(i, desc_cache, spks_cached, script_provider_cached));333BOOST_CHECK(spks == spks_cached);334BOOST_CHECK(GetKeyData(script_provider, flags) == GetKeyData(script_provider_cached, flags));335BOOST_CHECK(script_provider.scripts == script_provider_cached.scripts);336BOOST_CHECK(GetKeyOriginData(script_provider, flags) == GetKeyOriginData(script_provider_cached, flags));338// Check whether keys are in the cache339const auto& der_xpub_cache = desc_cache.GetCachedDerivedExtPubKeys();340const auto& parent_xpub_cache = desc_cache.GetCachedParentExtPubKeys();341size_t num_xpubs = CountXpubs(pub1);342size_t num_unique_xpubs = CountUniqueXpubs(pub1);343if (flags & MUSIG_DERIVATION) {344// Deriving from the aggregate will include the synthetic xpub of the aggregate in the caches and SigningProviders.345num_xpubs++;346num_unique_xpubs++;347}348if ((flags & RANGE) && !(flags & (DERIVE_HARDENED))) {349// For ranged, unhardened derivation, None of the keys in origins should appear in the cache but the cache should have parent keys350// But we can derive one level from each of those parent keys and find them all351BOOST_CHECK(der_xpub_cache.empty());352BOOST_CHECK(parent_xpub_cache.size() > 0);353std::set<CPubKey> pubkeys;354for (const auto& xpub_pair : parent_xpub_cache) {355const CExtPubKey& xpub = xpub_pair.second;356CExtPubKey der;357BOOST_CHECK(xpub.Derive(der, i));358pubkeys.insert(der.pubkey);359}360int count_pks = 0;361for (const auto& origin_pair : script_provider_cached.origins) {362const CPubKey& pk = origin_pair.second.first;363count_pks += pubkeys.count(pk);364}365if (flags & MUSIG_DERIVATION) {366if (!(flags & MIXED_MUSIG)) {367BOOST_CHECK_EQUAL(count_pks, 1);368}369BOOST_CHECK_EQUAL(num_xpubs, pubkeys.size());370} else {371if (flags & MUSIG) count_pks++; // One extra key for the aggregate key that is not in the cache372if (flags & MIXED_PUBKEYS) {373BOOST_CHECK_EQUAL(num_xpubs, count_pks);374} else {375BOOST_CHECK_EQUAL(script_provider_cached.origins.size(), count_pks);376}377}378} else if (num_xpubs > 0) {379// For ranged, hardened derivation, or not ranged, but has an xpub, all of the keys should appear in the cache380BOOST_CHECK_EQUAL(der_xpub_cache.size() + parent_xpub_cache.size(), num_xpubs);381if (!(flags & MIXED_PUBKEYS)) {382if (flags & UNIQUE_XPUBS) {383BOOST_CHECK_EQUAL(script_provider_cached.origins.size(), num_unique_xpubs);384} else {385BOOST_CHECK_EQUAL(script_provider_cached.origins.size(), num_xpubs);386}387}388// Get all of the derived pubkeys389std::set<CPubKey> pubkeys;390for (const auto& xpub_map_pair : der_xpub_cache) {391for (const auto& xpub_pair : xpub_map_pair.second) {392const CExtPubKey& xpub = xpub_pair.second;393pubkeys.insert(xpub.pubkey);394}395}396// Derive one level from all of the parents397for (const auto& xpub_pair : parent_xpub_cache) {398const CExtPubKey& xpub = xpub_pair.second;399pubkeys.insert(xpub.pubkey);400CExtPubKey der;401BOOST_CHECK(xpub.Derive(der, i));402pubkeys.insert(der.pubkey);403}404int count_pks = 0;405for (const auto& origin_pair : script_provider_cached.origins) {406const CPubKey& pk = origin_pair.second.first;407count_pks += pubkeys.count(pk);408}409if (flags & MUSIG_DERIVATION && !(flags & MIXED_PUBKEYS)) {410// pubkeys is one key per xpub + one derived key per xpub411BOOST_CHECK_EQUAL(2 * count_pks, pubkeys.size());412if (flags & UNIQUE_XPUBS) {413BOOST_CHECK_EQUAL(2 * num_unique_xpubs, pubkeys.size());414} else {415BOOST_CHECK_EQUAL(2 * num_xpubs, pubkeys.size());416}417} else {418if (flags & MUSIG) count_pks++; // One extra key for the aggregate key that is not in the cache419if (flags & MIXED_PUBKEYS) {420BOOST_CHECK_EQUAL(num_xpubs, count_pks);421} else {422BOOST_CHECK_EQUAL(script_provider_cached.origins.size(), count_pks);423}424}425} else if (!(flags & MIXED_PUBKEYS)) {426// Only const pubkeys, nothing should be cached427BOOST_CHECK(der_xpub_cache.empty());428BOOST_CHECK(parent_xpub_cache.empty());429}431// Make sure we can expand using cached xpubs for unhardened derivation432if (!(flags & DERIVE_HARDENED)) {433// Evaluate the descriptor at i + 1434FlatSigningProvider script_provider1, script_provider_cached1;435std::vector<CScript> spks1, spk1_from_cache;436BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i + 1, key_provider, spks1, script_provider1, nullptr));438// Try again but use the cache from expanding i. That cache won't have the pubkeys for i + 1, but will have the parent xpub for derivation.439BOOST_CHECK(parse_pub->ExpandFromCache(i + 1, desc_cache, spk1_from_cache, script_provider_cached1));440BOOST_CHECK(spks1 == spk1_from_cache);441BOOST_CHECK(GetKeyData(script_provider1, flags) == GetKeyData(script_provider_cached1, flags));442BOOST_CHECK(script_provider1.scripts == script_provider_cached1.scripts);443BOOST_CHECK(GetKeyOriginData(script_provider1, flags) == GetKeyOriginData(script_provider_cached1, flags));444}446// For each of the produced scripts, verify solvability, and when possible, try to sign a transaction spending it.447for (size_t n = 0; n < spks.size(); ++n) {448BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n]));450if (flags & (SIGNABLE | SIGNABLE_FAILS)) {451CMutableTransaction spend;452spend.nLockTime = spender_nlocktime;453spend.vin.resize(1);454spend.vin[0].nSequence = spender_nsequence;455spend.vout.resize(1);456std::vector<CTxOut> utxos(1);457PrecomputedTransactionData txdata;458txdata.Init(spend, std::move(utxos), /*force=*/true);459MutableTransactionSignatureCreator creator{spend, 0, CAmount{0}, &txdata, {.sighash_type = SIGHASH_DEFAULT}};460SignatureData sigdata;461// We assume there is no collision between the hashes (eg h1=SHA256(SHA256(x)) and h2=SHA256(x))462sigdata.sha256_preimages = preimages;463sigdata.hash256_preimages = preimages;464sigdata.ripemd160_preimages = preimages;465sigdata.hash160_preimages = preimages;466const auto prod_sig_res = ProduceSignature(FlatSigningProvider{keys_priv}.Merge(FlatSigningProvider{script_provider}), creator, spks[n], sigdata);467BOOST_CHECK_MESSAGE(prod_sig_res == !(flags & SIGNABLE_FAILS), prv);468}470/* Infer a descriptor from the generated script, and verify its solvability and that it roundtrips. */471auto inferred = InferDescriptor(spks[n], script_provider);472BOOST_CHECK_EQUAL(inferred->IsSolvable(), !(flags & UNSOLVABLE));473std::vector<CScript> spks_inferred;474FlatSigningProvider provider_inferred;475BOOST_CHECK(inferred->Expand(0, provider_inferred, spks_inferred, provider_inferred));476BOOST_CHECK_EQUAL(spks_inferred.size(), 1U);477BOOST_CHECK(spks_inferred[0] == spks[n]);478BOOST_CHECK_EQUAL(InferDescriptor(spks_inferred[0], provider_inferred)->IsSolvable(), !(flags & UNSOLVABLE));479BOOST_CHECK(GetKeyOriginData(provider_inferred, flags) == GetKeyOriginData(script_provider, flags));480}482// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),483// and then remove it from that set.484for (const auto& origin : script_provider.origins) {485BOOST_CHECK_MESSAGE(paths.contains(origin.second.second.path), "Unexpected key path: " + prv);486left_paths.erase(origin.second.second.path);487}489// Check that descriptors that have no hardened steps can self expand490BOOST_CHECK_EQUAL((t ? parse_priv : parse_pub)->CanSelfExpand(), !(flags & HARDENED));491}492}494// Verify no expected paths remain that were not observed.495BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);496}498void Check(const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags,499const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, std::optional<uint256> op_compat_desc_hash = std::nullopt,500const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, uint32_t spender_nlocktime=0,
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.