Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Several fixes and improvements #9

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
WALLET_BIN := build/wallet
GOLANGCI_VERSION := v1.60.3
golangci_version=v1.61.0
golangci_installed_version=$(shell golangci-lint version --format short 2>/dev/null)

.PHONY: demo run clean

Expand All @@ -18,11 +19,13 @@ demo:
clean:
rm -f $(WALLET_BIN)


# Install golangci-lint
lint-install:
@echo "--> Installing golangci-lint $(GOLANGCI_VERSION)"
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_VERSION)
@echo "--> Checking golangci-lint installation"
@if [ "$(golangci_installed_version)" != "$(golangci_version)" ]; then \
echo "--> Installing golangci-lint $(golangci_version)"; \
go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version); \
fi

# Run golangci-lint
lint:
Expand All @@ -34,4 +37,6 @@ lint:
lint-fix:
@echo "--> Running linter with fix"
$(MAKE) lint-install
@golangci-lint run --fix
@golangci-lint run --fix

.PHONY: lint lint-fix lint-install
10 changes: 5 additions & 5 deletions cmd/register/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package register

import (
// Import all provider packages here
"crypto-provider/pkg/factory"
_ "crypto-provider/pkg/provider/file"
_ "crypto-provider/pkg/provider/file/cmd"
"github.com/cosmos/crypto-provider/pkg/factory"
_ "github.com/cosmos/crypto-provider/pkg/impl/file"
_ "github.com/cosmos/crypto-provider/pkg/impl/file/cmd"
// Add other providers as needed
// _ "crypto-provider/pkg/provider/someprovider"
// _ "github.com/cosmos/crypto-provider/pkg/impl/someprovider"
)

// Init is a dummy function to ensure this package is imported
func Init() {
_ = factory.GetFactory()
_ = factory.GetGlobalFactory()
}
6 changes: 3 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package main

import (
"crypto-provider/pkg/cli"
"crypto-provider/pkg/keyring"
"crypto-provider/pkg/wallet"
"fmt"
"github.com/cosmos/crypto-provider/pkg/cli"
"github.com/cosmos/crypto-provider/pkg/keyring"
"github.com/cosmos/crypto-provider/pkg/wallet"
"os"

"github.com/spf13/cobra"
Expand Down
9 changes: 5 additions & 4 deletions demo/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package main

import (
"crypto-provider/pkg/components"
"crypto-provider/pkg/keyring"
"crypto-provider/pkg/provider/file"
"crypto-provider/pkg/wallet"
"crypto/rand"
"fmt"
"log"
"os"
"path/filepath"

"github.com/cosmos/crypto-provider/pkg/components"
"github.com/cosmos/crypto-provider/pkg/impl/file"
"github.com/cosmos/crypto-provider/pkg/keyring"
"github.com/cosmos/crypto-provider/pkg/wallet"
)

const TestFile = "testdata/file_1.json"
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module crypto-provider
module github.com/cosmos/crypto-provider

go 1.23

Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Key components:
- Implements a GetRootCmd() function using the singleton pattern and sync.Once for thread-safety.
- Initializes the root command and its flags.

2. Provider Commands (e.g., pkg/provider/file/cmd/command.go):
2. Provider Commands (e.g., pkg/impl/file/cmd/command.go):
- Each provider package implements its own set of subcommands.
- Uses an init() function to register its commands with the root command.

How to add a new provider cli:

1. Create a new package for your provider (e.g., pkg/provider/newprovider/cmd/).
1. Create a new package for your provider (e.g., pkg/impl/newprovider/cmd/).
2. In this package, create a file (e.g., command.go) with the following structure:
- Implement an init() function that calls GetRootCmd() and adds your provider's command.
- Create a NewCommand() function that returns a cobra.Command for your provider.
Expand Down
56 changes: 56 additions & 0 deletions pkg/components/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,33 @@ package components

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)

// CryptoProviderFactory is a factory interface for creating CryptoProviders.
// Must be implemented by each CryptoProvider.
type CryptoProviderFactory interface {
// Create creates a new CryptoProvider instance using the given source
Create(source BuildSource) (CryptoProvider, error)

// Save saves the CryptoProvider to the underlying storage
Save(provider CryptoProvider) error

// Type returns the type of the CryptoProvider that this factory creates
Type() string

// SupportedSources returns the sources that this factory supports building CryptoProviders from
SupportedSources() []string
}

// CryptoProviderConfig defines the configuration structure for CryptoProvider.
type CryptoProviderConfig struct {
ProviderType string `json:"provider_type"`
Options map[string]interface{} `json:"options"`
}

type BuildSource interface {
Type() string
Validate() error
Expand Down Expand Up @@ -72,3 +86,45 @@ func (m BuildSourceJson) Validate() error {
// Additional validation can be added here if needed
return nil
}

// BuildSourceConfig //////////////////////////////////////////////////////////////
// is a BuildSource implementation that uses CryptoProviderConfig as source
// /////////////////////////////////////////////////////////////////////////////////
type BuildSourceConfig struct {
Config CryptoProviderConfig
}

func (m BuildSourceConfig) Type() string { return "config" }
func (m BuildSourceConfig) Validate() error {
// Validate the Config field
if m.Config.ProviderType == "" {
return fmt.Errorf("provider_type is required in the configuration")
}
// Additional validation can be added here if needed
return nil
}

// BaseCryptoProviderFactory //////////////////////////////////////////////////////

type BaseFactory struct {
BaseDir string
}

func (f *BaseFactory) Save(provider CryptoProvider) error {
metadata := provider.Metadata()
filename := fmt.Sprintf("%s.json", metadata.Name)

path := filepath.Join(f.BaseDir, filename)

// Create the directory if it doesn't exist
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}

data, err := metadata.Serialize()
if err != nil {
return fmt.Errorf("failed to marshal metadata: %w", err)
}

return os.WriteFile(path, data, 0600)
}
5 changes: 5 additions & 0 deletions pkg/components/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ type PrivKey[T PubKey] interface {
Equals(other PrivKey[T]) bool
Type() string
}

// KeyFactory defines how a CryptoProvider creates and manages its keys
type KeyFactory interface {
GenPubKeyFromString(string) (PubKey, error)
}
13 changes: 11 additions & 2 deletions pkg/components/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/Masterminds/semver/v3"

"crypto-provider/pkg/keyring"
"github.com/cosmos/crypto-provider/pkg/keyring"
)

type ProviderConfig = map[string]any
Expand Down Expand Up @@ -34,7 +34,7 @@ func FromRecord(record *keyring.Record) (*ProviderMetadata, error) {
}

// Validate checks if the ProviderMetadata is valid
func (pm *ProviderMetadata) Validate() error {
func (pm ProviderMetadata) Validate() error {
_, err := semver.NewVersion(pm.Version)
if err != nil {
return fmt.Errorf("invalid version: %w", err)
Expand All @@ -53,3 +53,12 @@ func (pm *ProviderMetadata) Validate() error {

return nil
}

// Serialize returns a JSON serialized string of the ProviderMetadata
func (pm ProviderMetadata) Serialize() ([]byte, error) {
data, err := json.MarshalIndent(pm, "", " ")
if err != nil {
return nil, fmt.Errorf("failed to marshal metadata: %w", err)
}
return data, nil
}
11 changes: 11 additions & 0 deletions pkg/components/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,15 @@ type CryptoProvider interface {

// Metadata returns metadata for the crypto provider.
Metadata() ProviderMetadata

// GetPubKey returns the public key of the provider
GetPubKey() PubKey

// ProviderInitializer is an internal interface for keys initialization
ProviderInitializer
}

type ProviderInitializer interface {
// InitializeKeys initializes the keys for the provider.
InitializeKeys() error
}
49 changes: 45 additions & 4 deletions pkg/factory/factory.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package factory

import (
"crypto-provider/pkg/components"
"encoding/json"
"fmt"
"sync"

"github.com/cosmos/crypto-provider/pkg/components"
)

var (
Expand All @@ -15,7 +17,7 @@ type Factory struct {
registry map[string]components.CryptoProviderFactory
}

func GetFactory() *Factory {
func GetGlobalFactory() *Factory {
once.Do(func() {
factoryInstance = newFactory()
})
Expand All @@ -39,8 +41,10 @@ func (f *Factory) RegisterFactory(factory components.CryptoProviderFactory) erro
}

if _, exists := f.registry[providerType]; exists {
return fmt.Errorf("provider type %s is already registered", providerType)
fmt.Printf("warning: factory for provider type '%s' already registered\n", providerType)
return nil
}

f.registry[providerType] = factory
return nil
}
Expand All @@ -52,7 +56,44 @@ func (f *Factory) CreateCryptoProvider(providerType string, source components.Bu
return nil, fmt.Errorf("no factory registered for provider type: '%s'", providerType)
}

return factory.Create(source)
provider, err := factory.Create(source)
if err != nil {
return nil, err
}

// Type assert to internal interface
initializer, ok := provider.(components.ProviderInitializer)
if !ok {
return nil, fmt.Errorf("provider does not implement initializer interface")
}

// Initialize keys using internal interface
if err := initializer.InitializeKeys(); err != nil {
return nil, fmt.Errorf("failed to initialize keys: %w. Check the implementation if InitializeKeys()", err)
}

// Try get pubkey to check if it was initialized
pubKey := provider.GetPubKey()
if pubKey == nil {
return nil, fmt.Errorf("public key not available from provider")
}

return provider, nil
}

// LoadCryptoProvider loads a CryptoProvider from a raw JSON string.
func (f *Factory) LoadCryptoProvider(rawJSON string) (components.CryptoProvider, error) {
var config components.CryptoProviderConfig
if err := json.Unmarshal([]byte(rawJSON), &config); err != nil {
return nil, fmt.Errorf("failed to decode JSON: %w", err)
}

if config.ProviderType == "" {
return nil, fmt.Errorf("provider_type is required in the configuration")
}

source := components.BuildSourceConfig{Config: config}
return f.CreateCryptoProvider(config.ProviderType, source)
}

// newFactory creates a new Factory instance and initializes the registry map.
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/file/cmd/cli.go → pkg/impl/file/cmd/cli.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package cmd

import (
"crypto-provider/pkg/cli"
"fmt"
"github.com/cosmos/crypto-provider/pkg/cli"
"github.com/spf13/cobra"
)

Expand Down
3 changes: 2 additions & 1 deletion pkg/provider/file/config.go → pkg/impl/file/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package file

import (
"crypto-provider/pkg/components"
"encoding/json"
"fmt"

"github.com/cosmos/crypto-provider/pkg/components"
)

// FileProviderConfig holds the configuration for the File Provider
Expand Down
Loading
Loading