code.vegaprotocol.io/vega@v0.79.0/core/nodewallets/eth/keystore/wallet.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 keystore
    17  
    18  import (
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"sync"
    23  
    24  	"code.vegaprotocol.io/vega/core/nodewallets/registry"
    25  	"code.vegaprotocol.io/vega/libs/crypto"
    26  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    27  
    28  	"github.com/ethereum/go-ethereum/accounts"
    29  	"github.com/ethereum/go-ethereum/accounts/keystore"
    30  )
    31  
    32  const KeyStoreAlgoType = "eth"
    33  
    34  type loader interface {
    35  	Load(walletName, passphrase string) (*Wallet, error)
    36  }
    37  
    38  type Wallet struct {
    39  	loader     loader
    40  	name       string
    41  	acc        accounts.Account
    42  	ks         *keystore.KeyStore
    43  	passphrase string
    44  	address    crypto.PublicKey
    45  	mut        sync.Mutex
    46  }
    47  
    48  func newWallet(loader loader, walletName, passphrase string, data []byte) (*Wallet, error) {
    49  	// NewKeyStore always create a new wallet key store file
    50  	// we create this in tmp as we do not want to impact the original one.
    51  	tempFile := filepath.Join(os.TempDir(), vgrand.RandomStr(10))
    52  	ks := keystore.NewKeyStore(tempFile, keystore.StandardScryptN, keystore.StandardScryptP)
    53  
    54  	acc, err := ks.Import(data, passphrase, passphrase)
    55  	if err != nil {
    56  		return nil, fmt.Errorf("couldn't import Ethereum wallet in keystore: %w", err)
    57  	}
    58  
    59  	if err := ks.Unlock(acc, passphrase); err != nil {
    60  		return nil, fmt.Errorf("couldn't unlock Ethereum wallet: %w", err)
    61  	}
    62  
    63  	address := crypto.NewPublicKey(acc.Address.Hex(), acc.Address.Bytes())
    64  
    65  	return &Wallet{
    66  		loader:     loader,
    67  		name:       walletName,
    68  		acc:        acc,
    69  		ks:         ks,
    70  		passphrase: passphrase,
    71  		address:    address,
    72  	}, nil
    73  }
    74  
    75  func (w *Wallet) Cleanup() error {
    76  	// just remove the wallet from the tmp file
    77  	return w.ks.Delete(w.acc, w.passphrase)
    78  }
    79  
    80  func (w *Wallet) Name() string {
    81  	return w.name
    82  }
    83  
    84  func (w *Wallet) Chain() string {
    85  	return "ethereum"
    86  }
    87  
    88  func (w *Wallet) Sign(data []byte) ([]byte, error) {
    89  	return w.ks.SignHash(w.acc, data)
    90  }
    91  
    92  func (w *Wallet) Algo() string {
    93  	return KeyStoreAlgoType
    94  }
    95  
    96  func (w *Wallet) Version() (string, error) {
    97  	return "0", nil
    98  }
    99  
   100  func (w *Wallet) PubKey() crypto.PublicKey {
   101  	return w.address
   102  }
   103  
   104  func (w *Wallet) Reload(details registry.EthereumWalletDetails) error {
   105  	d, ok := details.(registry.EthereumKeyStoreWallet)
   106  	if !ok {
   107  		// this would mean an implementation error
   108  		panic(fmt.Errorf("failed to get EthereumKeyStoreWallet"))
   109  	}
   110  
   111  	nW, err := w.loader.Load(d.Name, d.Passphrase)
   112  	if err != nil {
   113  		return fmt.Errorf("failed to load new wallet: %w", err)
   114  	}
   115  
   116  	w.mut.Lock()
   117  	defer w.mut.Unlock()
   118  
   119  	w.name = nW.name
   120  	w.acc = nW.acc
   121  	w.ks = nW.ks
   122  	w.passphrase = nW.passphrase
   123  	w.address = nW.address
   124  
   125  	return nil
   126  }