perf(lite-backend): encode note addresses from pk_d instead of deriving
note_address() re-derived a note's payment address from its viewing key and diversifier (a group hash, an ivk hash and a 252-bit scalar multiplication) for every note in `notes` and `list` and for each new note in the block scan. The z-balance sums did the same derivation in their address filter, so `balance`, which sums four balances for each of Z z-addresses, cost Z x (N + 2U) derivations for N notes, U of them unspent. do_list_notes also encoded up to Z + Z^2 addresses to find the spendable ones and cloned that set once per transaction. Now: - note_address() encodes the note's stored pk_d with its diversifier. Both decryption and wallet load build that pk_d from the same key and diversifier, so the string is byte-identical; - the three z-balance sums share one address filter. The per-address sums behind `balance` compare (diversifier, pk_d) with each z-address directly; any other address string keeps the old exact string compare; - those sums work from a copy of the z-addresses whose lock is released before any note is read, so the txs-then-zkeys lock order that sync uses is unchanged; - do_list_notes builds the spendable-address set with Z encodes and borrows it. No output changes: balances and the notes/list/balance JSON are byte-identical. The address filter no longer contains an unwrap (it could not fail). Tests compare against verbatim copies of the old functions over randomized chains (note addresses, also after a reload; every filtered balance for own, diversified, foreign and junk address strings; the per-address balances; the spendable set), and a concurrency test fails with a deadlock if the zkeys lock is held across the sums. A differential run against the unmodified backend compared per-address balances and the JSON after every operation, through reorgs and reloads, and the old build re-saves the new files byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+10
-11
@@ -659,13 +659,14 @@ impl LightClient {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
|
||||
// Collect z addresses
|
||||
let z_addresses = wallet.get_all_zaddresses().iter().map(|zaddress| {
|
||||
let z_addresses = wallet.z_balances_by_address().into_iter()
|
||||
.map(|(address, zbalance, unconfirmed, verified_zbalance, spendable_zbalance)| {
|
||||
object!{
|
||||
"address" => zaddress.clone(),
|
||||
"zbalance" => wallet.zbalance(Some(zaddress.clone())),
|
||||
"unconfirmed" => wallet.unconfirmed_zbalance(Some(zaddress.clone())),
|
||||
"verified_zbalance" => wallet.verified_zbalance(Some(zaddress.clone())),
|
||||
"spendable_zbalance" => wallet.spendable_zbalance(Some(zaddress.clone()))
|
||||
"address" => address,
|
||||
"zbalance" => zbalance,
|
||||
"unconfirmed" => unconfirmed,
|
||||
"verified_zbalance" => verified_zbalance,
|
||||
"spendable_zbalance" => spendable_zbalance
|
||||
}
|
||||
}).collect::<Vec<JsonValue>>();
|
||||
|
||||
@@ -847,15 +848,13 @@ impl LightClient {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
|
||||
// First, collect all extfvk's that are spendable (i.e., we have the private key)
|
||||
let spendable_address: HashSet<String> = wallet.get_all_zaddresses().iter()
|
||||
.filter(|address| wallet.have_spending_key_for_zaddress(address))
|
||||
.map(|address| address.clone())
|
||||
.collect();
|
||||
let spendable_address_set: HashSet<String> = wallet.spendable_zaddress_set();
|
||||
let spendable_address: &HashSet<String> = &spendable_address_set;
|
||||
|
||||
// Collect Sapling notes
|
||||
wallet.txs.read().unwrap().iter()
|
||||
.flat_map( |(txid, wtx)| {
|
||||
let spendable_address = spendable_address.clone();
|
||||
let spendable_address: &HashSet<String> = spendable_address;
|
||||
wtx.notes.iter().filter_map(move |nd|
|
||||
if !all_notes && nd.spent.is_some() {
|
||||
None
|
||||
|
||||
Reference in New Issue
Block a user