From: busayo-OD Date: Sun, 6 Sep 2026 15:20:22 +0000 (+0100) Subject: fix(chain): clamp derivation index before apply_changeset X-Git-Url: http://internal-gitweb-vhost/-client-interface/%22https:/src/struct.CommandStringError.html?a=commitdiff_plain;h=803fd968d7999bda911eb2b968cd54a585fac8b4;p=bdk fix(chain): clamp derivation index before apply_changeset KeychainTxOutIndex::apply_changeset accepts ChangeSet::last_revealed values above BIP32_MAX_INDEX, violating an invariant relied on by the indexer. Clamp each last_revealed value to BIP32_MAX_INDEX before storing it, keeping changeset application infallible and monotone. --- diff --git a/crates/chain/src/indexer/keychain_txout.rs b/crates/chain/src/indexer/keychain_txout.rs index 33b5c972..e436ad7a 100644 --- a/crates/chain/src/indexer/keychain_txout.rs +++ b/crates/chain/src/indexer/keychain_txout.rs @@ -992,6 +992,7 @@ impl KeychainTxOutIndex { } } for (did, index) in changeset.last_revealed { + let index = index.min(BIP32_MAX_INDEX); let v = self.last_revealed.entry(did).or_default(); *v = index.max(*v); self.replenish_inner_index_did(did, self.lookahead); @@ -1226,4 +1227,35 @@ mod test { let index = KeychainTxOutIndex::::from_changeset(lookahead, false, init_cs); assert!(index.spk_cache.is_empty()); } + + #[test] + fn apply_changeset_clamps_out_of_range_index_and_derives_correct_spk() { + let s = DESCRIPTORS[0]; + let desc = Descriptor::parse_descriptor(&Secp256k1::new(), s) + .unwrap() + .0; + let mut index = KeychainTxOutIndex::new(0, false); + let did = desc.descriptor_id(); + let _ = index.insert_descriptor(0i32, desc.clone()); + let seed_index = BIP32_MAX_INDEX - 1; + let spk = desc + .at_derivation_index(seed_index) + .unwrap() + .script_pubkey(); + index.inner.insert_spk((0i32, seed_index), spk); + let changeset = ChangeSet { + last_revealed: [(did, BIP32_MAX_INDEX + 1)].into(), + ..Default::default() + }; + index.apply_changeset(changeset); + assert_eq!(index.last_revealed_index(0i32), Some(BIP32_MAX_INDEX)); + assert_eq!( + index.spk_at_index(0i32, BIP32_MAX_INDEX), + Some( + desc.at_derivation_index(BIP32_MAX_INDEX) + .unwrap() + .script_pubkey() + ) + ); + } }