Test Details Overview

Read rep weights for a tally as a single consistent snapshot (#5167)

* Read rep weights for a tally as a single consistent snapshot

The election ballot read each voting rep's weight with a separate locked
lookup, so a tally over N reps cost N lock acquisitions and could
interleave with ledger updates that touch several reps at once. The
online weight total summed per-rep reads the same way.

Add a batched rep_weights::get and ledger::weights that read all requested
reps under one lock, make the ballot's weight query take a span of reps,
and use it from the election and from online_reps::calculate_online.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Apply a representative and balance change as one rep weight update

move_add_sub implemented a block that changes representative and balance
at once as a move followed by a separate add or sub, each taking the
rep_weights mutex on its own. Between the two steps the cache held an
intermediate state, with the balance difference missing from or left
behind on one of the two reps, and a batch read could observe it.

Compute both new weights up front, write both to the store, and update
both cache entries together with the committed and unused totals under
one lock. move becomes the equal-amount case of the same operation.

rep_weights_move_add_sub_snapshot and weights_snapshot_send_change
observe the intermediate state with the previous implementation and
pass with this one. The rep_weights unit tests are renamed and
documented consistently and assert the stored and cached weights
alongside the entry counts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Check rep weight accounting through every block type and rollback

No test asserted the committed and unused weight totals while blocks
were processed and rolled back, and a receive into an epoch-opened
account, whose previous representative is the zero account, had no
weight assertion in either direction.

weights_all_block_types drives legacy send, open, receive and change,
state send, open, receive and send with a representative change, a
plain representative change, an epoch upgrade, an epoch open and a
receive into it through the ledger, then rolls every block back. After
each step the cached and stored weights are compared with a model of
each account's representative and balance, the committed total with
the sum of balances and the unused total with what became pending, and
a ledger rebuilt from the store is checked the same way.

rep_weights_append_from covers the startup merge directly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Guarantee the rep weight snapshot tests observe the update

The snapshot tests released the writer after the reader's first read
and stopped the reader once the writer was done, so a schedule that
pauses the reader right after the release lets the writer finish
unobserved and the test pass on a single read.

Make the writer wait after every update, block and rollback until the
reader has taken another snapshot, so every state the writer produces
is observed. The iteration counts drop to a hundred rounds or blocks,
which keeps the handshake cheap and the tests short.

Add deterministic counterparts that need no scheduling luck. A
nano::test::hooked_backend wraps the store backend and runs a callback
before every point access, and a ledger store can be built on it.
rep_weights_move_add_sub_mid_update and weights_snapshot_mid_update
batch-read the cache from that callback, which runs inside the update
before the cache lock is taken, exactly where a concurrent reader could
interleave. Both fail deterministically against the former split
implementation, together with the two unequal-amount snapshot tests,
and pass against the current one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Read the cached tally of an incoming fork as one weight snapshot

election::publish summed the vote cache weight behind a fork arriving
at a full ballot with one ledger.weight call per voter, the per-rep
pattern the batched ledger.weights replaced on the rest of the tally
path. Collect the voters and read their weights in one batched call,
so a weight moving between two of them while the sum is taken is
counted once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

__________

Type: commit

Hash: e9642068151d9a487b851a433caab1ac272359a1

Created: yesterday

__________

gr0v1ty/nano-node:e9642068151d9a487b851a433caab1ac272359a1

Test Results

Test Case Status Duration Median Deviation Log Graph