code.vegaprotocol.io/vega@v0.79.0/wallet/wallets/handler.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package wallets
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"code.vegaprotocol.io/vega/commands"
    25  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    26  	walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1"
    27  	wcommands "code.vegaprotocol.io/vega/wallet/commands"
    28  	wcrypto "code.vegaprotocol.io/vega/wallet/crypto"
    29  	"code.vegaprotocol.io/vega/wallet/wallet"
    30  )
    31  
    32  var ErrWalletDoesNotExists = errors.New("wallet does not exist")
    33  
    34  // Store abstracts the underlying storage for wallet data.
    35  type Store interface {
    36  	UnlockWallet(ctx context.Context, name, passphrase string) error
    37  	WalletExists(ctx context.Context, name string) (bool, error)
    38  	CreateWallet(ctx context.Context, w wallet.Wallet, passphrase string) error
    39  	UpdateWallet(ctx context.Context, w wallet.Wallet) error
    40  	GetWallet(ctx context.Context, name string) (wallet.Wallet, error)
    41  	GetWalletPath(name string) string
    42  	ListWallets(ctx context.Context) ([]string, error)
    43  }
    44  
    45  type Handler struct {
    46  	store Store
    47  
    48  	// just to make sure we do not access same file concurrently or the map
    49  	mu sync.RWMutex
    50  }
    51  
    52  func NewHandler(store Store) *Handler {
    53  	return &Handler{
    54  		store: store,
    55  	}
    56  }
    57  
    58  func (h *Handler) WalletExists(name string) bool {
    59  	h.mu.Lock()
    60  	defer h.mu.Unlock()
    61  
    62  	exist, _ := h.store.WalletExists(context.Background(), name)
    63  	return exist
    64  }
    65  
    66  func (h *Handler) ListWallets() ([]string, error) {
    67  	h.mu.Lock()
    68  	defer h.mu.Unlock()
    69  
    70  	return h.store.ListWallets(context.Background())
    71  }
    72  
    73  func (h *Handler) CreateWallet(name, passphrase string) (string, error) {
    74  	h.mu.Lock()
    75  	defer h.mu.Unlock()
    76  
    77  	if exists, err := h.store.WalletExists(context.Background(), name); err != nil {
    78  		return "", fmt.Errorf("couldn't verify the wallet existence: %w", err)
    79  	} else if exists {
    80  		return "", wallet.ErrWalletAlreadyExists
    81  	}
    82  
    83  	w, recoveryPhrase, err := wallet.NewHDWallet(name)
    84  	if err != nil {
    85  		return "", err
    86  	}
    87  
    88  	if err := h.store.CreateWallet(context.Background(), w, passphrase); err != nil {
    89  		return "", err
    90  	}
    91  
    92  	return recoveryPhrase, nil
    93  }
    94  
    95  func (h *Handler) ImportWallet(name, passphrase, recoveryPhrase string, keyDerivationVersion uint32) error {
    96  	h.mu.Lock()
    97  	defer h.mu.Unlock()
    98  
    99  	if exists, err := h.store.WalletExists(context.Background(), name); err != nil {
   100  		return fmt.Errorf("couldn't verify wallet existence: %w", err)
   101  	} else if exists {
   102  		return wallet.ErrWalletAlreadyExists
   103  	}
   104  
   105  	w, err := wallet.ImportHDWallet(name, recoveryPhrase, keyDerivationVersion)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	if err := h.store.CreateWallet(context.Background(), w, passphrase); err != nil {
   111  		return err
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  func (h *Handler) LoginWallet(name, passphrase string) error {
   118  	h.mu.Lock()
   119  	defer h.mu.Unlock()
   120  
   121  	ctx := context.Background()
   122  
   123  	if exists, err := h.store.WalletExists(ctx, name); err != nil {
   124  		return fmt.Errorf("couldn't verify wallet existence: %w", err)
   125  	} else if !exists {
   126  		return ErrWalletDoesNotExists
   127  	}
   128  
   129  	if err := h.store.UnlockWallet(ctx, name, passphrase); err != nil {
   130  		if errors.Is(err, wallet.ErrWrongPassphrase) {
   131  			return err
   132  		}
   133  		return fmt.Errorf("couldn't unlock wallet %q: %w", name, err)
   134  	}
   135  
   136  	if _, err := h.store.GetWallet(ctx, name); err != nil {
   137  		return fmt.Errorf("couldn't get wallet %q: %w", name, err)
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  func (h *Handler) GenerateKeyPair(name, passphrase string, meta []wallet.Metadata) (wallet.KeyPair, error) {
   144  	h.mu.Lock()
   145  	defer h.mu.Unlock()
   146  
   147  	if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil {
   148  		if errors.Is(err, wallet.ErrWrongPassphrase) {
   149  			return nil, err
   150  		}
   151  		return nil, fmt.Errorf("couldn't unlock wallet %q: %w", name, err)
   152  	}
   153  
   154  	w, err := h.store.GetWallet(context.Background(), name)
   155  	if err != nil {
   156  		return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err)
   157  	}
   158  
   159  	kp, err := w.GenerateKeyPair(meta)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	if err := h.store.UpdateWallet(context.Background(), w); err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	return kp, nil
   169  }
   170  
   171  func (h *Handler) SecureGenerateKeyPair(name, passphrase string, meta []wallet.Metadata) (string, error) {
   172  	kp, err := h.GenerateKeyPair(name, passphrase, meta)
   173  	if err != nil {
   174  		return "", err
   175  	}
   176  
   177  	return kp.PublicKey(), nil
   178  }
   179  
   180  func (h *Handler) GetPublicKey(name, pubKey string) (wallet.PublicKey, error) {
   181  	h.mu.RLock()
   182  	defer h.mu.RUnlock()
   183  
   184  	w, err := h.store.GetWallet(context.Background(), name)
   185  	if err != nil {
   186  		return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err)
   187  	}
   188  
   189  	return w.DescribePublicKey(pubKey)
   190  }
   191  
   192  func (h *Handler) ListPublicKeys(name string) ([]wallet.PublicKey, error) {
   193  	h.mu.RLock()
   194  	defer h.mu.RUnlock()
   195  
   196  	w, err := h.store.GetWallet(context.Background(), name)
   197  	if err != nil {
   198  		return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err)
   199  	}
   200  
   201  	return w.ListPublicKeys(), nil
   202  }
   203  
   204  func (h *Handler) ListKeyPairs(name string) ([]wallet.KeyPair, error) {
   205  	h.mu.RLock()
   206  	defer h.mu.RUnlock()
   207  
   208  	w, err := h.store.GetWallet(context.Background(), name)
   209  	if err != nil {
   210  		return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err)
   211  	}
   212  
   213  	return w.ListKeyPairs(), nil
   214  }
   215  
   216  func (h *Handler) SignAny(name string, inputData []byte, pubKey string) ([]byte, error) {
   217  	h.mu.RLock()
   218  	defer h.mu.RUnlock()
   219  
   220  	w, err := h.store.GetWallet(context.Background(), name)
   221  	if err != nil {
   222  		return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err)
   223  	}
   224  
   225  	return w.SignAny(pubKey, inputData)
   226  }
   227  
   228  func (h *Handler) SignTx(name string, req *walletpb.SubmitTransactionRequest, height uint64, chainID string) (*commandspb.Transaction, error) {
   229  	h.mu.RLock()
   230  	defer h.mu.RUnlock()
   231  
   232  	w, err := h.store.GetWallet(context.Background(), name)
   233  	if err != nil {
   234  		return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err)
   235  	}
   236  
   237  	marshaledInputData, err := wcommands.ToMarshaledInputData(req, height)
   238  	if err != nil {
   239  		return nil, fmt.Errorf("couldn't marshal input data: %w", err)
   240  	}
   241  
   242  	pubKey := req.GetPubKey()
   243  	signature, err := w.SignTx(pubKey, commands.BundleInputDataForSigning(marshaledInputData, chainID))
   244  	if err != nil {
   245  		return nil, fmt.Errorf("couldn't sign transaction: %w", err)
   246  	}
   247  
   248  	protoSignature := &commandspb.Signature{
   249  		Value:   signature.Value,
   250  		Algo:    signature.Algo,
   251  		Version: signature.Version,
   252  	}
   253  	return commands.NewTransaction(pubKey, marshaledInputData, protoSignature), nil
   254  }
   255  
   256  func (h *Handler) VerifyAny(inputData, sig []byte, pubKey string) (bool, error) {
   257  	h.mu.RLock()
   258  	defer h.mu.RUnlock()
   259  
   260  	return wcrypto.VerifyMessage(&wcrypto.VerifyMessageRequest{
   261  		Message:   inputData,
   262  		Signature: sig,
   263  		PubKey:    pubKey,
   264  	})
   265  }
   266  
   267  func (h *Handler) TaintKey(name, pubKey, passphrase string) error {
   268  	h.mu.Lock()
   269  	defer h.mu.Unlock()
   270  
   271  	if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil {
   272  		if errors.Is(err, wallet.ErrWrongPassphrase) {
   273  			return err
   274  		}
   275  		return fmt.Errorf("couldn't unlock wallet %q: %w", name, err)
   276  	}
   277  
   278  	w, err := h.store.GetWallet(context.Background(), name)
   279  	if err != nil {
   280  		return fmt.Errorf("couldn't get wallet %q: %w", name, err)
   281  	}
   282  
   283  	err = w.TaintKey(pubKey)
   284  	if err != nil {
   285  		return err
   286  	}
   287  
   288  	if err := h.store.UpdateWallet(context.Background(), w); err != nil {
   289  		return err
   290  	}
   291  
   292  	return nil
   293  }
   294  
   295  func (h *Handler) UntaintKey(name string, pubKey string, passphrase string) error {
   296  	h.mu.Lock()
   297  	defer h.mu.Unlock()
   298  
   299  	if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil {
   300  		if errors.Is(err, wallet.ErrWrongPassphrase) {
   301  			return err
   302  		}
   303  		return fmt.Errorf("couldn't unlock wallet %q: %w", name, err)
   304  	}
   305  
   306  	w, err := h.store.GetWallet(context.Background(), name)
   307  	if err != nil {
   308  		return fmt.Errorf("couldn't get wallet %q: %w", name, err)
   309  	}
   310  
   311  	err = w.UntaintKey(pubKey)
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	if err := h.store.UpdateWallet(context.Background(), w); err != nil {
   317  		return err
   318  	}
   319  
   320  	return nil
   321  }
   322  
   323  func (h *Handler) UpdateMeta(name, pubKey, passphrase string, meta []wallet.Metadata) error {
   324  	h.mu.Lock()
   325  	defer h.mu.Unlock()
   326  
   327  	if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil {
   328  		if errors.Is(err, wallet.ErrWrongPassphrase) {
   329  			return err
   330  		}
   331  		return fmt.Errorf("couldn't unlock wallet %q: %w", name, err)
   332  	}
   333  
   334  	w, err := h.store.GetWallet(context.Background(), name)
   335  	if err != nil {
   336  		return fmt.Errorf("couldn't get wallet %q: %w", name, err)
   337  	}
   338  
   339  	_, err = w.AnnotateKey(pubKey, meta)
   340  	if err != nil {
   341  		return err
   342  	}
   343  
   344  	if err := h.store.UpdateWallet(context.Background(), w); err != nil {
   345  		return err
   346  	}
   347  
   348  	return nil
   349  }
   350  
   351  func (h *Handler) GetWalletPath(name string) (string, error) {
   352  	return h.store.GetWalletPath(name), nil
   353  }