Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 60 additions & 29 deletions offchainreporting2/reportingplugin/median/median.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ type OffchainConfig struct {
// Be careful setting this. It can cause increased transaction load and
// costs.
TransmitDespiteContractReadError bool
// If AcceptAfterFullTransmissionScheduleElapsed is true, a report will
// be accepted by ShouldAcceptFinalizedReport if the full transmission
// schedule has elapsed since the last accepted report. This is useful
// to prevent missed heartbeats on chains where sometimes *all* nodes
// are unable to successfully land a transaction, because otherwise the
// "pending report check" will keep blocking later non-deviating
// reports from being accepted.
// This flag defaults to false. The corresponding flag on
// NumericalMedianFactory has the same effect; if either flag is true,
// the condition applies.
AcceptAfterFullTransmissionScheduleElapsed bool
}

func DecodeOffchainConfig(b []byte) (OffchainConfig, error) {
Expand All @@ -132,6 +143,7 @@ func DecodeOffchainConfig(b []byte) (OffchainConfig, error) {
configProto.GetAlphaAcceptPpb(),
time.Duration(configProto.GetDeltaCNanoseconds()),
configProto.GetTransmitDespiteContractReadError(),
configProto.GetAcceptAfterFullTransmissionScheduleElapsed(),
}, nil
}

Expand All @@ -148,6 +160,7 @@ func (c OffchainConfig) Encode() []byte {
c.AlphaAcceptPPB,
uint64(c.DeltaC),
c.TransmitDespiteContractReadError,
c.AcceptAfterFullTransmissionScheduleElapsed,
}
result, err := proto.Marshal(&configProto)
if err != nil {
Expand Down Expand Up @@ -286,10 +299,16 @@ type NumericalMedianFactory struct {
// function DefaultDeviationFunc. All oracles in the OCR protocol instance
// must run with the same deviation function.
DeviationFunc DeviationFunc
// The corresponding flag on OffchainConfig has the same effect;
// if either flag is true, the condition applies. See the
// comment on OffchainConfig.AcceptAfterFullTransmissionScheduleElapsed
// for details on what this does.
// It's preferable to configure this flag via OffchainConfig, but in cases
// where that is not feasible, you may set it here instead.
AcceptAfterFullTransmissionScheduleElapsed bool
}

func (fac NumericalMedianFactory) NewReportingPlugin(ctx context.Context, configuration types.ReportingPluginConfig) (types.ReportingPlugin, types.ReportingPluginInfo, error) {

offchainConfig, err := DecodeOffchainConfig(configuration.OffchainConfig)
if err != nil {
return nil, types.ReportingPluginInfo{}, err
Expand Down Expand Up @@ -324,6 +343,7 @@ func (fac NumericalMedianFactory) NewReportingPlugin(ctx context.Context, config
"onchainConfig": onchainConfig,
"includeGasPriceSubunitsInObservation": fac.IncludeGasPriceSubunitsInObservation,
"hasCustomDeviationFunc": fac.DeviationFunc != nil,
"acceptAfterFullTransmissionScheduleElapsed": fac.AcceptAfterFullTransmissionScheduleElapsed,
})

return &numericalMedian{
Expand All @@ -337,11 +357,14 @@ func (fac NumericalMedianFactory) NewReportingPlugin(ctx context.Context, config
logger,
fac.ReportCodec,
deviationFunc,
fac.AcceptAfterFullTransmissionScheduleElapsed || offchainConfig.AcceptAfterFullTransmissionScheduleElapsed,

configuration.ConfigDigest,
configuration.F,
configuration.DurationAllTransmissionStages,
epochRound{},
new(big.Int),
time.Now(),
maxReportLength,
}, types.ReportingPluginInfo{
"NumericalMedian",
Expand Down Expand Up @@ -376,22 +399,25 @@ func DefaultDeviationFunc(_ context.Context, thresholdPPB uint64, old *big.Int,
var _ types.ReportingPlugin = (*numericalMedian)(nil)

type numericalMedian struct {
offchainConfig OffchainConfig
onchainConfig OnchainConfig
contractTransmitter MedianContract
dataSource DataSource
juelsPerFeeCoinDataSource DataSource
gasPriceSubunitsDataSource DataSource
includeGasPriceSubunitsInObservation bool
logger loghelper.LoggerWithContext
reportCodec ReportCodec
deviationFunc DeviationFunc

configDigest types.ConfigDigest
f int
latestAcceptedEpochRound epochRound
latestAcceptedMedian *big.Int
maxReportLength int
offchainConfig OffchainConfig
onchainConfig OnchainConfig
contractTransmitter MedianContract
dataSource DataSource
juelsPerFeeCoinDataSource DataSource
gasPriceSubunitsDataSource DataSource
includeGasPriceSubunitsInObservation bool
logger loghelper.LoggerWithContext
reportCodec ReportCodec
deviationFunc DeviationFunc
acceptAfterFullTransmissionScheduleElapsed bool

configDigest types.ConfigDigest
f int
durationAllTransmissionStages time.Duration
latestAcceptedEpochRound epochRound
latestAcceptedMedian *big.Int
latestAcceptedTime time.Time
maxReportLength int
}

func (nm *numericalMedian) Query(ctx context.Context, repts types.ReportTimestamp) (types.Query, error) {
Expand Down Expand Up @@ -754,25 +780,30 @@ func (nm *numericalMedian) ShouldAcceptFinalizedReport(ctx context.Context, rept
nothingPending = !contractLatestTransmissionDetailsOrNil.EpochRound.Less(nm.latestAcceptedEpochRound)
}

result := medianContractReadErrorAcceptOverride || (contractConfigDigestMatches && reportFresh && (deviates || nothingPending))
fullTransmissionScheduleElapsedSinceLastAccepted := time.Since(nm.latestAcceptedTime) > nm.durationAllTransmissionStages

result := medianContractReadErrorAcceptOverride || (contractConfigDigestMatches && reportFresh && (deviates || nothingPending || (nm.acceptAfterFullTransmissionScheduleElapsed && fullTransmissionScheduleElapsedSinceLastAccepted)))

nm.logger.Debug("ShouldAcceptFinalizedReport() = result", commontypes.LogFields{
"contractLatestTransmissionDetailsOrNil": contractLatestTransmissionDetailsOrNil,
"reportEpochRound": reportEpochRound,
"latestAcceptedEpochRound": nm.latestAcceptedEpochRound,
"alphaAcceptInfinite": nm.offchainConfig.AlphaAcceptInfinite,
"alphaAcceptPPB": nm.offchainConfig.AlphaAcceptPPB,
"medianContractReadErrorAcceptOverride": medianContractReadErrorAcceptOverride,
"contractConfigDigestMatches": contractConfigDigestMatches,
"reportFresh": reportFresh,
"nothingPending": nothingPending,
"deviates": deviates,
"result": result,
"contractLatestTransmissionDetailsOrNil": contractLatestTransmissionDetailsOrNil,
"reportEpochRound": reportEpochRound,
"latestAcceptedEpochRound": nm.latestAcceptedEpochRound,
"alphaAcceptInfinite": nm.offchainConfig.AlphaAcceptInfinite,
"alphaAcceptPPB": nm.offchainConfig.AlphaAcceptPPB,
"medianContractReadErrorAcceptOverride": medianContractReadErrorAcceptOverride,
"contractConfigDigestMatches": contractConfigDigestMatches,
"reportFresh": reportFresh,
"nothingPending": nothingPending,
"acceptAfterFullTransmissionScheduleElapsed": nm.acceptAfterFullTransmissionScheduleElapsed,
"fullTransmissionScheduleElapsedSinceLastAccepted": fullTransmissionScheduleElapsedSinceLastAccepted,
"deviates": deviates,
"result": result,
})

if result {
nm.latestAcceptedEpochRound = reportEpochRound
nm.latestAcceptedMedian = reportMedian
nm.latestAcceptedTime = time.Now()
}

return result, nil
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions offchainreporting2plus/confighelper/confighelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func ContractSetConfigArgsForEthereumIntegrationTest(
alphaPPB,
0,
false,
false,
}.Encode(),
util.PointerTo(50 * time.Millisecond),
50 * time.Millisecond,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ type PublicConfig struct {
// attempt to transmit (if in their view the first and second stage didn't
// succeed).
//
// sum(S) should equal n.
// It is recommended to have sum(S) >= n to ensure all oracles are
// included as transmitters.
S []int
// Identities (i.e. public keys) of the oracles participating in this
// protocol instance.
Expand Down Expand Up @@ -92,6 +93,30 @@ func (c *PublicConfig) N() int {
return len(c.OracleIdentities)
}

// DurationAllTransmissionStages returns how long it takes until
// all transmission stages have elapsed, i.e. by what time the final
// transmitting oracle will have had one DeltaStage to transmit.
// Recall that the OCR2 transmission schedule is described by N, S, and DeltaStage.
func (c *PublicConfig) DurationAllTransmissionStages() time.Duration {
if c.N() == 0 {
return 0
}

coveredOracles := 0
lastNonZeroStageCount := 0
for stageIdx, stageSize := range c.S {
if stageSize > 0 {
lastNonZeroStageCount = stageIdx + 1
}
coveredOracles += stageSize
if coveredOracles >= c.N() {
return time.Duration(stageIdx+1) * c.DeltaStage
}
}

return time.Duration(lastNonZeroStageCount) * c.DeltaStage
}

func (c *PublicConfig) CheckParameterBounds() error {
if c.F < 0 || c.F > math.MaxUint8 {
return errors.Errorf("number of potentially faulty oracles must fit in 8 bits.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ type PublicConfig struct {
// attempt to transmit (if in their view the first and second stage didn't
// succeed).
//
// sum(S) should equal n.
// It is recommended to have sum(S) >= n to ensure all oracles are
// included as transmitters.
S []int
// Identities (i.e. public keys) of the oracles participating in this
// protocol instance.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ocr3_1config

import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
Expand Down Expand Up @@ -48,7 +47,7 @@ func SharedConfigFromContractConfig[RI any](
skipInsaneForProductionChecks bool,
change types.ContractConfig,
offchainKeyring types.OffchainKeyring,
onchainKeyring ocr3types.OnchainKeyring[RI],
onchainKeyring ocr3types.OnchainKeyring2[RI],
peerID string,
transmitAccount types.Account,
) (SharedConfig, commontypes.OracleID, error) {
Expand All @@ -59,38 +58,38 @@ func SharedConfigFromContractConfig[RI any](

oracleID := commontypes.OracleID(math.MaxUint8)
{
onchainPublicKey := onchainKeyring.PublicKey()
offchainPublicKey := offchainKeyring.OffchainPublicKey()
var found bool
for i, identity := range publicConfig.OracleIdentities {
if bytes.Equal(identity.OnchainPublicKey, onchainPublicKey) {
if identity.OffchainPublicKey != offchainPublicKey {
return SharedConfig{}, 0, errors.Errorf(
"OnchainPublicKey %x in publicConfig matches "+
"mine, but OffchainPublicKey does not: %v (config) vs %v (mine)",
onchainPublicKey, identity.OffchainPublicKey, offchainPublicKey)
}
if identity.PeerID != peerID {
return SharedConfig{}, 0, errors.Errorf(
"OnchainPublicKey %x in publicConfig matches "+
"mine, but PeerID does not: %v (config) vs %v (mine)",
onchainPublicKey, identity.PeerID, peerID)
}
if identity.TransmitAccount != transmitAccount {
return SharedConfig{}, 0, errors.Errorf(
"OnchainPublicKey %x in publicConfig matches "+
"mine, but TransmitAccount does not: %v (config) vs %v (mine)",
onchainPublicKey, identity.TransmitAccount, transmitAccount)
}
oracleID = commontypes.OracleID(i)
found = true
if identity.OffchainPublicKey != offchainPublicKey {
continue
}
if !onchainKeyring.Has(identity.OnchainPublicKey) {
return SharedConfig{}, 0, errors.Errorf(
"OffchainPublicKey %v in publicConfig matches "+
"mine, but OnchainPublicKey %x does not match keyring %s",
offchainPublicKey, identity.OnchainPublicKey, onchainKeyring.DebugIdentifier())
}
if identity.PeerID != peerID {
return SharedConfig{}, 0, errors.Errorf(
"OffchainPublicKey %v in publicConfig matches "+
"mine, but PeerID does not: %v (config) vs %v (mine)",
offchainPublicKey, identity.PeerID, peerID)
}
if identity.TransmitAccount != transmitAccount {
return SharedConfig{}, 0, errors.Errorf(
"OffchainPublicKey %v in publicConfig matches "+
"mine, but TransmitAccount does not: %v (config) vs %v (mine)",
offchainPublicKey, identity.TransmitAccount, transmitAccount)
}
oracleID = commontypes.OracleID(i)
found = true
}

if !found {
return SharedConfig{},
0,
fmt.Errorf("could not find my OnchainPublicKey %x in publicConfig", onchainPublicKey)
fmt.Errorf("could not find matching OffchainPublicKey in publicConfig for %v", offchainPublicKey)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ type PublicConfig struct {
// attempt to transmit (if in their view the first and second stage didn't
// succeed).
//
// sum(S) should equal n.
// It is recommended to have sum(S) >= n to ensure all oracles are
// included as transmitters.
S []int
// Identities (i.e. public keys) of the oracles participating in this
// protocol instance.
Expand Down
Loading
Loading