fix(litebe): make HD t-address derivation atomic under scan
Deriving the next HD transparent address read the key count under a read lock, released it, derived the address, and only then took a write lock to append it. Between reading the position and appending, another caller - for instance a scan deriving an address at the same time - could read the same position, derive the same address, and append it too, leaving a duplicate transparent key in the wallet. - The whole read-modify-write now runs under a single write guard, so the position a caller derives from reflects every key already added. The slow seed stretch is computed once before the guard is taken, so holding the lock does not block on it. - As a further guard, derivation skips a position whose address is already present and advances to the next, so no path can produce a duplicate even if positions were ever to overlap. - The starting index is computed exactly as before (it still counts imported keys too), so a wallet with no contention derives the same addresses in the same order. Uncontended derivation produces the same addresses in the same order, verified against the differential harness. The wallet file format is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+10
-8
@@ -659,15 +659,17 @@ impl LightWallet {
|
||||
return "Error: Can't add key while wallet is encrypted".to_string();
|
||||
}
|
||||
|
||||
let pos = self.tkeys.read().unwrap().len() as u32;
|
||||
// PBKDF2 (the slow part) outside the lock; hold ONE write guard across read-modify-write.
|
||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&self.seed, Language::English).unwrap(), "");
|
||||
|
||||
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos);
|
||||
let address = self.address_from_sk(&sk);
|
||||
|
||||
self.tkeys.write().unwrap().push(WalletTKey::new_hdkey(sk, address.clone()));
|
||||
|
||||
address
|
||||
let mut tkeys = self.tkeys.write().unwrap();
|
||||
let mut pos = tkeys.len() as u32; // same start index as today (counts imported keys too - unchanged)
|
||||
loop {
|
||||
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos);
|
||||
let address = self.address_from_sk(&sk);
|
||||
if tkeys.iter().any(|t| t.address == address) { pos += 1; continue; }
|
||||
tkeys.push(WalletTKey::new_hdkey(sk, address.clone()));
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_taddr(&mut self, sk: String, birthday: u64) -> String {
|
||||
|
||||
Reference in New Issue
Block a user