diff --git a/.env.example b/.env.example index 9af42e7..5ddd32e 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ -SERVICE_ACC_ADDRESS= -SERVICE_ACC_KEY_VALUE= +ADMIN_ACC_ADDRESS= +ADMIN_ACC_KEY_VALUE= DEFAULT_KEY_MANAGER= GOOGLE_APPLICATION_CREDENTIALS= GOOGLE_KMS_PROJECT_ID= diff --git a/account/service.go b/account/service.go index 555c38f..3ca031d 100644 --- a/account/service.go +++ b/account/service.go @@ -70,7 +70,7 @@ func (s *Service) Details(ctx context.Context, address string) (account data.Acc func (s *Service) ValidateAddress(address string) (err error) { flowAddress := flow.HexToAddress(address) if !flowAddress.IsValid(s.chainId) { - err = fmt.Errorf("'address': %s is not a valid address in '%s' chain", address, s.chainId) + err = fmt.Errorf("'%s' is not a valid address in '%s' chain", address, s.chainId) } return } diff --git a/data/gorm/accountstore.go b/data/gorm/accountstore.go index fcac1d2..aa590b5 100644 --- a/data/gorm/accountstore.go +++ b/data/gorm/accountstore.go @@ -1,17 +1,20 @@ package gorm import ( + "log" + "github.com/eqlabs/flow-nft-wallet-service/data" "gorm.io/gorm" ) type AccountStore struct { + l *log.Logger db *gorm.DB } -func newAccountStore(db *gorm.DB) *AccountStore { +func newAccountStore(l *log.Logger, db *gorm.DB) *AccountStore { db.AutoMigrate(&data.Account{}, &data.Key{}) - return &AccountStore{db} + return &AccountStore{l, db} } // List all accounts diff --git a/data/gorm/store.go b/data/gorm/store.go index aa0d0a2..6ed917c 100644 --- a/data/gorm/store.go +++ b/data/gorm/store.go @@ -1,7 +1,14 @@ package gorm import ( + "fmt" + "log" + + "github.com/caarlos0/env/v6" "github.com/eqlabs/flow-nft-wallet-service/data" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" "gorm.io/gorm" ) @@ -9,12 +16,38 @@ type Store struct { data.AccountStore } -func NewStore(dialector gorm.Dialector) (*Store, error) { +type Config struct { + DatabaseDSN string `env:"DB_DSN" envDefault:"wallet.db"` + DatabaseType string `env:"DB_TYPE" envDefault:"sqlite"` +} + +func NewStore(l *log.Logger) (store *Store, err error) { + cfg := Config{} + if err = env.Parse(&cfg); err != nil { + return + } + + var dialector gorm.Dialector + switch cfg.DatabaseType { + case data.DB_TYPE_POSTGRESQL: + dialector = postgres.Open(cfg.DatabaseDSN) + case data.DB_TYPE_MYSQL: + dialector = mysql.Open(cfg.DatabaseDSN) + case data.DB_TYPE_SQLITE: + dialector = sqlite.Open(cfg.DatabaseDSN) + default: + err = fmt.Errorf("database type '%s' not supported", cfg.DatabaseType) + return + } + db, err := gorm.Open(dialector, &gorm.Config{}) if err != nil { - return &Store{}, err + return } - return &Store{ - AccountStore: newAccountStore(db), - }, nil + + store = &Store{ + AccountStore: newAccountStore(l, db), + } + + return } diff --git a/handlers/accounts.go b/handlers/accounts.go index 27413c4..567da18 100644 --- a/handlers/accounts.go +++ b/handlers/accounts.go @@ -23,7 +23,7 @@ func (s *Accounts) List(rw http.ResponseWriter, r *http.Request) { s.l.Println("List accounts") accounts, err := s.as.List(context.Background()) if err != nil { - s.l.Printf("Error: %s\n", err) + s.l.Printf("Error: %v\n", err) rw.WriteHeader(http.StatusInternalServerError) rw.Write([]byte("Error")) return @@ -37,7 +37,7 @@ func (s *Accounts) Create(rw http.ResponseWriter, r *http.Request) { s.l.Println("Create account") account, err := s.as.Create(context.Background()) if err != nil { - s.l.Printf("Error: %s\n", err) + s.l.Printf("Error: %v\n", err) rw.WriteHeader(http.StatusInternalServerError) rw.Write([]byte("Error")) return @@ -52,7 +52,7 @@ func (s *Accounts) Details(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) account, err := s.as.Details(context.Background(), vars["address"]) if err != nil { - s.l.Printf("Error: %s\n", err) + s.l.Printf("Error: %v\n", err) rw.WriteHeader(http.StatusInternalServerError) rw.Write([]byte("Error")) return diff --git a/keys/simple/encryption.go b/keys/encryption/encryption.go similarity index 98% rename from keys/simple/encryption.go rename to keys/encryption/encryption.go index c955e70..4de30f5 100644 --- a/keys/simple/encryption.go +++ b/keys/encryption/encryption.go @@ -1,4 +1,4 @@ -package simple +package encryption import ( "crypto/aes" diff --git a/keys/google/google.go b/keys/google/google.go index 44e0f5b..064a142 100644 --- a/keys/google/google.go +++ b/keys/google/google.go @@ -3,56 +3,87 @@ package google import ( "context" "fmt" - "strings" + "github.com/eqlabs/flow-nft-wallet-service/keys" + "github.com/google/uuid" + "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go-sdk/crypto/cloudkms" - - kms "cloud.google.com/go/kms/apiv1" - kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" ) -// Creates a new asymmetric signing key in Google KMS and returns a cloudkms.Key (the "raw" result isn't needed) -func AsymKey(ctx context.Context, parent, id string) (createdKey cloudkms.Key, err error) { - kmsClient, err := kms.NewKeyManagementClient(ctx) +type Config struct { + ProjectID string `env:"GOOGLE_KMS_PROJECT_ID"` + LocationID string `env:"GOOGLE_KMS_LOCATION_ID"` + KeyRingID string `env:"GOOGLE_KMS_KEYRING_ID"` +} + +func Generate( + projectId, locationId, KeyRingId string, + keyIndex, weight int, +) (result keys.Wrapped, err error) { + ctx := context.Background() + + keyUUID := uuid.New() + + // Create the new key in Google KMS + createdKey, err := AsymKey( + ctx, + fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", projectId, locationId, KeyRingId), + fmt.Sprintf("flow-wallet-account-key-%s", keyUUID.String()), + ) if err != nil { return } - req := &kmspb.CreateCryptoKeyRequest{ - Parent: parent, - CryptoKeyId: id, - CryptoKey: &kmspb.CryptoKey{ - Purpose: kmspb.CryptoKey_ASYMMETRIC_SIGN, - VersionTemplate: &kmspb.CryptoKeyVersionTemplate{ - Algorithm: kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256, - }, - // TODO: Set relevant labels at creation, update post-creation if necessary - Labels: map[string]string{ - "service": "flow-nft-wallet-service", - "account_address": "", - "chain_id": "", - "environment": "development", - }, - }, + client, err := cloudkms.NewClient(ctx) + if err != nil { + return } - googleKey, err := kmsClient.CreateCryptoKey(ctx, req) + // Get the public key (using flow-go-sdk's cloudkms.Client) + publicKey, hashAlgorithm, err := client.GetPublicKey(ctx, createdKey) if err != nil { return } - // Append cryptoKeyVersions so that we can utilize the KeyFromResourceID method - createdKey, err = cloudkms.KeyFromResourceID(fmt.Sprintf("%s/cryptoKeyVersions/1", googleKey.Name)) + key := keys.Key{ + Index: keyIndex, + Type: keys.ACCOUNT_KEY_TYPE_GOOGLE_KMS, + Value: createdKey.ResourceID(), + } + + flowKey := flow.NewAccountKey(). + SetPublicKey(publicKey). + SetHashAlgo(hashAlgorithm). + SetWeight(weight) + flowKey.Index = keyIndex + + result.AccountKey = key + result.FlowKey = flowKey + + return +} + +func Signer( + ctx context.Context, + address string, + key keys.Key, +) (result crypto.Signer, err error) { + kmsClient, err := cloudkms.NewClient(ctx) if err != nil { - fmt.Println("Could not create cloudkms.Key from ResourceId:", googleKey.Name) return } - // Validate key name - if !strings.HasPrefix(createdKey.ResourceID(), googleKey.Name) { - fmt.Println("WARNING: created Google KMS key name does not match the expected", createdKey.ResourceID(), " vs ", googleKey.Name) - // TODO: Handle scenario + kmsKey, err := cloudkms.KeyFromResourceID(key.Value) + if err != nil { + return } + result, err = kmsClient.SignerForKey( + ctx, + flow.HexToAddress(address), + kmsKey, + ) + return } diff --git a/keys/google/helpers.go b/keys/google/helpers.go new file mode 100644 index 0000000..44e0f5b --- /dev/null +++ b/keys/google/helpers.go @@ -0,0 +1,58 @@ +package google + +import ( + "context" + "fmt" + "strings" + + "github.com/onflow/flow-go-sdk/crypto/cloudkms" + + kms "cloud.google.com/go/kms/apiv1" + kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" +) + +// Creates a new asymmetric signing key in Google KMS and returns a cloudkms.Key (the "raw" result isn't needed) +func AsymKey(ctx context.Context, parent, id string) (createdKey cloudkms.Key, err error) { + kmsClient, err := kms.NewKeyManagementClient(ctx) + if err != nil { + return + } + + req := &kmspb.CreateCryptoKeyRequest{ + Parent: parent, + CryptoKeyId: id, + CryptoKey: &kmspb.CryptoKey{ + Purpose: kmspb.CryptoKey_ASYMMETRIC_SIGN, + VersionTemplate: &kmspb.CryptoKeyVersionTemplate{ + Algorithm: kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256, + }, + // TODO: Set relevant labels at creation, update post-creation if necessary + Labels: map[string]string{ + "service": "flow-nft-wallet-service", + "account_address": "", + "chain_id": "", + "environment": "development", + }, + }, + } + + googleKey, err := kmsClient.CreateCryptoKey(ctx, req) + if err != nil { + return + } + + // Append cryptoKeyVersions so that we can utilize the KeyFromResourceID method + createdKey, err = cloudkms.KeyFromResourceID(fmt.Sprintf("%s/cryptoKeyVersions/1", googleKey.Name)) + if err != nil { + fmt.Println("Could not create cloudkms.Key from ResourceId:", googleKey.Name) + return + } + + // Validate key name + if !strings.HasPrefix(createdKey.ResourceID(), googleKey.Name) { + fmt.Println("WARNING: created Google KMS key name does not match the expected", createdKey.ResourceID(), " vs ", googleKey.Name) + // TODO: Handle scenario + } + + return +} diff --git a/keys/local/local.go b/keys/local/local.go new file mode 100644 index 0000000..b4f6cb5 --- /dev/null +++ b/keys/local/local.go @@ -0,0 +1,57 @@ +package local + +import ( + "crypto/rand" + + "github.com/eqlabs/flow-nft-wallet-service/keys" + "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" +) + +func Generate( + signAlgo crypto.SignatureAlgorithm, + hashAlgo crypto.HashAlgorithm, + keyIndex, weight int, +) (result keys.Wrapped, err error) { + seed := make([]byte, crypto.MinSeedLength) + _, err = rand.Read(seed) + if err != nil { + return + } + + privateKey, err := crypto.GeneratePrivateKey(signAlgo, seed) + if err != nil { + return + } + + flowKey := flow.NewAccountKey(). + FromPrivateKey(privateKey). + SetHashAlgo(hashAlgo). + SetWeight(weight) + + flowKey.Index = keyIndex + + key := keys.Key{ + Index: keyIndex, + Type: keys.ACCOUNT_KEY_TYPE_LOCAL, + Value: privateKey.String(), + } + + result.AccountKey = key + result.FlowKey = flowKey + + return +} + +func Signer( + signAlgo crypto.SignatureAlgorithm, + hashAlgo crypto.HashAlgorithm, + key keys.Key, +) (result crypto.Signer, err error) { + pk, err := crypto.DecodePrivateKeyHex(signAlgo, key.Value) + if err != nil { + return + } + result = crypto.NewInMemorySigner(pk, hashAlgo) + return +} diff --git a/keys/simple/manager.go b/keys/simple/manager.go index c8e3608..1176493 100644 --- a/keys/simple/manager.go +++ b/keys/simple/manager.go @@ -2,137 +2,94 @@ package simple import ( "context" - "crypto/rand" "fmt" - "os" + "log" + "github.com/caarlos0/env/v6" "github.com/eqlabs/flow-nft-wallet-service/data" "github.com/eqlabs/flow-nft-wallet-service/keys" + "github.com/eqlabs/flow-nft-wallet-service/keys/encryption" "github.com/eqlabs/flow-nft-wallet-service/keys/google" + "github.com/eqlabs/flow-nft-wallet-service/keys/local" "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/client" "github.com/onflow/flow-go-sdk/crypto" - "github.com/onflow/flow-go-sdk/crypto/cloudkms" - - "github.com/google/uuid" ) type KeyManager struct { - db data.Store - fc *client.Client - serviceAddress string - serviceKey keys.Key - defaultKeyManager string - crypter *SymmetricCrypter - signAlgo crypto.SignatureAlgorithm - hashAlgo crypto.HashAlgorithm + l *log.Logger + db data.Store + fc *client.Client + cfg Config + googleCfg google.Config + crypter *encryption.SymmetricCrypter + signAlgo crypto.SignatureAlgorithm + hashAlgo crypto.HashAlgorithm + adminAccountKey keys.Key } -type GoogleKMSConfig struct { - ProjectID string - LocationID string - KeyRingID string +type Config struct { + AdminAccountAddress string `env:"ADMIN_ACC_ADDRESS,required"` + AdminAccountKeyIndex int `env:"ADMIN_ACC_KEY_INDEX" envDefault:"0"` + AdminAccountKeyType string `env:"ADMIN_ACC_KEY_TYPE" envDefault:"local"` + AdminAccountKeyValue string `env:"ADMIN_ACC_KEY_VALUE,required"` + DefaultKeyManager string `env:"DEFAULT_KEY_MANAGER" envDefault:"local"` + EncryptionKey string `env:"ENCRYPTION_KEY"` } -func NewKeyManager( - db data.Store, - fc *client.Client, - serviceAddress string, - serviceKey keys.Key, - defaultKeyManager string, - encryptionKey string, -) (*KeyManager, error) { - return &KeyManager{ +func NewKeyManager(l *log.Logger, db data.Store, fc *client.Client) (result *KeyManager, err error) { + cfg := Config{} + if err = env.Parse(&cfg); err != nil { + return + } + + googleCfg := google.Config{} + if err = env.Parse(&googleCfg); err != nil { + return + } + + adminAccountKey := keys.Key{ + Index: cfg.AdminAccountKeyIndex, + Type: cfg.AdminAccountKeyType, + Value: cfg.AdminAccountKeyValue, + } + + crypter := encryption.NewCrypter([]byte(cfg.EncryptionKey)) + + result = &KeyManager{ + l, db, fc, - serviceAddress, - serviceKey, - defaultKeyManager, - NewCrypter([]byte(encryptionKey)), + cfg, + googleCfg, + crypter, crypto.ECDSA_P256, // TODO: config crypto.SHA3_256, // TODO: config - }, nil + adminAccountKey, + } + + return } -func (s *KeyManager) Generate(keyIndex int, weight int) (result keys.Wrapped, err error) { - switch s.defaultKeyManager { +func (s *KeyManager) Generate(keyIndex, weight int) (result keys.Wrapped, err error) { + switch s.cfg.DefaultKeyManager { case keys.ACCOUNT_KEY_TYPE_LOCAL: - seed := make([]byte, crypto.MinSeedLength) - _, err := rand.Read(seed) - if err != nil { - break - } - - privateKey, err := crypto.GeneratePrivateKey(s.signAlgo, seed) - if err != nil { - break - } - - flowKey := flow.NewAccountKey(). - FromPrivateKey(privateKey). - SetHashAlgo(s.hashAlgo). - SetWeight(weight) - flowKey.Index = keyIndex - - accountKey := keys.Key{ - Index: keyIndex, - Type: keys.ACCOUNT_KEY_TYPE_LOCAL, - Value: privateKey.String(), - } - - result.AccountKey = accountKey - result.FlowKey = flowKey - + result, err = local.Generate( + s.signAlgo, + s.hashAlgo, + keyIndex, + weight, + ) case keys.ACCOUNT_KEY_TYPE_GOOGLE_KMS: - // TODO: Take this as a param / config instead - gkmsConfig := GoogleKMSConfig{ - ProjectID: os.Getenv("GOOGLE_KMS_PROJECT_ID"), - LocationID: os.Getenv("GOOGLE_KMS_LOCATION_ID"), - KeyRingID: os.Getenv("GOOGLE_KMS_KEYRING_ID"), - } - - ctx := context.Background() - - keyUUID := uuid.New() - - // Create the new key in Google KMS - createdKey, err := google.AsymKey( - ctx, - fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", gkmsConfig.ProjectID, gkmsConfig.LocationID, gkmsConfig.KeyRingID), - fmt.Sprintf("flow-wallet-account-key-%s", keyUUID.String()), + result, err = google.Generate( + s.googleCfg.ProjectID, + s.googleCfg.LocationID, + s.googleCfg.KeyRingID, + keyIndex, + weight, ) - if err != nil { - break - } - - client, err := cloudkms.NewClient(ctx) - if err != nil { - break - } - - // Get the public key (using flow-go-sdk's cloudkms.Client) - publicKey, hashAlgorithm, err := client.GetPublicKey(ctx, createdKey) - if err != nil { - break - } - - accountKey := keys.Key{ - Index: keyIndex, - Type: keys.ACCOUNT_KEY_TYPE_GOOGLE_KMS, - Value: createdKey.ResourceID(), - } - - flowKey := flow.NewAccountKey(). - SetPublicKey(publicKey). - SetHashAlgo(hashAlgorithm). - SetWeight(weight) - flowKey.Index = keyIndex - - result.AccountKey = accountKey - result.FlowKey = flowKey - default: - err = fmt.Errorf("keyStore.Generate() not implmented for %s", s.defaultKeyManager) + err = fmt.Errorf("keyStore.Generate() not implmented for %s", s.cfg.DefaultKeyManager) } return } @@ -160,28 +117,28 @@ func (s *KeyManager) Load(key data.Key) (result keys.Key, err error) { } func (s *KeyManager) AdminAuthorizer() (keys.Authorizer, error) { - return s.MakeAuthorizer(s.serviceAddress) + return s.MakeAuthorizer(s.cfg.AdminAccountAddress) } func (s *KeyManager) UserAuthorizer(address string) (keys.Authorizer, error) { return s.MakeAuthorizer(address) } -func (s *KeyManager) MakeAuthorizer(address string) (authorizer keys.Authorizer, err error) { - var accountKey keys.Key +func (s *KeyManager) MakeAuthorizer(address string) (result keys.Authorizer, err error) { + var key keys.Key ctx := context.Background() - authorizer.Address = flow.HexToAddress(address) + result.Address = flow.HexToAddress(address) - if address == s.serviceAddress { - accountKey = s.serviceKey + if address == s.cfg.AdminAccountAddress { + key = s.adminAccountKey } else { var rawKey data.Key rawKey, err = s.db.AccountKey(address, 0) if err != nil { return } - accountKey, err = s.Load(rawKey) + key, err = s.Load(rawKey) if err != nil { return } @@ -192,39 +149,25 @@ func (s *KeyManager) MakeAuthorizer(address string) (authorizer keys.Authorizer, return } - authorizer.Key = flowAcc.Keys[accountKey.Index] + result.Key = flowAcc.Keys[key.Index] // TODO: Decide whether we want to allow this kind of flexibility - // or should we just panic if `accountKey.Type` != `s.defaultKeyManager` - switch accountKey.Type { + // or should we just panic if `key.Type` != `s.defaultKeyManager` + switch key.Type { case keys.ACCOUNT_KEY_TYPE_LOCAL: - pk, err := crypto.DecodePrivateKeyHex(s.signAlgo, accountKey.Value) + signer, err := local.Signer(s.signAlgo, s.hashAlgo, key) if err != nil { break } - authorizer.Signer = crypto.NewInMemorySigner(pk, s.hashAlgo) + result.Signer = signer case keys.ACCOUNT_KEY_TYPE_GOOGLE_KMS: - kmsClient, err := cloudkms.NewClient(ctx) - if err != nil { - break - } - - kmsKey, err := cloudkms.KeyFromResourceID(accountKey.Value) - if err != nil { - break - } - - sig, err := kmsClient.SignerForKey( - ctx, - flow.HexToAddress(address), - kmsKey, - ) + signer, err := google.Signer(ctx, address, key) if err != nil { break } - authorizer.Signer = sig + result.Signer = signer default: - err = fmt.Errorf("accountKey.Type not recognised: %s", accountKey.Type) + err = fmt.Errorf("key.Type not recognised: %s", key.Type) } return diff --git a/main.go b/main.go index 8735aa0..f4ee2fd 100644 --- a/main.go +++ b/main.go @@ -12,32 +12,19 @@ import ( "github.com/caarlos0/env/v6" "github.com/eqlabs/flow-nft-wallet-service/account" - "github.com/eqlabs/flow-nft-wallet-service/data" "github.com/eqlabs/flow-nft-wallet-service/data/gorm" "github.com/eqlabs/flow-nft-wallet-service/handlers" - "github.com/eqlabs/flow-nft-wallet-service/keys" "github.com/eqlabs/flow-nft-wallet-service/keys/simple" "github.com/gorilla/mux" "github.com/joho/godotenv" "github.com/onflow/flow-go-sdk/client" "google.golang.org/grpc" - "gorm.io/driver/mysql" - "gorm.io/driver/postgres" - "gorm.io/driver/sqlite" ) type config struct { - Host string `env:"HOST"` - Port int `env:"PORT" envDefault:"3000"` - ServiceAccountAddress string `env:"SERVICE_ACC_ADDRESS,required"` - ServiceAccountKeyIndex int `env:"SERVICE_ACC_KEY_INDEX" envDefault:"0"` - ServiceAccountKeyType string `env:"SERVICE_ACC_KEY_TYPE" envDefault:"local"` - ServiceAccountKeyValue string `env:"SERVICE_ACC_KEY_VALUE,required"` - DefaultKeyManager string `env:"DEFAULT_KEY_MANAGER" envDefault:"local"` - EncryptionKey string `env:"ENCRYPTION_KEY"` - DatabaseDSN string `env:"DB_DSN" envDefault:"wallet.db"` - DatabaseType string `env:"DB_TYPE" envDefault:"sqlite"` - FlowGateway string `env:"FLOW_GATEWAY" envDefault:"localhost:3569"` + Host string `env:"HOST"` + Port int `env:"PORT" envDefault:"3000"` + FlowGateway string `env:"FLOW_GATEWAY" envDefault:"localhost:3569"` } func main() { @@ -59,47 +46,33 @@ func main() { flag.BoolVar(&disable_nft, "disable-nft", false, "disable non-fungible token functionality") flag.Parse() + // Application wide logger l := log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile) + // Flow client // TODO: WithInsecure()? fc, err := client.New(cfg.FlowGateway, grpc.WithInsecure()) if err != nil { log.Fatal(err) } - var db data.Store - switch cfg.DatabaseType { - case data.DB_TYPE_POSTGRESQL: - db, err = gorm.NewStore(postgres.Open(cfg.DatabaseDSN)) - case data.DB_TYPE_MYSQL: - db, err = gorm.NewStore(mysql.Open(cfg.DatabaseDSN)) - case data.DB_TYPE_SQLITE: - db, err = gorm.NewStore(sqlite.Open(cfg.DatabaseDSN)) - default: - err = fmt.Errorf("database type '%s' not supported", cfg.DatabaseType) - } + // Database + db, err := gorm.NewStore(l) if err != nil { log.Fatal(err) } - km, err := simple.NewKeyManager( - db, - fc, - cfg.ServiceAccountAddress, - keys.Key{ - Index: cfg.ServiceAccountKeyIndex, - Type: cfg.ServiceAccountKeyType, - Value: cfg.ServiceAccountKeyValue, - }, - cfg.DefaultKeyManager, - cfg.EncryptionKey, - ) + // Key manager + km, err := simple.NewKeyManager(l, db, fc) if err != nil { log.Fatal(err) } + // Services accountService := account.NewService(l, db, km, fc) + // HTTP handling + accounts := handlers.NewAccounts(l, accountService) // transactions := handlers.NewTransactions(l, fc, db, km) // fungibleTokens := handlers.NewFungibleTokens(l, fc, db, km) @@ -136,6 +109,8 @@ func main() { // TODO: nfts + // Server boilerplate + srv := &http.Server{ Handler: r, Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),