github.com/0chain/gosdk@v1.17.11/zcnbridge/keystore.go (about)

     1  package zcnbridge
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"time"
     7  
     8  	hdw "github.com/0chain/gosdk/zcncore/ethhdwallet"
     9  	"github.com/ethereum/go-ethereum/accounts"
    10  	"github.com/ethereum/go-ethereum/accounts/keystore"
    11  	"github.com/ethereum/go-ethereum/common"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // DetailedAccount describes detailed account
    16  type DetailedAccount struct {
    17  	EthereumAddress,
    18  	PublicKey,
    19  	PrivateKey accounts.Account
    20  }
    21  
    22  // KeyStore is a wrapper, which exposes Ethereum KeyStore methods used by DEX bridge.
    23  type KeyStore interface {
    24  	Find(accounts.Account) (accounts.Account, error)
    25  	TimedUnlock(accounts.Account, string, time.Duration) error
    26  	SignHash(account accounts.Account, hash []byte) ([]byte, error)
    27  	GetEthereumKeyStore() *keystore.KeyStore
    28  }
    29  
    30  type keyStore struct {
    31  	ks *keystore.KeyStore
    32  }
    33  
    34  // NewKeyStore creates new KeyStore wrapper instance
    35  func NewKeyStore(path string) KeyStore {
    36  	return &keyStore{
    37  		ks: keystore.NewKeyStore(path, keystore.StandardScryptN, keystore.StandardScryptP),
    38  	}
    39  }
    40  
    41  // Find forwards request to Ethereum KeyStore Find method
    42  func (k *keyStore) Find(account accounts.Account) (accounts.Account, error) {
    43  	return k.ks.Find(account)
    44  }
    45  
    46  // TimedUnlock forwards request to Ethereum KeyStore TimedUnlock method
    47  func (k *keyStore) TimedUnlock(account accounts.Account, passPhrase string, timeout time.Duration) error {
    48  	return k.ks.TimedUnlock(account, passPhrase, timeout)
    49  }
    50  
    51  // SignHash forwards request to Ethereum KeyStore SignHash method
    52  func (k *keyStore) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
    53  	return k.ks.SignHash(account, hash)
    54  }
    55  
    56  // GetEthereumKeyStore returns Ethereum KeyStore instance
    57  func (k *keyStore) GetEthereumKeyStore() *keystore.KeyStore {
    58  	return k.ks
    59  }
    60  
    61  // ListStorageAccounts List available accounts
    62  //   - homedir is the home directory
    63  func ListStorageAccounts(homedir string) []common.Address {
    64  	keyDir := path.Join(homedir, EthereumWalletStorageDir)
    65  	ks := keystore.NewKeyStore(keyDir, keystore.StandardScryptN, keystore.StandardScryptP)
    66  	config := &accounts.Config{InsecureUnlockAllowed: false}
    67  	am := accounts.NewManager(config, ks)
    68  	addresses := am.Accounts()
    69  
    70  	return addresses
    71  }
    72  
    73  // DeleteAccount deletes account from wallet
    74  //   - homedir is the home directory
    75  //   - address is the account address
    76  func DeleteAccount(homedir, address string) bool {
    77  	keyDir := path.Join(homedir, EthereumWalletStorageDir)
    78  	ks := keystore.NewKeyStore(keyDir, keystore.StandardScryptN, keystore.StandardScryptP)
    79  	config := &accounts.Config{InsecureUnlockAllowed: false}
    80  	am := accounts.NewManager(config, ks)
    81  
    82  	wallet, err := am.Find(accounts.Account{
    83  		Address: common.HexToAddress(address),
    84  	})
    85  
    86  	if err != nil && wallet == nil {
    87  		fmt.Printf("failed to find account %s, error: %s", address, err)
    88  		return false
    89  	}
    90  
    91  	return true
    92  }
    93  
    94  // AccountExists checks if account exists
    95  //   - homedir is the home directory
    96  //   - address is the account address
    97  func AccountExists(homedir, address string) bool {
    98  	keyDir := path.Join(homedir, EthereumWalletStorageDir)
    99  	ks := keystore.NewKeyStore(keyDir, keystore.StandardScryptN, keystore.StandardScryptP)
   100  	config := &accounts.Config{InsecureUnlockAllowed: false}
   101  	am := accounts.NewManager(config, ks)
   102  
   103  	wallet, err := am.Find(accounts.Account{
   104  		Address: common.HexToAddress(address),
   105  	})
   106  
   107  	if err != nil && wallet == nil {
   108  		fmt.Printf("failed to find account %s, error: %s\n", address, err)
   109  		return false
   110  	}
   111  
   112  	status, _ := wallet.Status()
   113  	url := wallet.URL()
   114  
   115  	fmt.Printf("Account exists. Status: %s, Path: %s\n", status, url)
   116  
   117  	return true
   118  }
   119  
   120  // CreateKeyStorage create, restore or unlock key storage
   121  //   - homedir is the home directory
   122  //   - password is the password
   123  func CreateKeyStorage(homedir, password string) error {
   124  	keyDir := path.Join(homedir, EthereumWalletStorageDir)
   125  	ks := keystore.NewKeyStore(keyDir, keystore.StandardScryptN, keystore.StandardScryptP)
   126  	account, err := ks.NewAccount(password)
   127  	if err != nil {
   128  		return errors.Wrap(err, "failed to create keystore")
   129  	}
   130  	fmt.Printf("Created account: %s", account.Address.Hex())
   131  
   132  	return nil
   133  }
   134  
   135  // AccountAddressIndex represents client account and address indexes for multi-key wallet. Used to derive ethereum account.
   136  type AccountAddressIndex struct {
   137  	AccountIndex int
   138  	AddressIndex int
   139  
   140  	// Bip32 flag indicates if the account is derived using BIP32 derivation path.
   141  	Bip32 bool
   142  }
   143  
   144  // ImportAccount imports account using mnemonic
   145  //   - homedir is the home directory
   146  //   - mnemonic is the mnemonic phrase
   147  //   - password is the password
   148  //   - accountAddrIndex is the account and address indexes used for the derivation of the ethereum account
   149  func ImportAccount(homedir, mnemonic, password string, accountAddrIndex ...AccountAddressIndex) (string, error) {
   150  	// 1. Create storage and account if it doesn't exist and add account to it
   151  
   152  	keyDir := path.Join(homedir, EthereumWalletStorageDir)
   153  	ks := keystore.NewKeyStore(keyDir, keystore.StandardScryptN, keystore.StandardScryptP)
   154  
   155  	// 2. Init wallet
   156  
   157  	wallet, err := hdw.NewFromMnemonic(mnemonic)
   158  	if err != nil {
   159  		return "", errors.Wrap(err, "failed to import from mnemonic")
   160  	}
   161  
   162  	var aai AccountAddressIndex
   163  	if len(accountAddrIndex) > 0 {
   164  		aai = accountAddrIndex[0]
   165  	}
   166  
   167  	var pathD accounts.DerivationPath
   168  	if aai.Bip32 {
   169  		pathD = hdw.MustParseDerivationPath(fmt.Sprintf("m/44'/60'/0'/%d", aai.AddressIndex))
   170  	} else {
   171  		pathD = hdw.MustParseDerivationPath(fmt.Sprintf("m/44'/60'/%d'/0/%d", aai.AccountIndex, aai.AddressIndex))
   172  	}
   173  
   174  	account, err := wallet.Derive(pathD, true)
   175  	if err != nil {
   176  		return "", errors.Wrap(err, "failed parse derivation path")
   177  	}
   178  
   179  	key, err := wallet.PrivateKey(account)
   180  	if err != nil {
   181  		return "", errors.Wrap(err, "failed to get private key")
   182  	}
   183  
   184  	// 3. Find key
   185  
   186  	acc, err := ks.Find(account)
   187  	if err == nil {
   188  		fmt.Printf("Account already exists: %s\nPath: %s\n\n", acc.Address.Hex(), acc.URL.Path)
   189  		return acc.Address.Hex(), nil
   190  	}
   191  
   192  	// 4. Import the key if it doesn't exist
   193  
   194  	acc, err = ks.ImportECDSA(key, password)
   195  	if err != nil {
   196  		return "", errors.Wrap(err, "failed to get import private key")
   197  	}
   198  
   199  	fmt.Printf("Imported account %s to path: %s\n", acc.Address.Hex(), acc.URL.Path)
   200  
   201  	return acc.Address.Hex(), nil
   202  }