Documentation
¶
Overview ¶
Package llo (import path .../llo/dev/v31) implements the LLO reporting plugin against libocr's OCR3.1 interface (offchainreporting2plus/ocr3_1types).
Experimental ¶
This package lives under llo/dev and is experimental: OCR3.1 is not released, the state model is still moving, and the API carries no stability guarantee. See the llo/dev package documentation. It graduates to .../llo/v31 once the protocol version ships.
It is a dev-tree counterpart of the production OCR3.0 plugin at .../llo/v30, not a peer of it. Version-agnostic primitives (stream values, report codecs, aggregators, channel-definition helpers, opts cache, retirement types, lifecycle constants, limits and all generated protobuf types) live in the root llo package and are shared via a dot-import. This package is a self-contained plugin driver: it does not import v30.
State model ¶
Unlike v30 (which threads the full outcome through OutcomeContext.PreviousOutcome), v31 stores state in the replicated KeyValueState. The in-round KeyValueStateReader only supports point Read(key); it has no range scan, so the set of live channels is tracked in an explicit index key. See kv.go for the key schema. All values written to the KV store MUST be serialized deterministically (protobuf with Deterministic:true, or fixed-width big-endian integers) because the store is replicated across oracles and any divergence halts the protocol.
Blobs ¶
Observations carry per-stream values. When the serialized stream-value payload is large it is disseminated as a blob and referenced by handle in the observation, rather than sent inline. See observation.go.
Parity status (vs v30) ¶
Implemented: lifecycle bootstrap/transitions (staging→production promotion, retirement), channel add/remove voting, stream aggregation (median/mode/quote via the shared aggregators), min-report-interval validAfter, precursor construction, report generation, blob-backed observations.
Seconds-resolution overlap prevention (for report formats that encode timestamps at second granularity) is implemented in resolution.go and applied in both the current-round (isReportable) and previous-round (prevReportable) reportability checks.
DisableNilStreamValues (a channel with any nil stream aggregate is unreportable), cross-round timestamped-aggregate carry-forward (t/ KV keys, newer-wins monotonicity), and best-effort outcome/report telemetry are also implemented. Reportability is persisted per channel each round (the r/ KV key) so the next round can advance validAfter faithfully without re-deriving it from aggregates that are not otherwise persisted.
Calculated streams (EVMABIEncodeUnpackedExpr channels) are supported via the expression engine in calculated.go, run at the end of StateTransition.
History-backfill channels are supported: backfill.go selects the next observation to emit (advancing a per-channel watermark stored in validAfter), reportability and validAfter advancement account for it, and Reports emits the backfill report encoded with the target channel's codec.
v31 now covers the full v30 reporting-plugin feature set. Consensus-affecting logic (state transition, aggregation, reportability, backfill, calculated streams) is ported from v30; the transport differs (KV state + blobs).
Missing: Blob success path isn't unit-tested: ocr3_1types.BlobHandle has no exported constructor, it's in an internal package, so a test can't fabricate a handle. Tests cover the offload decision + inline fallback; the full broadcast→fetch→merge round-trip needs libocr-provided doubles or an integration test.
Index ¶
- Constants
- type Config
- type DSOpts
- type DataSource
- type Observation
- type Plugin
- func (p *Plugin) Close() error
- func (p *Plugin) Committed(ctx context.Context, seqNr uint64, _ ocr3_1types.KeyValueStateReader) error
- func (p *Plugin) Observation(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, ...) (ocrtypes.Observation, error)
- func (p *Plugin) ObservationQuorum(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, ...) (bool, error)
- func (p *Plugin) Query(ctx context.Context, seqNr uint64, _ ocr3_1types.KeyValueStateReader, ...) (ocrtypes.Query, error)
- func (p *Plugin) Reports(ctx context.Context, seqNr uint64, ...) ([]ocr3types.ReportPlus[llotypes.ReportInfo], error)
- func (p *Plugin) ShouldAcceptAttestedReport(context.Context, uint64, ocr3types.ReportWithInfo[llotypes.ReportInfo]) (bool, error)
- func (p *Plugin) ShouldTransmitAcceptedReport(context.Context, uint64, ocr3types.ReportWithInfo[llotypes.ReportInfo]) (bool, error)
- func (p *Plugin) StateTransition(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, ...) (ocr3_1types.ReportsPlusPrecursor, error)
- func (p *Plugin) ValidateObservation(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, ...) error
- type PluginFactory
- type PluginFactoryParams
- type ShouldRetireCache
Constants ¶
const DefaultBlobThreshold = 128 * 1024
DefaultBlobThreshold is the default serialized stream-value payload size in bytes above which an observation offloads its stream values to a blob.
const MaxMaxQueryBytesUnused = 0
MaxMaxQueryBytesUnused documents that LLO uses an empty query.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// VerboseLogging enables additional, potentially expensive logging.
VerboseLogging bool
}
Config holds v31 plugin behavior toggles.
type DSOpts ¶
type DSOpts = datasource.DSOpts
DSOpts and DataSource are the shared, version-agnostic data-source types. Kept as aliases here so existing llov31.DSOpts / llov31.DataSource references keep working. Lifecycle is carried directly via DSOpts.LifeCycleStage() (read from the KeyValueState at the Observe call site).
type DataSource ¶
type DataSource = datasource.DataSource
type Observation ¶
type Observation struct {
AttestedPredecessorRetirement []byte
ShouldRetire bool
UnixTimestampNanoseconds uint64
RemoveChannelIDs map[llotypes.ChannelID]struct{}
UpdateChannelDefinitions llotypes.ChannelDefinitions
StreamValues protocol.StreamValues
}
Observation is the decoded per-round observation. It mirrors the v30 Observation, but is disseminated using the v31 wire framing (which supports offloading the bulk stream-value payload to a blob).
type Plugin ¶
type Plugin struct {
Config Config
PredecessorConfigDigest *ocrtypes.ConfigDigest
ConfigDigest ocrtypes.ConfigDigest
PredecessorRetirementReportCache protocol.PredecessorRetirementReportCache
ShouldRetireCache ShouldRetireCache
ChannelDefinitionCache llotypes.ChannelDefinitionCache
DataSource DataSource
Logger logger.Logger
N int
F int
RetirementReportCodec protocol.RetirementReportCodec
ReportCodecs map[llotypes.ReportFormat]protocol.ReportCodec
DonID uint32
OptsCache *protocol.OptsCache
// Optional telemetry sinks; best-effort, non-blocking.
OutcomeTelemetryCh chan<- *protocol.LLOOutcomeTelemetry
ReportTelemetryCh chan<- *protocol.LLOReportTelemetry
MaxDurationObservation time.Duration
// From offchain config
ProtocolVersion uint32
DefaultMinReportIntervalNanoseconds uint64
// BlobThreshold is the serialized stream-value payload size (bytes) above
// which observations offload stream values to a blob. 0 disables offloading.
BlobThreshold int
}
Plugin is the OCR3.1 LLO reporting plugin.
func (*Plugin) Committed ¶
func (p *Plugin) Committed(ctx context.Context, seqNr uint64, _ ocr3_1types.KeyValueStateReader) error
Committed is a no-op: LLO has no on-commit side effects, and Committed is not guaranteed to be called for every seqNr. Outcome telemetry is emitted from StateTransition, so there is nothing to do here.
func (*Plugin) Observation ¶
func (p *Plugin) Observation(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, kvReader ocr3_1types.KeyValueStateReader, bbf ocr3_1types.BlobBroadcastFetcher) (ocrtypes.Observation, error)
Observation reads current state from the KeyValueState, gathers stream observations, votes on channel changes, and returns a (possibly blob-backed) serialized observation.
func (*Plugin) ObservationQuorum ¶
func (p *Plugin) ObservationQuorum(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, aos []ocrtypes.AttributedObservation, _ ocr3_1types.KeyValueStateReader, _ ocr3_1types.BlobFetcher) (bool, error)
ObservationQuorum uses the standard 2f+1 quorum.
func (*Plugin) Query ¶
func (p *Plugin) Query(ctx context.Context, seqNr uint64, _ ocr3_1types.KeyValueStateReader, _ ocr3_1types.BlobBroadcastFetcher) (ocrtypes.Query, error)
Query is empty: LLO oracles do not coordinate on what to observe.
func (*Plugin) Reports ¶
func (p *Plugin) Reports(ctx context.Context, seqNr uint64, rawPrecursor ocr3_1types.ReportsPlusPrecursor) ([]ocr3types.ReportPlus[llotypes.ReportInfo], error)
Reports generates the (possibly empty) list of reports from a precursor. It receives no KeyValueStateReader, so the precursor must be fully self-sufficient.
func (*Plugin) ShouldAcceptAttestedReport ¶
func (p *Plugin) ShouldAcceptAttestedReport(context.Context, uint64, ocr3types.ReportWithInfo[llotypes.ReportInfo]) (bool, error)
func (*Plugin) ShouldTransmitAcceptedReport ¶
func (p *Plugin) ShouldTransmitAcceptedReport(context.Context, uint64, ocr3types.ReportWithInfo[llotypes.ReportInfo]) (bool, error)
func (*Plugin) StateTransition ¶
func (p *Plugin) StateTransition(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, aos []ocrtypes.AttributedObservation, kvRW ocr3_1types.KeyValueStateReadWriter, bf ocr3_1types.BlobFetcher) (ocr3_1types.ReportsPlusPrecursor, error)
StateTransition mutates the replicated KeyValueState based on the round's observations and returns a self-sufficient precursor for Reports.
This is a faithful port of the core of the v30 Outcome computation, adapted to read/write per-channel keys in the KeyValueState instead of decoding and re-encoding a monolithic previous outcome.
func (*Plugin) ValidateObservation ¶
func (p *Plugin) ValidateObservation(ctx context.Context, seqNr uint64, _ ocrtypes.AttributedQuery, ao ocrtypes.AttributedObservation, kvReader ocr3_1types.KeyValueStateReader, bf ocr3_1types.BlobFetcher) error
ValidateObservation checks an observation is well-formed. Blob-referenced stream values are fetched so lengths can be validated.
type PluginFactory ¶
type PluginFactory struct {
PluginFactoryParams
}
func NewPluginFactory ¶
func NewPluginFactory(p PluginFactoryParams) *PluginFactory
func (*PluginFactory) NewReportingPlugin ¶
func (f *PluginFactory) NewReportingPlugin(ctx context.Context, cfg ocr3types.ReportingPluginConfig, _ ocr3_1types.BlobBroadcastFetcher) (ocr3_1types.ReportingPlugin[llotypes.ReportInfo], ocr3_1types.ReportingPluginInfo, error)
type PluginFactoryParams ¶
type PluginFactoryParams struct {
Config
protocol.PredecessorRetirementReportCache
ShouldRetireCache
protocol.RetirementReportCodec
llotypes.ChannelDefinitionCache
DataSource
logger.Logger
protocol.OnchainConfigCodec
ReportCodecs map[llotypes.ReportFormat]protocol.ReportCodec
// OutcomeTelemetryCh, if set, receives one telemetry struct per StateTransition.
OutcomeTelemetryCh chan<- *protocol.LLOOutcomeTelemetry
// ReportTelemetryCh, if set, receives one telemetry struct per emitted report.
ReportTelemetryCh chan<- *protocol.LLOReportTelemetry
// DonID is optional and used only for telemetry and logging.
DonID uint32
// BlobThreshold overrides DefaultBlobThreshold if non-zero. A negative value
// disables blob offloading.
BlobThreshold int
}
PluginFactoryParams bundles the dependencies needed to construct the v31 reporting plugin. It mirrors the v30 params, minus the outcome codec (state lives in the KeyValueState), and adds BlobThreshold.
type ShouldRetireCache ¶
type ShouldRetireCache interface {
ShouldRetire(digest ocrtypes.ConfigDigest) (bool, error)
}
ShouldRetireCache reads asynchronously from the onchain ConfigurationStore whether this protocol instance should retire.