diff --git a/accounts/jobs.go b/accounts/jobs.go index 2f9b9dd0..c2452195 100644 --- a/accounts/jobs.go +++ b/accounts/jobs.go @@ -8,7 +8,7 @@ import ( const AccountCreateJobType = "account_create" -func (s *Service) executeAccountCreateJob(ctx context.Context, j *jobs.Job) error { +func (s *ServiceImpl) executeAccountCreateJob(ctx context.Context, j *jobs.Job) error { if j.Type != AccountCreateJobType { return jobs.ErrInvalidJobType } diff --git a/accounts/options.go b/accounts/options.go index 01f817f0..43435e95 100644 --- a/accounts/options.go +++ b/accounts/options.go @@ -2,10 +2,10 @@ package accounts import "go.uber.org/ratelimit" -type ServiceOption func(*Service) +type ServiceOption func(*ServiceImpl) func WithTxRatelimiter(limiter ratelimit.Limiter) ServiceOption { - return func(svc *Service) { + return func(svc *ServiceImpl) { svc.txRateLimiter = limiter } } diff --git a/accounts/service.go b/accounts/service.go index 836de859..8acf3934 100644 --- a/accounts/service.go +++ b/accounts/service.go @@ -12,7 +12,6 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/jobs" "github.com/flow-hydraulics/flow-wallet-api/keys" "github.com/onflow/flow-go-sdk" - "github.com/onflow/flow-go-sdk/client" flow_templates "github.com/onflow/flow-go-sdk/templates" log "github.com/sirupsen/logrus" "go.uber.org/ratelimit" @@ -20,13 +19,22 @@ import ( const maxGasLimit = 9999 -// Service defines the API for account management. -type Service struct { +type Service interface { + List(limit, offset int) (result []Account, err error) + Create(ctx context.Context, sync bool) (*jobs.Job, *Account, error) + AddNonCustodialAccount(address string) (*Account, error) + DeleteNonCustodialAccount(address string) error + Details(address string) (Account, error) + InitAdminAccount(ctx context.Context) error +} + +// ServiceImpl defines the API for account management. +type ServiceImpl struct { cfg *configs.Config store Store km keys.Manager - fc *client.Client - wp *jobs.WorkerPool + fc flow_helpers.FlowClient + wp jobs.WorkerPool txRateLimiter ratelimit.Limiter } @@ -35,14 +43,14 @@ func NewService( cfg *configs.Config, store Store, km keys.Manager, - fc *client.Client, - wp *jobs.WorkerPool, + fc flow_helpers.FlowClient, + wp jobs.WorkerPool, opts ...ServiceOption, -) *Service { +) Service { var defaultTxRatelimiter = ratelimit.NewUnlimited() // TODO(latenssi): safeguard against nil config? - svc := &Service{cfg, store, km, fc, wp, defaultTxRatelimiter} + svc := &ServiceImpl{cfg, store, km, fc, wp, defaultTxRatelimiter} for _, opt := range opts { opt(svc) @@ -59,7 +67,7 @@ func NewService( } // List returns all accounts in the datastore. -func (s *Service) List(limit, offset int) (result []Account, err error) { +func (s *ServiceImpl) List(limit, offset int) (result []Account, err error) { o := datastore.ParseListOptions(limit, offset) return s.store.Accounts(o) } @@ -68,7 +76,7 @@ func (s *Service) List(limit, offset int) (result []Account, err error) { // It receives a new account with a corresponding private key or resource ID // and stores both in datastore. // It returns a job, the new account and a possible error. -func (s *Service) Create(ctx context.Context, sync bool) (*jobs.Job, *Account, error) { +func (s *ServiceImpl) Create(ctx context.Context, sync bool) (*jobs.Job, *Account, error) { log.WithFields(log.Fields{"sync": sync}).Trace("Create account") if !sync { @@ -93,7 +101,7 @@ func (s *Service) Create(ctx context.Context, sync bool) (*jobs.Job, *Account, e return nil, account, nil } -func (s *Service) AddNonCustodialAccount(_ context.Context, address string) (*Account, error) { +func (s *ServiceImpl) AddNonCustodialAccount(address string) (*Account, error) { log.WithFields(log.Fields{"address": address}).Trace("Add non-custodial account") a := &Account{ @@ -109,7 +117,7 @@ func (s *Service) AddNonCustodialAccount(_ context.Context, address string) (*Ac return a, nil } -func (s *Service) DeleteNonCustodialAccount(_ context.Context, address string) error { +func (s *ServiceImpl) DeleteNonCustodialAccount(address string) error { log.WithFields(log.Fields{"address": address}).Trace("Delete non-custodial account") a, err := s.store.Account(flow_helpers.HexString(address)) @@ -130,7 +138,7 @@ func (s *Service) DeleteNonCustodialAccount(_ context.Context, address string) e } // Details returns a specific account, does not include private keys -func (s *Service) Details(address string) (Account, error) { +func (s *ServiceImpl) Details(address string) (Account, error) { log.WithFields(log.Fields{"address": address}).Trace("Account details") // Check if the input is a valid address @@ -157,7 +165,7 @@ func (s *Service) Details(address string) (Account, error) { // generated key. Admin account is used to pay for the transaction. // // Returns created account and the flow transaction ID of the account creation. -func (s *Service) createAccount(ctx context.Context) (*Account, string, error) { +func (s *ServiceImpl) createAccount(ctx context.Context) (*Account, string, error) { account := &Account{Type: AccountTypeCustodial} // Important to ratelimit all the way up here so the keys and reference blocks diff --git a/accounts/service_init.go b/accounts/service_init.go index 867a0e4c..139cac12 100644 --- a/accounts/service_init.go +++ b/accounts/service_init.go @@ -13,7 +13,7 @@ import ( log "github.com/sirupsen/logrus" ) -func (s *Service) InitAdminAccount(ctx context.Context) error { +func (s *ServiceImpl) InitAdminAccount(ctx context.Context) error { log.Debug("Initializing admin account") a, err := s.store.Account(s.cfg.AdminAddress) @@ -64,7 +64,7 @@ func (s *Service) InitAdminAccount(ctx context.Context) error { return nil } -func (s *Service) addAdminProposalKeys(ctx context.Context, count uint16) error { +func (s *ServiceImpl) addAdminProposalKeys(ctx context.Context, count uint16) error { log. WithFields(log.Fields{"count": count}). diff --git a/accounts/store_gorm.go b/accounts/store_gorm.go index 6eca38dc..fd6f7327 100644 --- a/accounts/store_gorm.go +++ b/accounts/store_gorm.go @@ -9,7 +9,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} } diff --git a/accounts/test_helpers.go b/accounts/test_helpers.go index fa0b61fa..24e2ad2f 100644 --- a/accounts/test_helpers.go +++ b/accounts/test_helpers.go @@ -9,7 +9,6 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/templates/template_strings" "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" - "github.com/onflow/flow-go-sdk/client" flow_templates "github.com/onflow/flow-go-sdk/templates" ) @@ -17,7 +16,7 @@ import ( // AddContract is used only in tests func AddContract( ctx context.Context, - fc *client.Client, + fc flow_helpers.FlowClient, km keys.Manager, accountAddress string, contract flow_templates.Contract, diff --git a/chain_events/event.go b/chain_events/event.go index 4fc609d7..31ad74d7 100644 --- a/chain_events/event.go +++ b/chain_events/event.go @@ -1,12 +1,14 @@ package chain_events import ( + "context" + "github.com/onflow/flow-go-sdk" log "github.com/sirupsen/logrus" ) type handler interface { - Handle(flow.Event) + Handle(context.Context, flow.Event) } type event struct { @@ -22,7 +24,7 @@ func (e *event) Register(handler handler) { } // Trigger sends out an event with the payload -func (e *event) Trigger(payload flow.Event) { +func (e *event) Trigger(ctx context.Context, payload flow.Event) { log. WithFields(log.Fields{"payload": payload}). Trace("Handling Flow event") @@ -32,6 +34,6 @@ func (e *event) Trigger(payload flow.Event) { } for _, handler := range e.handlers { - go handler.Handle(payload) + go handler.Handle(ctx, payload) } } diff --git a/chain_events/listener.go b/chain_events/listener.go index cb51d867..3e74936f 100644 --- a/chain_events/listener.go +++ b/chain_events/listener.go @@ -7,6 +7,7 @@ import ( "time" wallet_errors "github.com/flow-hydraulics/flow-wallet-api/errors" + "github.com/flow-hydraulics/flow-wallet-api/flow_helpers" "github.com/flow-hydraulics/flow-wallet-api/system" "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/client" @@ -16,17 +17,22 @@ import ( type GetEventTypes func() ([]string, error) -type Listener struct { +type Listener interface { + Start() Listener + Stop() +} + +type ListenerImpl struct { ticker *time.Ticker stopChan chan struct{} - fc *client.Client + fc flow_helpers.FlowClient db Store getTypes GetEventTypes maxBlocks uint64 interval time.Duration startingHeight uint64 - systemService *system.Service + systemService system.Service } type ListenerStatus struct { @@ -39,16 +45,16 @@ func (ListenerStatus) TableName() string { } func NewListener( - fc *client.Client, + fc flow_helpers.FlowClient, db Store, getTypes GetEventTypes, maxDiff uint64, interval time.Duration, startingHeight uint64, opts ...ListenerOption, -) *Listener { +) Listener { - listener := &Listener{ + listener := &ListenerImpl{ ticker: nil, stopChan: make(chan struct{}), fc: fc, @@ -68,7 +74,7 @@ func NewListener( return listener } -func (l *Listener) run(ctx context.Context, start, end uint64) error { +func (l *ListenerImpl) run(ctx context.Context, start, end uint64) error { events := make([]flow.Event, 0) eventTypes, err := l.getTypes() @@ -91,13 +97,13 @@ func (l *Listener) run(ctx context.Context, start, end uint64) error { } for _, event := range events { - Event.Trigger(event) + Event.Trigger(ctx, event) } return nil } -func (l *Listener) Start() *Listener { +func (l *ListenerImpl) Start() Listener { if l.ticker != nil { // Already started return l @@ -194,7 +200,7 @@ func (l *Listener) Start() *Listener { return l } -func (l *Listener) initHeight() error { +func (l *ListenerImpl) initHeight() error { return l.db.LockedStatus(func(status *ListenerStatus) error { if l.startingHeight > 0 && status.LatestHeight < l.startingHeight-1 { status.LatestHeight = l.startingHeight - 1 @@ -215,7 +221,7 @@ func (l *Listener) initHeight() error { }) } -func (l *Listener) Stop() { +func (l *ListenerImpl) Stop() { log.Debug("Stopping Flow event listener") close(l.stopChan) @@ -227,7 +233,7 @@ func (l *Listener) Stop() { l.ticker = nil } -func (l *Listener) systemHalted() (bool, error) { +func (l *ListenerImpl) systemHalted() (bool, error) { if l.systemService != nil { return l.systemService.IsHalted() } diff --git a/chain_events/option.go b/chain_events/option.go index 3a332a9d..02173de3 100644 --- a/chain_events/option.go +++ b/chain_events/option.go @@ -4,10 +4,10 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/system" ) -type ListenerOption func(*Listener) +type ListenerOption func(*ListenerImpl) -func WithSystemService(svc *system.Service) ListenerOption { - return func(listener *Listener) { +func WithSystemService(svc system.Service) ListenerOption { + return func(listener *ListenerImpl) { listener.systemService = svc } } diff --git a/chain_events/store_gorm.go b/chain_events/store_gorm.go index 3bf2b2e4..879620fc 100644 --- a/chain_events/store_gorm.go +++ b/chain_events/store_gorm.go @@ -8,7 +8,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} } diff --git a/flow_helpers/flow_helpers.go b/flow_helpers/flow_helpers.go index 8b43d7f7..c96b36f4 100644 --- a/flow_helpers/flow_helpers.go +++ b/flow_helpers/flow_helpers.go @@ -11,18 +11,28 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/errors" "github.com/jpillora/backoff" + "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/client" "google.golang.org/grpc" ) -type GetTransactionResultFunc func(ctx context.Context, id flow.Identifier, opts ...grpc.CallOption) (*flow.TransactionResult, error) +type FlowClient interface { + ExecuteScriptAtLatestBlock(ctx context.Context, script []byte, arguments []cadence.Value, opts ...grpc.CallOption) (cadence.Value, error) + GetAccount(ctx context.Context, address flow.Address, opts ...grpc.CallOption) (*flow.Account, error) + GetAccountAtLatestBlock(ctx context.Context, address flow.Address, opts ...grpc.CallOption) (*flow.Account, error) + GetTransaction(ctx context.Context, txID flow.Identifier, opts ...grpc.CallOption) (*flow.Transaction, error) + GetTransactionResult(ctx context.Context, txID flow.Identifier, opts ...grpc.CallOption) (*flow.TransactionResult, error) + GetLatestBlockHeader(ctx context.Context, isSealed bool, opts ...grpc.CallOption) (*flow.BlockHeader, error) + GetEventsForHeightRange(ctx context.Context, query client.EventRangeQuery, opts ...grpc.CallOption) ([]client.BlockEvents, error) + SendTransaction(ctx context.Context, tx flow.Transaction, opts ...grpc.CallOption) error +} const hexPrefix = "0x" // LatestBlockId retuns the flow.Identifier for the latest block in the chain. -func LatestBlockId(ctx context.Context, c *client.Client) (*flow.Identifier, error) { - block, err := c.GetLatestBlockHeader(ctx, true) +func LatestBlockId(ctx context.Context, flowClient FlowClient) (*flow.Identifier, error) { + block, err := flowClient.GetLatestBlockHeader(ctx, true) if err != nil { return nil, err } @@ -34,7 +44,7 @@ func LatestBlockId(ctx context.Context, c *client.Client) (*flow.Identifier, err // - the transaction gets an error status // - the transaction gets a "TransactionStatusSealed" or "TransactionStatusExpired" status // - timeout is reached -func WaitForSeal(ctx context.Context, getResult GetTransactionResultFunc, id flow.Identifier, timeout time.Duration) (*flow.TransactionResult, error) { +func WaitForSeal(ctx context.Context, flowClient FlowClient, id flow.Identifier, timeout time.Duration) (*flow.TransactionResult, error) { var ( result *flow.TransactionResult err error @@ -54,7 +64,7 @@ func WaitForSeal(ctx context.Context, getResult GetTransactionResultFunc, id flo } for { - result, err = getResult(ctx, id) + result, err = flowClient.GetTransactionResult(ctx, id) if err != nil { return nil, err } @@ -79,11 +89,11 @@ func WaitForSeal(ctx context.Context, getResult GetTransactionResultFunc, id flo } // SendAndWait sends the transaction and waits for the transaction to be sealed -func SendAndWait(ctx context.Context, c *client.Client, tx flow.Transaction, timeout time.Duration) (*flow.TransactionResult, error) { - if err := c.SendTransaction(ctx, tx); err != nil { +func SendAndWait(ctx context.Context, flowClient FlowClient, tx flow.Transaction, timeout time.Duration) (*flow.TransactionResult, error) { + if err := flowClient.SendTransaction(ctx, tx); err != nil { return nil, err } - return WaitForSeal(ctx, c.GetTransactionResult, tx.ID(), timeout) + return WaitForSeal(ctx, flowClient, tx.ID(), timeout) } func HexString(str string) string { diff --git a/flow_helpers/flow_helpers_test.go b/flow_helpers/flow_helpers_test.go index da507fdc..dade0421 100644 --- a/flow_helpers/flow_helpers_test.go +++ b/flow_helpers/flow_helpers_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" + "github.com/flow-hydraulics/flow-wallet-api/flow_helpers/internal" "github.com/onflow/flow-go-sdk" - "google.golang.org/grpc" ) func TestAddressValidationAndFormatting(t *testing.T) { @@ -43,23 +43,11 @@ func TestAddressValidationAndFormatting(t *testing.T) { func TestWaitForSeal(t *testing.T) { t.Run("backoff", func(t *testing.T) { + flowClient := new(internal.MockFlowClient) ctx := context.Background() - callCount := 0 - getResult := func(ctx context.Context, id flow.Identifier, opts ...grpc.CallOption) (*flow.TransactionResult, error) { - callCount++ - - status := flow.TransactionStatusPending - - if callCount >= 3 { - status = flow.TransactionStatusSealed - } - - return &flow.TransactionResult{Status: status}, nil - } - start := time.Now() - if _, err := WaitForSeal(ctx, getResult, flow.EmptyID, 0); err != nil { + if _, err := WaitForSeal(ctx, flowClient, flow.EmptyID, 0); err != nil { t.Fatalf("did not expect an error, got: %s", err) } diff --git a/flow_helpers/internal/flow_client.go b/flow_helpers/internal/flow_client.go new file mode 100644 index 00000000..e98af764 --- /dev/null +++ b/flow_helpers/internal/flow_client.go @@ -0,0 +1,51 @@ +package internal + +import ( + "context" + + "github.com/onflow/cadence" + "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/client" + "google.golang.org/grpc" +) + +type MockFlowClient struct { + getTransactionResultCallCount uint +} + +func (c *MockFlowClient) ExecuteScriptAtLatestBlock(ctx context.Context, script []byte, arguments []cadence.Value, opts ...grpc.CallOption) (cadence.Value, error) { + return nil, nil +} + +func (c *MockFlowClient) GetAccount(ctx context.Context, address flow.Address, opts ...grpc.CallOption) (*flow.Account, error) { + return nil, nil +} + +func (c *MockFlowClient) GetAccountAtLatestBlock(ctx context.Context, address flow.Address, opts ...grpc.CallOption) (*flow.Account, error) { + return nil, nil +} + +func (c *MockFlowClient) GetTransaction(ctx context.Context, txID flow.Identifier, opts ...grpc.CallOption) (*flow.Transaction, error) { + return nil, nil +} + +func (c *MockFlowClient) GetTransactionResult(ctx context.Context, txID flow.Identifier, opts ...grpc.CallOption) (*flow.TransactionResult, error) { + c.getTransactionResultCallCount++ + status := flow.TransactionStatusPending + if c.getTransactionResultCallCount >= 3 { + status = flow.TransactionStatusSealed + } + return &flow.TransactionResult{Status: status}, nil +} + +func (c *MockFlowClient) GetLatestBlockHeader(ctx context.Context, isSealed bool, opts ...grpc.CallOption) (*flow.BlockHeader, error) { + return nil, nil +} + +func (c *MockFlowClient) GetEventsForHeightRange(ctx context.Context, query client.EventRangeQuery, opts ...grpc.CallOption) ([]client.BlockEvents, error) { + return nil, nil +} + +func (c *MockFlowClient) SendTransaction(ctx context.Context, tx flow.Transaction, opts ...grpc.CallOption) error { + return nil +} diff --git a/handlers/accounts.go b/handlers/accounts.go index 2e9d0149..4742a318 100644 --- a/handlers/accounts.go +++ b/handlers/accounts.go @@ -10,11 +10,11 @@ import ( // It provides list, create and details APIs. // It uses an account service to interface with data. type Accounts struct { - service *accounts.Service + service accounts.Service } // NewAccounts initiates a new accounts server. -func NewAccounts(service *accounts.Service) *Accounts { +func NewAccounts(service accounts.Service) *Accounts { return &Accounts{service} } diff --git a/handlers/accounts_func.go b/handlers/accounts_func.go index f6168416..3872b738 100644 --- a/handlers/accounts_func.go +++ b/handlers/accounts_func.go @@ -92,7 +92,7 @@ func (s *Accounts) AddNonCustodialAccountFunc(rw http.ResponseWriter, r *http.Re return } - a, err := s.service.AddNonCustodialAccount(r.Context(), b.Address) + a, err := s.service.AddNonCustodialAccount(b.Address) if err != nil { handleError(rw, r, err) } @@ -103,7 +103,7 @@ func (s *Accounts) AddNonCustodialAccountFunc(rw http.ResponseWriter, r *http.Re func (s *Accounts) DeleteNonCustodialAccountFunc(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - err := s.service.DeleteNonCustodialAccount(r.Context(), vars["address"]) + err := s.service.DeleteNonCustodialAccount(vars["address"]) if err != nil { handleError(rw, r, err) return diff --git a/handlers/jobs.go b/handlers/jobs.go index 3ed3cdcd..66c52582 100644 --- a/handlers/jobs.go +++ b/handlers/jobs.go @@ -10,11 +10,11 @@ import ( // It provides details API. // It uses jobs service to interface with data. type Jobs struct { - service *jobs.Service + service jobs.Service } // NewJobs initiates a new jobs server. -func NewJobs(service *jobs.Service) *Jobs { +func NewJobs(service jobs.Service) *Jobs { return &Jobs{service} } diff --git a/handlers/system.go b/handlers/system.go index bd0a857b..d1425fa7 100644 --- a/handlers/system.go +++ b/handlers/system.go @@ -11,10 +11,10 @@ import ( // System is a HTTP server for system settings management. type System struct { - service *system.Service + service system.Service } -func NewSystem(service *system.Service) *System { +func NewSystem(service system.Service) *System { return &System{service} } diff --git a/handlers/templates.go b/handlers/templates.go index baf23af6..20e7d7bf 100644 --- a/handlers/templates.go +++ b/handlers/templates.go @@ -8,10 +8,10 @@ import ( // Templates is a HTTP server for template management. type Templates struct { - service *templates.Service + service templates.Service } -func NewTemplates(service *templates.Service) *Templates { +func NewTemplates(service templates.Service) *Templates { return &Templates{service} } diff --git a/handlers/tokens.go b/handlers/tokens.go index ca5a9d5b..df2830fc 100644 --- a/handlers/tokens.go +++ b/handlers/tokens.go @@ -8,10 +8,10 @@ import ( ) type Tokens struct { - service *tokens.Service + service tokens.Service } -func NewTokens(service *tokens.Service) *Tokens { +func NewTokens(service tokens.Service) *Tokens { return &Tokens{service} } diff --git a/handlers/transactions.go b/handlers/transactions.go index e7c51973..e4072d80 100644 --- a/handlers/transactions.go +++ b/handlers/transactions.go @@ -7,11 +7,11 @@ import ( ) type Transactions struct { - service *transactions.Service + service transactions.Service } // NewTransactions initiates a new transactions server. -func NewTransactions(service *transactions.Service) *Transactions { +func NewTransactions(service transactions.Service) *Transactions { return &Transactions{service} } diff --git a/jobs/jobs_test.go b/jobs/jobs_test.go index 4d89abb9..96a34d93 100644 --- a/jobs/jobs_test.go +++ b/jobs/jobs_test.go @@ -36,7 +36,7 @@ func TestScheduleSendNotification(t *testing.T) { logger, hook := test.NewNullLogger() ctx, cancel := context.WithCancel(context.Background()) - wp := WorkerPool{ + wp := WorkerPoolImpl{ context: ctx, cancelContext: cancel, executors: make(map[string]ExecutorFunc), @@ -105,7 +105,7 @@ func TestExecuteSendNotification(t *testing.T) { logger, hook := test.NewNullLogger() ctx, cancel := context.WithCancel(context.Background()) - wp := WorkerPool{ + wp := WorkerPoolImpl{ context: ctx, cancelContext: cancel, executors: make(map[string]ExecutorFunc), @@ -161,7 +161,7 @@ func TestExecuteSendNotification(t *testing.T) { logger, hook := test.NewNullLogger() ctx, cancel := context.WithCancel(context.Background()) - wp := WorkerPool{ + wp := WorkerPoolImpl{ context: ctx, cancelContext: cancel, executors: make(map[string]ExecutorFunc), @@ -208,7 +208,7 @@ func TestExecuteSendNotification(t *testing.T) { logger, _ := test.NewNullLogger() ctx, cancel := context.WithCancel(context.Background()) - wp := WorkerPool{ + wp := WorkerPoolImpl{ context: ctx, cancelContext: cancel, executors: make(map[string]ExecutorFunc), @@ -250,7 +250,7 @@ func TestExecuteSendNotification(t *testing.T) { logger, hook := test.NewNullLogger() ctx, cancel := context.WithCancel(context.Background()) - wp := WorkerPool{ + wp := WorkerPoolImpl{ context: ctx, cancelContext: cancel, executors: make(map[string]ExecutorFunc), @@ -314,7 +314,7 @@ func TestJobErrorMessages(t *testing.T) { defer svr.Close() ctx, cancel := context.WithCancel(context.Background()) - wp := WorkerPool{ + wp := WorkerPoolImpl{ context: ctx, cancelContext: cancel, executors: make(map[string]ExecutorFunc), diff --git a/jobs/options.go b/jobs/options.go index 59d10955..b609d52d 100644 --- a/jobs/options.go +++ b/jobs/options.go @@ -9,11 +9,11 @@ import ( "gorm.io/datatypes" ) -type WorkerPoolOption func(*WorkerPool) +type WorkerPoolOption func(*WorkerPoolImpl) type JobOption func(*Job) func WithJobStatusWebhook(u string, timeout time.Duration) WorkerPoolOption { - return func(wp *WorkerPool) { + return func(wp *WorkerPoolImpl) { if u == "" { return } @@ -32,38 +32,38 @@ func WithJobStatusWebhook(u string, timeout time.Duration) WorkerPoolOption { } } -func WithSystemService(svc *system.Service) WorkerPoolOption { - return func(wp *WorkerPool) { +func WithSystemService(svc system.Service) WorkerPoolOption { + return func(wp *WorkerPoolImpl) { wp.systemService = svc } } func WithLogger(logger *log.Logger) WorkerPoolOption { - return func(wp *WorkerPool) { + return func(wp *WorkerPoolImpl) { wp.logger = logger } } func WithMaxJobErrorCount(count int) WorkerPoolOption { - return func(wp *WorkerPool) { + return func(wp *WorkerPoolImpl) { wp.maxJobErrorCount = count } } func WithDbJobPollInterval(d time.Duration) WorkerPoolOption { - return func(wp *WorkerPool) { + return func(wp *WorkerPoolImpl) { wp.dbJobPollInterval = d } } func WithAcceptedGracePeriod(d time.Duration) WorkerPoolOption { - return func(wp *WorkerPool) { + return func(wp *WorkerPoolImpl) { wp.acceptedGracePeriod = d } } func WithReSchedulableGracePeriod(d time.Duration) WorkerPoolOption { - return func(wp *WorkerPool) { + return func(wp *WorkerPoolImpl) { wp.reSchedulableGracePeriod = d } } diff --git a/jobs/service.go b/jobs/service.go index b67e8c76..094be5c2 100644 --- a/jobs/service.go +++ b/jobs/service.go @@ -10,18 +10,23 @@ import ( log "github.com/sirupsen/logrus" ) -// Service defines the API for job HTTP handlers. -type Service struct { +type Service interface { + List(limit, offset int) (*[]Job, error) + Details(jobID string) (*Job, error) +} + +// ServiceImpl defines the API for job HTTP handlers. +type ServiceImpl struct { store Store } // NewService initiates a new job service. -func NewService(store Store) *Service { - return &Service{store} +func NewService(store Store) Service { + return &ServiceImpl{store} } // List returns all jobs in the datastore. -func (s *Service) List(limit, offset int) (*[]Job, error) { +func (s *ServiceImpl) List(limit, offset int) (*[]Job, error) { log.WithFields(log.Fields{"limit": limit, "offset": offset}).Trace("List jobs") o := datastore.ParseListOptions(limit, offset) @@ -35,7 +40,7 @@ func (s *Service) List(limit, offset int) (*[]Job, error) { } // Details returns a specific job. -func (s *Service) Details(jobID string) (*Job, error) { +func (s *ServiceImpl) Details(jobID string) (*Job, error) { log.WithFields(log.Fields{"jobID": jobID}).Trace("Job details") id, err := uuid.Parse(jobID) diff --git a/jobs/store_gorm.go b/jobs/store_gorm.go index 4a74ff75..b32ba3de 100644 --- a/jobs/store_gorm.go +++ b/jobs/store_gorm.go @@ -12,7 +12,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} } diff --git a/jobs/workerpool.go b/jobs/workerpool.go index dec66ad6..84145b91 100644 --- a/jobs/workerpool.go +++ b/jobs/workerpool.go @@ -40,7 +40,18 @@ var ( type ExecutorFunc func(ctx context.Context, j *Job) error -type WorkerPool struct { +type WorkerPool interface { + RegisterExecutor(jobType string, executorF ExecutorFunc) + CreateJob(jobType, txID string, opts ...JobOption) (*Job, error) + Schedule(j *Job) error + Status() (WorkerPoolStatus, error) + Start() + Stop(wait bool) + Capacity() uint + QueueSize() uint +} + +type WorkerPoolImpl struct { started bool wg *sync.WaitGroup jobChan chan *Job @@ -60,7 +71,7 @@ type WorkerPool struct { reSchedulableGracePeriod time.Duration notificationConfig *NotificationConfig - systemService *system.Service + systemService system.Service } type WorkerPoolStatus struct { @@ -69,10 +80,10 @@ type WorkerPoolStatus struct { WorkerCount int `json:"workerCount"` } -func NewWorkerPool(db Store, capacity uint, workerCount uint, opts ...WorkerPoolOption) *WorkerPool { +func NewWorkerPool(db Store, capacity uint, workerCount uint, opts ...WorkerPoolOption) WorkerPool { ctx, cancel := context.WithCancel(context.Background()) - pool := &WorkerPool{ + pool := &WorkerPoolImpl{ wg: &sync.WaitGroup{}, jobChan: make(chan *Job, capacity), stopChan: make(chan struct{}), @@ -104,7 +115,7 @@ func NewWorkerPool(db Store, capacity uint, workerCount uint, opts ...WorkerPool return pool } -func (wp *WorkerPool) Status() (WorkerPoolStatus, error) { +func (wp *WorkerPoolImpl) Status() (WorkerPoolStatus, error) { var status WorkerPoolStatus query, err := wp.store.Status() @@ -138,7 +149,7 @@ func (wp *WorkerPool) Status() (WorkerPoolStatus, error) { } // CreateJob constructs a new Job for type `jobType` ready for scheduling. -func (wp *WorkerPool) CreateJob(jobType, txID string, opts ...JobOption) (*Job, error) { +func (wp *WorkerPoolImpl) CreateJob(jobType, txID string, opts ...JobOption) (*Job, error) { // Init job job := &Job{ State: Init, @@ -159,12 +170,12 @@ func (wp *WorkerPool) CreateJob(jobType, txID string, opts ...JobOption) (*Job, return job, nil } -func (wp *WorkerPool) RegisterExecutor(jobType string, executorF ExecutorFunc) { +func (wp *WorkerPoolImpl) RegisterExecutor(jobType string, executorF ExecutorFunc) { wp.executors[jobType] = executorF } // Schedule will try to immediately schedule the run of a job -func (wp *WorkerPool) Schedule(j *Job) error { +func (wp *WorkerPoolImpl) Schedule(j *Job) error { entry := j.logEntry(wp.logger.WithFields(log.Fields{ "package": "jobs", "function": "WorkerPool.Schedule", @@ -193,7 +204,7 @@ func (wp *WorkerPool) Schedule(j *Job) error { return nil } -func (wp *WorkerPool) Start() { +func (wp *WorkerPoolImpl) Start() { if !wp.started { wp.started = true wp.startWorkers() @@ -201,7 +212,7 @@ func (wp *WorkerPool) Start() { } } -func (wp *WorkerPool) Stop(wait bool) { +func (wp *WorkerPoolImpl) Stop(wait bool) { close(wp.stopChan) // Give time for the stop channel to signal before closing job channel time.Sleep(time.Millisecond * 100) @@ -212,15 +223,15 @@ func (wp *WorkerPool) Stop(wait bool) { } } -func (wp *WorkerPool) Capacity() uint { +func (wp *WorkerPoolImpl) Capacity() uint { return wp.capacity } -func (wp *WorkerPool) QueueSize() uint { +func (wp *WorkerPoolImpl) QueueSize() uint { return uint(len(wp.jobChan)) } -func (wp *WorkerPool) accept(job *Job) bool { +func (wp *WorkerPoolImpl) accept(job *Job) bool { entry := job.logEntry(wp.logger.WithFields(log.Fields{ "package": "jobs", "function": "WorkerPool.accept", @@ -236,14 +247,14 @@ func (wp *WorkerPool) accept(job *Job) bool { return true } -func (wp *WorkerPool) systemHalted() (bool, error) { +func (wp *WorkerPoolImpl) systemHalted() (bool, error) { if wp.systemService != nil { return wp.systemService.IsHalted() } return false, nil } -func (wp *WorkerPool) startDBJobScheduler() { +func (wp *WorkerPoolImpl) startDBJobScheduler() { go func() { var restTime time.Duration @@ -287,7 +298,7 @@ func (wp *WorkerPool) startDBJobScheduler() { }() } -func (wp *WorkerPool) startWorkers() { +func (wp *WorkerPoolImpl) startWorkers() { for i := uint(0); i < wp.workerCount; i++ { wp.wg.Add(1) go func() { @@ -327,7 +338,7 @@ func (wp *WorkerPool) startWorkers() { } } -func (wp *WorkerPool) tryEnqueue(job *Job, block bool) bool { +func (wp *WorkerPoolImpl) tryEnqueue(job *Job, block bool) bool { if block { select { case <-wp.stopChan: @@ -347,7 +358,7 @@ func (wp *WorkerPool) tryEnqueue(job *Job, block bool) bool { } } -func (wp *WorkerPool) process(job *Job) error { +func (wp *WorkerPoolImpl) process(job *Job) error { entry := job.logEntry(wp.logger.WithFields(log.Fields{ "package": "jobs", "function": "WorkerPool.process", @@ -401,7 +412,7 @@ func (wp *WorkerPool) process(job *Job) error { } if (job.State == Failed || job.State == Complete) && job.ShouldSendNotification && wp.notificationConfig.ShouldSendJobStatus() { - if err := ScheduleJobStatusNotification(wp, job); err != nil { + if err := wp.scheduleJobStatusNotification(job); err != nil { entry. WithFields(log.Fields{"error": err}). Warn("Could not schedule a status update notification for job") @@ -411,7 +422,7 @@ func (wp *WorkerPool) process(job *Job) error { return nil } -func (wp *WorkerPool) executeSendJobStatus(ctx context.Context, j *Job) error { +func (wp *WorkerPoolImpl) executeSendJobStatus(ctx context.Context, j *Job) error { if j.Type != SendJobStatusJobType { return ErrInvalidJobType } @@ -425,7 +436,7 @@ func PermanentFailure(err error) error { return fmt.Errorf("%w: %s", ErrPermanentFailure, err.Error()) } -func ScheduleJobStatusNotification(wp *WorkerPool, parent *Job) error { +func (wp *WorkerPoolImpl) scheduleJobStatusNotification(parent *Job) error { entry := parent.logEntry(wp.logger.WithFields(log.Fields{ "package": "jobs", "function": "ScheduleJobStatusNotification", diff --git a/keys/basic/keys.go b/keys/basic/keys.go index 9fb8a7fa..56f75912 100644 --- a/keys/basic/keys.go +++ b/keys/basic/keys.go @@ -15,13 +15,12 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/keys/google" "github.com/flow-hydraulics/flow-wallet-api/keys/local" "github.com/onflow/flow-go-sdk" - "github.com/onflow/flow-go-sdk/client" "github.com/onflow/flow-go-sdk/crypto" ) type KeyManager struct { store keys.Store - fc *client.Client + fc flow_helpers.FlowClient crypter encryption.Crypter adminAccountKey keys.Private cfg *configs.Config @@ -29,7 +28,7 @@ type KeyManager struct { // NewKeyManager initiates a new key manager. // It uses encryption.AESCrypter to encrypt and decrypt the keys. -func NewKeyManager(cfg *configs.Config, store keys.Store, fc *client.Client) *KeyManager { +func NewKeyManager(cfg *configs.Config, store keys.Store, fc flow_helpers.FlowClient) *KeyManager { // TODO(latenssi): safeguard against nil config? if cfg.DefaultKeyWeight < 0 { diff --git a/keys/store_gorm.go b/keys/store_gorm.go index 9c9484ef..458b9356 100644 --- a/keys/store_gorm.go +++ b/keys/store_gorm.go @@ -10,7 +10,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} } diff --git a/system/options.go b/system/options.go index d6a4820f..13c87f93 100644 --- a/system/options.go +++ b/system/options.go @@ -4,10 +4,10 @@ import ( "time" ) -type ServiceOption func(*Service) +type ServiceOption func(*ServiceImpl) func WithPauseDuration(duration time.Duration) ServiceOption { - return func(svc *Service) { + return func(svc *ServiceImpl) { svc.pauseDuration = duration } } diff --git a/system/service.go b/system/service.go index bcfb8990..fe1fa5c0 100644 --- a/system/service.go +++ b/system/service.go @@ -8,15 +8,22 @@ import ( log "github.com/sirupsen/logrus" ) -type Service struct { +type Service interface { + GetSettings() (*Settings, error) + SaveSettings(settings *Settings) error + Pause() error + IsHalted() (bool, error) +} + +type ServiceImpl struct { store Store pauseDuration time.Duration } const defaultPauseDuration = time.Minute -func NewService(store Store, opts ...ServiceOption) *Service { - svc := &Service{ +func NewService(store Store, opts ...ServiceOption) Service { + svc := &ServiceImpl{ store: store, pauseDuration: defaultPauseDuration, } @@ -29,11 +36,11 @@ func NewService(store Store, opts ...ServiceOption) *Service { return svc } -func (svc *Service) GetSettings() (*Settings, error) { +func (svc *ServiceImpl) GetSettings() (*Settings, error) { return svc.store.GetSettings() } -func (svc *Service) SaveSettings(settings *Settings) error { +func (svc *ServiceImpl) SaveSettings(settings *Settings) error { if settings.ID == 0 { return fmt.Errorf("settings object has no ID, get an existing settings first and alter it") } @@ -41,7 +48,7 @@ func (svc *Service) SaveSettings(settings *Settings) error { return svc.store.SaveSettings(settings) } -func (svc *Service) Pause() error { +func (svc *ServiceImpl) Pause() error { log.Trace("Pause system") settings, err := svc.GetSettings() if err != nil { @@ -51,7 +58,7 @@ func (svc *Service) Pause() error { return svc.SaveSettings(settings) } -func (svc *Service) IsHalted() (bool, error) { +func (svc *ServiceImpl) IsHalted() (bool, error) { s, err := svc.GetSettings() if err != nil { return false, err diff --git a/system/store_gorm.go b/system/store_gorm.go index 7bf1882f..00579045 100644 --- a/system/store_gorm.go +++ b/system/store_gorm.go @@ -8,7 +8,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} } diff --git a/templates/service.go b/templates/service.go index 36e5a773..e0496171 100644 --- a/templates/service.go +++ b/templates/service.go @@ -10,7 +10,16 @@ import ( log "github.com/sirupsen/logrus" ) -type Service struct { +type Service interface { + AddToken(t *Token) error + ListTokens(tType TokenType) (*[]BasicToken, error) + GetTokenById(id uint64) (*Token, error) + GetTokenByName(name string) (*Token, error) + RemoveToken(id uint64) error + TokenFromEvent(e flow.Event) (*Token, error) +} + +type ServiceImpl struct { store Store cfg *configs.Config } @@ -30,7 +39,7 @@ func parseEnabledTokens(envEnabledTokens []string) map[string]Token { return enabledTokens } -func NewService(cfg *configs.Config, store Store) *Service { +func NewService(cfg *configs.Config, store Store) Service { // TODO(latenssi): safeguard against nil config? // Add all enabled tokens from config as fungible tokens @@ -59,10 +68,10 @@ func NewService(cfg *configs.Config, store Store) *Service { store.InsertTemp(&token) } - return &Service{store, cfg} + return &ServiceImpl{store, cfg} } -func (s *Service) AddToken(t *Token) error { +func (s *ServiceImpl) AddToken(t *Token) error { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(t.Address, s.cfg.ChainID) if err != nil { @@ -83,23 +92,23 @@ func (s *Service) AddToken(t *Token) error { return s.store.Insert(t) } -func (s *Service) ListTokens(tType TokenType) (*[]BasicToken, error) { +func (s *ServiceImpl) ListTokens(tType TokenType) (*[]BasicToken, error) { return s.store.List(tType) } -func (s *Service) GetTokenById(id uint64) (*Token, error) { +func (s *ServiceImpl) GetTokenById(id uint64) (*Token, error) { return s.store.GetById(id) } -func (s *Service) GetTokenByName(name string) (*Token, error) { +func (s *ServiceImpl) GetTokenByName(name string) (*Token, error) { return s.store.GetByName(name) } -func (s *Service) RemoveToken(id uint64) error { +func (s *ServiceImpl) RemoveToken(id uint64) error { return s.store.Remove(id) } -func (s *Service) TokenFromEvent(e flow.Event) (*Token, error) { +func (s *ServiceImpl) TokenFromEvent(e flow.Event) (*Token, error) { // Example event: // A.0ae53cb6e3f42a79.FlowToken.TokensDeposited ss := strings.Split(e.Type, ".") diff --git a/templates/store_gorm.go b/templates/store_gorm.go index a737f085..ae856332 100644 --- a/templates/store_gorm.go +++ b/templates/store_gorm.go @@ -12,7 +12,7 @@ type GormStore struct { tempStore map[string]*Token } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db, make(map[string]*Token)} } diff --git a/tests/accounts_service_test.go b/tests/accounts_service_test.go index a304c7b6..7a9721df 100644 --- a/tests/accounts_service_test.go +++ b/tests/accounts_service_test.go @@ -14,7 +14,7 @@ func Test_Add_New_Non_Custodial_Account(t *testing.T) { addr := "0x0123456789" - a, err := svc.AddNonCustodialAccount(context.Background(), addr) + a, err := svc.AddNonCustodialAccount(addr) if err != nil { t.Fatal(err) } @@ -30,12 +30,12 @@ func Test_Add_Existing_Non_Custodial_Account_fails(t *testing.T) { addr := "0x0123456789" - _, err := svc.AddNonCustodialAccount(context.Background(), addr) + _, err := svc.AddNonCustodialAccount(addr) if err != nil { t.Fatal(err) } - _, err = svc.AddNonCustodialAccount(context.Background(), addr) + _, err = svc.AddNonCustodialAccount(addr) if err == nil { t.Fatal("expected error, got nil") } @@ -47,18 +47,18 @@ func Test_Add_Non_Custodial_Account_After_Delete(t *testing.T) { addr := "0x0123456789" - _, err := svc.AddNonCustodialAccount(context.Background(), addr) + _, err := svc.AddNonCustodialAccount(addr) if err != nil { t.Fatal(err) } - err = svc.DeleteNonCustodialAccount(context.Background(), addr) + err = svc.DeleteNonCustodialAccount(addr) if err != nil { t.Fatal(err) } // One must be able to add the same account again after it was deleted. - _, err = svc.AddNonCustodialAccount(context.Background(), addr) + _, err = svc.AddNonCustodialAccount(addr) if err != nil { t.Fatal(err) } @@ -70,7 +70,7 @@ func Test_Delete_Non_Existing_Account(t *testing.T) { addr := "0x0123456789" - err := svc.DeleteNonCustodialAccount(context.Background(), addr) + err := svc.DeleteNonCustodialAccount(addr) if err != nil { t.Fatal(err) } @@ -85,7 +85,7 @@ func Test_Delete_Fails_On_Custodial_Account(t *testing.T) { t.Fatal(err) } - err = svc.DeleteNonCustodialAccount(context.Background(), a.Address) + err = svc.DeleteNonCustodialAccount(a.Address) if err == nil { t.Fatal("expected error, got nil") } @@ -97,17 +97,17 @@ func Test_Delete_Non_Custodial_Account_Is_Idempotent(t *testing.T) { addr := "0x0123456789" - _, err := svc.AddNonCustodialAccount(context.Background(), addr) + _, err := svc.AddNonCustodialAccount(addr) if err != nil { t.Fatal(err) } - err = svc.DeleteNonCustodialAccount(context.Background(), addr) + err = svc.DeleteNonCustodialAccount(addr) if err != nil { t.Fatal(err) } - err = svc.DeleteNonCustodialAccount(context.Background(), addr) + err = svc.DeleteNonCustodialAccount(addr) if err != nil { t.Fatal(err) } diff --git a/tests/test/flow.go b/tests/test/flow.go index 02a0449e..5be12346 100644 --- a/tests/test/flow.go +++ b/tests/test/flow.go @@ -23,7 +23,7 @@ var ( max_tx_wait = 10 * time.Second ) -func NewFlowClient(t *testing.T, cfg *configs.Config) *client.Client { +func NewFlowClient(t *testing.T, cfg *configs.Config) flow_helpers.FlowClient { fc, err := client.New(cfg.AccessAPIHost, grpc.WithInsecure()) if err != nil { t.Fatal(err) @@ -45,7 +45,7 @@ func NewFlowClient(t *testing.T, cfg *configs.Config) *client.Client { return fc } -func NewFlowAccount(t *testing.T, fc *client.Client, creatorAddress flow.Address, creatorKey *flow.AccountKey, creatorSigner crypto.Signer) *flow.Account { +func NewFlowAccount(t *testing.T, fc flow_helpers.FlowClient, creatorAddress flow.Address, creatorKey *flow.AccountKey, creatorSigner crypto.Signer) *flow.Account { seed := make([]byte, seed_length) readRandom(t, seed) @@ -91,7 +91,7 @@ func NewFlowAccount(t *testing.T, fc *client.Client, creatorAddress flow.Address panic("failed to send transaction to network") } - result, err := flow_helpers.WaitForSeal(context.Background(), fc.GetTransactionResult, tx.ID(), max_tx_wait) + result, err := flow_helpers.WaitForSeal(context.Background(), fc, tx.ID(), max_tx_wait) if err != nil { t.Fatal(err) } diff --git a/tests/test/jobs.go b/tests/test/jobs.go index 15fa3593..d292c11d 100644 --- a/tests/test/jobs.go +++ b/tests/test/jobs.go @@ -7,7 +7,7 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/jobs" ) -func WaitForJob(jobSvc *jobs.Service, jobId string) (*jobs.Job, error) { +func WaitForJob(jobSvc jobs.Service, jobId string) (*jobs.Job, error) { for { if job, err := jobSvc.Details(jobId); err != nil { return nil, err diff --git a/tests/test/service.go b/tests/test/service.go index ad95d3f0..94be928a 100644 --- a/tests/test/service.go +++ b/tests/test/service.go @@ -22,27 +22,27 @@ import ( ) type Services interface { - GetAccounts() *accounts.Service - GetJobs() *jobs.Service - GetTemplates() *templates.Service - GetTokens() *tokens.Service - GetTransactions() *transactions.Service - GetSystem() *system.Service + GetAccounts() accounts.Service + GetJobs() jobs.Service + GetTemplates() templates.Service + GetTokens() tokens.Service + GetTransactions() transactions.Service + GetSystem() system.Service GetKeyManager() keys.Manager - GetListener() *chain_events.Listener + GetListener() chain_events.Listener } type svcs struct { - accountService *accounts.Service - jobService *jobs.Service - templateService *templates.Service - tokenService *tokens.Service - transactionService *transactions.Service - systemService *system.Service + accountService accounts.Service + jobService jobs.Service + templateService templates.Service + tokenService tokens.Service + transactionService transactions.Service + systemService system.Service keyManager keys.Manager - listener *chain_events.Listener + listener chain_events.Listener } func GetDatabase(t *testing.T, cfg *configs.Config) *upstreamgorm.DB { @@ -79,9 +79,9 @@ func GetServices(t *testing.T, cfg *configs.Config) Services { goleak.IgnoreTopFunction("database/sql.(*DB).connectionOpener"), goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("github.com/flow-hydraulics/flow-wallet-api/jobs.(*WorkerPool).startWorkers.func1"), - goleak.IgnoreTopFunction("github.com/flow-hydraulics/flow-wallet-api/jobs.(*WorkerPool).startDBJobScheduler.func1"), - goleak.IgnoreTopFunction("github.com/flow-hydraulics/flow-wallet-api/chain_events.(*Listener).Start.func1"), + goleak.IgnoreTopFunction("github.com/flow-hydraulics/flow-wallet-api/jobs.(*WorkerPoolImpl).startWorkers.func1"), + goleak.IgnoreTopFunction("github.com/flow-hydraulics/flow-wallet-api/jobs.(*WorkerPoolImpl).startDBJobScheduler.func1"), + goleak.IgnoreTopFunction("github.com/flow-hydraulics/flow-wallet-api/chain_events.(*ListenerImpl).Start.func1"), ) }) @@ -171,23 +171,23 @@ func GetServices(t *testing.T, cfg *configs.Config) Services { } } -func (s *svcs) GetAccounts() *accounts.Service { +func (s *svcs) GetAccounts() accounts.Service { return s.accountService } -func (s *svcs) GetJobs() *jobs.Service { +func (s *svcs) GetJobs() jobs.Service { return s.jobService } -func (s *svcs) GetTemplates() *templates.Service { +func (s *svcs) GetTemplates() templates.Service { return s.templateService } -func (s *svcs) GetTokens() *tokens.Service { +func (s *svcs) GetTokens() tokens.Service { return s.tokenService } -func (s *svcs) GetTransactions() *transactions.Service { +func (s *svcs) GetTransactions() transactions.Service { return s.transactionService } @@ -195,10 +195,10 @@ func (s *svcs) GetKeyManager() keys.Manager { return s.keyManager } -func (s *svcs) GetListener() *chain_events.Listener { +func (s *svcs) GetListener() chain_events.Listener { return s.listener } -func (s *svcs) GetSystem() *system.Service { +func (s *svcs) GetSystem() system.Service { return s.systemService } diff --git a/tests/watchlist_deposit_tracking_test.go b/tests/watchlist_deposit_tracking_test.go index 019196a3..5d1f9175 100644 --- a/tests/watchlist_deposit_tracking_test.go +++ b/tests/watchlist_deposit_tracking_test.go @@ -11,7 +11,6 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/tests/test" "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" - "github.com/onflow/flow-go-sdk/client" ) var max_tx_wait = 10 * time.Second @@ -38,7 +37,7 @@ func Test_NonCustodialAccountDepositTracking(t *testing.T) { t.Logf("non-custodial account: %q", nonCustodialAccount.Address.Hex()) t.Logf(" custodial account: %q", custodialAccount.Address) - _, err = accountSvc.AddNonCustodialAccount(context.Background(), nonCustodialAccount.Address.Hex()) + _, err = accountSvc.AddNonCustodialAccount(nonCustodialAccount.Address.Hex()) if err != nil { t.Fatal(err) } @@ -77,7 +76,7 @@ func Test_NonCustodialAccountDepositTracking(t *testing.T) { } } -func transferTokens(t *testing.T, ctx context.Context, fc *client.Client, km keys.Manager, amount, proposerAddr, receiverAddr string) { +func transferTokens(t *testing.T, ctx context.Context, fc flow_helpers.FlowClient, km keys.Manager, amount, proposerAddr, receiverAddr string) { t.Helper() amountArg, err := cadence.NewUFix64(amount) @@ -125,13 +124,13 @@ func transferTokens(t *testing.T, ctx context.Context, fc *client.Client, km key t.Fatal(err) } - _, err = flow_helpers.WaitForSeal(ctx, fc.GetTransactionResult, tx.ID(), max_tx_wait) + _, err = flow_helpers.WaitForSeal(ctx, fc, tx.ID(), max_tx_wait) if err != nil { t.Fatal(err) } } -func verifyBalance(t *testing.T, fc *client.Client, address flow.Address, expected uint64) { +func verifyBalance(t *testing.T, fc flow_helpers.FlowClient, address flow.Address, expected uint64) { t.Helper() a, err := fc.GetAccount(context.Background(), address) diff --git a/tokens/account_events.go b/tokens/account_events.go index 7644ed2f..0ac97ec3 100644 --- a/tokens/account_events.go +++ b/tokens/account_events.go @@ -8,8 +8,8 @@ import ( ) type AccountAddedHandler struct { - TemplateService *templates.Service - TokenService *Service + TemplateService templates.Service + TokenService Service } func (h *AccountAddedHandler) Handle(payload accounts.AccountAddedPayload) { @@ -18,22 +18,7 @@ func (h *AccountAddedHandler) Handle(payload accounts.AccountAddedPayload) { } func (h *AccountAddedHandler) addFlowToken(address string) { - token, err := h.TemplateService.GetTokenByName("FlowToken") - if err != nil { - log. - WithFields(log.Fields{"error": err}). - Warn("Error while adding FlowToken to new account") - } - - // No need to setup FlowToken - - err = h.TokenService.store.InsertAccountToken(&AccountToken{ - AccountAddress: address, - TokenAddress: token.Address, - TokenName: token.Name, - TokenType: token.Type, - }) - if err != nil { + if err := h.TokenService.AddAccountToken("FlowToken", address); err != nil { log. WithFields(log.Fields{"error": err}). Warn("Error while adding FlowToken to new account") diff --git a/tokens/chain_events.go b/tokens/chain_events.go index 36228989..f976427f 100644 --- a/tokens/chain_events.go +++ b/tokens/chain_events.go @@ -1,6 +1,7 @@ package tokens import ( + "context" "strings" "github.com/flow-hydraulics/flow-wallet-api/accounts" @@ -12,20 +13,20 @@ import ( ) type ChainEventHandler struct { - AccountService *accounts.Service - ChainListener *chain_events.Listener - TemplateService *templates.Service - TokenService *Service + AccountService accounts.Service + ChainListener chain_events.Listener + TemplateService templates.Service + TokenService Service } -func (h *ChainEventHandler) Handle(event flow.Event) { +func (h *ChainEventHandler) Handle(ctx context.Context, event flow.Event) { isDeposit := strings.Contains(event.Type, "Deposit") if isDeposit { - h.handleDeposit(event) + h.handleDeposit(ctx, event) } } -func (h *ChainEventHandler) handleDeposit(event flow.Event) { +func (h *ChainEventHandler) handleDeposit(ctx context.Context, event flow.Event) { // We don't have to care about tokens that are not in the database // as we could not even listen to events for them token, err := h.TemplateService.TokenFromEvent(event) @@ -42,7 +43,7 @@ func (h *ChainEventHandler) handleDeposit(event flow.Event) { return } - if err = h.TokenService.RegisterDeposit(token, event.TransactionID, account, amountOrNftID.String()); err != nil { + if err = h.TokenService.RegisterDeposit(ctx, token, event.TransactionID, account, amountOrNftID.String()); err != nil { log. WithFields(log.Fields{"error": err}). Warn("Error while registering a deposit") diff --git a/tokens/jobs.go b/tokens/jobs.go index ac68ef54..2e42808f 100644 --- a/tokens/jobs.go +++ b/tokens/jobs.go @@ -14,7 +14,7 @@ type withdrawalCreateJobAttributes struct { Request WithdrawalRequest } -func (s *Service) executeCreateWithdrawalJob(ctx context.Context, j *jobs.Job) error { +func (s *ServiceImpl) executeCreateWithdrawalJob(ctx context.Context, j *jobs.Job) error { if j.Type != WithdrawalCreateJobType { return jobs.ErrInvalidJobType } diff --git a/tokens/service.go b/tokens/service.go index fa566c19..7c8be841 100644 --- a/tokens/service.go +++ b/tokens/service.go @@ -16,7 +16,6 @@ import ( "github.com/flow-hydraulics/flow-wallet-api/transactions" "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" - "github.com/onflow/flow-go-sdk/client" log "github.com/sirupsen/logrus" ) @@ -25,14 +24,30 @@ const ( queryTypeDeposit = "deposit" ) -type Service struct { +type Service interface { + Setup(ctx context.Context, sync bool, tokenName, address string) (*jobs.Job, *transactions.Transaction, error) + AddAccountToken(tokenName, address string) error + AccountTokens(address string, tType templates.TokenType) ([]AccountToken, error) + Details(ctx context.Context, tokenName, address string) (*Details, error) + CreateWithdrawal(ctx context.Context, sync bool, sender string, request WithdrawalRequest) (*jobs.Job, *transactions.Transaction, error) + ListWithdrawals(address, tokenName string) ([]*TokenWithdrawal, error) + ListDeposits(address, tokenName string) ([]*TokenDeposit, error) + GetWithdrawal(address, tokenName, transactionId string) (*TokenWithdrawal, error) + GetDeposit(address, tokenName, transactionId string) (*TokenDeposit, error) + RegisterDeposit(ctx context.Context, token *templates.Token, transactionId flow.Identifier, recipient accounts.Account, amountOrNftID string) error + + // DeployTokenContractForAccount is only used in tests + DeployTokenContractForAccount(ctx context.Context, runSync bool, tokenName, address string) error +} + +type ServiceImpl struct { store Store km keys.Manager - fc *client.Client - wp *jobs.WorkerPool - transactions *transactions.Service - templates *templates.Service - accounts *accounts.Service + fc flow_helpers.FlowClient + wp jobs.WorkerPool + transactions transactions.Service + templates templates.Service + accounts accounts.Service cfg *configs.Config } @@ -40,15 +55,15 @@ func NewService( cfg *configs.Config, store Store, km keys.Manager, - fc *client.Client, - wp *jobs.WorkerPool, - txs *transactions.Service, - tes *templates.Service, - acs *accounts.Service, -) *Service { + fc flow_helpers.FlowClient, + wp jobs.WorkerPool, + txs transactions.Service, + tes templates.Service, + acs accounts.Service, +) Service { // TODO(latenssi): safeguard against nil config? - svc := &Service{store, km, fc, wp, txs, tes, acs, cfg} + svc := &ServiceImpl{store, km, fc, wp, txs, tes, acs, cfg} if wp == nil { panic("workerpool nil") @@ -60,7 +75,7 @@ func NewService( return svc } -func (s *Service) Setup(ctx context.Context, sync bool, tokenName, address string) (*jobs.Job, *transactions.Transaction, error) { +func (s *ServiceImpl) Setup(ctx context.Context, sync bool, tokenName, address string) (*jobs.Job, *transactions.Transaction, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -106,7 +121,31 @@ func (s *Service) Setup(ctx context.Context, sync bool, tokenName, address strin return job, tx, err } -func (s *Service) AccountTokens(address string, tType templates.TokenType) ([]AccountToken, error) { +func (s *ServiceImpl) AddAccountToken(tokenName, address string) error { + // Check if the input is a valid address + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) + if err != nil { + return err + } + + token, err := s.templates.GetTokenByName(tokenName) + if err != nil { + return err + } + + if err := s.store.InsertAccountToken(&AccountToken{ + AccountAddress: address, + TokenAddress: token.Address, + TokenName: token.Name, + TokenType: token.Type, + }); err != nil { + return err + } + + return nil +} + +func (s *ServiceImpl) AccountTokens(address string, tType templates.TokenType) ([]AccountToken, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -117,7 +156,7 @@ func (s *Service) AccountTokens(address string, tType templates.TokenType) ([]Ac } // Details is used to get the accounts balance (or similar for NFTs) for a token. -func (s *Service) Details(ctx context.Context, tokenName, address string) (*Details, error) { +func (s *ServiceImpl) Details(ctx context.Context, tokenName, address string) (*Details, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -147,7 +186,7 @@ func (s *Service) Details(ctx context.Context, tokenName, address string) (*Deta return &Details{TokenName: token.Name, Balance: &Balance{CadenceValue: res}}, nil } -func (s *Service) CreateWithdrawal(ctx context.Context, sync bool, sender string, request WithdrawalRequest) (*jobs.Job, *transactions.Transaction, error) { +func (s *ServiceImpl) CreateWithdrawal(ctx context.Context, sync bool, sender string, request WithdrawalRequest) (*jobs.Job, *transactions.Transaction, error) { log.WithFields(log.Fields{"sync": sync}).Trace("Create withdrawal") if !sync { @@ -181,7 +220,7 @@ func (s *Service) CreateWithdrawal(ctx context.Context, sync bool, sender string } } -func (s *Service) ListTransfers(queryType, address, tokenName string) ([]*TokenTransfer, error) { +func (s *ServiceImpl) listTransfers(queryType, address, tokenName string) ([]*TokenTransfer, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -211,8 +250,8 @@ func (s *Service) ListTransfers(queryType, address, tokenName string) ([]*TokenT } } -func (s *Service) ListWithdrawals(address, tokenName string) ([]*TokenWithdrawal, error) { - tt, err := s.ListTransfers(queryTypeWithdrawal, address, tokenName) +func (s *ServiceImpl) ListWithdrawals(address, tokenName string) ([]*TokenWithdrawal, error) { + tt, err := s.listTransfers(queryTypeWithdrawal, address, tokenName) if err != nil { return nil, err } @@ -224,8 +263,8 @@ func (s *Service) ListWithdrawals(address, tokenName string) ([]*TokenWithdrawal return res, nil } -func (s *Service) ListDeposits(address, tokenName string) ([]*TokenDeposit, error) { - tt, err := s.ListTransfers(queryTypeDeposit, address, tokenName) +func (s *ServiceImpl) ListDeposits(address, tokenName string) ([]*TokenDeposit, error) { + tt, err := s.listTransfers(queryTypeDeposit, address, tokenName) if err != nil { return nil, err } @@ -237,7 +276,7 @@ func (s *Service) ListDeposits(address, tokenName string) ([]*TokenDeposit, erro return res, nil } -func (s *Service) GetTransfer(queryType, address, tokenName, transactionId string) (*TokenTransfer, error) { +func (s *ServiceImpl) getTransfer(queryType, address, tokenName, transactionId string) (*TokenTransfer, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -273,8 +312,8 @@ func (s *Service) GetTransfer(queryType, address, tokenName, transactionId strin } } -func (s *Service) GetWithdrawal(address, tokenName, transactionId string) (*TokenWithdrawal, error) { - t, err := s.GetTransfer(queryTypeWithdrawal, address, tokenName, transactionId) +func (s *ServiceImpl) GetWithdrawal(address, tokenName, transactionId string) (*TokenWithdrawal, error) { + t, err := s.getTransfer(queryTypeWithdrawal, address, tokenName, transactionId) if err != nil { return nil, err } @@ -282,8 +321,8 @@ func (s *Service) GetWithdrawal(address, tokenName, transactionId string) (*Toke return &w, nil } -func (s *Service) GetDeposit(address, tokenName, transactionId string) (*TokenDeposit, error) { - t, err := s.GetTransfer(queryTypeDeposit, address, tokenName, transactionId) +func (s *ServiceImpl) GetDeposit(address, tokenName, transactionId string) (*TokenDeposit, error) { + t, err := s.getTransfer(queryTypeDeposit, address, tokenName, transactionId) if err != nil { return nil, err } @@ -292,7 +331,7 @@ func (s *Service) GetDeposit(address, tokenName, transactionId string) (*TokenDe } // RegisterDeposit is an internal API for registering token deposits from on-chain events. -func (s *Service) RegisterDeposit(token *templates.Token, transactionId flow.Identifier, recipient accounts.Account, amountOrNftID string) error { +func (s *ServiceImpl) RegisterDeposit(ctx context.Context, token *templates.Token, transactionId flow.Identifier, recipient accounts.Account, amountOrNftID string) error { var ( ftAmount string nftId uint64 @@ -315,7 +354,7 @@ func (s *Service) RegisterDeposit(token *templates.Token, transactionId flow.Ide // Get existing transaction or create one transaction := s.transactions.GetOrCreateTransaction(transactionId.Hex()) - flowTx, err := s.fc.GetTransaction(context.Background(), transactionId) + flowTx, err := s.fc.GetTransaction(ctx, transactionId) if err != nil { return err } @@ -373,7 +412,7 @@ func (s *Service) RegisterDeposit(token *templates.Token, transactionId flow.Ide // createWithdrawal will synchronously create a withdrawal and store the transfer. // Used in job execution and sync API calls. -func (s *Service) createWithdrawal(ctx context.Context, sender string, request WithdrawalRequest) (*transactions.Transaction, error) { +func (s *ServiceImpl) createWithdrawal(ctx context.Context, sender string, request WithdrawalRequest) (*transactions.Transaction, error) { // Check if the sender is a valid address sender, err := flow_helpers.ValidateAddress(sender, s.cfg.ChainID) if err != nil { diff --git a/tokens/store_gorm.go b/tokens/store_gorm.go index 2fd18323..960c0d3a 100644 --- a/tokens/store_gorm.go +++ b/tokens/store_gorm.go @@ -13,7 +13,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} } diff --git a/tokens/test_helpers.go b/tokens/test_helpers.go index b36c49ce..22dcd5fb 100644 --- a/tokens/test_helpers.go +++ b/tokens/test_helpers.go @@ -11,7 +11,7 @@ import ( ) // DeployTokenContractForAccount is used for testing purposes. -func (s *Service) DeployTokenContractForAccount(ctx context.Context, runSync bool, tokenName, address string) error { +func (s *ServiceImpl) DeployTokenContractForAccount(ctx context.Context, runSync bool, tokenName, address string) error { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { diff --git a/transactions/jobs.go b/transactions/jobs.go index de3c1e8e..bc2cbabc 100644 --- a/transactions/jobs.go +++ b/transactions/jobs.go @@ -8,7 +8,7 @@ import ( const TransactionJobType = "transaction" -func (s *Service) executeTransactionJob(ctx context.Context, j *jobs.Job) error { +func (s *ServiceImpl) executeTransactionJob(ctx context.Context, j *jobs.Job) error { if j.Type != TransactionJobType { return jobs.ErrInvalidJobType } diff --git a/transactions/options.go b/transactions/options.go index baf6aaff..1d9aa9d3 100644 --- a/transactions/options.go +++ b/transactions/options.go @@ -2,10 +2,10 @@ package transactions import "go.uber.org/ratelimit" -type ServiceOption func(*Service) +type ServiceOption func(*ServiceImpl) func WithTxRatelimiter(limiter ratelimit.Limiter) ServiceOption { - return func(svc *Service) { + return func(svc *ServiceImpl) { svc.txRateLimiter = limiter } } diff --git a/transactions/service.go b/transactions/service.go index b4158e75..e3b12339 100644 --- a/transactions/service.go +++ b/transactions/service.go @@ -18,12 +18,24 @@ import ( "google.golang.org/grpc/codes" ) -// Service defines the API for transaction HTTP handlers. -type Service struct { +type Service interface { + Create(ctx context.Context, sync bool, proposerAddress string, code string, args []Argument, tType Type) (*jobs.Job, *Transaction, error) + Sign(ctx context.Context, proposerAddress string, code string, args []Argument) (*SignedTransaction, error) + List(limit, offset int) ([]Transaction, error) + ListForAccount(tType Type, address string, limit, offset int) ([]Transaction, error) + Details(ctx context.Context, transactionId string) (*Transaction, error) + DetailsForAccount(ctx context.Context, tType Type, address, transactionId string) (*Transaction, error) + ExecuteScript(ctx context.Context, code string, args []Argument) (cadence.Value, error) + UpdateTransaction(t *Transaction) error + GetOrCreateTransaction(transactionId string) *Transaction +} + +// ServiceImpl defines the API for transaction HTTP handlers. +type ServiceImpl struct { store Store km keys.Manager - fc *client.Client - wp *jobs.WorkerPool + fc flow_helpers.FlowClient + wp jobs.WorkerPool cfg *configs.Config txRateLimiter ratelimit.Limiter } @@ -33,14 +45,14 @@ func NewService( cfg *configs.Config, store Store, km keys.Manager, - fc *client.Client, - wp *jobs.WorkerPool, + fc flow_helpers.FlowClient, + wp jobs.WorkerPool, opts ...ServiceOption, -) *Service { +) Service { var defaultTxRatelimiter = ratelimit.NewUnlimited() // TODO(latenssi): safeguard against nil config? - svc := &Service{store, km, fc, wp, cfg, defaultTxRatelimiter} + svc := &ServiceImpl{store, km, fc, wp, cfg, defaultTxRatelimiter} for _, opt := range opts { opt(svc) @@ -56,7 +68,7 @@ func NewService( return svc } -func (s *Service) Create(ctx context.Context, sync bool, proposerAddress string, code string, args []Argument, tType Type) (*jobs.Job, *Transaction, error) { +func (s *ServiceImpl) Create(ctx context.Context, sync bool, proposerAddress string, code string, args []Argument, tType Type) (*jobs.Job, *Transaction, error) { transaction, err := s.newTransaction(ctx, proposerAddress, code, args, tType) if err != nil { return nil, nil, fmt.Errorf("error while getting new transaction: %w", err) @@ -89,7 +101,7 @@ func (s *Service) Create(ctx context.Context, sync bool, proposerAddress string, } } -func (s *Service) Sign(ctx context.Context, proposerAddress string, code string, args []Argument) (*SignedTransaction, error) { +func (s *ServiceImpl) Sign(ctx context.Context, proposerAddress string, code string, args []Argument) (*SignedTransaction, error) { flowTx, err := s.buildFlowTransaction(ctx, proposerAddress, code, args) if err != nil { return nil, err @@ -99,13 +111,13 @@ func (s *Service) Sign(ctx context.Context, proposerAddress string, code string, } // List returns all transactions in the datastore. -func (s *Service) List(limit, offset int) ([]Transaction, error) { +func (s *ServiceImpl) List(limit, offset int) ([]Transaction, error) { o := datastore.ParseListOptions(limit, offset) return s.store.Transactions(o) } // ListForAccount returns all transactions in the datastore for a given account. -func (s *Service) ListForAccount(tType Type, address string, limit, offset int) ([]Transaction, error) { +func (s *ServiceImpl) ListForAccount(tType Type, address string, limit, offset int) ([]Transaction, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -118,7 +130,7 @@ func (s *Service) ListForAccount(tType Type, address string, limit, offset int) } // Details returns a specific transaction. -func (s *Service) Details(ctx context.Context, transactionId string) (*Transaction, error) { +func (s *ServiceImpl) Details(ctx context.Context, transactionId string) (*Transaction, error) { // Check if the input is a valid transaction id if err := flow_helpers.ValidateTransactionId(transactionId); err != nil { return nil, err @@ -146,7 +158,7 @@ func (s *Service) Details(ctx context.Context, transactionId string) (*Transacti } // DetailsForAccount returns a specific transaction. -func (s *Service) DetailsForAccount(ctx context.Context, tType Type, address, transactionId string) (*Transaction, error) { +func (s *ServiceImpl) DetailsForAccount(ctx context.Context, tType Type, address, transactionId string) (*Transaction, error) { // Check if the input is a valid address address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { @@ -180,7 +192,7 @@ func (s *Service) DetailsForAccount(ctx context.Context, tType Type, address, tr } // Execute a script -func (s *Service) ExecuteScript(ctx context.Context, code string, args []Argument) (cadence.Value, error) { +func (s *ServiceImpl) ExecuteScript(ctx context.Context, code string, args []Argument) (cadence.Value, error) { return s.fc.ExecuteScriptAtLatestBlock( ctx, []byte(code), @@ -188,15 +200,15 @@ func (s *Service) ExecuteScript(ctx context.Context, code string, args []Argumen ) } -func (s *Service) UpdateTransaction(t *Transaction) error { +func (s *ServiceImpl) UpdateTransaction(t *Transaction) error { return s.store.UpdateTransaction(t) } -func (s *Service) GetOrCreateTransaction(transactionId string) *Transaction { +func (s *ServiceImpl) GetOrCreateTransaction(transactionId string) *Transaction { return s.store.GetOrCreateTransaction(transactionId) } -func (s *Service) buildFlowTransaction(ctx context.Context, proposerAddress, code string, arguments []Argument) (*flow.Transaction, error) { +func (s *ServiceImpl) buildFlowTransaction(ctx context.Context, proposerAddress, code string, arguments []Argument) (*flow.Transaction, error) { latestBlockID, err := flow_helpers.LatestBlockId(ctx, s.fc) if err != nil { return nil, err @@ -252,7 +264,7 @@ func (s *Service) buildFlowTransaction(ctx context.Context, proposerAddress, cod return flowTx, nil } -func (s *Service) newTransaction(ctx context.Context, proposerAddress string, code string, args []Argument, tType Type) (*Transaction, error) { +func (s *ServiceImpl) newTransaction(ctx context.Context, proposerAddress string, code string, args []Argument, tType Type) (*Transaction, error) { tx := &Transaction{ ProposerAddress: proposerAddress, TransactionType: tType, @@ -269,7 +281,7 @@ func (s *Service) newTransaction(ctx context.Context, proposerAddress string, co return tx, nil } -func (s *Service) getProposalAuthorizer(ctx context.Context, proposerAddress string) (keys.Authorizer, error) { +func (s *ServiceImpl) getProposalAuthorizer(ctx context.Context, proposerAddress string) (keys.Authorizer, error) { // Validate the input address. proposerAddress, err := flow_helpers.ValidateAddress(proposerAddress, s.cfg.ChainID) if err != nil { @@ -292,7 +304,7 @@ func (s *Service) getProposalAuthorizer(ctx context.Context, proposerAddress str return proposer, nil } -func (s *Service) sendTransaction(ctx context.Context, tx *Transaction) error { +func (s *ServiceImpl) sendTransaction(ctx context.Context, tx *Transaction) error { // TODO: we should "recreate" the transaction as proposal key sequence numbering // might have gotten out of sync by now (in async situations) diff --git a/transactions/store_gorm.go b/transactions/store_gorm.go index 9bb8cf24..910e1191 100644 --- a/transactions/store_gorm.go +++ b/transactions/store_gorm.go @@ -9,7 +9,7 @@ type GormStore struct { db *gorm.DB } -func NewGormStore(db *gorm.DB) *GormStore { +func NewGormStore(db *gorm.DB) Store { return &GormStore{db} }