From 743e9fb83c01e50c646c4953170eb610c925fbf4 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Fri, 13 Dec 2024 21:27:15 +0100 Subject: [PATCH] fix tests and rebase --- Makefile | 1 - api/server.go | 2 + bootstrap/bootstrap.go | 20 ++-- bootstrap/utils.go | 40 ------- cmd/run/cmd.go | 1 - models/mocks/Engine.go | 91 ---------------- services/ingestion/engine_test.go | 9 +- services/requester/key_store_component.go | 125 ++++++++++++++++++++++ services/signer/signer.go | 92 ---------------- tests/integration_test.go | 10 +- 10 files changed, 146 insertions(+), 245 deletions(-) delete mode 100644 bootstrap/utils.go delete mode 100644 models/mocks/Engine.go create mode 100644 services/requester/key_store_component.go delete mode 100644 services/signer/signer.go diff --git a/Makefile b/Makefile index ece681220..c46985cf1 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,6 @@ generate: mockery --dir=storage --name=TransactionIndexer --output=storage/mocks mockery --dir=storage --name=TraceIndexer --output=storage/mocks mockery --all --dir=services/ingestion --output=services/ingestion/mocks - mockery --dir=models --name=Engine --output=models/mocks .PHONY: ci ci: check-tidy test e2e-test diff --git a/api/server.go b/api/server.go index f964da028..be303c923 100644 --- a/api/server.go +++ b/api/server.go @@ -18,6 +18,8 @@ import ( "strings" "time" + "github.com/onflow/go-ethereum/core" + "github.com/onflow/flow-go/module/component" "github.com/onflow/flow-go/module/irrecoverable" diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index c0f4c5aa6..aa72d087e 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -18,7 +18,6 @@ import ( "github.com/hashicorp/go-multierror" "github.com/onflow/flow-evm-gateway/api" "github.com/onflow/flow-evm-gateway/config" - "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go/cmd" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm" @@ -34,7 +33,6 @@ import ( errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/ingestion" "github.com/onflow/flow-evm-gateway/services/replayer" - "github.com/onflow/flow-evm-gateway/services/signer" pebble2 "github.com/onflow/flow-evm-gateway/storage/pebble" ) @@ -92,8 +90,8 @@ type EVMGatewayNodeBuilder struct { Client *requester.CrossSporkClient Storages *Storages // Signer is used for signing flow transactions - Signer crypto.Signer Publishers *Publishers + Keystore *requester.KeyStoreComponent } func (fnb *EVMGatewayNodeBuilder) Build() (cmd.Node, error) { @@ -209,7 +207,7 @@ func (fnb *EVMGatewayNodeBuilder) Initialize() error { func (fnb *EVMGatewayNodeBuilder) LoadComponentsAndModules() { fnb.initPublishers() - fnb.Component("Transaction Signer", fnb.initSigner) + fnb.Component("Key Store", fnb.initKeyStore) fnb.Component("API Server", fnb.apiServerComponent) fnb.Component("Event Ingestion Engine", fnb.eventIngestionEngineComponent) fnb.Component("Metrics Server", fnb.metricsServerComponent) @@ -238,6 +236,7 @@ func (fnb *EVMGatewayNodeBuilder) apiServerComponent(cfg config.Config) (module. fnb.Client, fnb.Publishers.Transaction, log, + cfg, ) blocksProvider := replayer.NewBlocksProvider( @@ -251,11 +250,11 @@ func (fnb *EVMGatewayNodeBuilder) apiServerComponent(cfg config.Config) (module. blocksProvider, fnb.Client, cfg, - fnb.Signer, log, fnb.Storages.Blocks, txPool, fnb.Metrics, + fnb.Keystore.KeyStore, ) if err != nil { return nil, fmt.Errorf("failed to create EVM requester: %w", err) @@ -393,6 +392,7 @@ func (fnb *EVMGatewayNodeBuilder) eventIngestionEngineComponent(cfg config.Confi fnb.Logger, fnb.Client, chainID, + fnb.Keystore, latestCadenceHeight, ) @@ -542,10 +542,12 @@ func (fnb *EVMGatewayNodeBuilder) initStorage() error { return nil } -func (fnb *EVMGatewayNodeBuilder) initSigner(config config.Config) (module.ReadyDoneAware, error) { - sig := signer.NewSigner(fnb.Logger, config) - fnb.Signer = sig - return sig, nil +func (fnb *EVMGatewayNodeBuilder) initKeyStore(cfg config.Config) (module.ReadyDoneAware, error) { + keystore := requester.NewKeyStoreComponent(fnb.Logger, cfg, fnb.Client) + + fnb.Keystore = keystore + + return keystore, nil } func (fnb *EVMGatewayNodeBuilder) initPublishers() { diff --git a/bootstrap/utils.go b/bootstrap/utils.go deleted file mode 100644 index 0c7d741c5..000000000 --- a/bootstrap/utils.go +++ /dev/null @@ -1,40 +0,0 @@ -package bootstrap - -import ( - "context" - "fmt" - - "github.com/onflow/flow-evm-gateway/config" - "github.com/onflow/flow-evm-gateway/services/requester" - "github.com/onflow/flow-go-sdk/crypto" - "github.com/rs/zerolog" -) - -// createSigner creates the signer based on either a single coa key being -// provided and using a simple in-memory signer, or a Cloud KMS key being -// provided and using a Cloud KMS signer. -func createSigner( - ctx context.Context, - config config.Config, - logger zerolog.Logger, -) (crypto.Signer, error) { - var signer crypto.Signer - var err error - switch { - case config.COAKey != nil: - signer, err = crypto.NewInMemorySigner(config.COAKey, crypto.SHA3_256) - case config.COACloudKMSKey != nil: - signer, err = requester.NewKMSKeySigner( - ctx, - *config.COACloudKMSKey, - logger, - ) - default: - return nil, fmt.Errorf("must provide either single COA / Cloud KMS key") - } - if err != nil { - return nil, fmt.Errorf("failed to create a COA signer: %w", err) - } - - return signer, nil -} diff --git a/cmd/run/cmd.go b/cmd/run/cmd.go index 8365a049d..27e392f6d 100644 --- a/cmd/run/cmd.go +++ b/cmd/run/cmd.go @@ -1,7 +1,6 @@ package run import ( - "encoding/json" "fmt" "math/big" "os" diff --git a/models/mocks/Engine.go b/models/mocks/Engine.go deleted file mode 100644 index 5373c9a69..000000000 --- a/models/mocks/Engine.go +++ /dev/null @@ -1,91 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// Engine is an autogenerated mock type for the Engine type -type Engine struct { - mock.Mock -} - -// Done provides a mock function with given fields: -func (_m *Engine) Done() <-chan struct{} { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Done") - } - - var r0 <-chan struct{} - if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan struct{}) - } - } - - return r0 -} - -// Ready provides a mock function with given fields: -func (_m *Engine) Ready() <-chan struct{} { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Ready") - } - - var r0 <-chan struct{} - if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan struct{}) - } - } - - return r0 -} - -// Run provides a mock function with given fields: ctx -func (_m *Engine) Run(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Run") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Stop provides a mock function with given fields: -func (_m *Engine) Stop() { - _m.Called() -} - -// NewEngine creates a new instance of Engine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewEngine(t interface { - mock.TestingT - Cleanup(func()) -}) *Engine { - mock := &Engine{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/services/ingestion/engine_test.go b/services/ingestion/engine_test.go index 27987697a..950f52262 100644 --- a/services/ingestion/engine_test.go +++ b/services/ingestion/engine_test.go @@ -69,6 +69,11 @@ func TestSerialBlockIngestion(t *testing.T) { return eventsChan }) + blocksPublisher := models.NewPublisher[*models.Block](zerolog.Nop()) + blocksPublisher.Start(ictx) + logsPublisher := models.NewPublisher[[]*gethTypes.Log](zerolog.Nop()) + logsPublisher.Start(ictx) + engine := NewEventIngestionEngine( subscriber, replayer.NewBlocksProvider(blocks, flowGo.Emulator, nil), @@ -78,8 +83,8 @@ func TestSerialBlockIngestion(t *testing.T) { receipts, transactions, traces, - models.NewPublisher[*models.Block](zerolog.Nop()), - models.NewPublisher[[]*gethTypes.Log](zerolog.Nop()), + blocksPublisher, + logsPublisher, zerolog.Nop(), metrics.NopCollector, defaultReplayerConfig(), diff --git a/services/requester/key_store_component.go b/services/requester/key_store_component.go new file mode 100644 index 000000000..8324c827a --- /dev/null +++ b/services/requester/key_store_component.go @@ -0,0 +1,125 @@ +package requester + +import ( + "context" + "fmt" + + "github.com/onflow/flow-evm-gateway/config" + "github.com/onflow/flow-go-sdk/access" + "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/rs/zerolog" +) + +type KeyStoreComponent struct { + *KeyStore + + log zerolog.Logger + config config.Config + client access.Client + startupCompleted chan struct{} +} + +var _ component.Component = (*KeyStoreComponent)(nil) + +func NewKeyStoreComponent(log zerolog.Logger, config config.Config, client access.Client) *KeyStoreComponent { + ks := &KeyStoreComponent{ + log: log, + config: config, + client: client, + startupCompleted: make(chan struct{}), + } + + return ks +} + +func (k *KeyStoreComponent) Start(ctx irrecoverable.SignalerContext) { + defer close(k.startupCompleted) + + k.log.Info().Msg("starting key store component") + + accountKeys := make([]*AccountKey, 0) + account, err := k.client.GetAccount(ctx, k.config.COAAddress) + if err != nil { + ctx.Throw(fmt.Errorf( + "failed to get signer info account for address: %s, with: %w", + k.config.COAAddress, + err, + )) + + return + } + signer, err := createSigner(ctx, k.config, k.log) + + if err != nil { + ctx.Throw(err) + + return + } + for _, key := range account.Keys { + accountKeys = append(accountKeys, &AccountKey{ + AccountKey: *key, + Address: k.config.COAAddress, + Signer: signer, + }) + } + + k.KeyStore = NewKeyStore(accountKeys) + +} + +func (k *KeyStoreComponent) Ready() <-chan struct{} { + ready := make(chan struct{}) + + go func() { + <-k.startupCompleted + close(ready) + }() + + return ready +} + +func (k *KeyStoreComponent) Done() <-chan struct{} { + done := make(chan struct{}) + + go func() { + <-k.startupCompleted + + // This is where we would close the KMS client connection, + // but it currently does not have a close method + + close(done) + }() + + return done +} + +// createSigner creates the signer based on either a single coa key being +// provided and using a simple in-memory signer, or a Cloud KMS key being +// provided and using a Cloud KMS signer. +func createSigner( + ctx context.Context, + config config.Config, + logger zerolog.Logger, +) (crypto.Signer, error) { + var signer crypto.Signer + var err error + switch { + case config.COAKey != nil: + signer, err = crypto.NewInMemorySigner(config.COAKey, crypto.SHA3_256) + case config.COACloudKMSKey != nil: + signer, err = NewKMSKeySigner( + ctx, + *config.COACloudKMSKey, + logger, + ) + default: + return nil, fmt.Errorf("must provide either single COA / Cloud KMS key") + } + if err != nil { + return nil, fmt.Errorf("failed to create a COA signer: %w", err) + } + + return signer, nil +} diff --git a/services/signer/signer.go b/services/signer/signer.go deleted file mode 100644 index 8bcbefd87..000000000 --- a/services/signer/signer.go +++ /dev/null @@ -1,92 +0,0 @@ -package signer - -import ( - "fmt" - - "github.com/onflow/flow-evm-gateway/config" - "github.com/onflow/flow-evm-gateway/services/requester" - "github.com/onflow/flow-go-sdk/crypto" - "github.com/onflow/flow-go/module/component" - "github.com/onflow/flow-go/module/irrecoverable" - "github.com/rs/zerolog" -) - -type Signer struct { - crypto.Signer - - log zerolog.Logger - config config.Config - - startupCompleted chan struct{} - closeKMSClient func() -} - -var _ component.Component = (*Signer)(nil) -var _ crypto.Signer = (*Signer)(nil) - -func NewSigner(log zerolog.Logger, config config.Config) *Signer { - return &Signer{ - log: log, - config: config, - startupCompleted: make(chan struct{}), - } -} - -func (s *Signer) Start(ctx irrecoverable.SignalerContext) { - cfg := s.config - defer close(s.startupCompleted) - - var err error - switch { - case cfg.COAKey != nil: - s.Signer, err = crypto.NewInMemorySigner(cfg.COAKey, crypto.SHA3_256) - case cfg.COAKeys != nil: - s.Signer, err = requester.NewKeyRotationSigner(cfg.COAKeys, crypto.SHA3_256) - case len(cfg.COACloudKMSKeys) > 0: - var signer *requester.KMSKeyRotationSigner - signer, err = requester.NewKMSKeyRotationSigner( - ctx, - cfg.COACloudKMSKeys, - s.log, - ) - s.closeKMSClient = func() { - // TODO(JanezP): this should definitely be a closer. Open a PR in the sdk - // signer.Close() - } - s.Signer = signer - default: - ctx.Throw(fmt.Errorf("must provide either single COA / keylist of COA keys / COA cloud KMS keys")) - return - } - if err != nil { - ctx.Throw(fmt.Errorf("failed to create a COA signer: %w", err)) - return - } -} - -func (s *Signer) Ready() <-chan struct{} { - ready := make(chan struct{}) - - go func() { - <-s.startupCompleted - close(ready) - }() - - return ready -} - -func (s *Signer) Done() <-chan struct{} { - done := make(chan struct{}) - - go func() { - <-s.startupCompleted - - if s.closeKMSClient != nil { - s.closeKMSClient() - } - - close(done) - }() - - return done -} diff --git a/tests/integration_test.go b/tests/integration_test.go index 396021e62..4ef66dd2c 100644 --- a/tests/integration_test.go +++ b/tests/integration_test.go @@ -184,15 +184,7 @@ func Test_ConcurrentTransactionSubmissionWithLocalIndex(t *testing.T) { url: fmt.Sprintf("%s:%d", cfg.RPCHost, cfg.RPCPort), } - ready := make(chan struct{}) - go func() { - err = bootstrap.Run(ctx, cfg, func() { - close(ready) - }) - require.NoError(t, err) - }() - - <-ready + startGateway(t, ctx, cfg) time.Sleep(3 * time.Second) // some time to startup