Documentation
¶
Index ¶
- Constants
- Variables
- func BlobHandleMarshalledBytesUpperBound(n int, f int) int
- type BlobBroadcastFetcher
- type BlobBroadcaster
- type BlobExpirationHint
- type BlobExpirationHintSequenceNumber
- type BlobFetcher
- type BlobHandle
- type Database
- type KeyValueDatabase
- type KeyValueDatabaseFactory
- type KeyValueDatabaseIterator
- type KeyValueDatabaseReadTransaction
- type KeyValueDatabaseReadWriteTransaction
- type KeyValueIteratordeprecated
- type KeyValueReadTransactiondeprecated
- type KeyValueReadWriteTransactiondeprecated
- type KeyValueReadWriterdeprecated
- type KeyValueReaderdeprecated
- type KeyValueStateReadTransaction
- type KeyValueStateReadWriter
- type KeyValueStateReader
- type ReadOnlyKeyValueState
- type ReportingPlugin
- type ReportingPluginFactory
- type ReportingPluginFactory2
- type ReportingPluginInfo
- type ReportingPluginInfo1
- type ReportingPluginLimits
- type ReportsPlusPrecursor
Constants ¶
const ( MaxMaxQueryBytes = mib / 2 MaxMaxObservationBytes = mib / 2 MaxMaxReportsPlusPrecursorBytes = 5 * mib MaxMaxReportBytes = 5 * mib MaxMaxReportCount = 2000 MaxMaxKeyValueKeyBytes = 1 * mib MaxMaxKeyValueValueBytes = 2 * mib MaxMaxKeyValueModifiedKeys = 10_000 MaxMaxKeyValueModifiedKeysPlusValuesBytes = 10 * mib MaxMaxBlobPayloadBytes = 5 * mib )
It's much easier to increase these than to decrease them, so we start with conservative values. Talk to the maintainers if you need higher limits for your plugin.
Variables ¶
var ErrKeyValueDatabaseDoesNotExist = fmt.Errorf("key value database does not exist")
Functions ¶
func BlobHandleMarshalledBytesUpperBound ¶
BlobHandleMarshalledBytesUpperBound returns a conservative upper bound on the marshalled length of a BlobHandle. The n & f parameters must match those in ocr3types.ReportingPluginConfig.
This is useful for computing limits like MaxObservationBytes when blob handles will be included in observations.
Example usage in a ReportingPluginFactory:
func (f *Factory) NewReportingPlugin(
ctx context.Context,
config ocr3types.ReportingPluginConfig,
bbf ocr3_1types.BlobBroadcastFetcher,
) (ocr3_1types.ReportingPlugin[RI], ocr3_1types.ReportingPluginInfo, error) {
maxBlobHandleBytes := ocr3_1types.BlobHandleMarshalledBytesUpperBound(config.N, config.F)
maxObservationBytes := baseObservationSize + numBlobHandles*maxBlobHandleBytes
// ...
}
Types ¶
type BlobBroadcastFetcher ¶
type BlobBroadcastFetcher interface {
BlobBroadcaster
BlobFetcher
}
type BlobBroadcaster ¶
type BlobBroadcaster interface {
// BroadcastBlob broadcasts a blob with the given payload and expiration
// hint, returning a handle that can be used to refer to the blob in the
// future (e.g. in a query or observation). Note that BroadcastBlob may
// return an error, and callers must gracefully handle such an error. Errors
// might be transient, and related to resource exhaustion prevention, so
// callers are encouraged to employ a backoff when retrying. An error in
// BroadcastBlob should not cause Observation to error also. BroadcastBlob
// is a blocking call, and callers must ensure that it fits into their
// runtime envelope.
BroadcastBlob(ctx context.Context, payload []byte, expirationHint BlobExpirationHint) (BlobHandle, error)
}
type BlobExpirationHint ¶
type BlobExpirationHint interface {
// contains filtered or unexported methods
}
type BlobExpirationHintSequenceNumber ¶
type BlobExpirationHintSequenceNumber struct{ SeqNr uint64 }
type BlobFetcher ¶
type BlobFetcher interface {
// FetchBlob fetches the payload corresponding to the BlobHandle passed.
// Callers must gracefully handle errors returned by FetchBlob. FetchBlob is
// a blocking call, and callers must ensure that it fits into their runtime
// envelope.
FetchBlob(ctx context.Context, handle BlobHandle) ([]byte, error)
}
type BlobHandle ¶
type BlobHandle = blobtypes.BlobHandle
type KeyValueDatabase ¶
type KeyValueDatabase interface {
NewReadWriteTransaction() (KeyValueDatabaseReadWriteTransaction, error)
NewReadTransaction() (KeyValueDatabaseReadTransaction, error)
Close() error
}
KeyValueDatabase is a key-value database.
NewReadTransaction and NewReadWriteTransaction may be called concurrently with each other and themselves. At most one read-write transaction is active at a time: NewReadWriteTransaction blocks until the previous read-write transaction has been committed or discarded.
Every transaction must be committed or discarded (see KeyValueDatabaseReadTransaction) before Close is called. Close must not be called concurrently with any other method, and no method may be called after Close.
type KeyValueDatabaseFactory ¶
type KeyValueDatabaseFactory interface {
NewKeyValueDatabase(configDigest types.ConfigDigest) (KeyValueDatabase, error)
NewKeyValueDatabaseIfExists(configDigest types.ConfigDigest) (KeyValueDatabase, error)
}
type KeyValueDatabaseIterator ¶
type KeyValueDatabaseIterator interface {
// Next prepares the next key-value pair for reading. It returns true on
// success, or false if there is no next key-value pair or an error occurred
// while preparing it.
Next() bool
// Key returns the key of the current key-value pair.
Key() []byte
// Value returns the value of the current key-value pair. An error value
// indicates a failure to retrieve the value, and the caller is responsible
// for handling it. Even if all errors are nil, [KeyValueDatabaseIterator.Err] must
// be checked after iteration is completed.
Value() ([]byte, error)
// Err returns any error encountered during iteration. Must be checked after
// the end of the iteration, to ensure that no key-value pairs were missed
// due to iteration errors. Errors in [KeyValueDatabaseIterator.Value] are distinct
// and will not cause a non-nil error.
Err() error
// Close closes the iterator and releases any resources associated with it.
// Must be called in any case, even if the iteration encountered any
// error through [KeyValueDatabaseIterator.Value] or [KeyValueDatabaseIterator.Err].
// After Close returns, no further calls may be made on the iterator,
// including another Close.
Close() error
}
Example usage:
it := kvReader.Range(loKey, hiKeyExcl)
defer it.Close()
for it.Next() {
key := it.Key()
value, err := it.Value()
if err != nil {
// handle error
}
// process key and value
}
if err := it.Err(); err != nil {
// handle error
}
type KeyValueDatabaseReadTransaction ¶
type KeyValueDatabaseReadTransaction interface {
// If the key exists, the returned value must not be nil!
Read(key []byte) ([]byte, error)
// Range iterates over the key-value pairs with keys in the range [loKey,
// hiKeyExcl), in ascending order of key. Key-value stores typically store
// keys in a sorted order, making this a fast operation. loKey can be set to
// 0 length or nil for iteration without a lower bound. hiKeyExcl can be set
// to 0 length or nil for iteration without an upper bound.
//
// WARNING: DO NOT perform any writes/deletes to the key-value store while
// the iterator is opened.
Range(loKey []byte, hiKeyExcl []byte) KeyValueDatabaseIterator
// Discard discards the transaction and releases its resources. See
// [KeyValueDatabaseReadTransaction] for the lifetime rules.
Discard()
}
A KeyValueDatabaseReadTransaction is a read transaction over the database.
Lifetime ¶
Every transaction must be discarded — or, for a KeyValueDatabaseReadWriteTransaction, committed (or discarded to abort it). All iterators obtained from Range must be closed before the transaction is discarded or committed. After Discard or Commit return, no further calls may be made on the transaction, except that Discard may be called multiple times.
Concurrency ¶
Read and Range may be called concurrently with each other and themselves. Iterators from different Range calls may be used concurrently with each other and with Read and Range calls, but an individual iterator is not safe for concurrent use. Discard must not be called concurrently with any other call on the transaction.
type KeyValueDatabaseReadWriteTransaction ¶
type KeyValueDatabaseReadWriteTransaction interface {
KeyValueDatabaseReadTransaction
// A value of nil is interpreted as an empty slice, and does *not* delete
// the key. For deletions you must use the Delete method.
Write(key []byte, value []byte) error
Delete(key []byte) error
// Commit commits the transaction. Whether it succeeds or fails, no
// further calls other than Discard may be made on the transaction once
// Commit returns.
Commit() error
}
A KeyValueDatabaseReadWriteTransaction is a read-write transaction over the database. Writes and deletes are visible to subsequent reads within the same transaction. See KeyValueDatabaseReadTransaction for the lifetime rules.
Concurrency ¶
Unlike a read transaction, a read-write transaction is not safe for concurrent use.
type KeyValueIterator
deprecated
type KeyValueIterator = KeyValueDatabaseIterator
Deprecated: Use KeyValueDatabaseIterator instead.
type KeyValueReadTransaction
deprecated
type KeyValueReadTransaction = KeyValueDatabaseReadTransaction
Deprecated: Use KeyValueDatabaseReadTransaction instead.
type KeyValueReadWriteTransaction
deprecated
type KeyValueReadWriteTransaction = KeyValueDatabaseReadWriteTransaction
Deprecated: Use KeyValueDatabaseReadWriteTransaction instead.
type KeyValueReadWriter
deprecated
type KeyValueReadWriter = KeyValueStateReadWriter
Deprecated: Use KeyValueStateReadWriter instead.
type KeyValueReader
deprecated
type KeyValueReader = KeyValueStateReader
Deprecated: Use KeyValueStateReader instead.
type KeyValueStateReadTransaction ¶
type KeyValueStateReadTransaction interface {
KeyValueStateReader
// SeqNr returns the sequence number this transaction reads at: it shows you
// the KeyValueState as it was once SeqNr() had been committed. Newer
// sequence numbers may be committed while you hold the transaction; what it
// shows you will not change.
SeqNr() uint64
// Discard releases the transaction. Calling it more than once is fine.
// After Discard returns, no further calls other than Discard may be made
// on the transaction.
Discard()
}
KeyValueStateReadTransaction provides read access to the replicated KeyValueState at a fixed point in time.
See ReadOnlyKeyValueState for transaction ownership, lifetime, resource usage, and monitoring guidance. A transaction is safe for concurrent use.
See ReadOnlyKeyValueState.NewReadTransaction for a deferred-discard example.
type KeyValueStateReadWriter ¶
type KeyValueStateReadWriter interface {
KeyValueStateReader
// Write writes the value for the provided key.
// A nil key or value is interpreted as an empty slice.
// To delete a key, use the Delete method.
Write(key []byte, value []byte) error
// Delete deletes the provided key and its associated value.
// A nil key is interpreted as an empty slice.
Delete(key []byte) error
}
KeyValueStateReadWriter provides read and write access to the replicated KeyValueState.
Write and Delete are not safe for concurrent use: interleaved concurrent writes and deletes may corrupt the resulting state transition. Perform all writes and deletes from a single goroutine.
type KeyValueStateReader ¶
type KeyValueStateReader interface {
// Read reads the value associated with the given key.
// A nil key is interpreted as an empty slice.
// If the key exists, the returned value will be non-nil.
// If the key does not exist, it returns (nil, nil).
Read(key []byte) ([]byte, error)
}
KeyValueStateReader provides read access to the replicated KeyValueState. Read is safe for concurrent use.
type ReadOnlyKeyValueState ¶
type ReadOnlyKeyValueState interface {
// NewReadTransaction returns a [KeyValueStateReadTransaction] over the
// replicated KeyValueState. See [ReadOnlyKeyValueState] for transaction
// ownership, lifetime, resource usage, and monitoring guidance.
//
// ctx applies only to this call, not to the returned transaction.
// Canceling ctx does not discard the transaction or prevent further reads;
// you must still call Discard.
//
// Defer Discard immediately after successfully opening a transaction:
//
// readTransaction, err := keyValueState.NewReadTransaction(ctx)
// if err != nil {
// return err
// }
// defer readTransaction.Discard()
//
// Errors can occur during normal operation, for example when the
// KeyValueState is temporarily unavailable while the oracle is starting up.
// Fail the request you were serving and try again later.
// See [ReadOnlyKeyValueState] for restrictions on use from ReportingPlugin
// methods.
NewReadTransaction(ctx context.Context) (KeyValueStateReadTransaction, error)
}
ReadOnlyKeyValueState gives you read access to the replicated KeyValueState, for example to answer requests from your own clients.
ReportingPlugin method restrictions ¶
Do not use it, or any transaction obtained from it, inside ReportingPlugin methods that:
- are marked pure‡; or
- receive a KeyValueStateReader or KeyValueStateReadWriter argument.
Pure‡ methods must return results determined solely by their arguments, but the state read through ReadOnlyKeyValueState is not. Methods that receive a state reader or read-writer argument must use that argument instead, so that they access state at the appropriate sequence number.
Transaction ownership and resource usage ¶
You are responsible for bounding the number of transactions you keep open concurrently. The library does not enforce a cap: excessive concurrent usage or leaked transactions can exhaust resources and degrade or stall the oracle.
You must discard every transaction you open, promptly after reading what you need. An open transaction prevents the database from reclaiming older versions of the data it can still see. Long-held or leaked transactions can cause unbounded storage growth. Do not call out over the network, wait on a lock, or block on other work while holding a transaction open. See ReadOnlyKeyValueState.NewReadTransaction for a deferred-discard example.
Plugin shutdown ¶
Before ReportingPlugin.Close returns, the plugin must discard all its transactions and stop any goroutines that could open or use transactions. Do not use ReadOnlyKeyValueState or its transactions after ReportingPlugin.Close returns.
Monitoring ¶
Use the following Prometheus metrics to monitor transaction usage. Subtract ocr3_1_read_only_key_value_state_discarded_transactions_total from ocr3_1_read_only_key_value_state_opened_transactions_total for matching label sets to track the number currently open. Monitor ocr3_1_read_only_key_value_state_oldest_open_transaction_age_seconds to detect long-held transactions and leaks.
type ReportingPlugin ¶
type ReportingPlugin[RI any] interface { // Query creates a Query that is sent from the leader to all follower nodes // as part of the request for an observation. Be careful! A malicious leader // could equivocate (i.e. send different queries to different followers.) // Many applications will likely be better off always using an empty query // if the oracles don't need to coordinate on what to observe (e.g. in case // of a price feed) or the underlying data source offers an (eventually) // consistent view to different oracles (e.g. in case of observing a // blockchain). // // You may assume that the seqNr is increasing monotonically (though *not* // strictly) across the lifetime of a protocol instance. // // The keyValueStateReader gives read access to the replicated KeyValueState // at the point after seqNr - 1 is committed. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) // // The blobBroadcastFetcher enables broadcasting and fetching blobs. Broadcasting blobs // can be a more efficient data dissemination method than direct use of Query/Observation // when data is larger. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted // at this function and to pass it to separate goroutines, but they must stop using it before this // function returns.) Query( ctx context.Context, seqNr uint64, keyValueStateReader KeyValueStateReader, blobBroadcastFetcher BlobBroadcastFetcher, ) (types.Query, error) // Observation gets an observation from the underlying data source. Returns // a value or an error. // // You may assume that the seqNr is increasing monotonically (though *not* // strictly) across the lifetime of a protocol instance. // // The keyValueStateReader gives read access to the replicated KeyValueState // at the point after seqNr - 1 is committed. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) // // The blobBroadcastFetcher enables broadcasting and fetching blobs. Broadcasting blobs // can be a more efficient data dissemination method than direct use of Query/Observation // when data is larger. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted // at this function and to pass it to separate goroutines, but they must stop using it before this // function returns.) Observation( ctx context.Context, seqNr uint64, aq types.AttributedQuery, keyValueStateReader KeyValueStateReader, blobBroadcastFetcher BlobBroadcastFetcher, ) (types.Observation, error) // ValidateObservation should return an error if an observation isn't well-formed. // Non-well-formed observations will be discarded by the protocol. // // This function must be pure‡. // // You may assume that the seqNr is increasing monotonically (though *not* // strictly) across the lifetime of a protocol instance. // // This function may be called multiple times for the same seqNr, e.g. once // for each observation. // // The keyValueStateReader gives read access to the replicated KeyValueState // at the point after seqNr - 1 is committed. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) // // The blobFetcher enables fetching blobs. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) ValidateObservation( ctx context.Context, seqNr uint64, aq types.AttributedQuery, ao types.AttributedObservation, keyValueStateReader KeyValueStateReader, blobFetcher BlobFetcher, ) error // ObservationQuorum indicates whether the provided valid (according to // ValidateObservation) observations are sufficient to construct an outcome. // // This function must be pure‡. // // This function may be called multiple times for the same seqNr, e.g. on // the leader as more observations arrive. // // This is an advanced feature. The "default" approach (what OCR1 & OCR2 // did) is to have this function call // quorumhelper.ObservationCountReachesObservationQuorum(QuorumTwoFPlusOne, ...) // // If you write a custom implementation, be sure to consider that Byzantine // oracles may not contribute valid observations, and you still want your // plugin to remain live. This function must be monotone in aos, i.e. if // it returns true for aos, it must also return true for any // superset of aos. // // The AttributedObservations are guaranteed to be from distinct oracles. // // The keyValueStateReader gives read access to the replicated KeyValueState // at the point after seqNr - 1 is committed. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) // // The blobFetcher enables fetching blobs. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) ObservationQuorum( ctx context.Context, seqNr uint64, aq types.AttributedQuery, aos []types.AttributedObservation, keyValueStateReader KeyValueStateReader, blobFetcher BlobFetcher, ) (quorumReached bool, err error) // StateTransition modifies the state of the Reporting Plugin, based on // the attributed query and the set of attributed observations of the round. // Generates ReportsPlusPrecursor, which encodes a possibly empty list of // reports. // // This function must be pure‡. // // You may assume that the seqNr is increasing monotonically (though *not* // strictly) across the lifetime of a protocol instance. // // You may assume that the provided list of attributed observations has been // (1) validated by ValidateObservation on each element, (2) checked // by ObservationQuorum to have reached quorum, and (3) all observations are // from distinct oracles. // // The keyValueStateReadWriter gives read and write access to the replicated KeyValueState // from the point after seqNr - 1 is committed. Writing to the keyValueStateReadWriter allows // a ReportingPlugin to modify the replicated KeyValueState. The keyValueStateReadWriter must not be // used outside the execution of this function. (It's okay to use it anywhere in the call tree rooted // at this function and to pass it to separate goroutines, but they must stop using it before this // function returns. Do not call Write or Delete concurrently from multiple goroutines; perform all // writes and deletes from a single goroutine.) // // The blobFetcher enables fetching blobs. It must not be used outside the execution // of this function. (It's okay to use it anywhere in the call tree rooted at this function // and to pass it to separate goroutines, but they must stop using it before this // function returns.) StateTransition( ctx context.Context, seqNr uint64, aq types.AttributedQuery, aos []types.AttributedObservation, keyValueStateReadWriter KeyValueStateReadWriter, blobFetcher BlobFetcher, ) (ReportsPlusPrecursor, error) // Committed notifies the plugin that seqNr has been committed. // It might not be called for all sequence numbers. // Returning an error != nil from this method will not lead to the round being aborted/not committed. // // Don't do anything slow in here. // // The keyValueStateReader gives read access to the replicated KeyValueState // in the state that it is after the StateTransition for seqNr is computed. // It must not be used outside the execution of this function. (It's okay to // use it anywhere in the call tree rooted at this function and to pass it to // separate goroutines, but they must stop using it before this function // returns.) Committed( ctx context.Context, seqNr uint64, keyValueStateReader KeyValueStateReader, ) error // Reports generates a (possibly empty) list of reports from a ReportsPlusPrecursor. Each report // will be signed and possibly be transmitted to the contract. (Depending on // ShouldAcceptAttestedReport & ShouldTransmitAcceptedReport) // // This function must be pure‡. // // This is likely to change in the future. It will likely be returning a // list of report batches, where each batch goes into its own Merkle tree. Reports( ctx context.Context, seqNr uint64, reportsPlusPrecursor ReportsPlusPrecursor, ) ([]ocr3types.ReportPlus[RI], error) // ShouldAcceptAttestedReport decides whether a report should be accepted for transmission. // Any report passed to this function will have been attested, i.e. signed by f+1 // oracles. // // Don't make assumptions about the seqNr order in which this function // is called. ShouldAcceptAttestedReport( ctx context.Context, seqNr uint64, reportWithInfo ocr3types.ReportWithInfo[RI], ) (bool, error) // ShouldTransmitAcceptedReport decides whether the given report should actually // be broadcast to the contract. This is invoked just before the broadcast occurs. // Any report passed to this function will have been signed by a quorum of oracles // and been accepted by ShouldAcceptAttestedReport. // // Don't make assumptions about the seqNr order in which this function // is called. // // As mentioned above, you should gracefully handle only a subset of a // ReportingPlugin's functions being invoked for a given report. For // example, due to reloading persisted pending transmissions from the // database upon oracle restart, this function may be called with reports // that no other function of this instance of this interface has ever // been invoked on. ShouldTransmitAcceptedReport( ctx context.Context, seqNr uint64, reportWithInfo ocr3types.ReportWithInfo[RI], ) (bool, error) // If Close is called a second time, it may return an error but must not // panic. This will always be called when a plugin is no longer // needed, e.g. on shutdown of the protocol instance or shutdown of the // oracle node. This will only be called after any calls to other functions // of the plugin have completed. // // See [ReadOnlyKeyValueState] for transaction and goroutine cleanup // requirements that must be satisfied before Close returns. Close() error }
Overview ¶
A ReportingPlugin allows plugging custom logic into the OCR protocol. The OCR protocol handles cryptography, networking, ensuring that a sufficient number of nodes is in agreement about any report, transmitting the report to the contract, etc. The ReportingPlugin handles application-specific logic. To do so, the ReportingPlugin defines a number of callbacks that are called by the OCR protocol logic at certain points in the protocol's execution flow. The report generated by the ReportingPlugin must be in a format understood by the contract (or offchain target system) that the reports are transmitted to.
Byzantine Fault Tolerance ¶
We assume that each correct node participating in the protocol instance will be running the same ReportingPlugin implementation. However, not all nodes may be correct; up to f nodes may be faulty in arbitrary ways (aka byzantine faults). For example, faulty nodes could be down, have intermittent connectivity issues, send garbage messages, or be controlled by an adversary.
Execution Flow ¶
For a protocol round where everything is working correctly, follower oracles will call Observation, ValidateObservation, ObservationQuorum, StateTransition, Committed, and Reports. The leader oracle will additionally call Query at the beginning of the round. For each report, ShouldAcceptAttestedReport will be called, iff the oracle is in the set of transmitters for the report. If ShouldAcceptAttestedReport returns true, ShouldTransmitAcceptedReport will be called. However, a ReportingPlugin must also correctly handle the case where faults occur.
In particular, a ReportingPlugin must deal with cases where:
- only a subset of the functions on the ReportingPlugin are invoked for a given round
- the observation returned by Observation is not included in the list of AttributedObservations passed to StateTransition
- a types.Query or types.Observation is malformed. (For defense in depth, it is also recommended that malformed elements in the key value state are handled gracefully.)
- instances of the ReportingPlugin run by different oracles have different call traces. E.g., the ReportingPlugin's Observation function may have been invoked on node A, but not on node B.
Engineering requirements for ReportingPlugin implementations ¶
All functions on a ReportingPlugin must be thread-safe.
The execution of the functions in the ReportingPlugin is on the critical path of the protocol's execution. A blocking function may block the oracle from participating in the protocol. Functions should be designed to generally return as quickly as possible and honor context expiration. Context expiration may occur for a number of reasons, including (1) shutdown of the protocol instance, (2) the protocol's progression through epochs (whether they're abandoned or completed successfully), and (3) timeout parameters. See the documentation on ocr3_1config.PublicConfig for more information on how to configure timeouts.
Many functions on the ReportingPlugin are marked as pure‡. This is because they may be invoked in arbitrary order, and because they must return deterministic results in order to ensure agreement across different oracles. This means that they must act as pure functions, with a few important clarifications and exceptions:
- In cases where the function returns an error, the determinism requirement is lifted. (But beware: if too many oracles encounter errors, the protocol may fail to progress.)
- Side effects arising from calling methods on a KeyValueStateReader argument are allowed.
- Side effects arising from calling methods on a KeyValueStateReadWriter argument are allowed.
- Side effects arising from calling methods on a BlobFetcher argument are allowed.
- Side effects arising from calling methods on a BlobBroadcastFetcher argument are allowed.
- Unobservable side effects (e.g. memoization of expensive computations) are allowed. Be careful!
- In cases where the function does not return an error, the context.Context argument must not affect the observable behavior of the function. Note that this does not preclude a function from returning an error on context expiration.
For a given OCR protocol instance, there can be many (consecutive) instances of a ReportingPlugin, e.g. due to software restarts. If you need ReportingPlugin state to survive across restarts, you should probably persist it in the KeyValueState. A ReportingPlugin instance will only ever serve a single protocol instance. State is not preserved between protocol instances. A fresh protocol instance will start with a clean state. Carrying state between different protocol instances is up to the ReportingPlugin logic.
type ReportingPluginFactory ¶
type ReportingPluginFactory[RI any] interface { // Creates a new reporting plugin instance. The instance may have // associated goroutines or hold system resources, which should be // released when its Close() function is called. NewReportingPlugin( context.Context, ocr3types.ReportingPluginConfig, BlobBroadcastFetcher, ) (ReportingPlugin[RI], ReportingPluginInfo, error) }
type ReportingPluginFactory2 ¶
type ReportingPluginFactory2[RI any] interface { // Creates a new reporting plugin instance. The instance may have // associated goroutines or hold system resources, which should be // released when its Close() function is called. NewReportingPlugin( context.Context, ocr3types.ReportingPluginConfig, BlobBroadcastFetcher, ReadOnlyKeyValueState, ) (ReportingPlugin[RI], ReportingPluginInfo, error) }
ReportingPluginFactory2 is like ReportingPluginFactory but additionally provides a ReadOnlyKeyValueState. See its documentation for transaction ownership and lifetime requirements.
type ReportingPluginInfo ¶
type ReportingPluginInfo interface {
// contains filtered or unexported methods
}
type ReportingPluginInfo1 ¶
type ReportingPluginInfo1 struct {
// Used for debugging purposes.
Name string
Limits ReportingPluginLimits
}
func (ReportingPluginInfo1) Validate ¶
func (i ReportingPluginInfo1) Validate() error
type ReportingPluginLimits ¶
type ReportingPluginLimits struct {
MaxQueryBytes int
MaxObservationBytes int
MaxReportsPlusPrecursorBytes int
MaxReportBytes int
MaxReportCount int
// MaxKeyValueModifiedKeys upper bounds the number of modified keys inside
// the StateTransition method. A key might be modified multiple times within
// a single StateTransition, but will only count once towards the limit. A
// modification that resets the value of a key to its original value at the
// start of StateTransition will still count towards the limit.
MaxKeyValueModifiedKeys int
// MaxKeyValueModifiedKeysPlusValuesBytes upper bounds the cumulative bytes
// for all modifications to key-values inside the StateTransition method.
// For a write modification, both the key and the value count towards the
// limit. For a delete modification, only the key counts towards the limit.
// For each key, only its last modification counts towards the limit. A
// modification that resets the value of a key to its original value at the
// start of StateTransition will still count towards the limit.
MaxKeyValueModifiedKeysPlusValuesBytes int
// MaxBlobPayloadBytes upper bounds the payload bytes for a single blob. A
// broadcast with a larger payload will be rejected.
MaxBlobPayloadBytes int
// MaxPerOracleUnexpiredBlobCumulativePayloadBytes upper bounds the
// cumulative payload length for all unreaped blobs from a single oracle.
// Inbound broadcasts that would violate this upper bound will be rejected.
// Due to the asynchronous nature of blob reaping, an expired blob might not
// be reaped yet, and thus might still be counted towards this upper bound.
// The number of unreaped blobs eventually approximates the number of
// unexpired blobs. Consequently, you must set this value loosely,
// multiplying by a factor that accounts for a blob reaping interval in the
// tens of seconds.
MaxPerOracleUnexpiredBlobCumulativePayloadBytes int
// MaxPerOracleUnexpiredBlobCount upper bounds the number of unreaped
// blobs from a single oracle. Inbound broadcasts that would violate this
// upper bound will be rejected. Due to the asynchronous nature of blob
// reaping, an expired blob might not be reaped yet, and thus might still be
// counted towards this upper bound. The number of unreaped blobs eventually
// approximates the number of unexpired blobs. Consequently, you must set
// this value loosely, multiplying by a factor that accounts for a blob
// reaping interval in the tens of seconds.
MaxPerOracleUnexpiredBlobCount int
}
Limits for data returned by the ReportingPlugin. Used for computing rate limits and defending against outsized messages. Messages are checked against these values during (de)serialization. Be careful when changing these values, they could lead to different versions of a ReportingPlugin being unable to communicate with each other.
MaxKeyValueModifiedKeys and MaxKeyValueModifiedKeysPlusValuesBytes concern modifications to key-values inside the StateTransition method, rather than returned data. Write(k, v) and Delete(k) count as modifications. A modification that resets the value of a key to its original value at the start of StateTransition will still count towards the limit.
func (ReportingPluginLimits) Validate ¶
func (l ReportingPluginLimits) Validate() error
Validate checks that all fields are within their allowed ranges. It returns a joined error listing every out-of-range field, or nil if valid.
type ReportsPlusPrecursor ¶
type ReportsPlusPrecursor []byte
The precursor value from which a list of ocr3types.ReportPlus is generated