Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: optimised fetching key from keystore dir #1196

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions accounts/accountUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
"os"
"razor/core/types"
)

Expand All @@ -16,42 +15,22 @@ 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)
}

//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)
}

//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)
}
29 changes: 22 additions & 7 deletions accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ import (
"crypto/ecdsa"
"errors"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"os"
"razor/core/types"
"razor/logger"
"razor/path"
"strings"
)

var log = logger.NewLogger()
var (
log = logger.NewLogger()
ksInstance *keystore.KeyStore
)

// InitializeKeystore directly initializes the global keystore instance.
func initializeKeystore(keystorePath string) {
log.Info("Initialising keystoreInstance...")
ksInstance = keystore.NewKeyStore(keystorePath, keystore.StandardScryptN, keystore.StandardScryptP)
}

//This function takes path and password as input and returns new account
func (AccountUtils) CreateAccount(keystorePath string, password string) accounts.Account {
Expand All @@ -29,13 +40,13 @@ func (AccountUtils) CreateAccount(keystorePath string, password string) accounts
}

//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
Expand All @@ -44,11 +55,15 @@ func (AccountUtils) GetPrivateKeyFromKeystore(keystorePath string, password stri
}

//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)
func (AccountUtils) GetPrivateKey(address string, password string, keystoreDirPath string) (*ecdsa.PrivateKey, error) {
if ksInstance == nil {
initializeKeystore(keystoreDirPath)
}

allAccounts := ksInstance.Accounts()
for _, account := range allAccounts {
if strings.EqualFold(account.Address.Hex(), address) {
return AccountUtilsInterface.GetPrivateKeyFromKeystore(account.URL.Path, password)
return getPrivateKeyFromKeystore(account.URL.Path, password)
}
}
return nil, errors.New("no keystore file found")
Expand Down
120 changes: 45 additions & 75 deletions accounts/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"crypto/ecdsa"
"errors"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -116,17 +115,12 @@ func TestCreateAccount(t *testing.T) {
}
}

func TestGetPrivateKeyFromKeystore(t *testing.T) {
var password string
var keystorePath string
var privateKey *ecdsa.PrivateKey
var jsonBytes []byte
func Test_getPrivateKeyFromKeystore(t *testing.T) {
password := "Razor@123"

type args struct {
jsonBytes []byte
jsonBytesErr error
key *keystore.Key
keyErr error
keystoreFilePath string
password string
}
tests := []struct {
name string
Expand All @@ -135,54 +129,35 @@ func TestGetPrivateKeyFromKeystore(t *testing.T) {
wantErr bool
}{
{
name: "Test 1: When GetPrivateKey function executes successfully",
name: "Test 1: When keystore file is present and getPrivateKeyFromKeystore function executes successfully",
args: args{
jsonBytes: jsonBytes,
key: &keystore.Key{
PrivateKey: privateKey,
},
keystoreFilePath: "test_accounts/UTC--2024-03-20T07-03-56.358521000Z--911654feb423363fb771e04e18d1e7325ae10a91",
password: password,
},
want: privateKey,
wantErr: false,
},
{
name: "Test 2: When there is an error in reading data from file",
name: "Test 2: When there is no keystore file present at the desired path",
args: args{
jsonBytesErr: errors.New("error in reading data"),
key: &keystore.Key{
PrivateKey: nil,
},
keystoreFilePath: "test_accounts/UTC--2024-03-20T07-03-56.358521000Z--211654feb423363fb771e04e18d1e7325ae10a91",
password: password,
},
want: nil,
wantErr: true,
},
{
name: "Test 3: When there is an error in fetching private key",
name: "Test 3: When password is incorrect for the desired keystore file",
args: args{
jsonBytes: jsonBytes,
key: &keystore.Key{
PrivateKey: nil,
},
keyErr: errors.New("private key error"),
keystoreFilePath: "test_accounts/UTC--2024-03-20T07-03-56.358521000Z--911654feb423363fb771e04e18d1e7325ae10a91",
password: "Razor@456",
},
want: privateKey,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
accountsMock := new(mocks.AccountInterface)
AccountUtilsInterface = accountsMock

accountsMock.On("ReadFile", mock.AnythingOfType("string")).Return(tt.args.jsonBytes, tt.args.jsonBytesErr)
accountsMock.On("DecryptKey", mock.Anything, mock.AnythingOfType("string")).Return(tt.args.key, tt.args.keyErr)

accountUtils := &AccountUtils{}
got, err := accountUtils.GetPrivateKeyFromKeystore(keystorePath, password)
if got != tt.want {
t.Errorf("Private key from GetPrivateKey, got = %v, want %v", got, tt.want)
}
_, err := getPrivateKeyFromKeystore(tt.args.keystoreFilePath, tt.args.password)
if (err != nil) != tt.wantErr {
t.Errorf("GetPrivateKeyFromKeystore() error = %v, wantErr %v", err, tt.wantErr)
return
Expand All @@ -192,64 +167,59 @@ func TestGetPrivateKeyFromKeystore(t *testing.T) {
}

func TestGetPrivateKey(t *testing.T) {
var password string
var keystorePath string
var privateKey *ecdsa.PrivateKey

accountsList := []accounts.Account{
{Address: common.HexToAddress("0x000000000000000000000000000000000000dea1"),
URL: accounts.URL{Scheme: "TestKeyScheme", Path: "test/key/path"},
},
{Address: common.HexToAddress("0x000000000000000000000000000000000000dea2"),
URL: accounts.URL{Scheme: "TestKeyScheme", Path: "test/key/path"},
},
}
password := "Razor@123"
keystoreDirPath := "test_accounts"

type args struct {
address string
accounts []accounts.Account
privateKey *ecdsa.PrivateKey
address string
password string
keystoreDirPath string
}
tests := []struct {
name string
args args
want *ecdsa.PrivateKey
wantErr bool
}{
{
name: "Test 1: When input address is present in accountsList",
name: "Test 1: When input address with correct password is present in keystore directory",
args: args{
address: "0x000000000000000000000000000000000000dea1",
accounts: accountsList,
privateKey: privateKey,
address: "0x911654feb423363fb771e04e18d1e7325ae10a91",
password: password,
keystoreDirPath: keystoreDirPath,
},
want: privateKey,
wantErr: false,
},
{
name: "Test 2: When input address is not present in accountsList",
name: "Test 2: When input upper case address with correct password is present in keystore directory",
args: args{
address: "0x000000000000000000000000000000000000dea3",
accounts: accountsList,
privateKey: privateKey,
address: "0x2F5F59615689B706B6AD13FD03343DCA28784989",
password: password,
keystoreDirPath: keystoreDirPath,
},
wantErr: false,
},
{
name: "Test 3: When provided address is not present in keystore directory",
args: args{
address: "0x911654feb423363fb771e04e18d1e7325ae10a91_not_present",
keystoreDirPath: keystoreDirPath,
},
wantErr: true,
},
{
name: "Test 4: When input address with incorrect password is present in keystore directory",
args: args{
address: "0x911654feb423363fb771e04e18d1e7325ae10a91",
password: "incorrect password",
keystoreDirPath: keystoreDirPath,
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
accountsMock := new(mocks.AccountInterface)
AccountUtilsInterface = accountsMock

accountsMock.On("Accounts", mock.AnythingOfType("string")).Return(tt.args.accounts)
accountsMock.On("GetPrivateKeyFromKeystore", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(tt.args.privateKey, nil)

accountUtils := &AccountUtils{}
got, err := accountUtils.GetPrivateKey(tt.args.address, password, keystorePath)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetPrivateKey() got = %v, want %v", got, tt.want)
}
_, err := accountUtils.GetPrivateKey(tt.args.address, tt.args.password, tt.args.keystoreDirPath)
if (err != nil) != tt.wantErr {
t.Errorf("GetPrivateKey() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
Loading
Loading