Skip to content

Commit 0e95e1a

Browse files
committed
Merge bitcoin#35437: migrate: Handle HD chains that have identical seeds but different IDs
de92208 migrate: Handle HD chains that have identical seeds but different IDs (Ava Chow) Pull request description: The seed ID is calculated from a pubkey produced by treating the seed as a private key. This calculation includes a pubkey compression parameter, even thought that compression is completely irrelevant for the usage of the seed as a BIP 32 seed. Thus migration should detect if a seed has been used multiple times by checking if the computed master key was already processed. The spkm_migration fuzzer needs to have it's added descriptors accounting updated for this fix. It should not be possible for users to actually run into this problem as all HD chains use seeds with the pubkey compression option set. Fixes bitcoin#35434 ACKs for top commit: kevkevinpal: crACK [de92208](bitcoin@de92208) marcofleon: crACK de92208 rkrux: code review ACK de92208 Tree-SHA512: c420a24722fd6a94bf6656f195bad3432ba54c38b3c49a02750577281d0864988fd6d44cd9594b57cfaf33061a1e250e21378e3637b4e9a45f2d7aad6045884d
2 parents 3413609 + de92208 commit 0e95e1a

2 files changed

Lines changed: 20 additions & 11 deletions

File tree

src/wallet/scriptpubkeyman.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -628,19 +628,28 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
628628

629629
bool can_support_hd_split_feature = m_hd_chain.nVersion >= CHDChain::VERSION_HD_CHAIN_SPLIT;
630630

631+
std::set<CExtPubKey> master_xpubs;
631632
for (const CHDChain& chain : chains) {
633+
if (chain.seed_id.IsNull()) continue;
634+
635+
// Get the master xprv
636+
CKey seed_key;
637+
if (!GetKey(chain.seed_id, seed_key)) {
638+
assert(false);
639+
}
640+
CExtKey master_key;
641+
master_key.SetSeed(seed_key);
642+
643+
// Get the xpub and verify that we haven't already seen this xpub before
644+
CExtPubKey master_xpub = master_key.Neuter();
645+
const auto& [_, inserted] = master_xpubs.insert(master_xpub);
646+
if (!inserted) continue;
647+
632648
for (int i = 0; i < 2; ++i) {
633649
// Skip if doing internal chain and split chain is not supported
634-
if (chain.seed_id.IsNull() || (i == 1 && !can_support_hd_split_feature)) {
650+
if (i == 1 && !can_support_hd_split_feature) {
635651
continue;
636652
}
637-
// Get the master xprv
638-
CKey seed_key;
639-
if (!GetKey(chain.seed_id, seed_key)) {
640-
assert(false);
641-
}
642-
CExtKey master_key;
643-
master_key.SetSeed(seed_key);
644653

645654
// Make the combo descriptor
646655
std::string xpub = EncodeExtPubKey(master_key.Neuter());

src/wallet/test/fuzz/scriptpubkeyman.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,10 @@ FUZZ_TARGET(spkm_migration, .init = initialize_spkm_migration)
257257

258258
bool add_inactive_hd_chain{fuzzed_data_provider.ConsumeBool() && !keys.empty()};
259259
if (add_inactive_hd_chain) {
260-
hd_key = PickValue(fuzzed_data_provider, keys);
260+
CKey inactive_hd_key = PickValue(fuzzed_data_provider, keys);
261261
hd_chain.nVersion = fuzzed_data_provider.ConsumeBool() ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
262-
bool dup_chain = hd_chain.seed_id == hd_key.GetPubKey().GetID();
263-
hd_chain.seed_id = hd_key.GetPubKey().GetID();
262+
bool dup_chain = hd_key.IsValid() && std::equal(hd_key.begin(), hd_key.end(), inactive_hd_key.begin());
263+
hd_chain.seed_id = inactive_hd_key.GetPubKey().GetID();
264264
legacy_data.AddInactiveHDChain(hd_chain);
265265
if (!dup_chain) added_chains++;
266266
}

0 commit comments

Comments
 (0)