Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: deprecate earning receiver address #320

Merged
merged 5 commits into from
Aug 6, 2024
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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
GO_LINES_IGNORED_DIRS=contracts
GO_PACKAGES=./chainio/... ./crypto/... ./logging/... \
./types/... ./utils/... ./signer/... ./cmd/... \
./signerv2/...
./signerv2/... ./aws/... ./internal/... ./metrics/... \
./nodeapi/... ./cmd/... ./services/... ./testutils/...
shrimalmadhur marked this conversation as resolved.
Show resolved Hide resolved
GO_FOLDERS=$(shell echo ${GO_PACKAGES} | sed -e "s/\.\///g" | sed -e "s/\/\.\.\.//g")
help:
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
Expand Down
1 change: 0 additions & 1 deletion chainio/clients/elcontracts/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ func (r *ChainReader) GetOperatorDetails(opts *bind.CallOpts, operator types.Ope

return types.Operator{
Address: operator.Address,
EarningsReceiverAddress: operatorDetails.DeprecatedEarningsReceiver.Hex(),
StakerOptOutWindowBlocks: operatorDetails.StakerOptOutWindowBlocks,
DelegationApproverAddress: operatorDetails.DelegationApprover.Hex(),
}, nil
Expand Down
8 changes: 6 additions & 2 deletions chainio/clients/elcontracts/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ func (w *ChainWriter) RegisterAsOperator(ctx context.Context, operator types.Ope

w.logger.Infof("registering operator %s to EigenLayer", operator.Address)
opDetails := delegationmanager.IDelegationManagerOperatorDetails{
DeprecatedEarningsReceiver: gethcommon.HexToAddress(operator.EarningsReceiverAddress),
// Earning receiver has been deprecated, so we just use the operator address as a dummy value
// Any reward related setup is via RewardsCoordinator contract
DeprecatedEarningsReceiver: gethcommon.HexToAddress(operator.Address),
StakerOptOutWindowBlocks: operator.StakerOptOutWindowBlocks,
DelegationApprover: gethcommon.HexToAddress(operator.DelegationApproverAddress),
}
Expand Down Expand Up @@ -194,7 +196,9 @@ func (w *ChainWriter) UpdateOperatorDetails(

w.logger.Infof("updating operator details of operator %s to EigenLayer", operator.Address)
opDetails := delegationmanager.IDelegationManagerOperatorDetails{
DeprecatedEarningsReceiver: gethcommon.HexToAddress(operator.EarningsReceiverAddress),
// Earning receiver has been deprecated, so we just use the operator address as a dummy value
// Any reward related setup is via RewardsCoordinator contract
DeprecatedEarningsReceiver: gethcommon.HexToAddress(operator.Address),
DelegationApprover: gethcommon.HexToAddress(operator.DelegationApproverAddress),
StakerOptOutWindowBlocks: operator.StakerOptOutWindowBlocks,
}
Expand Down
9 changes: 7 additions & 2 deletions internal/fakes/eth_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package fakes
import (
"context"
"errors"
"math/big"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"math/big"
)

const (
Expand Down Expand Up @@ -90,6 +91,10 @@ func (f *EthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]t
return []types.Log{}, nil
}

func (f *EthClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
func (f *EthClient) SubscribeFilterLogs(
ctx context.Context,
q ethereum.FilterQuery,
ch chan<- types.Log,
) (ethereum.Subscription, error) {
return nil, nil
}
32 changes: 22 additions & 10 deletions metrics/collectors/economic/economic.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ type Collector struct {
operatorId types.OperatorId
quorumNames map[types.QuorumNum]string
// metrics
// TODO(samlaf): I feel like eigenlayer-core metrics like slashingStatus and delegatedShares, which are not avs specific,
// TODO(samlaf): I feel like eigenlayer-core metrics like slashingStatus and delegatedShares, which are not avs
// specific,
// should not be here, and should instead be collected by some eigenlayer-cli daemon or something, since
// otherwise every avs will be exporting these same metrics for no reason.

Expand Down Expand Up @@ -85,8 +86,9 @@ func NewCollector(
avsRegistryReader: avsRegistryReader,
logger: logger,
operatorAddr: operatorAddr,
// we don't fetch operatorId here because operator might not yet be registered (and hence not have an operatorId)
// we cache operatorId dynamically in the collect function() inside, which allows constructing collector before registering operator
// we don't fetch operatorId here because operator might not yet be registered (and hence not have an
// operatorId) we cache operatorId dynamically in the collect function() inside, which allows constructing
// collector before registering operator
operatorId: [32]byte{},
quorumNames: quorumNames,
slashingStatus: prometheus.NewDesc(
Expand Down Expand Up @@ -145,10 +147,12 @@ func (ec *Collector) initOperatorId() error {
// constant metrics with the results
func (ec *Collector) Collect(ch chan<- prometheus.Metric) {
// collect slashingStatus metric
// TODO(samlaf): note that this call is not avs specific, so every avs will have the same value if the operator has been slashed
// TODO(samlaf): note that this call is not avs specific, so every avs will have the same value if the operator has
// been slashed
// if we want instead to only output 1 if the operator has been slashed for a specific avs, we have 2 choices:
// 1. keep this collector format but query the OperatorFrozen event from a subgraph
// 2. subscribe to the event and keep a local state of whether the operator has been slashed, exporting it via normal prometheus instrumentation
// 2. subscribe to the event and keep a local state of whether the operator has been slashed, exporting it via
// normal prometheus instrumentation
operatorIsFrozen, err := ec.elReader.OperatorIsFrozen(nil, ec.operatorAddr)
if err != nil {
ec.logger.Error("Failed to get slashing incurred", "err", err)
Expand All @@ -163,7 +167,11 @@ func (ec *Collector) Collect(ch chan<- prometheus.Metric) {
// collect registeredStake metric
err = ec.initOperatorId()
if err != nil {
ec.logger.Warn("Failed to fetch and cache operator id. Skipping collection of registeredStake metric.", "err", err)
ec.logger.Warn(
"Failed to fetch and cache operator id. Skipping collection of registeredStake metric.",
"err",
err,
)
} else {
// probably should start using the avsregistry service instead of avsRegistryReader so that we can
// swap out backend for a subgraph eventually
Expand All @@ -189,14 +197,18 @@ func (ec *Collector) Collect(ch chan<- prometheus.Metric) {
// // We'll emit all 3 units in case this is needed for whatever reason
// // might want to change this behavior if this is emitting too many metrics
// sharesWeiFloat, _ := sharesWei.Float64()
// // TODO(samlaf): add the token name.. probably need to have a hardcoded dict per env (mainnet, goerli, etc)? Is it really that important..?
// ch <- prometheus.MustNewConstMetric(ec.delegatedShares, prometheus.GaugeValue, sharesWeiFloat, strategyAddr.String(), "wei", "token")
// // TODO(samlaf): add the token name.. probably need to have a hardcoded dict per env (mainnet, goerli, etc)? Is
// it really that important..?
// ch <- prometheus.MustNewConstMetric(ec.delegatedShares, prometheus.GaugeValue,
// sharesWeiFloat, strategyAddr.String(), "wei", "token")

// sharesGweiFloat, _ := sharesWei.Div(sharesWei, big.NewInt(1e9)).Float64()
// ch <- prometheus.MustNewConstMetric(ec.delegatedShares, prometheus.GaugeValue, sharesGweiFloat, strategyAddr.String(), "gwei", "token")
// ch <- prometheus.MustNewConstMetric(ec.delegatedShares, prometheus.GaugeValue, sharesGweiFloat,
// strategyAddr.String(), "gwei", "token")

// sharesEtherFloat, _ := sharesWei.Div(sharesWei, big.NewInt(1e18)).Float64()
// ch <- prometheus.MustNewConstMetric(ec.delegatedShares, prometheus.GaugeValue, sharesEtherFloat, strategyAddr.String(), "ether", "token")
// ch <- prometheus.MustNewConstMetric(ec.delegatedShares, prometheus.GaugeValue, sharesEtherFloat,
// strategyAddr.String(), "ether", "token")
// }
// }
}
6 changes: 5 additions & 1 deletion metrics/collectors/rpc_calls/rpc_calls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@ func TestRpcCallsCollector(t *testing.T) {
// assert.Equal(t, 1.0, testutil.ToFloat64(suite.metrics.rpcRequestDurationSeconds))

rpcCallsCollector.AddRPCRequestTotal("testmethod", "testclient/testversion")
assert.Equal(t, 1.0, testutil.ToFloat64(rpcCallsCollector.rpcRequestTotal.WithLabelValues("testmethod", "testclient/testversion")))
assert.Equal(
t,
1.0,
testutil.ToFloat64(rpcCallsCollector.rpcRequestTotal.WithLabelValues("testmethod", "testclient/testversion")),
)
}
11 changes: 7 additions & 4 deletions metrics/eigenmetrics.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Package metrics implements the avs node prometheus metrics spec: https://docs.eigenlayer.xyz/eigenlayer/avs-guides/spec/metrics/metrics-prom-spec
// Package metrics implements the avs node prometheus metrics spec:
// https://docs.eigenlayer.xyz/eigenlayer/avs-guides/spec/metrics/metrics-prom-spec
package metrics

import (
Expand Down Expand Up @@ -28,7 +29,8 @@ var _ Metrics = (*EigenMetrics)(nil)

// Follows the structure from https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#hdr-A_Basic_Example
// TODO(samlaf): I think each avs runs in a separate docker bridge network.
// In order for prometheus to scrape the metrics does the address need to be 0.0.0.0:port to accept connections from other networks?
// In order for prometheus to scrape the metrics does the address need to be 0.0.0.0:port to accept connections from
// other networks?
func NewEigenMetrics(avsName, ipPortAddress string, reg prometheus.Registerer, logger logging.Logger) *EigenMetrics {

metrics := &EigenMetrics{
Expand Down Expand Up @@ -61,8 +63,9 @@ func (m *EigenMetrics) initMetrics() {
// Performance score starts as 100, and goes down if node doesn't perform well
m.performanceScore.Set(100)

// TODO(samlaf): should we initialize the feeEarnedTotal? This would require the user to pass in a list of tokens for which to initialize the metric
// same for rpcRequestDurationSeconds and rpcRequestTotal... we could initialize them to be 0 on every json-rpc... but is that really necessary?
// TODO(samlaf): should we initialize the feeEarnedTotal? This would require the user to pass in a list of tokens
// for which to initialize the metric same for rpcRequestDurationSeconds and rpcRequestTotal... we could initialize
// them to be 0 on every json-rpc... but is that really necessary?
}

// AddEigenFeeEarnedTotal adds the fee earned to the total fee earned metric
Expand Down
12 changes: 10 additions & 2 deletions metrics/eigenmetrics_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,17 @@ func ExampleEigenMetrics() {
0: "ethQuorum",
1: "someOtherTokenQuorum",
}
// We must register the economic metrics separately because they are exported metrics (from jsonrpc or subgraph calls)
// We must register the economic metrics separately because they are exported metrics (from jsonrpc or subgraph
// calls)
// and not instrumented metrics: see https://prometheus.io/docs/instrumenting/writing_clientlibs/#overall-structure
economicMetricsCollector := economic.NewCollector(clients.ElChainReader, clients.AvsRegistryChainReader, "exampleAvs", logger, operatorEcdsaAddr, quorumNames)
economicMetricsCollector := economic.NewCollector(
clients.ElChainReader,
clients.AvsRegistryChainReader,
"exampleAvs",
logger,
operatorEcdsaAddr,
quorumNames,
)
reg.MustRegister(economicMetricsCollector)

rpcCallsCollector := rpccalls.NewCollector("exampleAvs", reg)
Expand Down
3 changes: 2 additions & 1 deletion metrics/eigenmetrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ func (suite *MetricsTestSuite) TestEigenMetricsServerIntegration() {
assert.NoError(suite.T(), err)

// We only check for "eigen_performance_score" since it's the only metric that doesn't have a label
// the other metrics have labels (they are vectors) so they don't appear in the output unless we use them once at least
// the other metrics have labels (they are vectors) so they don't appear in the output unless we use them once at
// least
assert.Contains(suite.T(), string(body), "eigen_fees_earned_total")
assert.Contains(suite.T(), string(body), "eigen_performance_score")
}
Expand Down
3 changes: 2 additions & 1 deletion metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
)

// Metrics is the interface for the EigenMetrics server
// it only wraps 2 of the 6 methods required by the spec (https://docs.eigenlayer.xyz/eigenlayer/avs-guides/spec/metrics/metrics-prom-spec)
// it only wraps 2 of the 6 methods required by the spec
// (https://docs.eigenlayer.xyz/eigenlayer/avs-guides/spec/metrics/metrics-prom-spec)
// the others are implemented by the economics and rpc_calls collectors,
// and need to be registered with the metrics server prometheus registry
type Metrics interface {
Expand Down
7 changes: 6 additions & 1 deletion nodeapi/nodeapi_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ func ExampleNodeApi() {
nodeApi := nodeapi.NewNodeApi("testAvs", "v0.0.1", "localhost:8080", logger)
// register a service with the nodeApi. This could be a db, a cache, a queue, etc.
// see https://docs.eigenlayer.xyz/eigenlayer/avs-guides/spec/api/#get-eigennodeservices
nodeApi.RegisterNewService("testServiceId", "testServiceName", "testServiceDescription", nodeapi.ServiceStatusInitializing)
nodeApi.RegisterNewService(
"testServiceId",
"testServiceName",
"testServiceDescription",
nodeapi.ServiceStatusInitializing,
)

// this starts the nodeApi server in a goroutine, so no need to wrap it in a go func
nodeApi.Start()
Expand Down
27 changes: 23 additions & 4 deletions nodeapi/nodeapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ func TestNodeHandler(t *testing.T) {
assert.NoError(t, err)

assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, "{\"node_name\":\"testAvs\",\"node_version\":\"v0.0.1\",\"spec_version\":\"v0.0.1\"}\n", string(data))
assert.Equal(
t,
"{\"node_name\":\"testAvs\",\"node_version\":\"v0.0.1\",\"spec_version\":\"v0.0.1\"}\n",
string(data),
)
}

func TestHealthHandler(t *testing.T) {
Expand Down Expand Up @@ -107,7 +111,12 @@ func TestServicesHandler(t *testing.T) {
"one service": {
nodeApi: func() *NodeApi {
nodeApi := NewNodeApi("testAvs", "v0.0.1", "localhost:8080", logger)
nodeApi.RegisterNewService("testServiceId", "testServiceName", "testServiceDescription", ServiceStatusUp)
nodeApi.RegisterNewService(
"testServiceId",
"testServiceName",
"testServiceDescription",
ServiceStatusUp,
)
return nodeApi
}(),
wantStatusCode: http.StatusOK,
Expand All @@ -116,8 +125,18 @@ func TestServicesHandler(t *testing.T) {
"two services": {
nodeApi: func() *NodeApi {
nodeApi := NewNodeApi("testAvs", "v0.0.1", "localhost:8080", logger)
nodeApi.RegisterNewService("testServiceId", "testServiceName", "testServiceDescription", ServiceStatusUp)
nodeApi.RegisterNewService("testServiceId2", "testServiceName2", "testServiceDescription2", ServiceStatusDown)
nodeApi.RegisterNewService(
"testServiceId",
"testServiceName",
"testServiceDescription",
ServiceStatusUp,
)
nodeApi.RegisterNewService(
"testServiceId2",
"testServiceName2",
"testServiceDescription2",
ServiceStatusDown,
)
return nodeApi
}(),
wantStatusCode: http.StatusOK,
Expand Down
28 changes: 22 additions & 6 deletions services/avsregistry/avsregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,32 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)

// AvsRegistryServicemService is a service that indexes the Avs Registry contracts and provides a way to query for operator state
// AvsRegistryServicemService is a service that indexes the Avs Registry contracts and provides a way to query for
// operator state
// at certain blocks, including operatorIds, pubkeys, and staking status in each quorum.
type AvsRegistryService interface {
// GetOperatorsAvsState returns the state of an avs wrt to a list of quorums at a certain block.
// The state includes the operatorId, pubkey, and staking amount in each quorum.
GetOperatorsAvsStateAtBlock(ctx context.Context, quorumNumbers types.QuorumNums, blockNumber types.BlockNum) (map[types.OperatorId]types.OperatorAvsState, error)
GetOperatorsAvsStateAtBlock(
ctx context.Context,
quorumNumbers types.QuorumNums,
blockNumber types.BlockNum,
) (map[types.OperatorId]types.OperatorAvsState, error)
// GetQuorumsAvsStateAtBlock returns the aggregated data for a list of quorums at a certain block.
// The aggregated data includes the aggregated pubkey and total stake in each quorum.
// This information is derivable from the Operators Avs State (returned from GetOperatorsAvsStateAtBlock), but this function is provided for convenience.
GetQuorumsAvsStateAtBlock(ctx context.Context, quorumNumbers types.QuorumNums, blockNumber types.BlockNum) (map[types.QuorumNum]types.QuorumAvsState, error)
// GetCheckSignaturesIndices returns the registry indices of the nonsigner operators specified by nonSignerOperatorIds who were registered at referenceBlockNumber.
GetCheckSignaturesIndices(opts *bind.CallOpts, referenceBlockNumber types.BlockNum, quorumNumbers types.QuorumNums, nonSignerOperatorIds []types.OperatorId) (opstateretriever.OperatorStateRetrieverCheckSignaturesIndices, error)
// This information is derivable from the Operators Avs State (returned from GetOperatorsAvsStateAtBlock), but this
// function is provided for convenience.
GetQuorumsAvsStateAtBlock(
ctx context.Context,
quorumNumbers types.QuorumNums,
blockNumber types.BlockNum,
) (map[types.QuorumNum]types.QuorumAvsState, error)
// GetCheckSignaturesIndices returns the registry indices of the nonsigner operators specified by
// nonSignerOperatorIds who were registered at referenceBlockNumber.
GetCheckSignaturesIndices(
opts *bind.CallOpts,
referenceBlockNumber types.BlockNum,
quorumNumbers types.QuorumNums,
nonSignerOperatorIds []types.OperatorId,
) (opstateretriever.OperatorStateRetrieverCheckSignaturesIndices, error)
}
Loading