Skip to content

Commit

Permalink
AVS-215, AVS-259: Add Subgraphs (#233)
Browse files Browse the repository at this point in the history
* added querier

* added grahql query

* cleanup

* added optimistic test

* AVS-259: Subgraph test (#244)

* cleanup

* added optimistic test

* fixed broken test

* Subgraph testing (#245)

* cleanup

* added optimistic test

* fixed broken test

* chore: forge init

* forge install: forge-std

v1.8.2

* addeed integration test

* added remappings

* integration test

* added integration test halpers

* fixed

* cleaned gir modules
  • Loading branch information
Sidu28 authored May 29, 2024
1 parent aebab47 commit d3cc43d
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
Expand Down
142 changes: 142 additions & 0 deletions services/operatorsinfo/operatorsinfo_subgraph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package operatorsinfo

import (
"context"
"encoding/hex"
"errors"
"fmt"

"github.com/Layr-Labs/eigensdk-go/crypto/bls"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/Layr-Labs/eigensdk-go/types"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/ethereum/go-ethereum/common"
"github.com/shurcooL/graphql"
)

type (
QueryOperatorByAddressGql struct {
Operator IndexedOperatorInfoGql `graphql:"operator(address: $address)"`
}
OperatorsInfoServiceSubgraph struct {
logger logging.Logger
client GraphQLQuerier
name string
}
SocketUpdates struct {
Socket graphql.String
}
IndexedOperatorInfoGql struct {
Address graphql.String
PubkeyG1_X graphql.String `graphql:"pubkeyG1_X"`
PubkeyG1_Y graphql.String `graphql:"pubkeyG1_Y"`
PubkeyG2_X []graphql.String `graphql:"pubkeyG2_X"`
PubkeyG2_Y []graphql.String `graphql:"pubkeyG2_Y"`
// Socket is the socket address of the operator, in the form "host:port"
SocketUpdates []SocketUpdates `graphql:"socketUpdates(first: 1, orderBy: blockNumber, orderDirection: desc)"`
}
IndexedOperatorInfo struct {
// PubKeyG1 and PubKeyG2 are the public keys of the operator, which are retrieved from the EigenDAPubKeyCompendium smart contract
PubkeyG1 *G1Point
PubkeyG2 *G2Point
// Socket is the socket address of the operator, in the form "host:port"
Socket string
}
G2Point struct {
*bn254.G2Affine
}
G1Point struct {
*bn254.G1Affine
}
GraphQLQuerier interface {
Query(ctx context.Context, q any, variables map[string]any) error
}
)

var _ OperatorsInfoService = (*OperatorsInfoServiceSubgraph)(nil)

// NewOperatorsInfoServiceSubgraph constructs a OperatorsInfoServiceSubgraph and starts it in a goroutine.
// It takes a context as argument because the "backfilling" of the database is done inside this constructor,
// so we wait for all past NewPubkeyRegistration/OperatorSocketUpdate events to be queried and the db to be filled before returning the service.
// The constructor is thus following a RAII-like pattern, of initializing the serving during construction.
// Using a separate initialize() function might lead to some users forgetting to call it and the service not behaving properly.
func NewOperatorsInfoServiceSubgraph(
ctx context.Context,
client GraphQLQuerier,
logger logging.Logger,
) *OperatorsInfoServiceSubgraph {
return &OperatorsInfoServiceSubgraph{
logger: logger,
client: client,
name: "OperatorsInfoServiceSubgraph",
}
}

// TODO(samlaf): we might want to also add an async version of this method that returns a channel of operator pubkeys?
func (ops *OperatorsInfoServiceSubgraph) GetOperatorInfo(ctx context.Context, operator common.Address) (operatorPubkeys types.OperatorInfo, operatorFound bool) {
operatorInfo, err := ops.getIndexedOperatorInfoByOperatorId(ctx, operator)
if err != nil {
return types.OperatorInfo{}, false
}
return *operatorInfo, true
}

func (ops *OperatorsInfoServiceSubgraph) getIndexedOperatorInfoByOperatorId(ctx context.Context, operator common.Address) (*types.OperatorInfo, error) {
var (
query QueryOperatorByAddressGql
variables = map[string]any{
"id": graphql.String(fmt.Sprintf("0x%s", hex.EncodeToString(operator[:]))),
}
)
err := ops.client.Query(ctx, &query, variables)
if err != nil {
ops.logger.Error("Error requesting info for operator", "err", err, "operator", hex.EncodeToString(operator[:]))
return nil, err
}

return convertIndexedOperatorInfoGqlToOperatorInfo(&query.Operator)
}

func convertIndexedOperatorInfoGqlToOperatorInfo(operator *IndexedOperatorInfoGql) (*types.OperatorInfo, error) {

if len(operator.SocketUpdates) == 0 {
return nil, errors.New("no socket found for operator")
}

pubkeyG1 := new(bn254.G1Affine)
_, err := pubkeyG1.X.SetString(string(operator.PubkeyG1_X))
if err != nil {
return nil, err
}
_, err = pubkeyG1.Y.SetString(string(operator.PubkeyG1_Y))
if err != nil {
return nil, err
}

pubkeyG2 := new(bn254.G2Affine)
_, err = pubkeyG2.X.A1.SetString(string(operator.PubkeyG2_X[0]))
if err != nil {
return nil, err
}
_, err = pubkeyG2.X.A0.SetString(string(operator.PubkeyG2_X[1]))
if err != nil {
return nil, err
}
_, err = pubkeyG2.Y.A1.SetString(string(operator.PubkeyG2_Y[0]))
if err != nil {
return nil, err
}
_, err = pubkeyG2.Y.A0.SetString(string(operator.PubkeyG2_Y[1]))
if err != nil {
return nil, err
}

return &types.OperatorInfo{
Socket: types.Socket(string(operator.SocketUpdates[0].Socket)),
Pubkeys: types.OperatorPubkeys{
G1Pubkey: &bls.G1Point{G1Affine: pubkeyG1},
G2Pubkey: &bls.G2Point{G2Affine: pubkeyG2},
},
}, nil

}
63 changes: 63 additions & 0 deletions services/operatorsinfo/operatorsinfo_subgraph_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package operatorsinfo

import (
"context"
"testing"

"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/Layr-Labs/eigensdk-go/types"
"github.com/ethereum/go-ethereum/common"
"github.com/shurcooL/graphql"
"github.com/stretchr/testify/assert"
)

type mockGraphQLQuerier struct {
QueryFn func(ctx context.Context, q any, variables map[string]any) error
}

func (m mockGraphQLQuerier) Query(ctx context.Context, q any, variables map[string]any) error {
return m.QueryFn(ctx, q, variables)
}

func TestIndexedChainState_GetIndexedOperatorState(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
if err != nil {
t.Fatal(err)
}

operatorAddress := common.Address{0x1}

operatorsQueryCalled := false
querier := &mockGraphQLQuerier{}
querier.QueryFn = func(ctx context.Context, q any, variables map[string]any) error {
switch res := q.(type) {
case *QueryOperatorByAddressGql:
if operatorsQueryCalled {
return nil
}
res.Operator = IndexedOperatorInfoGql{
Address: graphql.String(operatorAddress.String()),
PubkeyG1_X: "3336192159512049190945679273141887248666932624338963482128432381981287252980",
PubkeyG1_Y: "15195175002875833468883745675063986308012687914999552116603423331534089122704",
PubkeyG2_X: []graphql.String{
"21597023645215426396093421944506635812143308313031252511177204078669540440732",
"11405255666568400552575831267661419473985517916677491029848981743882451844775",
},
PubkeyG2_Y: []graphql.String{
"9416989242565286095121881312760798075882411191579108217086927390793923664442",
"13612061731370453436662267863740141021994163834412349567410746669651828926551",
},
SocketUpdates: []SocketUpdates{{Socket: "localhost:32006;32007"}},
}
operatorsQueryCalled = true
return nil
default:
return nil
}
}

cs := NewOperatorsInfoServiceSubgraph(context.Background(), querier, logger)
operatorPubkeys, success := cs.GetOperatorInfo(context.Background(), operatorAddress)
assert.True(t, success)
assert.Equal(t, operatorPubkeys.Socket, types.Socket("localhost:32006;32007"))
}

0 comments on commit d3cc43d

Please sign in to comment.