The round needed the local tip and reached it through LastAccepted plus
GetBlock, which named this package's Block. The node's VM answers with its own,
so the whole interface went unsatisfied and adoption refused every chain — the
same braiding as the summary type, one field over.
Tip is a height. That is the entire question the round asks: the floor no
candidate may sit at or below. Nothing about which type a block arrives as
belongs in it.
Co-authored-by: Hanzo Dev <[email protected]>
A VM's summary interface is declared where the VM is built. Naming one concrete
declaration here meant every VM carrying an identical one under another name
failed the assertion — and a failed assertion is indistinguishable from a VM
that cannot sync at all, so a wired client sat in a shipped build doing nothing.
That is exactly what happened: the node's VM answers with its own StateSummary,
the round demanded this package's, and adoption never ran on any chain.
The serving half took the summary type as a parameter for this reason and did
not have the problem. Now both halves read the same way, and neither cares which
declaration a summary arrives under so long as it answers the two or three
questions being asked of it.
Co-authored-by: Hanzo Dev <[email protected]>
source.go carries Source, VM, Offer and Ballot — every type summary.go names.
It was never tracked, so the published module did not compile at all for a
consumer: undefined Source, undefined VM, undefined Ballot. The package's own
tests passed the whole time, because a test builds against the working tree.
Also corrects two of my own from the previous commit. A seed at or below the
recovery hint stands again: the certified frontier belonging under what the VM
has already applied is the safe direction, and the dangerous one — above — is
what the walk-from-hint arm exists for. And the quorum-engine test harness now
wires stake weighting, so a transport-authenticated vote is judged the way
production judges it; without it the membership check refused every vote,
including the validators' own.
Co-authored-by: Hanzo Dev <[email protected]>
A node could adopt a peer's state and could not serve one. Adoption already
existed in engine/chain/summary; nothing answered the two requests it makes, so
every node was a client of a service no node provided.
The serving half reads the VM and returns what to send, leaving framing and
transport to the node. It never consults StateSyncEnabled: a node's own
preference says nothing about what its peers may ask, and a chain that answers
only when it would also ask leaves a fleet unable to repair itself the moment its
healthy members have nothing left to sync to.
Silence and an empty reply are distinct answers. No summary, or an error, sends
nothing and the requester closes its round on its deadline. A request naming no
heights gets an empty reply — "none of those" — which completes the tally at
once. Swap them and every round either stalls or under-counts.
The op-space carries no wire for a singular by-height request, so nothing here
answers one.
Also removes the per-summary overflow check in elect. Its stake only ever sums a
subset of the ballots `answered` already sums, and `answered` is checked one line
earlier per ballot, so the second check could never fire. The invariant is now
stated where the addition happens. Coverage is 100% on both halves, reached by
deleting the branch rather than by asserting an impossible case.
Comments across the engine drop incident detail for the rule each one holds.
Co-authored-by: Hanzo Dev <[email protected]>