From a00d9432088a7cb0efe5cde1763a648925fe4e93 Mon Sep 17 00:00:00 2001 From: Graham Goh Date: Tue, 15 Oct 2024 12:19:06 +0900 Subject: [PATCH 1/2] feat(cosmos): introduce cosmosKeys graphql query Similar to eTHKeys and solanaKeys, we need cosmosKeys that will be used by the operator ui to show a list of addresses. JIRA: https://smartcontract-it.atlassian.net/browse/DPA-1157 --- core/web/resolver/cosmos_key.go | 43 +++++++++++++++ core/web/resolver/cosmos_key_test.go | 73 +++++++++++++++++++++++++ core/web/schema/type/cosmos_key.graphql | 7 +++ 3 files changed, 123 insertions(+) create mode 100644 core/web/resolver/cosmos_key.go create mode 100644 core/web/resolver/cosmos_key_test.go create mode 100644 core/web/schema/type/cosmos_key.graphql diff --git a/core/web/resolver/cosmos_key.go b/core/web/resolver/cosmos_key.go new file mode 100644 index 00000000000..42a0464b97b --- /dev/null +++ b/core/web/resolver/cosmos_key.go @@ -0,0 +1,43 @@ +package resolver + +import ( + "github.com/graph-gophers/graphql-go" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" +) + +type CosmosKeyResolver struct { + key cosmoskey.Key +} + +func NewCosmosKey(key cosmoskey.Key) *CosmosKeyResolver { + return &CosmosKeyResolver{key: key} +} + +func NewCosmosKeys(keys []cosmoskey.Key) []*CosmosKeyResolver { + var resolvers []*CosmosKeyResolver + + for _, k := range keys { + resolvers = append(resolvers, NewCosmosKey(k)) + } + + return resolvers +} + +func (r *CosmosKeyResolver) ID() graphql.ID { + return graphql.ID(r.key.PublicKeyStr()) +} + +// -- GetCosmosKeys Query -- + +type CosmosKeysPayloadResolver struct { + keys []cosmoskey.Key +} + +func NewCosmosKeysPayload(keys []cosmoskey.Key) *CosmosKeysPayloadResolver { + return &CosmosKeysPayloadResolver{keys: keys} +} + +func (r *CosmosKeysPayloadResolver) Results() []*CosmosKeyResolver { + return NewCosmosKeys(r.keys) +} diff --git a/core/web/resolver/cosmos_key_test.go b/core/web/resolver/cosmos_key_test.go new file mode 100644 index 00000000000..1fe1140680b --- /dev/null +++ b/core/web/resolver/cosmos_key_test.go @@ -0,0 +1,73 @@ +package resolver + +import ( + "context" + "errors" + "fmt" + "testing" + + gqlerrors "github.com/graph-gophers/graphql-go/errors" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" +) + +func TestResolver_CosmosKeys(t *testing.T) { + t.Parallel() + + query := ` + query GetCosmosKeys { + cosmosKeys { + results { + id + } + } + }` + k := cosmoskey.MustNewInsecure(keystest.NewRandReaderFromSeed(1)) + result := fmt.Sprintf(` + { + "cosmosKeys": { + "results": [ + { + "id": "%s" + } + ] + } + }`, k.PublicKeyStr()) + gError := errors.New("error") + + testCases := []GQLTestCase{ + unauthorizedTestCase(GQLTestCase{query: query}, "cosmosKeys"), + { + name: "success", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.cosmos.On("GetAll").Return([]cosmoskey.Key{k}, nil) + f.Mocks.keystore.On("Cosmos").Return(f.Mocks.cosmos) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: result, + }, + { + name: "no keys returned by GetAll", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.cosmos.On("GetAll").Return([]cosmoskey.Key{}, gError) + f.Mocks.keystore.On("Cosmos").Return(f.Mocks.cosmos) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: `null`, + errors: []*gqlerrors.QueryError{ + { + Extensions: nil, + ResolverError: gError, + Path: []interface{}{"cosmosKeys"}, + Message: gError.Error(), + }, + }, + }, + } + + RunGQLTests(t, testCases) +} diff --git a/core/web/schema/type/cosmos_key.graphql b/core/web/schema/type/cosmos_key.graphql new file mode 100644 index 00000000000..47de4ff9c41 --- /dev/null +++ b/core/web/schema/type/cosmos_key.graphql @@ -0,0 +1,7 @@ +type CosmosKey { + id: ID! +} + +type CosmosKeysPayload { + results: [CosmosKey!]! +} From 5bd2917da501a57eb03a0c3d73e88441748c773a Mon Sep 17 00:00:00 2001 From: Graham Goh Date: Tue, 15 Oct 2024 12:22:22 +0900 Subject: [PATCH 2/2] feat(starknet): introduce starknetKeys graphql query Similar to eTHKeys and solanaKeys, we need starknetKeys that will be used by the operator ui to show a list of addresses. JIRA: https://smartcontract-it.atlassian.net/browse/DPA-1157 --- .changeset/stale-pugs-sin.md | 5 ++ core/web/resolver/cosmos_key_test.go | 1 + core/web/resolver/query.go | 24 ++++++++ core/web/resolver/resolver_test.go | 4 ++ core/web/resolver/starknet_key.go | 43 +++++++++++++ core/web/resolver/starknet_key_test.go | 74 +++++++++++++++++++++++ core/web/schema/schema.graphql | 2 + core/web/schema/type/starknet_key.graphql | 7 +++ 8 files changed, 160 insertions(+) create mode 100644 .changeset/stale-pugs-sin.md create mode 100644 core/web/resolver/starknet_key.go create mode 100644 core/web/resolver/starknet_key_test.go create mode 100644 core/web/schema/type/starknet_key.graphql diff --git a/.changeset/stale-pugs-sin.md b/.changeset/stale-pugs-sin.md new file mode 100644 index 00000000000..58ebd2d7acd --- /dev/null +++ b/.changeset/stale-pugs-sin.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#added introduce cosmosKeys and starknetKeys graphql query diff --git a/core/web/resolver/cosmos_key_test.go b/core/web/resolver/cosmos_key_test.go index 1fe1140680b..9068e453626 100644 --- a/core/web/resolver/cosmos_key_test.go +++ b/core/web/resolver/cosmos_key_test.go @@ -7,6 +7,7 @@ import ( "testing" gqlerrors "github.com/graph-gophers/graphql-go/errors" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" ) diff --git a/core/web/resolver/query.go b/core/web/resolver/query.go index c8f20d81dc8..d3e1215834a 100644 --- a/core/web/resolver/query.go +++ b/core/web/resolver/query.go @@ -582,6 +582,30 @@ func (r *Resolver) AptosKeys(ctx context.Context) (*AptosKeysPayloadResolver, er return NewAptosKeysPayload(keys), nil } +func (r *Resolver) CosmosKeys(ctx context.Context) (*CosmosKeysPayloadResolver, error) { + if err := authenticateUser(ctx); err != nil { + return nil, err + } + keys, err := r.App.GetKeyStore().Cosmos().GetAll() + if err != nil { + return nil, err + } + + return NewCosmosKeysPayload(keys), nil +} + +func (r *Resolver) StarkNetKeys(ctx context.Context) (*StarkNetKeysPayloadResolver, error) { + if err := authenticateUser(ctx); err != nil { + return nil, err + } + keys, err := r.App.GetKeyStore().StarkNet().GetAll() + if err != nil { + return nil, err + } + + return NewStarkNetKeysPayload(keys), nil +} + func (r *Resolver) SQLLogging(ctx context.Context) (*GetSQLLoggingPayloadResolver, error) { if err := authenticateUser(ctx); err != nil { return nil, err diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index a8af6eae7ff..0d365b0891e 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -52,6 +52,8 @@ type mocks struct { vrf *keystoreMocks.VRF solana *keystoreMocks.Solana aptos *keystoreMocks.Aptos + cosmos *keystoreMocks.Cosmos + starknet *keystoreMocks.StarkNet chain *legacyEvmORMMocks.Chain legacyEVMChains *legacyEvmORMMocks.LegacyChainContainer relayerChainInterops *chainlinkMocks.FakeRelayerChainInteroperators @@ -108,6 +110,8 @@ func setupFramework(t *testing.T) *gqlTestFramework { vrf: keystoreMocks.NewVRF(t), solana: keystoreMocks.NewSolana(t), aptos: keystoreMocks.NewAptos(t), + cosmos: keystoreMocks.NewCosmos(t), + starknet: keystoreMocks.NewStarkNet(t), chain: legacyEvmORMMocks.NewChain(t), legacyEVMChains: legacyEvmORMMocks.NewLegacyChainContainer(t), relayerChainInterops: &chainlinkMocks.FakeRelayerChainInteroperators{}, diff --git a/core/web/resolver/starknet_key.go b/core/web/resolver/starknet_key.go new file mode 100644 index 00000000000..a3835e5153a --- /dev/null +++ b/core/web/resolver/starknet_key.go @@ -0,0 +1,43 @@ +package resolver + +import ( + "github.com/graph-gophers/graphql-go" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" +) + +type StarkNetKeyResolver struct { + key starkkey.Key +} + +func NewStarkNetKey(key starkkey.Key) *StarkNetKeyResolver { + return &StarkNetKeyResolver{key: key} +} + +func NewStarkNetKeys(keys []starkkey.Key) []*StarkNetKeyResolver { + var resolvers []*StarkNetKeyResolver + + for _, k := range keys { + resolvers = append(resolvers, NewStarkNetKey(k)) + } + + return resolvers +} + +func (r *StarkNetKeyResolver) ID() graphql.ID { + return graphql.ID(r.key.StarkKeyStr()) +} + +// -- GetStarkNetKeys Query -- + +type StarkNetKeysPayloadResolver struct { + keys []starkkey.Key +} + +func NewStarkNetKeysPayload(keys []starkkey.Key) *StarkNetKeysPayloadResolver { + return &StarkNetKeysPayloadResolver{keys: keys} +} + +func (r *StarkNetKeysPayloadResolver) Results() []*StarkNetKeyResolver { + return NewStarkNetKeys(r.keys) +} diff --git a/core/web/resolver/starknet_key_test.go b/core/web/resolver/starknet_key_test.go new file mode 100644 index 00000000000..dff26afb33f --- /dev/null +++ b/core/web/resolver/starknet_key_test.go @@ -0,0 +1,74 @@ +package resolver + +import ( + "context" + "errors" + "fmt" + "testing" + + gqlerrors "github.com/graph-gophers/graphql-go/errors" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" +) + +func TestResolver_StarkNetKeys(t *testing.T) { + t.Parallel() + + query := ` + query GetStarkNetKeys { + starknetKeys { + results { + id + } + } + }` + k := starkkey.MustNewInsecure(keystest.NewRandReaderFromSeed(1)) + result := fmt.Sprintf(` + { + "starknetKeys": { + "results": [ + { + "id": "%s" + } + ] + } + }`, k.StarkKeyStr()) + gError := errors.New("error") + + testCases := []GQLTestCase{ + unauthorizedTestCase(GQLTestCase{query: query}, "starknetKeys"), + { + name: "success", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.starknet.On("GetAll").Return([]starkkey.Key{k}, nil) + f.Mocks.keystore.On("StarkNet").Return(f.Mocks.starknet) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: result, + }, + { + name: "no keys returned by GetAll", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.starknet.On("GetAll").Return([]starkkey.Key{}, gError) + f.Mocks.keystore.On("StarkNet").Return(f.Mocks.starknet) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: `null`, + errors: []*gqlerrors.QueryError{ + { + Extensions: nil, + ResolverError: gError, + Path: []interface{}{"starknetKeys"}, + Message: gError.Error(), + }, + }, + }, + } + + RunGQLTests(t, testCases) +} diff --git a/core/web/schema/schema.graphql b/core/web/schema/schema.graphql index 845570c727f..415be25b77d 100644 --- a/core/web/schema/schema.graphql +++ b/core/web/schema/schema.graphql @@ -34,6 +34,8 @@ type Query { p2pKeys: P2PKeysPayload! solanaKeys: SolanaKeysPayload! aptosKeys: AptosKeysPayload! + cosmosKeys: CosmosKeysPayload! + starknetKeys: StarkNetKeysPayload! sqlLogging: GetSQLLoggingPayload! vrfKey(id: ID!): VRFKeyPayload! vrfKeys: VRFKeysPayload! diff --git a/core/web/schema/type/starknet_key.graphql b/core/web/schema/type/starknet_key.graphql new file mode 100644 index 00000000000..bf8dbb864c2 --- /dev/null +++ b/core/web/schema/type/starknet_key.graphql @@ -0,0 +1,7 @@ +type StarkNetKey { + id: ID! +} + +type StarkNetKeysPayload { + results: [StarkNetKey!]! +}