From b66c56da417fb7b3d199e1efbf2681798329c154 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Mon, 6 May 2024 08:48:24 -0700 Subject: [PATCH 01/11] added querier --- services/querier.go | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 services/querier.go diff --git a/services/querier.go b/services/querier.go new file mode 100644 index 00000000..c601b4e0 --- /dev/null +++ b/services/querier.go @@ -0,0 +1,48 @@ +package thegraph + +import ( + "context" + "errors" + "time" +) + +type RetryQuerier struct { + GraphQLQuerier + Backoff time.Duration + MaxRetries int +} + +var _ GraphQLQuerier = (*RetryQuerier)(nil) + +func NewRetryQuerier(q GraphQLQuerier, backoff time.Duration, maxRetries int) *RetryQuerier { + return &RetryQuerier{ + GraphQLQuerier: q, + Backoff: backoff, + MaxRetries: maxRetries, + } +} + +func (q *RetryQuerier) Query(ctx context.Context, query any, variables map[string]any) error { + + retryCount := 0 + backoff := q.Backoff + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if retryCount > q.MaxRetries { + return errors.New("max retries exceeded") + } + retryCount++ + + err := q.GraphQLQuerier.Query(ctx, query, variables) + if err == nil { + return nil + } + + time.Sleep(backoff) + backoff *= 2 + } + } +} From fb2886e51fbae1b2f270d090d16ab7777120f4d6 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Tue, 7 May 2024 09:17:43 -0700 Subject: [PATCH 02/11] added grahql query --- go.mod | 1 + go.sum | 2 + .../operatorsinfo/operatorsinfo_subgraph.go | 139 ++++++++++++++++++ services/querier.go | 48 ------ 4 files changed, 142 insertions(+), 48 deletions(-) create mode 100644 services/operatorsinfo/operatorsinfo_subgraph.go delete mode 100644 services/querier.go diff --git a/go.mod b/go.mod index 4e1c7520..d0fc8427 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index a52fa98c..62274a4f 100644 --- a/go.sum +++ b/go.sum @@ -240,6 +240,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= diff --git a/services/operatorsinfo/operatorsinfo_subgraph.go b/services/operatorsinfo/operatorsinfo_subgraph.go new file mode 100644 index 00000000..d915c214 --- /dev/null +++ b/services/operatorsinfo/operatorsinfo_subgraph.go @@ -0,0 +1,139 @@ +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 ( + QueryOperatorByIdGql struct { + Operator IndexedOperatorInfoGql `graphql:"operator(id: $id)"` + } + OperatorsInfoServiceSubgraph struct { + logger logging.Logger + client *graphql.Client + } + SocketUpdates struct { + Socket graphql.String + } + IndexedOperatorInfoGql struct { + Id 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 + } +) + +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, + url string, + logger logging.Logger, +) *OperatorsInfoServiceSubgraph { + client := graphql.NewClient(url, nil) + + return &OperatorsInfoServiceSubgraph{ + logger: logger, + client: client, + } +} + +// 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 QueryOperatorByIdGql + variables = map[string]any{ + "id": graphql.String(fmt.Sprintf("0x%s", hex.EncodeToString(operator[:]))), + } + ) + err := ops.client.Query(context.Background(), &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 + +} diff --git a/services/querier.go b/services/querier.go deleted file mode 100644 index c601b4e0..00000000 --- a/services/querier.go +++ /dev/null @@ -1,48 +0,0 @@ -package thegraph - -import ( - "context" - "errors" - "time" -) - -type RetryQuerier struct { - GraphQLQuerier - Backoff time.Duration - MaxRetries int -} - -var _ GraphQLQuerier = (*RetryQuerier)(nil) - -func NewRetryQuerier(q GraphQLQuerier, backoff time.Duration, maxRetries int) *RetryQuerier { - return &RetryQuerier{ - GraphQLQuerier: q, - Backoff: backoff, - MaxRetries: maxRetries, - } -} - -func (q *RetryQuerier) Query(ctx context.Context, query any, variables map[string]any) error { - - retryCount := 0 - backoff := q.Backoff - for { - select { - case <-ctx.Done(): - return ctx.Err() - default: - if retryCount > q.MaxRetries { - return errors.New("max retries exceeded") - } - retryCount++ - - err := q.GraphQLQuerier.Query(ctx, query, variables) - if err == nil { - return nil - } - - time.Sleep(backoff) - backoff *= 2 - } - } -} From ecfedbb209477afde704f38a830e9ed4ff9e4622 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Mon, 13 May 2024 09:38:43 -0700 Subject: [PATCH 03/11] cleanup --- services/operatorsinfo/operatorsinfo_subgraph.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/operatorsinfo/operatorsinfo_subgraph.go b/services/operatorsinfo/operatorsinfo_subgraph.go index d915c214..40d5b808 100644 --- a/services/operatorsinfo/operatorsinfo_subgraph.go +++ b/services/operatorsinfo/operatorsinfo_subgraph.go @@ -85,7 +85,7 @@ func (ops *OperatorsInfoServiceSubgraph) getIndexedOperatorInfoByOperatorId(ctx "id": graphql.String(fmt.Sprintf("0x%s", hex.EncodeToString(operator[:]))), } ) - err := ops.client.Query(context.Background(), &query, variables) + 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 From 9ff18c2ca514ebf03d968432c1cdec5d47c9fb77 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Mon, 13 May 2024 10:41:21 -0700 Subject: [PATCH 04/11] added optimistic test --- .../operatorsinfo/operatorsinfo_subgraph.go | 8 +-- .../operatorsinfo_subgraph_test.go | 59 +++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 services/operatorsinfo/operatorsinfo_subgraph_test.go diff --git a/services/operatorsinfo/operatorsinfo_subgraph.go b/services/operatorsinfo/operatorsinfo_subgraph.go index 40d5b808..891ee504 100644 --- a/services/operatorsinfo/operatorsinfo_subgraph.go +++ b/services/operatorsinfo/operatorsinfo_subgraph.go @@ -15,8 +15,8 @@ import ( ) type ( - QueryOperatorByIdGql struct { - Operator IndexedOperatorInfoGql `graphql:"operator(id: $id)"` + QueryOperatorByAddressGql struct { + Operator IndexedOperatorInfoGql `graphql:"operator(address: $address)"` } OperatorsInfoServiceSubgraph struct { logger logging.Logger @@ -26,7 +26,7 @@ type ( Socket graphql.String } IndexedOperatorInfoGql struct { - Id graphql.String + Address graphql.String PubkeyG1_X graphql.String `graphql:"pubkeyG1_X"` PubkeyG1_Y graphql.String `graphql:"pubkeyG1_Y"` PubkeyG2_X []graphql.String `graphql:"pubkeyG2_X"` @@ -80,7 +80,7 @@ func (ops *OperatorsInfoServiceSubgraph) GetOperatorInfo(ctx context.Context, op func (ops *OperatorsInfoServiceSubgraph) getIndexedOperatorInfoByOperatorId(ctx context.Context, operator common.Address) (*types.OperatorInfo, error) { var ( - query QueryOperatorByIdGql + query QueryOperatorByAddressGql variables = map[string]any{ "id": graphql.String(fmt.Sprintf("0x%s", hex.EncodeToString(operator[:]))), } diff --git a/services/operatorsinfo/operatorsinfo_subgraph_test.go b/services/operatorsinfo/operatorsinfo_subgraph_test.go new file mode 100644 index 00000000..87d9800c --- /dev/null +++ b/services/operatorsinfo/operatorsinfo_subgraph_test.go @@ -0,0 +1,59 @@ +package operatorsinfo + +import ( + "context" + "testing" + + "github.com/Layr-Labs/eigensdk-go/logging" + "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 := logging.NewNoopLogger() + + 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(), operatorAddress.String(), logger) + operatorPubkeys, err := cs.GetOperatorInfo(context.Background(), operatorAddress) + assert.True(t, err) + assert.Equal(t, operatorPubkeys.Socket, "localhost:32006;32007") +} From 079f1e0204e8aabc5f7c10d235cec27615704a65 Mon Sep 17 00:00:00 2001 From: kachapah <60323455+Sidu28@users.noreply.github.com> Date: Tue, 14 May 2024 08:30:10 -0700 Subject: [PATCH 05/11] AVS-259: Subgraph test (#244) * cleanup * added optimistic test * fixed broken test --- .../operatorsinfo/operatorsinfo_subgraph.go | 23 ++++--- .../operatorsinfo_subgraph_test.go | 60 +++++++++++++++++++ 2 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 services/operatorsinfo/operatorsinfo_subgraph_test.go diff --git a/services/operatorsinfo/operatorsinfo_subgraph.go b/services/operatorsinfo/operatorsinfo_subgraph.go index d915c214..08ee8b61 100644 --- a/services/operatorsinfo/operatorsinfo_subgraph.go +++ b/services/operatorsinfo/operatorsinfo_subgraph.go @@ -15,18 +15,19 @@ import ( ) type ( - QueryOperatorByIdGql struct { - Operator IndexedOperatorInfoGql `graphql:"operator(id: $id)"` + QueryOperatorByAddressGql struct { + Operator IndexedOperatorInfoGql `graphql:"operator(address: $address)"` } OperatorsInfoServiceSubgraph struct { logger logging.Logger - client *graphql.Client + client GraphQLQuerier + name string } SocketUpdates struct { Socket graphql.String } IndexedOperatorInfoGql struct { - Id graphql.String + Address graphql.String PubkeyG1_X graphql.String `graphql:"pubkeyG1_X"` PubkeyG1_Y graphql.String `graphql:"pubkeyG1_Y"` PubkeyG2_X []graphql.String `graphql:"pubkeyG2_X"` @@ -47,6 +48,9 @@ type ( G1Point struct { *bn254.G1Affine } + GraphQLQuerier interface { + Query(ctx context.Context, q any, variables map[string]any) error + } ) var _ OperatorsInfoService = (*OperatorsInfoServiceSubgraph)(nil) @@ -58,14 +62,13 @@ var _ OperatorsInfoService = (*OperatorsInfoServiceSubgraph)(nil) // 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, - url string, + client GraphQLQuerier, logger logging.Logger, ) *OperatorsInfoServiceSubgraph { - client := graphql.NewClient(url, nil) - return &OperatorsInfoServiceSubgraph{ logger: logger, client: client, + name: "OperatorsInfoServiceSubgraph", } } @@ -80,12 +83,14 @@ func (ops *OperatorsInfoServiceSubgraph) GetOperatorInfo(ctx context.Context, op func (ops *OperatorsInfoServiceSubgraph) getIndexedOperatorInfoByOperatorId(ctx context.Context, operator common.Address) (*types.OperatorInfo, error) { var ( - query QueryOperatorByIdGql + query QueryOperatorByAddressGql variables = map[string]any{ "id": graphql.String(fmt.Sprintf("0x%s", hex.EncodeToString(operator[:]))), } ) - err := ops.client.Query(context.Background(), &query, variables) + fmt.Print("NAME OF SUBRAPH", ops.name) + fmt.Print("NAME OF SUBRAPH: ", ops.client) + 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 diff --git a/services/operatorsinfo/operatorsinfo_subgraph_test.go b/services/operatorsinfo/operatorsinfo_subgraph_test.go new file mode 100644 index 00000000..b64a3fc6 --- /dev/null +++ b/services/operatorsinfo/operatorsinfo_subgraph_test.go @@ -0,0 +1,60 @@ +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 := logging.NewNoopLogger() + + 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, err := cs.GetOperatorInfo(context.Background(), operatorAddress) + assert.True(t, err) + assert.Equal(t, operatorPubkeys.Socket, types.Socket("localhost:32006;32007")) +} From 9a80d5cdbf1b324d0bfbcbbfb5c213e8b26ba60f Mon Sep 17 00:00:00 2001 From: kachapah <60323455+Sidu28@users.noreply.github.com> Date: Tue, 14 May 2024 09:03:53 -0700 Subject: [PATCH 06/11] Subgraph testing (#245) * cleanup * added optimistic test * fixed broken test * chore: forge init * forge install: forge-std v1.8.2 * addeed integration test --- .gitmodules | 3 + .../.github/workflows/test.yml | 34 +++ .../integration_test_deployment/.gitignore | 14 + .../BLSApkRegistryMock.sol | 284 ++++++++++++++++++ .../integration_test_deployment/README.md | 66 ++++ .../deployment_script.sol | 86 ++++++ .../integration_test_deployment/foundry.toml | 6 + .../integration_test_deployment/lib/forge-std | 1 + .../script/Counter.s.sol | 12 + .../script/deployment_script.s.sol | 86 ++++++ .../src/Counter.sol | 14 + .../test/BLSApkRegistryMock.sol | 284 ++++++++++++++++++ .../test/Counter.t.sol | 24 ++ ...operatorsinfo_subgraph_integration_test.go | 1 + 14 files changed, 915 insertions(+) create mode 100644 services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml create mode 100644 services/operatorsinfo/integration_test_deployment/.gitignore create mode 100644 services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol create mode 100644 services/operatorsinfo/integration_test_deployment/README.md create mode 100644 services/operatorsinfo/integration_test_deployment/deployment_script.sol create mode 100644 services/operatorsinfo/integration_test_deployment/foundry.toml create mode 160000 services/operatorsinfo/integration_test_deployment/lib/forge-std create mode 100644 services/operatorsinfo/integration_test_deployment/script/Counter.s.sol create mode 100644 services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol create mode 100644 services/operatorsinfo/integration_test_deployment/src/Counter.sol create mode 100644 services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol create mode 100644 services/operatorsinfo/integration_test_deployment/test/Counter.t.sol create mode 100644 services/operatorsinfo/operatorsinfo_subgraph_integration_test.go diff --git a/.gitmodules b/.gitmodules index ef878cad..af2aceb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "contracts/lib/eigenlayer-middleware"] path = contracts/lib/eigenlayer-middleware url = git@github.com:Layr-Labs/eigenlayer-middleware.git +[submodule "services/operatorsinfo/integration_test_deployment/lib/forge-std"] + path = services/operatorsinfo/integration_test_deployment/lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml b/services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml new file mode 100644 index 00000000..9282e829 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: test + +on: workflow_dispatch + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/services/operatorsinfo/integration_test_deployment/.gitignore b/services/operatorsinfo/integration_test_deployment/.gitignore new file mode 100644 index 00000000..85198aaa --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol b/services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol new file mode 100644 index 00000000..df9795c4 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {BLSApkRegistryStorage} from "../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistryStorage.sol"; + +import {IRegistryCoordinator} from "../../contracts/lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; + +import {BN254} from "../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; + +contract BLSApkRegistryMock is BLSApkRegistryStorage { + using BN254 for BN254.G1Point; + + /// @notice when applied to a function, only allows the RegistryCoordinator to call it + modifier onlyRegistryCoordinator() { + require( + msg.sender == address(registryCoordinator), + "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + ); + _; + } + + /// @notice Sets the (immutable) `registryCoordinator` address + constructor( + IRegistryCoordinator _registryCoordinator + ) BLSApkRegistryStorage(_registryCoordinator) {} + + /******************************************************************************* + EXTERNAL FUNCTIONS - REGISTRY COORDINATOR + *******************************************************************************/ + + /** + * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. + * @param operator The address of the operator to register. + * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. + * @dev access restricted to the RegistryCoordinator + * @dev Preconditions (these are assumed, not validated in this contract): + * 1) `quorumNumbers` has no duplicates + * 2) `quorumNumbers.length` != 0 + * 3) `quorumNumbers` is ordered in ascending order + * 4) the operator is not already registered + */ + function registerOperator( + address operator, + bytes memory quorumNumbers + ) public virtual onlyRegistryCoordinator { + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + + // Update each quorum's aggregate pubkey + _processQuorumApkUpdate(quorumNumbers, pubkey); + + // Return pubkeyHash, which will become the operator's unique id + emit OperatorAddedToQuorums(operator, getOperatorId(operator), quorumNumbers); + } + + /** + * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. + * @param operator The address of the operator to deregister. + * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. + * @dev access restricted to the RegistryCoordinator + * @dev Preconditions (these are assumed, not validated in this contract): + * 1) `quorumNumbers` has no duplicates + * 2) `quorumNumbers.length` != 0 + * 3) `quorumNumbers` is ordered in ascending order + * 4) the operator is not already deregistered + * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for + */ + function deregisterOperator( + address operator, + bytes memory quorumNumbers + ) public virtual onlyRegistryCoordinator { + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + + // Update each quorum's aggregate pubkey + _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); + emit OperatorRemovedFromQuorums(operator, getOperatorId(operator), quorumNumbers); + } + + /** + * @notice Initializes a new quorum by pushing its first apk update + * @param quorumNumber The number of the new quorum + */ + function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { + require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); + + apkHistory[quorumNumber].push(ApkUpdate({ + apkHash: bytes24(0), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); + } + + /** + * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. + * @param operator is the operator for whom the key is being registered + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership + */ + function registerBLSPublicKey( + address operator, + PubkeyRegistrationParams calldata params, + BN254.G1Point calldata pubkeyRegistrationMessageHash + ) external returns (bytes32 operatorId) { + bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); + require( + pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" + ); + require( + operatorToPubkeyHash[operator] == bytes32(0), + "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" + ); + require( + pubkeyHashToOperator[pubkeyHash] == address(0), + "BLSApkRegistry.registerBLSPublicKey: public key already registered" + ); + + // gamma = h(sigma, P, P', H(m)) + uint256 gamma = uint256(keccak256(abi.encodePacked( + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.Y, + pubkeyRegistrationMessageHash.X, + pubkeyRegistrationMessageHash.Y + ))) % BN254.FR_MODULUS; + + // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') + require(BN254.pairing( + params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), + BN254.negGeneratorG2(), + pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), + params.pubkeyG2 + ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + + operatorToPubkey[operator] = params.pubkeyG1; + operatorToPubkeyHash[operator] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = operator; + + emit NewPubkeyRegistration(operator, params.pubkeyG1, params.pubkeyG2); + return pubkeyHash; + } + + /******************************************************************************* + INTERNAL FUNCTIONS + *******************************************************************************/ + + function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { + BN254.G1Point memory newApk; + + for (uint256 i = 0; i < quorumNumbers.length; i++) { + // Validate quorum exists and get history length + uint8 quorumNumber = uint8(quorumNumbers[i]); + uint256 historyLength = apkHistory[quorumNumber].length; + require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + + // Update aggregate public key for this quorum + newApk = currentApk[quorumNumber].plus(point); + currentApk[quorumNumber] = newApk; + bytes24 newApkHash = bytes24(BN254.hashG1Point(newApk)); + + // Update apk history. If the last update was made in this block, update the entry + // Otherwise, push a new historical entry and update the prev->next pointer + ApkUpdate storage lastUpdate = apkHistory[quorumNumber][historyLength - 1]; + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.apkHash = newApkHash; + } else { + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + apkHistory[quorumNumber].push(ApkUpdate({ + apkHash: newApkHash, + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); + } + } + } + + /******************************************************************************* + VIEW FUNCTIONS + *******************************************************************************/ + /** + * @notice Returns the pubkey and pubkey hash of an operator + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0), + "BLSApkRegistry.getRegisteredPubkey: operator is not registered" + ); + + return (pubkey, pubkeyHash); + } + + /** + * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` + * @dev Returns the current indices if `blockNumber >= block.number` + */ + function getApkIndicesAtBlockNumber( + bytes calldata quorumNumbers, + uint256 blockNumber + ) external view returns (uint32[] memory) { + uint32[] memory indices = new uint32[](quorumNumbers.length); + + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; + if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { + revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); + } + + // Loop backward through apkHistory until we find an entry that preceeds `blockNumber` + for (uint256 j = quorumApkUpdatesLength; j > 0; j--) { + if (apkHistory[quorumNumber][j - 1].updateBlockNumber <= blockNumber) { + indices[i] = uint32(j - 1); + break; + } + } + } + return indices; + } + + /// @notice Returns the current APK for the provided `quorumNumber ` + function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) { + return currentApk[quorumNumber]; + } + + /// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber` + function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { + return apkHistory[quorumNumber][index]; + } + + /** + * @notice get hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; + * called by checkSignatures in BLSSignatureChecker.sol. + * @param quorumNumber is the quorum whose ApkHash is being retrieved + * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved + * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage + */ + function getApkHashAtBlockNumberAndIndex( + uint8 quorumNumber, + uint32 blockNumber, + uint256 index + ) external view returns (bytes24) { + ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; + + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ + require( + blockNumber >= quorumApkUpdate.updateBlockNumber, + "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + ); + require( + quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, + "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + ); + + return quorumApkUpdate.apkHash; + } + + /// @notice Returns the length of ApkUpdates for the provided `quorumNumber` + function getApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { + return uint32(apkHistory[quorumNumber].length); + } + + /// @notice Returns the operator address for the given `pubkeyHash` + function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { + return pubkeyHashToOperator[pubkeyHash]; + } + + /// @notice returns the ID used to identify the `operator` within this AVS + /// @dev Returns zero in the event that the `operator` has never registered for the AVS + function getOperatorId(address operator) public view returns (bytes32) { + return operatorToPubkeyHash[operator]; + } +} diff --git a/services/operatorsinfo/integration_test_deployment/README.md b/services/operatorsinfo/integration_test_deployment/README.md new file mode 100644 index 00000000..9265b455 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/services/operatorsinfo/integration_test_deployment/deployment_script.sol b/services/operatorsinfo/integration_test_deployment/deployment_script.sol new file mode 100644 index 00000000..ec1476aa --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/deployment_script.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import "forge-std/Script.sol"; +import {RegistryCoordinatorMock} from "../../contracts/lib/eigenlayer-middleware/test/mocks/RegistryCoordinatorMock.sol"; +import {BLSApkRegistry} from "../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistry.sol"; + +import {IBLSApkRegistry} from "../../contracts/lib/eigenlayer-middleware/src/interfaces/IBLSApkRegistry.sol"; +import {BN254} from "../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; +import "forge-std/Test.sol"; + +import {BLSApkRegistryMock} from "./BLSApkRegistryMock.sol"; + + + +contract TestRegistryDeploy is Script { + using BN254 for BN254.G1Point; + Vm cheats = Vm(VM_ADDRESS); + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + RegistryCoordinatorMock registryCoordMock = new RegistryCoordinatorMock(); + BLSApkRegistryMock registry = new BLSApkRegistryMock(registryCoordMock); + // vm.stopBroadcast(); + + + + // vm.startBroadcast(address(registryCoordMock)); + + address operator = address(123); + + IBLSApkRegistry.PubkeyRegistrationParams memory pubkeyRegistrationParams; + + uint256 privKey = 69; + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul( + privKey + ); + + BN254.G1Point memory defaultPubkey = pubkeyRegistrationParams.pubkeyG1; + bytes32 defaultPubkeyHash = BN254.hashG1Point(defaultPubkey); + + //privKey*G2 + pubkeyRegistrationParams.pubkeyG2.X[ + 1 + ] = 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; + pubkeyRegistrationParams.pubkeyG2.X[ + 0 + ] = 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; + pubkeyRegistrationParams.pubkeyG2.Y[ + 1 + ] = 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; + pubkeyRegistrationParams.pubkeyG2.Y[ + 0 + ] = 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; + + // BN254.G1Point memory messageHash = registryCoordMock + // .pubkeyRegistrationMessageHash(operator); + // pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + // operator + // ); + BN254.G1Point memory messageHash; + + + // cheats.startPrank(address(registryCoordMock)); + registry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); + // cheats.stopPrank(); + + vm.stopBroadcast(); + } + + + // function _signMessage( + // address signer + // ) internal view returns (BN254.G1Point memory) { + // BN254.G1Point memory messageHash = registryCoordinator + // .pubkeyRegistrationMessageHash(signer); + // return BN254.scalar_mul(messageHash, privKey); + // } +} \ No newline at end of file diff --git a/services/operatorsinfo/integration_test_deployment/foundry.toml b/services/operatorsinfo/integration_test_deployment/foundry.toml new file mode 100644 index 00000000..25b918f9 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/services/operatorsinfo/integration_test_deployment/lib/forge-std b/services/operatorsinfo/integration_test_deployment/lib/forge-std new file mode 160000 index 00000000..978ac6fa --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/services/operatorsinfo/integration_test_deployment/script/Counter.s.sol b/services/operatorsinfo/integration_test_deployment/script/Counter.s.sol new file mode 100644 index 00000000..df9ee8b0 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/script/Counter.s.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; + +contract CounterScript is Script { + function setUp() public {} + + function run() public { + vm.broadcast(); + } +} diff --git a/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol b/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol new file mode 100644 index 00000000..3e136a00 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import "forge-std/Script.sol"; +import {RegistryCoordinatorMock} from "../../../contracts/lib/eigenlayer-middleware/test/mocks/RegistryCoordinatorMock.sol"; +import {BLSApkRegistry} from "../../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistry.sol"; + +import {IBLSApkRegistry} from "../../../contracts/lib/eigenlayer-middleware/src/interfaces/IBLSApkRegistry.sol"; +import {BN254} from "../../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; +import "forge-std/Test.sol"; + +import {BLSApkRegistryMock} from "../test/BLSApkRegistryMock.sol"; + + + +contract TestRegistryDeploy is Script { + using BN254 for BN254.G1Point; + Vm cheats = Vm(VM_ADDRESS); + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + RegistryCoordinatorMock registryCoordMock = new RegistryCoordinatorMock(); + BLSApkRegistryMock registry = new BLSApkRegistryMock(registryCoordMock); + // vm.stopBroadcast(); + + + + // vm.startBroadcast(address(registryCoordMock)); + + address operator = address(123); + + IBLSApkRegistry.PubkeyRegistrationParams memory pubkeyRegistrationParams; + + uint256 privKey = 69; + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul( + privKey + ); + + BN254.G1Point memory defaultPubkey = pubkeyRegistrationParams.pubkeyG1; + bytes32 defaultPubkeyHash = BN254.hashG1Point(defaultPubkey); + + //privKey*G2 + pubkeyRegistrationParams.pubkeyG2.X[ + 1 + ] = 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; + pubkeyRegistrationParams.pubkeyG2.X[ + 0 + ] = 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; + pubkeyRegistrationParams.pubkeyG2.Y[ + 1 + ] = 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; + pubkeyRegistrationParams.pubkeyG2.Y[ + 0 + ] = 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; + + // BN254.G1Point memory messageHash = registryCoordMock + // .pubkeyRegistrationMessageHash(operator); + // pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + // operator + // ); + BN254.G1Point memory messageHash; + + + // cheats.startPrank(address(registryCoordMock)); + registry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); + // cheats.stopPrank(); + + vm.stopBroadcast(); + } + + + // function _signMessage( + // address signer + // ) internal view returns (BN254.G1Point memory) { + // BN254.G1Point memory messageHash = registryCoordinator + // .pubkeyRegistrationMessageHash(signer); + // return BN254.scalar_mul(messageHash, privKey); + // } +} \ No newline at end of file diff --git a/services/operatorsinfo/integration_test_deployment/src/Counter.sol b/services/operatorsinfo/integration_test_deployment/src/Counter.sol new file mode 100644 index 00000000..aded7997 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol b/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol new file mode 100644 index 00000000..4ea3ea9b --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {BLSApkRegistryStorage} from "../../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistryStorage.sol"; + +import {IRegistryCoordinator} from "../../../contracts/lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; + +import {BN254} from "../../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; + +contract BLSApkRegistryMock is BLSApkRegistryStorage { + using BN254 for BN254.G1Point; + + /// @notice when applied to a function, only allows the RegistryCoordinator to call it + modifier onlyRegistryCoordinator() { + require( + msg.sender == address(registryCoordinator), + "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + ); + _; + } + + /// @notice Sets the (immutable) `registryCoordinator` address + constructor( + IRegistryCoordinator _registryCoordinator + ) BLSApkRegistryStorage(_registryCoordinator) {} + + /******************************************************************************* + EXTERNAL FUNCTIONS - REGISTRY COORDINATOR + *******************************************************************************/ + + /** + * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. + * @param operator The address of the operator to register. + * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. + * @dev access restricted to the RegistryCoordinator + * @dev Preconditions (these are assumed, not validated in this contract): + * 1) `quorumNumbers` has no duplicates + * 2) `quorumNumbers.length` != 0 + * 3) `quorumNumbers` is ordered in ascending order + * 4) the operator is not already registered + */ + function registerOperator( + address operator, + bytes memory quorumNumbers + ) public virtual onlyRegistryCoordinator { + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + + // Update each quorum's aggregate pubkey + _processQuorumApkUpdate(quorumNumbers, pubkey); + + // Return pubkeyHash, which will become the operator's unique id + emit OperatorAddedToQuorums(operator, getOperatorId(operator), quorumNumbers); + } + + /** + * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. + * @param operator The address of the operator to deregister. + * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. + * @dev access restricted to the RegistryCoordinator + * @dev Preconditions (these are assumed, not validated in this contract): + * 1) `quorumNumbers` has no duplicates + * 2) `quorumNumbers.length` != 0 + * 3) `quorumNumbers` is ordered in ascending order + * 4) the operator is not already deregistered + * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for + */ + function deregisterOperator( + address operator, + bytes memory quorumNumbers + ) public virtual onlyRegistryCoordinator { + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + + // Update each quorum's aggregate pubkey + _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); + emit OperatorRemovedFromQuorums(operator, getOperatorId(operator), quorumNumbers); + } + + /** + * @notice Initializes a new quorum by pushing its first apk update + * @param quorumNumber The number of the new quorum + */ + function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { + require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); + + apkHistory[quorumNumber].push(ApkUpdate({ + apkHash: bytes24(0), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); + } + + /** + * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. + * @param operator is the operator for whom the key is being registered + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership + */ + function registerBLSPublicKey( + address operator, + PubkeyRegistrationParams calldata params, + BN254.G1Point calldata pubkeyRegistrationMessageHash + ) external returns (bytes32 operatorId) { + bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); + require( + pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" + ); + require( + operatorToPubkeyHash[operator] == bytes32(0), + "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" + ); + require( + pubkeyHashToOperator[pubkeyHash] == address(0), + "BLSApkRegistry.registerBLSPublicKey: public key already registered" + ); + + // gamma = h(sigma, P, P', H(m)) + uint256 gamma = uint256(keccak256(abi.encodePacked( + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.Y, + pubkeyRegistrationMessageHash.X, + pubkeyRegistrationMessageHash.Y + ))) % BN254.FR_MODULUS; + + // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') + require(BN254.pairing( + params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), + BN254.negGeneratorG2(), + pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), + params.pubkeyG2 + ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + + operatorToPubkey[operator] = params.pubkeyG1; + operatorToPubkeyHash[operator] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = operator; + + emit NewPubkeyRegistration(operator, params.pubkeyG1, params.pubkeyG2); + return pubkeyHash; + } + + /******************************************************************************* + INTERNAL FUNCTIONS + *******************************************************************************/ + + function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { + BN254.G1Point memory newApk; + + for (uint256 i = 0; i < quorumNumbers.length; i++) { + // Validate quorum exists and get history length + uint8 quorumNumber = uint8(quorumNumbers[i]); + uint256 historyLength = apkHistory[quorumNumber].length; + require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + + // Update aggregate public key for this quorum + newApk = currentApk[quorumNumber].plus(point); + currentApk[quorumNumber] = newApk; + bytes24 newApkHash = bytes24(BN254.hashG1Point(newApk)); + + // Update apk history. If the last update was made in this block, update the entry + // Otherwise, push a new historical entry and update the prev->next pointer + ApkUpdate storage lastUpdate = apkHistory[quorumNumber][historyLength - 1]; + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.apkHash = newApkHash; + } else { + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + apkHistory[quorumNumber].push(ApkUpdate({ + apkHash: newApkHash, + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); + } + } + } + + /******************************************************************************* + VIEW FUNCTIONS + *******************************************************************************/ + /** + * @notice Returns the pubkey and pubkey hash of an operator + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0), + "BLSApkRegistry.getRegisteredPubkey: operator is not registered" + ); + + return (pubkey, pubkeyHash); + } + + /** + * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` + * @dev Returns the current indices if `blockNumber >= block.number` + */ + function getApkIndicesAtBlockNumber( + bytes calldata quorumNumbers, + uint256 blockNumber + ) external view returns (uint32[] memory) { + uint32[] memory indices = new uint32[](quorumNumbers.length); + + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; + if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { + revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); + } + + // Loop backward through apkHistory until we find an entry that preceeds `blockNumber` + for (uint256 j = quorumApkUpdatesLength; j > 0; j--) { + if (apkHistory[quorumNumber][j - 1].updateBlockNumber <= blockNumber) { + indices[i] = uint32(j - 1); + break; + } + } + } + return indices; + } + + /// @notice Returns the current APK for the provided `quorumNumber ` + function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) { + return currentApk[quorumNumber]; + } + + /// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber` + function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { + return apkHistory[quorumNumber][index]; + } + + /** + * @notice get hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; + * called by checkSignatures in BLSSignatureChecker.sol. + * @param quorumNumber is the quorum whose ApkHash is being retrieved + * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved + * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage + */ + function getApkHashAtBlockNumberAndIndex( + uint8 quorumNumber, + uint32 blockNumber, + uint256 index + ) external view returns (bytes24) { + ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; + + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ + require( + blockNumber >= quorumApkUpdate.updateBlockNumber, + "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + ); + require( + quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, + "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + ); + + return quorumApkUpdate.apkHash; + } + + /// @notice Returns the length of ApkUpdates for the provided `quorumNumber` + function getApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { + return uint32(apkHistory[quorumNumber].length); + } + + /// @notice Returns the operator address for the given `pubkeyHash` + function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { + return pubkeyHashToOperator[pubkeyHash]; + } + + /// @notice returns the ID used to identify the `operator` within this AVS + /// @dev Returns zero in the event that the `operator` has never registered for the AVS + function getOperatorId(address operator) public view returns (bytes32) { + return operatorToPubkeyHash[operator]; + } +} diff --git a/services/operatorsinfo/integration_test_deployment/test/Counter.t.sol b/services/operatorsinfo/integration_test_deployment/test/Counter.t.sol new file mode 100644 index 00000000..54b724f7 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go b/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go new file mode 100644 index 00000000..32a47d83 --- /dev/null +++ b/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go @@ -0,0 +1 @@ +package operatorsinfo From 48f51eb703f37149b1aafd504bae4fd4e7dcd2b5 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Tue, 14 May 2024 09:58:35 -0700 Subject: [PATCH 07/11] added remappings --- .gitmodules | 3 + .../deployment_script.sol | 86 ------------------- .../lib/eigenlayer-middleware | 1 + .../remappings.txt | 3 + .../script/Counter.s.sol | 12 --- .../script/deployment_script.s.sol | 8 +- .../test/BLSApkRegistryMock.sol | 6 +- .../test/Counter.t.sol | 24 ------ 8 files changed, 14 insertions(+), 129 deletions(-) delete mode 100644 services/operatorsinfo/integration_test_deployment/deployment_script.sol create mode 160000 services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware create mode 100644 services/operatorsinfo/integration_test_deployment/remappings.txt delete mode 100644 services/operatorsinfo/integration_test_deployment/script/Counter.s.sol delete mode 100644 services/operatorsinfo/integration_test_deployment/test/Counter.t.sol diff --git a/.gitmodules b/.gitmodules index af2aceb2..132c389c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "services/operatorsinfo/integration_test_deployment/lib/forge-std"] path = services/operatorsinfo/integration_test_deployment/lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware"] + path = services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware + url = https://github.com/Layr-Labs/eigenlayer-middleware diff --git a/services/operatorsinfo/integration_test_deployment/deployment_script.sol b/services/operatorsinfo/integration_test_deployment/deployment_script.sol deleted file mode 100644 index ec1476aa..00000000 --- a/services/operatorsinfo/integration_test_deployment/deployment_script.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.12; - -import "forge-std/Script.sol"; -import {RegistryCoordinatorMock} from "../../contracts/lib/eigenlayer-middleware/test/mocks/RegistryCoordinatorMock.sol"; -import {BLSApkRegistry} from "../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistry.sol"; - -import {IBLSApkRegistry} from "../../contracts/lib/eigenlayer-middleware/src/interfaces/IBLSApkRegistry.sol"; -import {BN254} from "../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; -import "forge-std/Test.sol"; - -import {BLSApkRegistryMock} from "./BLSApkRegistryMock.sol"; - - - -contract TestRegistryDeploy is Script { - using BN254 for BN254.G1Point; - Vm cheats = Vm(VM_ADDRESS); - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - RegistryCoordinatorMock registryCoordMock = new RegistryCoordinatorMock(); - BLSApkRegistryMock registry = new BLSApkRegistryMock(registryCoordMock); - // vm.stopBroadcast(); - - - - // vm.startBroadcast(address(registryCoordMock)); - - address operator = address(123); - - IBLSApkRegistry.PubkeyRegistrationParams memory pubkeyRegistrationParams; - - uint256 privKey = 69; - pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul( - privKey - ); - - BN254.G1Point memory defaultPubkey = pubkeyRegistrationParams.pubkeyG1; - bytes32 defaultPubkeyHash = BN254.hashG1Point(defaultPubkey); - - //privKey*G2 - pubkeyRegistrationParams.pubkeyG2.X[ - 1 - ] = 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; - pubkeyRegistrationParams.pubkeyG2.X[ - 0 - ] = 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; - pubkeyRegistrationParams.pubkeyG2.Y[ - 1 - ] = 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; - pubkeyRegistrationParams.pubkeyG2.Y[ - 0 - ] = 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; - - // BN254.G1Point memory messageHash = registryCoordMock - // .pubkeyRegistrationMessageHash(operator); - // pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - // operator - // ); - BN254.G1Point memory messageHash; - - - // cheats.startPrank(address(registryCoordMock)); - registry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); - // cheats.stopPrank(); - - vm.stopBroadcast(); - } - - - // function _signMessage( - // address signer - // ) internal view returns (BN254.G1Point memory) { - // BN254.G1Point memory messageHash = registryCoordinator - // .pubkeyRegistrationMessageHash(signer); - // return BN254.scalar_mul(messageHash, privKey); - // } -} \ No newline at end of file diff --git a/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware b/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware new file mode 160000 index 00000000..a23de118 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware @@ -0,0 +1 @@ +Subproject commit a23de118e7d16081d350c7f83c24261d1421b0ba diff --git a/services/operatorsinfo/integration_test_deployment/remappings.txt b/services/operatorsinfo/integration_test_deployment/remappings.txt new file mode 100644 index 00000000..7110cc77 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/remappings.txt @@ -0,0 +1,3 @@ +ds-test/=lib/ds-test/src/ +eigenlayer-middleware/=lib/eigenlayer-middleware/ + diff --git a/services/operatorsinfo/integration_test_deployment/script/Counter.s.sol b/services/operatorsinfo/integration_test_deployment/script/Counter.s.sol deleted file mode 100644 index df9ee8b0..00000000 --- a/services/operatorsinfo/integration_test_deployment/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol b/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol index 3e136a00..4b1640bf 100644 --- a/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol +++ b/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.12; import "forge-std/Script.sol"; -import {RegistryCoordinatorMock} from "../../../contracts/lib/eigenlayer-middleware/test/mocks/RegistryCoordinatorMock.sol"; -import {BLSApkRegistry} from "../../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistry.sol"; +import {RegistryCoordinatorMock} from "eigenlayer-middleware/test/mocks/RegistryCoordinatorMock.sol"; +import {BLSApkRegistry} from "eigenlayer-middleware/src/BLSApkRegistry.sol"; -import {IBLSApkRegistry} from "../../../contracts/lib/eigenlayer-middleware/src/interfaces/IBLSApkRegistry.sol"; -import {BN254} from "../../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; +import {IBLSApkRegistry} from "eigenlayer-middleware/src/interfaces/IBLSApkRegistry.sol"; +import {BN254} from "eigenlayer-middleware/src/libraries/BN254.sol"; import "forge-std/Test.sol"; import {BLSApkRegistryMock} from "../test/BLSApkRegistryMock.sol"; diff --git a/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol b/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol index 4ea3ea9b..2d9d3b48 100644 --- a/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol +++ b/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {BLSApkRegistryStorage} from "../../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistryStorage.sol"; +import {BLSApkRegistryStorage} from "eigenlayer-middleware/src/BLSApkRegistryStorage.sol"; -import {IRegistryCoordinator} from "../../../contracts/lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; +import {IRegistryCoordinator} from "eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; -import {BN254} from "../../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; +import {BN254} from "eigenlayer-middleware/src/libraries/BN254.sol"; contract BLSApkRegistryMock is BLSApkRegistryStorage { using BN254 for BN254.G1Point; diff --git a/services/operatorsinfo/integration_test_deployment/test/Counter.t.sol b/services/operatorsinfo/integration_test_deployment/test/Counter.t.sol deleted file mode 100644 index 54b724f7..00000000 --- a/services/operatorsinfo/integration_test_deployment/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} From 7dc8a2fdadebc6fa34f64c0ba7ca9ad21d2edf09 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Tue, 14 May 2024 15:01:53 -0700 Subject: [PATCH 08/11] integration test --- .gitmodules | 3 +++ .../lib/eigenlayer-middleware-offchain | 1 + 2 files changed, 4 insertions(+) create mode 160000 services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain diff --git a/.gitmodules b/.gitmodules index 132c389c..edffa5bf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware"] path = services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware url = https://github.com/Layr-Labs/eigenlayer-middleware +[submodule "services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain"] + path = services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain + url = https://github.com/Layr-Labs/eigenlayer-middleware-offchain diff --git a/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain b/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain new file mode 160000 index 00000000..d6892146 --- /dev/null +++ b/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain @@ -0,0 +1 @@ +Subproject commit d68921464d9662ed9e983191b2ab91072a921d88 From 5183a398b84ac7b3813add112612a03698d23d97 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Tue, 21 May 2024 09:14:00 -0700 Subject: [PATCH 09/11] added integration test halpers --- ...operatorsinfo_subgraph_integration_test.go | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go b/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go index 32a47d83..98b3f1db 100644 --- a/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go +++ b/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go @@ -1 +1,100 @@ package operatorsinfo + +import ( + "bytes" + "fmt" + "log" + "os" + "os/exec" + "testing" +) + +func TestStartAnvilChain(t *testing.T) { + pid, err := startAnvilChain() + if err != nil { + t.Fatal(err) + } + fmt.Println("Anvil chain started with PID", pid) + + curr_path, err := os.Getwd() + changeDirectory(curr_path + "integration_test_deployment/lib/eigenlayer-middleware-offchain/subgraphs/BLSApkRegistry") + + _, err = startGraph() + if err != nil { + t.Fatal(err) + } + _, err = startSubgraph() + if err != nil { + t.Fatal(err) + } + + // err = stopAnvilChain(pid) + // if err != nil { + // t.Fatal(err) + // } +} + +func startAnvilChain() (int, error) { + return execCmd("anvil", []string{"--host", "0.0.0.0"}, []string{}) +} + +func startGraph() (int, error) { + return execCmd("docker", []string{"compose", "up"}, []string{}) +} + +func startSubgraph() (int, error) { + _, err := execCmd("graph", []string{"codegen"}, []string{}) + if err != nil { + return 0, err + } + _, err = execCmd("graph", []string{"build"}, []string{}) + if err != nil { + return 0, err + } + _, err = execCmd("npm", []string{"run", "create-local"}, []string{}) + if err != nil { + return 0, err + } + return execCmd("npm", []string{"run", "deploy-local"}, []string{}) +} + +func stopAnvilChain(pid int) error { + _, err := execCmd("kill", []string{"-9", fmt.Sprintf("%d", pid)}, []string{}) + return err +} + +func execCmd(name string, args []string, envVars []string) (int, error) { + cmd := exec.Command(name, args...) + if len(envVars) > 0 { + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, envVars...) + } + var out bytes.Buffer + var stderr bytes.Buffer + // TODO: When these are uncommented, the deployer sometimes fails to start anvil + // cmd.Stdout = &out + // cmd.Stderr = &stderr + + fmt.Print("Running command: ", cmd.String()) + err := cmd.Run() + fmt.Print("Running command: ", cmd.Process.Pid) + if err != nil { + return 0, fmt.Errorf("%s: %s", err.Error(), stderr.String()) + } + fmt.Print(out.String()) + pid := cmd.Process.Pid + return pid, nil +} + +func changeDirectory(path string) { + err := os.Chdir(path) + if err != nil { + log.Panicf("Failed to change directories. Error: %s", err) + } + + newDir, err := os.Getwd() + if err != nil { + log.Panicf("Failed to get working directory. Error: %s", err) + } + log.Printf("Current Working Directory: %s\n", newDir) +} From f6569c322b558d154b9e1f7e87528a58dd5f3828 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Tue, 28 May 2024 06:54:52 -0700 Subject: [PATCH 10/11] fixed --- .../.github/workflows/test.yml | 34 --- .../integration_test_deployment/.gitignore | 14 - .../BLSApkRegistryMock.sol | 284 ------------------ .../integration_test_deployment/README.md | 66 ---- .../integration_test_deployment/foundry.toml | 6 - .../lib/eigenlayer-middleware | 1 - .../lib/eigenlayer-middleware-offchain | 1 - .../integration_test_deployment/lib/forge-std | 1 - .../remappings.txt | 3 - .../script/deployment_script.s.sol | 86 ------ .../src/Counter.sol | 14 - .../test/BLSApkRegistryMock.sol | 284 ------------------ .../operatorsinfo/operatorsinfo_subgraph.go | 2 - ...operatorsinfo_subgraph_integration_test.go | 100 ------ .../operatorsinfo_subgraph_test.go | 9 +- 15 files changed, 6 insertions(+), 899 deletions(-) delete mode 100644 services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml delete mode 100644 services/operatorsinfo/integration_test_deployment/.gitignore delete mode 100644 services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol delete mode 100644 services/operatorsinfo/integration_test_deployment/README.md delete mode 100644 services/operatorsinfo/integration_test_deployment/foundry.toml delete mode 160000 services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware delete mode 160000 services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain delete mode 160000 services/operatorsinfo/integration_test_deployment/lib/forge-std delete mode 100644 services/operatorsinfo/integration_test_deployment/remappings.txt delete mode 100644 services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol delete mode 100644 services/operatorsinfo/integration_test_deployment/src/Counter.sol delete mode 100644 services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol delete mode 100644 services/operatorsinfo/operatorsinfo_subgraph_integration_test.go diff --git a/services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml b/services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml deleted file mode 100644 index 9282e829..00000000 --- a/services/operatorsinfo/integration_test_deployment/.github/workflows/test.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: test - -on: workflow_dispatch - -env: - FOUNDRY_PROFILE: ci - -jobs: - check: - strategy: - fail-fast: true - - name: Foundry project - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Run Forge build - run: | - forge --version - forge build --sizes - id: build - - - name: Run Forge tests - run: | - forge test -vvv - id: test diff --git a/services/operatorsinfo/integration_test_deployment/.gitignore b/services/operatorsinfo/integration_test_deployment/.gitignore deleted file mode 100644 index 85198aaa..00000000 --- a/services/operatorsinfo/integration_test_deployment/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Compiler files -cache/ -out/ - -# Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/**/dry-run/ - -# Docs -docs/ - -# Dotenv file -.env diff --git a/services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol b/services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol deleted file mode 100644 index df9795c4..00000000 --- a/services/operatorsinfo/integration_test_deployment/BLSApkRegistryMock.sol +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {BLSApkRegistryStorage} from "../../contracts/lib/eigenlayer-middleware/src/BLSApkRegistryStorage.sol"; - -import {IRegistryCoordinator} from "../../contracts/lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; - -import {BN254} from "../../contracts/lib/eigenlayer-middleware/src/libraries/BN254.sol"; - -contract BLSApkRegistryMock is BLSApkRegistryStorage { - using BN254 for BN254.G1Point; - - /// @notice when applied to a function, only allows the RegistryCoordinator to call it - modifier onlyRegistryCoordinator() { - require( - msg.sender == address(registryCoordinator), - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); - _; - } - - /// @notice Sets the (immutable) `registryCoordinator` address - constructor( - IRegistryCoordinator _registryCoordinator - ) BLSApkRegistryStorage(_registryCoordinator) {} - - /******************************************************************************* - EXTERNAL FUNCTIONS - REGISTRY COORDINATOR - *******************************************************************************/ - - /** - * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered - */ - function registerOperator( - address operator, - bytes memory quorumNumbers - ) public virtual onlyRegistryCoordinator { - // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); - - // Update each quorum's aggregate pubkey - _processQuorumApkUpdate(quorumNumbers, pubkey); - - // Return pubkeyHash, which will become the operator's unique id - emit OperatorAddedToQuorums(operator, getOperatorId(operator), quorumNumbers); - } - - /** - * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ - function deregisterOperator( - address operator, - bytes memory quorumNumbers - ) public virtual onlyRegistryCoordinator { - // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); - - // Update each quorum's aggregate pubkey - _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); - emit OperatorRemovedFromQuorums(operator, getOperatorId(operator), quorumNumbers); - } - - /** - * @notice Initializes a new quorum by pushing its first apk update - * @param quorumNumber The number of the new quorum - */ - function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); - - apkHistory[quorumNumber].push(ApkUpdate({ - apkHash: bytes24(0), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); - } - - /** - * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. - * @param operator is the operator for whom the key is being registered - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership - */ - function registerBLSPublicKey( - address operator, - PubkeyRegistrationParams calldata params, - BN254.G1Point calldata pubkeyRegistrationMessageHash - ) external returns (bytes32 operatorId) { - bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); - require( - operatorToPubkeyHash[operator] == bytes32(0), - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); - - // gamma = h(sigma, P, P', H(m)) - uint256 gamma = uint256(keccak256(abi.encodePacked( - params.pubkeyRegistrationSignature.X, - params.pubkeyRegistrationSignature.Y, - params.pubkeyG1.X, - params.pubkeyG1.Y, - params.pubkeyG2.X, - params.pubkeyG2.Y, - pubkeyRegistrationMessageHash.X, - pubkeyRegistrationMessageHash.Y - ))) % BN254.FR_MODULUS; - - // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') - require(BN254.pairing( - params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), - BN254.negGeneratorG2(), - pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - params.pubkeyG2 - ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); - - operatorToPubkey[operator] = params.pubkeyG1; - operatorToPubkeyHash[operator] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = operator; - - emit NewPubkeyRegistration(operator, params.pubkeyG1, params.pubkeyG2); - return pubkeyHash; - } - - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - - function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { - BN254.G1Point memory newApk; - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - // Validate quorum exists and get history length - uint8 quorumNumber = uint8(quorumNumbers[i]); - uint256 historyLength = apkHistory[quorumNumber].length; - require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); - - // Update aggregate public key for this quorum - newApk = currentApk[quorumNumber].plus(point); - currentApk[quorumNumber] = newApk; - bytes24 newApkHash = bytes24(BN254.hashG1Point(newApk)); - - // Update apk history. If the last update was made in this block, update the entry - // Otherwise, push a new historical entry and update the prev->next pointer - ApkUpdate storage lastUpdate = apkHistory[quorumNumber][historyLength - 1]; - if (lastUpdate.updateBlockNumber == uint32(block.number)) { - lastUpdate.apkHash = newApkHash; - } else { - lastUpdate.nextUpdateBlockNumber = uint32(block.number); - apkHistory[quorumNumber].push(ApkUpdate({ - apkHash: newApkHash, - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); - } - } - } - - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey - */ - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - - /** - * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` - * @dev Returns the current indices if `blockNumber >= block.number` - */ - function getApkIndicesAtBlockNumber( - bytes calldata quorumNumbers, - uint256 blockNumber - ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](quorumNumbers.length); - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; - if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { - revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); - } - - // Loop backward through apkHistory until we find an entry that preceeds `blockNumber` - for (uint256 j = quorumApkUpdatesLength; j > 0; j--) { - if (apkHistory[quorumNumber][j - 1].updateBlockNumber <= blockNumber) { - indices[i] = uint32(j - 1); - break; - } - } - } - return indices; - } - - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) { - return currentApk[quorumNumber]; - } - - /// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber` - function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { - return apkHistory[quorumNumber][index]; - } - - /** - * @notice get hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; - * called by checkSignatures in BLSSignatureChecker.sol. - * @param quorumNumber is the quorum whose ApkHash is being retrieved - * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved - * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage - */ - function getApkHashAtBlockNumberAndIndex( - uint8 quorumNumber, - uint32 blockNumber, - uint256 index - ) external view returns (bytes24) { - ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; - - /** - * Validate that the update is valid for the given blockNumber: - * - blockNumber should be >= the update block number - * - the next update block number should be either 0 or strictly greater than blockNumber - */ - require( - blockNumber >= quorumApkUpdate.updateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" - ); - require( - quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" - ); - - return quorumApkUpdate.apkHash; - } - - /// @notice Returns the length of ApkUpdates for the provided `quorumNumber` - function getApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { - return uint32(apkHistory[quorumNumber].length); - } - - /// @notice Returns the operator address for the given `pubkeyHash` - function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { - return pubkeyHashToOperator[pubkeyHash]; - } - - /// @notice returns the ID used to identify the `operator` within this AVS - /// @dev Returns zero in the event that the `operator` has never registered for the AVS - function getOperatorId(address operator) public view returns (bytes32) { - return operatorToPubkeyHash[operator]; - } -} diff --git a/services/operatorsinfo/integration_test_deployment/README.md b/services/operatorsinfo/integration_test_deployment/README.md deleted file mode 100644 index 9265b455..00000000 --- a/services/operatorsinfo/integration_test_deployment/README.md +++ /dev/null @@ -1,66 +0,0 @@ -## Foundry - -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** - -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -## Documentation - -https://book.getfoundry.sh/ - -## Usage - -### Build - -```shell -$ forge build -``` - -### Test - -```shell -$ forge test -``` - -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Deploy - -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` diff --git a/services/operatorsinfo/integration_test_deployment/foundry.toml b/services/operatorsinfo/integration_test_deployment/foundry.toml deleted file mode 100644 index 25b918f9..00000000 --- a/services/operatorsinfo/integration_test_deployment/foundry.toml +++ /dev/null @@ -1,6 +0,0 @@ -[profile.default] -src = "src" -out = "out" -libs = ["lib"] - -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware b/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware deleted file mode 160000 index a23de118..00000000 --- a/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a23de118e7d16081d350c7f83c24261d1421b0ba diff --git a/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain b/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain deleted file mode 160000 index d6892146..00000000 --- a/services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d68921464d9662ed9e983191b2ab91072a921d88 diff --git a/services/operatorsinfo/integration_test_deployment/lib/forge-std b/services/operatorsinfo/integration_test_deployment/lib/forge-std deleted file mode 160000 index 978ac6fa..00000000 --- a/services/operatorsinfo/integration_test_deployment/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/services/operatorsinfo/integration_test_deployment/remappings.txt b/services/operatorsinfo/integration_test_deployment/remappings.txt deleted file mode 100644 index 7110cc77..00000000 --- a/services/operatorsinfo/integration_test_deployment/remappings.txt +++ /dev/null @@ -1,3 +0,0 @@ -ds-test/=lib/ds-test/src/ -eigenlayer-middleware/=lib/eigenlayer-middleware/ - diff --git a/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol b/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol deleted file mode 100644 index 4b1640bf..00000000 --- a/services/operatorsinfo/integration_test_deployment/script/deployment_script.s.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.12; - -import "forge-std/Script.sol"; -import {RegistryCoordinatorMock} from "eigenlayer-middleware/test/mocks/RegistryCoordinatorMock.sol"; -import {BLSApkRegistry} from "eigenlayer-middleware/src/BLSApkRegistry.sol"; - -import {IBLSApkRegistry} from "eigenlayer-middleware/src/interfaces/IBLSApkRegistry.sol"; -import {BN254} from "eigenlayer-middleware/src/libraries/BN254.sol"; -import "forge-std/Test.sol"; - -import {BLSApkRegistryMock} from "../test/BLSApkRegistryMock.sol"; - - - -contract TestRegistryDeploy is Script { - using BN254 for BN254.G1Point; - Vm cheats = Vm(VM_ADDRESS); - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - RegistryCoordinatorMock registryCoordMock = new RegistryCoordinatorMock(); - BLSApkRegistryMock registry = new BLSApkRegistryMock(registryCoordMock); - // vm.stopBroadcast(); - - - - // vm.startBroadcast(address(registryCoordMock)); - - address operator = address(123); - - IBLSApkRegistry.PubkeyRegistrationParams memory pubkeyRegistrationParams; - - uint256 privKey = 69; - pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul( - privKey - ); - - BN254.G1Point memory defaultPubkey = pubkeyRegistrationParams.pubkeyG1; - bytes32 defaultPubkeyHash = BN254.hashG1Point(defaultPubkey); - - //privKey*G2 - pubkeyRegistrationParams.pubkeyG2.X[ - 1 - ] = 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; - pubkeyRegistrationParams.pubkeyG2.X[ - 0 - ] = 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; - pubkeyRegistrationParams.pubkeyG2.Y[ - 1 - ] = 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; - pubkeyRegistrationParams.pubkeyG2.Y[ - 0 - ] = 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; - - // BN254.G1Point memory messageHash = registryCoordMock - // .pubkeyRegistrationMessageHash(operator); - // pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - // operator - // ); - BN254.G1Point memory messageHash; - - - // cheats.startPrank(address(registryCoordMock)); - registry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); - // cheats.stopPrank(); - - vm.stopBroadcast(); - } - - - // function _signMessage( - // address signer - // ) internal view returns (BN254.G1Point memory) { - // BN254.G1Point memory messageHash = registryCoordinator - // .pubkeyRegistrationMessageHash(signer); - // return BN254.scalar_mul(messageHash, privKey); - // } -} \ No newline at end of file diff --git a/services/operatorsinfo/integration_test_deployment/src/Counter.sol b/services/operatorsinfo/integration_test_deployment/src/Counter.sol deleted file mode 100644 index aded7997..00000000 --- a/services/operatorsinfo/integration_test_deployment/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol b/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol deleted file mode 100644 index 2d9d3b48..00000000 --- a/services/operatorsinfo/integration_test_deployment/test/BLSApkRegistryMock.sol +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {BLSApkRegistryStorage} from "eigenlayer-middleware/src/BLSApkRegistryStorage.sol"; - -import {IRegistryCoordinator} from "eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; - -import {BN254} from "eigenlayer-middleware/src/libraries/BN254.sol"; - -contract BLSApkRegistryMock is BLSApkRegistryStorage { - using BN254 for BN254.G1Point; - - /// @notice when applied to a function, only allows the RegistryCoordinator to call it - modifier onlyRegistryCoordinator() { - require( - msg.sender == address(registryCoordinator), - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); - _; - } - - /// @notice Sets the (immutable) `registryCoordinator` address - constructor( - IRegistryCoordinator _registryCoordinator - ) BLSApkRegistryStorage(_registryCoordinator) {} - - /******************************************************************************* - EXTERNAL FUNCTIONS - REGISTRY COORDINATOR - *******************************************************************************/ - - /** - * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered - */ - function registerOperator( - address operator, - bytes memory quorumNumbers - ) public virtual onlyRegistryCoordinator { - // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); - - // Update each quorum's aggregate pubkey - _processQuorumApkUpdate(quorumNumbers, pubkey); - - // Return pubkeyHash, which will become the operator's unique id - emit OperatorAddedToQuorums(operator, getOperatorId(operator), quorumNumbers); - } - - /** - * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ - function deregisterOperator( - address operator, - bytes memory quorumNumbers - ) public virtual onlyRegistryCoordinator { - // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); - - // Update each quorum's aggregate pubkey - _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); - emit OperatorRemovedFromQuorums(operator, getOperatorId(operator), quorumNumbers); - } - - /** - * @notice Initializes a new quorum by pushing its first apk update - * @param quorumNumber The number of the new quorum - */ - function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); - - apkHistory[quorumNumber].push(ApkUpdate({ - apkHash: bytes24(0), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); - } - - /** - * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. - * @param operator is the operator for whom the key is being registered - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership - */ - function registerBLSPublicKey( - address operator, - PubkeyRegistrationParams calldata params, - BN254.G1Point calldata pubkeyRegistrationMessageHash - ) external returns (bytes32 operatorId) { - bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); - require( - operatorToPubkeyHash[operator] == bytes32(0), - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); - - // gamma = h(sigma, P, P', H(m)) - uint256 gamma = uint256(keccak256(abi.encodePacked( - params.pubkeyRegistrationSignature.X, - params.pubkeyRegistrationSignature.Y, - params.pubkeyG1.X, - params.pubkeyG1.Y, - params.pubkeyG2.X, - params.pubkeyG2.Y, - pubkeyRegistrationMessageHash.X, - pubkeyRegistrationMessageHash.Y - ))) % BN254.FR_MODULUS; - - // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') - require(BN254.pairing( - params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), - BN254.negGeneratorG2(), - pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - params.pubkeyG2 - ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); - - operatorToPubkey[operator] = params.pubkeyG1; - operatorToPubkeyHash[operator] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = operator; - - emit NewPubkeyRegistration(operator, params.pubkeyG1, params.pubkeyG2); - return pubkeyHash; - } - - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - - function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { - BN254.G1Point memory newApk; - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - // Validate quorum exists and get history length - uint8 quorumNumber = uint8(quorumNumbers[i]); - uint256 historyLength = apkHistory[quorumNumber].length; - require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); - - // Update aggregate public key for this quorum - newApk = currentApk[quorumNumber].plus(point); - currentApk[quorumNumber] = newApk; - bytes24 newApkHash = bytes24(BN254.hashG1Point(newApk)); - - // Update apk history. If the last update was made in this block, update the entry - // Otherwise, push a new historical entry and update the prev->next pointer - ApkUpdate storage lastUpdate = apkHistory[quorumNumber][historyLength - 1]; - if (lastUpdate.updateBlockNumber == uint32(block.number)) { - lastUpdate.apkHash = newApkHash; - } else { - lastUpdate.nextUpdateBlockNumber = uint32(block.number); - apkHistory[quorumNumber].push(ApkUpdate({ - apkHash: newApkHash, - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); - } - } - } - - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey - */ - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - - /** - * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` - * @dev Returns the current indices if `blockNumber >= block.number` - */ - function getApkIndicesAtBlockNumber( - bytes calldata quorumNumbers, - uint256 blockNumber - ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](quorumNumbers.length); - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; - if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { - revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); - } - - // Loop backward through apkHistory until we find an entry that preceeds `blockNumber` - for (uint256 j = quorumApkUpdatesLength; j > 0; j--) { - if (apkHistory[quorumNumber][j - 1].updateBlockNumber <= blockNumber) { - indices[i] = uint32(j - 1); - break; - } - } - } - return indices; - } - - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) { - return currentApk[quorumNumber]; - } - - /// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber` - function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { - return apkHistory[quorumNumber][index]; - } - - /** - * @notice get hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; - * called by checkSignatures in BLSSignatureChecker.sol. - * @param quorumNumber is the quorum whose ApkHash is being retrieved - * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved - * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage - */ - function getApkHashAtBlockNumberAndIndex( - uint8 quorumNumber, - uint32 blockNumber, - uint256 index - ) external view returns (bytes24) { - ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; - - /** - * Validate that the update is valid for the given blockNumber: - * - blockNumber should be >= the update block number - * - the next update block number should be either 0 or strictly greater than blockNumber - */ - require( - blockNumber >= quorumApkUpdate.updateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" - ); - require( - quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" - ); - - return quorumApkUpdate.apkHash; - } - - /// @notice Returns the length of ApkUpdates for the provided `quorumNumber` - function getApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { - return uint32(apkHistory[quorumNumber].length); - } - - /// @notice Returns the operator address for the given `pubkeyHash` - function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { - return pubkeyHashToOperator[pubkeyHash]; - } - - /// @notice returns the ID used to identify the `operator` within this AVS - /// @dev Returns zero in the event that the `operator` has never registered for the AVS - function getOperatorId(address operator) public view returns (bytes32) { - return operatorToPubkeyHash[operator]; - } -} diff --git a/services/operatorsinfo/operatorsinfo_subgraph.go b/services/operatorsinfo/operatorsinfo_subgraph.go index 08ee8b61..4a6346a0 100644 --- a/services/operatorsinfo/operatorsinfo_subgraph.go +++ b/services/operatorsinfo/operatorsinfo_subgraph.go @@ -88,8 +88,6 @@ func (ops *OperatorsInfoServiceSubgraph) getIndexedOperatorInfoByOperatorId(ctx "id": graphql.String(fmt.Sprintf("0x%s", hex.EncodeToString(operator[:]))), } ) - fmt.Print("NAME OF SUBRAPH", ops.name) - fmt.Print("NAME OF SUBRAPH: ", ops.client) err := ops.client.Query(ctx, &query, variables) if err != nil { ops.logger.Error("Error requesting info for operator", "err", err, "operator", hex.EncodeToString(operator[:])) diff --git a/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go b/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go deleted file mode 100644 index 98b3f1db..00000000 --- a/services/operatorsinfo/operatorsinfo_subgraph_integration_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package operatorsinfo - -import ( - "bytes" - "fmt" - "log" - "os" - "os/exec" - "testing" -) - -func TestStartAnvilChain(t *testing.T) { - pid, err := startAnvilChain() - if err != nil { - t.Fatal(err) - } - fmt.Println("Anvil chain started with PID", pid) - - curr_path, err := os.Getwd() - changeDirectory(curr_path + "integration_test_deployment/lib/eigenlayer-middleware-offchain/subgraphs/BLSApkRegistry") - - _, err = startGraph() - if err != nil { - t.Fatal(err) - } - _, err = startSubgraph() - if err != nil { - t.Fatal(err) - } - - // err = stopAnvilChain(pid) - // if err != nil { - // t.Fatal(err) - // } -} - -func startAnvilChain() (int, error) { - return execCmd("anvil", []string{"--host", "0.0.0.0"}, []string{}) -} - -func startGraph() (int, error) { - return execCmd("docker", []string{"compose", "up"}, []string{}) -} - -func startSubgraph() (int, error) { - _, err := execCmd("graph", []string{"codegen"}, []string{}) - if err != nil { - return 0, err - } - _, err = execCmd("graph", []string{"build"}, []string{}) - if err != nil { - return 0, err - } - _, err = execCmd("npm", []string{"run", "create-local"}, []string{}) - if err != nil { - return 0, err - } - return execCmd("npm", []string{"run", "deploy-local"}, []string{}) -} - -func stopAnvilChain(pid int) error { - _, err := execCmd("kill", []string{"-9", fmt.Sprintf("%d", pid)}, []string{}) - return err -} - -func execCmd(name string, args []string, envVars []string) (int, error) { - cmd := exec.Command(name, args...) - if len(envVars) > 0 { - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, envVars...) - } - var out bytes.Buffer - var stderr bytes.Buffer - // TODO: When these are uncommented, the deployer sometimes fails to start anvil - // cmd.Stdout = &out - // cmd.Stderr = &stderr - - fmt.Print("Running command: ", cmd.String()) - err := cmd.Run() - fmt.Print("Running command: ", cmd.Process.Pid) - if err != nil { - return 0, fmt.Errorf("%s: %s", err.Error(), stderr.String()) - } - fmt.Print(out.String()) - pid := cmd.Process.Pid - return pid, nil -} - -func changeDirectory(path string) { - err := os.Chdir(path) - if err != nil { - log.Panicf("Failed to change directories. Error: %s", err) - } - - newDir, err := os.Getwd() - if err != nil { - log.Panicf("Failed to get working directory. Error: %s", err) - } - log.Printf("Current Working Directory: %s\n", newDir) -} diff --git a/services/operatorsinfo/operatorsinfo_subgraph_test.go b/services/operatorsinfo/operatorsinfo_subgraph_test.go index b64a3fc6..40595f1c 100644 --- a/services/operatorsinfo/operatorsinfo_subgraph_test.go +++ b/services/operatorsinfo/operatorsinfo_subgraph_test.go @@ -20,7 +20,10 @@ func (m mockGraphQLQuerier) Query(ctx context.Context, q any, variables map[stri } func TestIndexedChainState_GetIndexedOperatorState(t *testing.T) { - logger := logging.NewNoopLogger() + logger, err := logging.NewZapLogger(logging.Development) + if err != nil { + t.Fatal(err) + } operatorAddress := common.Address{0x1} @@ -54,7 +57,7 @@ func TestIndexedChainState_GetIndexedOperatorState(t *testing.T) { } cs := NewOperatorsInfoServiceSubgraph(context.Background(), querier, logger) - operatorPubkeys, err := cs.GetOperatorInfo(context.Background(), operatorAddress) - assert.True(t, err) + operatorPubkeys, success := cs.GetOperatorInfo(context.Background(), operatorAddress) + assert.True(t, success) assert.Equal(t, operatorPubkeys.Socket, types.Socket("localhost:32006;32007")) } From 5667b64521b0cf3c37d6a53f7e2d41c53071aeb3 Mon Sep 17 00:00:00 2001 From: SiddyJ Date: Wed, 29 May 2024 06:54:56 -0700 Subject: [PATCH 11/11] cleaned gir modules --- .gitmodules | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.gitmodules b/.gitmodules index edffa5bf..ef878cad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,3 @@ [submodule "contracts/lib/eigenlayer-middleware"] path = contracts/lib/eigenlayer-middleware url = git@github.com:Layr-Labs/eigenlayer-middleware.git -[submodule "services/operatorsinfo/integration_test_deployment/lib/forge-std"] - path = services/operatorsinfo/integration_test_deployment/lib/forge-std - url = https://github.com/foundry-rs/forge-std -[submodule "services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware"] - path = services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware - url = https://github.com/Layr-Labs/eigenlayer-middleware -[submodule "services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain"] - path = services/operatorsinfo/integration_test_deployment/lib/eigenlayer-middleware-offchain - url = https://github.com/Layr-Labs/eigenlayer-middleware-offchain