code.vegaprotocol.io/vega@v0.79.0/core/nodewallets/vega/loader.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 vega
    17  
    18  import (
    19  	"context"
    20  	"encoding/hex"
    21  	"fmt"
    22  	"path/filepath"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/libs/crypto"
    26  	"code.vegaprotocol.io/vega/paths"
    27  	"code.vegaprotocol.io/vega/wallet/wallet"
    28  	storev1 "code.vegaprotocol.io/vega/wallet/wallet/store/v1"
    29  	"code.vegaprotocol.io/vega/wallet/wallets"
    30  )
    31  
    32  type WalletLoader struct {
    33  	walletHome string
    34  }
    35  
    36  func InitialiseWalletLoader(vegaPaths paths.Paths) (*WalletLoader, error) {
    37  	walletHome, err := vegaPaths.CreateDataDirFor(paths.VegaNodeWalletsDataHome)
    38  	if err != nil {
    39  		return nil, fmt.Errorf("couldn't get the directory path for %s: %w", paths.VegaNodeWalletsDataHome, err)
    40  	}
    41  
    42  	return &WalletLoader{
    43  		walletHome: walletHome,
    44  	}, nil
    45  }
    46  
    47  func (l *WalletLoader) Generate(passphrase string) (*Wallet, map[string]string, error) {
    48  	data := map[string]string{}
    49  	store, err := storev1.InitialiseStore(l.walletHome, false)
    50  	if err != nil {
    51  		return nil, nil, err
    52  	}
    53  	defer store.Close()
    54  
    55  	handler := wallets.NewHandler(store)
    56  
    57  	walletName := fmt.Sprintf("vega.%v", time.Now().UnixNano())
    58  	mnemonic, err := handler.CreateWallet(walletName, passphrase)
    59  	if err != nil {
    60  		return nil, nil, err
    61  	}
    62  	data["mnemonic"] = mnemonic
    63  	data["walletFilePath"] = store.GetWalletPath(walletName)
    64  
    65  	_, err = handler.GenerateKeyPair(walletName, passphrase, []wallet.Metadata{})
    66  	if err != nil {
    67  		return nil, nil, err
    68  	}
    69  
    70  	w, err := newWallet(l, store, walletName, passphrase)
    71  	if err != nil {
    72  		return nil, nil, fmt.Errorf("couldn't create wallet: %w", err)
    73  	}
    74  	return w, data, nil
    75  }
    76  
    77  func (l *WalletLoader) Load(walletName, passphrase string) (*Wallet, error) {
    78  	store, err := storev1.InitialiseStore(l.walletHome, false)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	defer store.Close()
    83  
    84  	return newWallet(l, store, walletName, passphrase)
    85  }
    86  
    87  func (l *WalletLoader) Import(sourceFilePath string, passphrase string) (*Wallet, map[string]string, error) {
    88  	ctx := context.Background()
    89  
    90  	sourcePath, sourceWalletName := filepath.Split(sourceFilePath)
    91  
    92  	w, err := importSourceWallet(ctx, sourcePath, sourceWalletName, passphrase)
    93  	if err != nil {
    94  		return nil, nil, err
    95  	}
    96  
    97  	destStore, err := storev1.InitialiseStore(l.walletHome, false)
    98  	if err != nil {
    99  		return nil, nil, fmt.Errorf("couldn't initialise destination wallet store: %w", err)
   100  	}
   101  	defer destStore.Close()
   102  
   103  	destWalletName := fmt.Sprintf("vega.%v", time.Now().UnixNano())
   104  	w.SetName(destWalletName)
   105  	err = destStore.CreateWallet(ctx, w, passphrase)
   106  	if err != nil {
   107  		return nil, nil, fmt.Errorf("couldn't save the wallet %s: %w", destWalletName, err)
   108  	}
   109  
   110  	destWallet, err := newWallet(l, destStore, destWalletName, passphrase)
   111  	if err != nil {
   112  		return nil, nil, fmt.Errorf("couldn't create wallet: %w", err)
   113  	}
   114  
   115  	data := map[string]string{
   116  		"walletFilePath": destStore.GetWalletPath(destWalletName),
   117  	}
   118  
   119  	return destWallet, data, nil
   120  }
   121  
   122  func importSourceWallet(ctx context.Context, sourcePath string, sourceWalletName string, passphrase string) (wallet.Wallet, error) {
   123  	sourceStore, err := storev1.InitialiseStore(sourcePath, false)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("couldn't initialise source wallet store: %w", err)
   126  	}
   127  	defer sourceStore.Close()
   128  
   129  	if err := sourceStore.UnlockWallet(ctx, sourceWalletName, passphrase); err != nil {
   130  		return nil, fmt.Errorf("couldn't unlock the source wallet: %w", err)
   131  	}
   132  
   133  	w, err := sourceStore.GetWallet(ctx, sourceWalletName)
   134  	if err != nil {
   135  		return nil, fmt.Errorf("couldn't get source wallet %s: %w", sourceWalletName, err)
   136  	}
   137  	return w, nil
   138  }
   139  
   140  func newWallet(loader loader, store *storev1.FileStore, walletName, passphrase string) (*Wallet, error) {
   141  	ctx := context.Background()
   142  	if err := store.UnlockWallet(ctx, walletName, passphrase); err != nil {
   143  		return nil, fmt.Errorf("could not unlock the wallet %q: %w", walletName, err)
   144  	}
   145  
   146  	w, err := store.GetWallet(ctx, walletName)
   147  	if err != nil {
   148  		return nil, fmt.Errorf("could not get wallet %q: %w", walletName, err)
   149  	}
   150  
   151  	keyPairs := w.ListKeyPairs()
   152  
   153  	if keyPairCount := len(keyPairs); keyPairCount == 0 {
   154  		return nil, fmt.Errorf("vega wallet for node requires to have 1 key pair, none found")
   155  	} else if keyPairCount != 1 {
   156  		return nil, fmt.Errorf("vega wallet for node requires to have max 1 key pair, found %v", keyPairCount)
   157  	}
   158  
   159  	keyPair := keyPairs[0]
   160  
   161  	pubKey, err := getPubKey(keyPair)
   162  	if err != nil {
   163  		return nil, fmt.Errorf("couldn't get public key: %w", err)
   164  	}
   165  
   166  	walletID, err := getID(w)
   167  	if err != nil {
   168  		return nil, fmt.Errorf("couldn't get wallet ID: %w", err)
   169  	}
   170  
   171  	return &Wallet{
   172  		loader:   loader,
   173  		name:     walletName,
   174  		keyPair:  keyPair,
   175  		pubKey:   pubKey,
   176  		walletID: walletID,
   177  	}, nil
   178  }
   179  
   180  func getPubKey(keyPair wallet.KeyPair) (crypto.PublicKey, error) {
   181  	decodedPubKey, err := hex.DecodeString(keyPair.PublicKey())
   182  	if err != nil {
   183  		return crypto.PublicKey{}, fmt.Errorf("couldn't decode public key as hexadecimal: %w", err)
   184  	}
   185  
   186  	return crypto.NewPublicKey(keyPair.PublicKey(), decodedPubKey), nil
   187  }
   188  
   189  func getID(w wallet.Wallet) (crypto.PublicKey, error) {
   190  	decodedID, err := hex.DecodeString(w.ID())
   191  	if err != nil {
   192  		return crypto.PublicKey{}, fmt.Errorf("couldn't decode wallet ID as hexadecimal: %w", err)
   193  	}
   194  
   195  	return crypto.NewPublicKey(w.ID(), decodedID), nil
   196  }