Skip to content

Commit

Permalink
refactor: used the same keystore instance to fetch pKey everytime (#1200
Browse files Browse the repository at this point in the history
)

* refactor: used regex instead of ks.Accounts to match keystore file

* refactor: added more extensive realistic testing for fetching of private key

* ci: ignore test-accounts directory in coverage

* refactor: added test when multiple timestamps keystore file present for same account

* refactor: covered lower and upper case addresses

* revert: optimised fetching key from keystore dir

* fix: reused same keystore instance for fetching pkey every time

* refactor: added realistic tests for fetching of pKey

* refactor: removed unused accounts() from interface

* refactor: used AccountManager struct with a keystore as a method caller

* refactor: fix tests

* refactor: removed commented test code

* refactor: returned interface instead of type struct instance from AccountManagerForKeystore()

* refactor: fixed create tests

* refactor: compared expected and returned pKey values in tests

* refactor: requested changes
  • Loading branch information
Yashk767 authored Apr 26, 2024
1 parent 5ae7e15 commit e4064c5
Show file tree
Hide file tree
Showing 62 changed files with 818 additions and 829 deletions.
58 changes: 14 additions & 44 deletions accounts/accountUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,26 @@
package accounts

import (
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
"os"
"razor/core/types"
)

//go:generate mockery --name AccountInterface --output ./mocks/ --case=underscore

var AccountUtilsInterface AccountInterface

type AccountInterface interface {
CreateAccount(path string, password string) accounts.Account
GetPrivateKeyFromKeystore(keystorePath string, password string) (*ecdsa.PrivateKey, error)
GetPrivateKey(address string, password string, keystorePath string) (*ecdsa.PrivateKey, error)
SignData(hash []byte, account types.Account, defaultPath string) ([]byte, error)
Accounts(path string) []accounts.Account
NewAccount(path string, passphrase string) (accounts.Account, error)
DecryptKey(jsonBytes []byte, password string) (*keystore.Key, error)
Sign(digestHash []byte, prv *ecdsa.PrivateKey) ([]byte, error)
ReadFile(filename string) ([]byte, error)
}

type AccountUtils struct{}

//This function returns all the accounts in form of array
func (accountUtils AccountUtils) Accounts(path string) []accounts.Account {
ks := keystore.NewKeyStore(path, keystore.StandardScryptN, keystore.StandardScryptP)
return ks.Accounts()
}

//This function takes path and pass phrase as input and returns the new account
func (accountUtils AccountUtils) NewAccount(path string, passphrase string) (accounts.Account, error) {
ks := keystore.NewKeyStore(path, keystore.StandardScryptN, keystore.StandardScryptP)
accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, ks)
return ks.NewAccount(passphrase)
}

//This function takes json bytes array and password as input and returns the decrypted key
func (accountUtils AccountUtils) DecryptKey(jsonBytes []byte, password string) (*keystore.Key, error) {
return keystore.DecryptKey(jsonBytes, password)
type AccountManager struct {
Keystore *keystore.KeyStore
}

//This function takes hash in form of byte array and private key as input and returns signature as byte array
func (accountUtils AccountUtils) Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
return crypto.Sign(digestHash, prv)
func NewAccountManager(keystorePath string) *AccountManager {
ks := keystore.NewKeyStore(keystorePath, keystore.StandardScryptN, keystore.StandardScryptP)
return &AccountManager{
Keystore: ks,
}
}

//This function takes name of the file as input and returns the file data as byte array
func (accountUtils AccountUtils) ReadFile(filename string) ([]byte, error) {
return os.ReadFile(filename)
// InitAccountStruct initializes an Account struct with provided details.
func InitAccountStruct(address, password string, accountManager types.AccountManagerInterface) types.Account {
return types.Account{
Address: address,
Password: password,
AccountManager: accountManager,
}
}
49 changes: 29 additions & 20 deletions accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"crypto/ecdsa"
"errors"
"github.com/ethereum/go-ethereum/accounts"
"razor/core/types"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
"os"
"razor/logger"
"razor/path"
"strings"
Expand All @@ -14,51 +16,58 @@ import (
var log = logger.NewLogger()

//This function takes path and password as input and returns new account
func (AccountUtils) CreateAccount(keystorePath string, password string) accounts.Account {
func (am *AccountManager) CreateAccount(keystorePath string, password string) accounts.Account {
if _, err := path.OSUtilsInterface.Stat(keystorePath); path.OSUtilsInterface.IsNotExist(err) {
mkdirErr := path.OSUtilsInterface.Mkdir(keystorePath, 0700)
if mkdirErr != nil {
log.Fatal("Error in creating directory: ", mkdirErr)
}
}
newAcc, err := AccountUtilsInterface.NewAccount(keystorePath, password)
newAcc, err := am.NewAccount(password)
if err != nil {
log.Fatal("Error in creating account: ", err)
}
return newAcc
}

//This function takes path and pass phrase as input and returns the new account
func (am *AccountManager) NewAccount(passphrase string) (accounts.Account, error) {
ks := am.Keystore
accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, ks)
return ks.NewAccount(passphrase)
}

//This function takes address of account, password and keystore path as input and returns private key of account
func (am *AccountManager) GetPrivateKey(address string, password string) (*ecdsa.PrivateKey, error) {
allAccounts := am.Keystore.Accounts()
for _, account := range allAccounts {
if strings.EqualFold(account.Address.Hex(), address) {
return getPrivateKeyFromKeystore(account.URL.Path, password)
}
}
return nil, errors.New("no keystore file found")
}

//This function takes and path of keystore and password as input and returns private key of account
func (AccountUtils) GetPrivateKeyFromKeystore(keystorePath string, password string) (*ecdsa.PrivateKey, error) {
jsonBytes, err := AccountUtilsInterface.ReadFile(keystorePath)
func getPrivateKeyFromKeystore(keystoreFilePath string, password string) (*ecdsa.PrivateKey, error) {
jsonBytes, err := os.ReadFile(keystoreFilePath)
if err != nil {
log.Error("Error in reading keystore: ", err)
return nil, err
}
key, err := AccountUtilsInterface.DecryptKey(jsonBytes, password)
key, err := keystore.DecryptKey(jsonBytes, password)
if err != nil {
log.Error("Error in fetching private key: ", err)
return nil, err
}
return key.PrivateKey, nil
}

//This function takes address of account, password and keystore path as input and returns private key of account
func (AccountUtils) GetPrivateKey(address string, password string, keystorePath string) (*ecdsa.PrivateKey, error) {
allAccounts := AccountUtilsInterface.Accounts(keystorePath)
for _, account := range allAccounts {
if strings.EqualFold(account.Address.Hex(), address) {
return AccountUtilsInterface.GetPrivateKeyFromKeystore(account.URL.Path, password)
}
}
return nil, errors.New("no keystore file found")
}

//This function takes hash, account and path as input and returns the signed data as array of byte
func (AccountUtils) SignData(hash []byte, account types.Account, defaultPath string) ([]byte, error) {
privateKey, err := AccountUtilsInterface.GetPrivateKey(account.Address, account.Password, defaultPath)
func (am *AccountManager) SignData(hash []byte, address string, password string) ([]byte, error) {
privateKey, err := am.GetPrivateKey(address, password)
if err != nil {
return nil, err
}
return AccountUtilsInterface.Sign(hash, privateKey)
return crypto.Sign(hash, privateKey)
}
Loading

0 comments on commit e4064c5

Please sign in to comment.