Skip to content

SPOR recommendations - #270

Draft
brunotm wants to merge 26 commits into
masterfrom
bm/DS-4103-spor-assessment-recommendations
Draft

brunotm wants to merge 26 commits into
masterfrom
bm/DS-4103-spor-assessment-recommendations

Conversation

@brunotm

@brunotm brunotm commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

No description provided.

…t declared limits

The factory declares libocr's maximum for every limit, but several values the
plugin produces were not bounded by any admission rule, so libocr could reject
the write or message and fail the round for every oracle at once.
…t admission

Add three consensus-relevant limits: MaxTotalStreamEntries (50_000) over
Σ len(cd.Streams) for the whole definition set, MaxChannelOptsBytes (16 KiB)
per channel, and MaxTotalOptsBytes (1 MiB) over the set. Sized against a
production definitions file (705 live channels, 4_520 stream entries, 304 KiB of
opts, a 475 KiB c/defs record) so the caps sit above where real configurations
grow. Worst case at every cap marshals to 1.31 MiB, 66% of libocr's 2 MiB
per-key limit.
MaxDecimalExponent bounds where a decimal's point sits, not how many digits
precede it, so a value with a legal exponent could carry an arbitrarily long
coefficient. A single stream value was therefore unbounded in bytes on every
path carrying one.
…sus fact

Reportability is a consensus decision; encoding is not. isReportable is a
pure function of the precursor and its result is persisted as
reportedLastRound, which the next round reads to advance validAfter. But
p.ReportCodecs is node-local plugin state that the state transition
cannot read without forking, so a channel whose format no codec covers
stays reportable, Reports drops it with a log line, and validAfter
advances over a round that emitted nothing.

Carry codec coverage as a replicated fact instead. Each observation
advertises the report formats its oracle can encode; StateTransition
tallies the advertisements and snapshots the tally on the precursor, so
reportableChannels and the persisted reportedLastRound read the identical
number. isReportable then requires 2f+1 advertised supporters for the
format a channel's report is encoded with.
…ready committed definitions

Log the finding and continue in v30 and v31 instead of returning an error, so
channel votes are still cast and the offending channel can be removed. The
channels stay observed.
…ob lifetime

Split MaxSnapshotRounds, the local gate on how stale a node's own stream
values may be when it references them, from BlobLifetimeRounds, the
expiration hint that decides how long peers can fetch the blob. Require
BlobFetchMarginRounds between them so a referenceable handle is always
still fetchable.

Derive the wall-clock snapshot age bound from the round period measured
across consecutive Take calls rather than from MaxDurationObservation,
which is unrelated to the round cadence and can reject every snapshot.
Leave the bound inert until a period is measured, and escalate a
sustained miss streak to an error log.

Use the snapshot timestamp as the observation timestamp.
ValidateObservation merged the observation's updates onto committed state
but kept the channels it voted to remove. That set is one no vote can
produce, so a swap of one channel for another while the set sits on a
whole-set budget failed verification on a ceiling its own vote clears.

Take the removals out of the merged set. Whole-set budgets cannot be
enforced per observation anyway as votes from different oracles combine,
and what gets committed is decided by the per-hash threshold.
Verification decodes channels opts up to four times per analysis (the
codec Verify and VerifyForAdmission, FeedID, and the calculated stream
IDs), and a round analyses largely the same set three times over: the
committed definitions and the desired ones in Observation, and the set
each update-carrying observation advocates in ValidateObservation.

Share one cache across all three call sites, so the parallel ValidateObservation
calls hit what Observation already decoded, and prunes it against the committed set,
which is the authority on which channels exist.

Over 2000 channels carrying 250 bytes of opts, The Observation two analyses
go from 12.1ms to 1.08ms and each ValidateObservation from 5.9ms to
0.51ms, with per-round allocations dropping from 40185 to 199.
Memoize blob payloads per round, scoped to the sequence number,
so a handle referenced by an observation is fetched,
decompressed and unmarshaled once for ValidateObservation and
StateTransition together instead of once per phase.

Retry a refused blob broadcast inside the pump cycle, bounded by
BlobBroadcastAttempts and by the cycle observation timeout, so
stream values that were gathered fine are not thrown away by one
transport failure.
To regenerate run
GOLDEN_UPDATE=1 go test ./llo/dev/v31/ -run Golden
Verifying an attested predecessor retirement report needs the predecessor
signer set, which StateTransition read from the node-local retirement report
cache.  Vote the signer set into a new cold key, c/pred, and verify against that.
Observation carries the local set until c/pred exists, which moves the
node-local read to where oracles are allowed to differ.

A report that fails verification against the agreed set now costs only the
report. The observation timestamp, votes and stream values still count,
where before one malformed field removed an oracle from the round.

Return an observationTally from decodeObservations instead of eleven values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant