diff --git a/disperser/cmd/dataapi/config.go b/disperser/cmd/dataapi/config.go index b8c074620e..fea431782d 100644 --- a/disperser/cmd/dataapi/config.go +++ b/disperser/cmd/dataapi/config.go @@ -30,6 +30,7 @@ type Config struct { ServerMode string AllowOrigins []string EjectionToken string + NonsigningRateThreshold int BLSOperatorStateRetrieverAddr string EigenDAServiceManagerAddr string @@ -76,8 +77,10 @@ func NewConfig(ctx *cli.Context) (Config, error) { Secret: ctx.GlobalString(flags.PrometheusServerSecretFlag.Name), Cluster: ctx.GlobalString(flags.PrometheusMetricsClusterLabelFlag.Name), }, - AllowOrigins: ctx.GlobalStringSlice(flags.AllowOriginsFlag.Name), - EjectionToken: ejectionToken, + AllowOrigins: ctx.GlobalStringSlice(flags.AllowOriginsFlag.Name), + EjectionToken: ejectionToken, + NonsigningRateThreshold: ctx.GlobalInt(flags.NonsigningRateThresholdFlag.Name), + MetricsConfig: dataapi.MetricsConfig{ HTTPPort: ctx.GlobalString(flags.MetricsHTTPPort.Name), EnableMetrics: ctx.GlobalBool(flags.EnableMetricsFlag.Name), diff --git a/disperser/cmd/dataapi/flags/flags.go b/disperser/cmd/dataapi/flags/flags.go index 6250a4c938..8a427ff8c4 100644 --- a/disperser/cmd/dataapi/flags/flags.go +++ b/disperser/cmd/dataapi/flags/flags.go @@ -146,6 +146,13 @@ var ( Value: 6 * time.Minute, EnvVar: common.PrefixEnvVar(envVarPrefix, "TRANSACTION_TIMEOUT"), } + NonsigningRateThresholdFlag = cli.IntFlag{ + Name: common.PrefixFlag(FlagPrefix, "nonsigning-rate-threshold"), + Usage: "only operators with nonsigning rate >= this threshold are eligible for ejection, this value must be in range [10, 100], any value not in this range means disabling this flag", + Required: false, + Value: -1, + EnvVar: common.PrefixEnvVar(envVarPrefix, "NONSIGNING_RATE_THRESHOLD"), + } ) var requiredFlags = []cli.Flag{ @@ -172,6 +179,7 @@ var requiredFlags = []cli.Flag{ var optionalFlags = []cli.Flag{ ServerModeFlag, MetricsHTTPPort, + NonsigningRateThresholdFlag, } // Flags contains the list of configuration options available to the binary. diff --git a/disperser/cmd/dataapi/main.go b/disperser/cmd/dataapi/main.go index b8d72599cb..5cf2be2234 100644 --- a/disperser/cmd/dataapi/main.go +++ b/disperser/cmd/dataapi/main.go @@ -124,7 +124,7 @@ func RunDataApi(ctx *cli.Context) error { subgraphClient, tx, chainState, - dataapi.NewEjector(wallet, client, logger, tx, metrics, config.TxnTimeout), + dataapi.NewEjector(wallet, client, logger, tx, metrics, config.TxnTimeout, config.NonsigningRateThreshold), logger, metrics, nil, diff --git a/disperser/dataapi/ejector.go b/disperser/dataapi/ejector.go index 3d3fad713b..076b2ef88e 100644 --- a/disperser/dataapi/ejector.go +++ b/disperser/dataapi/ejector.go @@ -54,25 +54,27 @@ func computePerfScore(metric *OperatorNonsigningPercentageMetrics) float64 { } type Ejector struct { - wallet walletsdk.Wallet - ethClient common.EthClient - logger logging.Logger - transactor core.Transactor - metrics *Metrics - txnTimeout time.Duration + wallet walletsdk.Wallet + ethClient common.EthClient + logger logging.Logger + transactor core.Transactor + metrics *Metrics + txnTimeout time.Duration + nonsigningRateThreshold int // For serializing the ejection requests. mu sync.Mutex } -func NewEjector(wallet walletsdk.Wallet, ethClient common.EthClient, logger logging.Logger, tx core.Transactor, metrics *Metrics, txnTimeout time.Duration) *Ejector { +func NewEjector(wallet walletsdk.Wallet, ethClient common.EthClient, logger logging.Logger, tx core.Transactor, metrics *Metrics, txnTimeout time.Duration, nonsigningRateThreshold int) *Ejector { return &Ejector{ - wallet: wallet, - ethClient: ethClient, - logger: logger.With("component", "Ejector"), - transactor: tx, - metrics: metrics, - txnTimeout: txnTimeout, + wallet: wallet, + ethClient: ethClient, + logger: logger.With("component", "Ejector"), + transactor: tx, + metrics: metrics, + txnTimeout: txnTimeout, + nonsigningRateThreshold: nonsigningRateThreshold, } } @@ -82,6 +84,11 @@ func (e *Ejector) Eject(ctx context.Context, nonsigningRate *OperatorsNonsigning nonsigners := make([]*OperatorNonsigningPercentageMetrics, 0) for _, metric := range nonsigningRate.Data { + // If nonsigningRateThreshold is set and valid, we will only eject operators with + // nonsigning rate >= nonsigningRateThreshold. + if e.nonsigningRateThreshold >= 10 && e.nonsigningRateThreshold <= 100 && metric.Percentage < float64(e.nonsigningRateThreshold) { + continue + } // Collect only the nonsigners who violate the SLA. if metric.Percentage/100.0 > 1-stakeShareToSLA(metric.StakePercentage/100.0) { nonsigners = append(nonsigners, metric) diff --git a/disperser/dataapi/server_test.go b/disperser/dataapi/server_test.go index 2f14eff6ed..49159dec80 100644 --- a/disperser/dataapi/server_test.go +++ b/disperser/dataapi/server_test.go @@ -462,7 +462,7 @@ func getEjector(t *testing.T) *ejectorComponents { ctrl := gomock.NewController(t) w := sdkmock.NewMockWallet(ctrl) ethClient := &commonmock.MockEthClient{} - ejector := dataapi.NewEjector(w, ethClient, mockLogger, mockTx, metrics, 100*time.Millisecond) + ejector := dataapi.NewEjector(w, ethClient, mockLogger, mockTx, metrics, 100*time.Millisecond, -1) return &ejectorComponents{ wallet: w, ethClient: ethClient,