Abstract `accounts.Key` and geth `keystore`
This commit is contained in:
parent
608de7fa2d
commit
287e5cdf79
|
@ -10,13 +10,11 @@ import (
|
|||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/keystore"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
@ -37,8 +35,7 @@ var zeroAddress = types.Address{}
|
|||
// Manager represents account manager interface.
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
keystore *keystore.KeyStore
|
||||
manager *accounts.Manager
|
||||
keystore types.KeyStore
|
||||
|
||||
accountsGenerator *generator.Generator
|
||||
onboarding *Onboarding
|
||||
|
@ -48,46 +45,13 @@ type Manager struct {
|
|||
watchAddresses []types.Address
|
||||
}
|
||||
|
||||
// NewManager returns new node account manager.
|
||||
func NewManager() *Manager {
|
||||
m := &Manager{}
|
||||
m.accountsGenerator = generator.New(m)
|
||||
return m
|
||||
}
|
||||
|
||||
// InitKeystore sets key manager and key store.
|
||||
func (m *Manager) InitKeystore(keydir string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
manager, err := makeAccountManager(keydir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.manager = manager
|
||||
backends := manager.Backends(keystore.KeyStoreType)
|
||||
if len(backends) == 0 {
|
||||
return ErrAccountKeyStoreMissing
|
||||
}
|
||||
keyStore, ok := backends[0].(*keystore.KeyStore)
|
||||
if !ok {
|
||||
return ErrAccountKeyStoreMissing
|
||||
}
|
||||
m.keystore = keyStore
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) GetKeystore() *keystore.KeyStore {
|
||||
// GetKeystore is only used in tests
|
||||
func (m *Manager) GetKeystore() types.KeyStore {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.keystore
|
||||
}
|
||||
|
||||
func (m *Manager) GetManager() *accounts.Manager {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.manager
|
||||
}
|
||||
|
||||
// AccountsGenerator returns accountsGenerator.
|
||||
func (m *Manager) AccountsGenerator() *generator.Generator {
|
||||
return m.accountsGenerator
|
||||
|
@ -153,7 +117,7 @@ func (m *Manager) RecoverAccount(password, mnemonic string) (Info, error) {
|
|||
|
||||
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
||||
// If no error is returned, then account is considered verified.
|
||||
func (m *Manager) VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
||||
func (m *Manager) VerifyAccountPassword(keyStoreDir, address, password string) (*types.Key, error) {
|
||||
var err error
|
||||
var foundKeyFile []byte
|
||||
|
||||
|
@ -202,7 +166,7 @@ func (m *Manager) VerifyAccountPassword(keyStoreDir, address, password string) (
|
|||
}
|
||||
|
||||
// avoid swap attack
|
||||
if types.Address(key.Address) != addressObj {
|
||||
if key.Address != addressObj {
|
||||
return nil, fmt.Errorf("account mismatch: have %s, want %s", key.Address.Hex(), addressObj.Hex())
|
||||
}
|
||||
|
||||
|
@ -240,9 +204,9 @@ func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) {
|
|||
|
||||
address := crypto.PubkeyToAddress(privKey.PublicKey)
|
||||
id := uuid.NewRandom()
|
||||
key := &keystore.Key{
|
||||
key := &types.Key{
|
||||
Id: id,
|
||||
Address: common.Address(address),
|
||||
Address: address,
|
||||
PrivateKey: privKey,
|
||||
}
|
||||
|
||||
|
@ -302,9 +266,13 @@ func (m *Manager) ImportAccount(privateKey *ecdsa.PrivateKey, password string) (
|
|||
|
||||
account, err := m.keystore.ImportECDSA(privateKey, password)
|
||||
|
||||
return types.Address(account.Address), err
|
||||
return account.Address, err
|
||||
}
|
||||
|
||||
// ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields
|
||||
// of the Key struct.
|
||||
// ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0,
|
||||
// and ExtendedKey is the extended key of the BIP44 key at index 1.
|
||||
func (m *Manager) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
|
||||
if m.keystore == nil {
|
||||
return "", "", ErrAccountKeyStoreMissing
|
||||
|
@ -418,18 +386,17 @@ func (m *Manager) ImportOnboardingAccount(id string, password string) (Info, str
|
|||
// AddressToDecryptedAccount tries to load decrypted key for a given account.
|
||||
// The running node, has a keystore directory which is loaded on start. Key file
|
||||
// for a given address is expected to be in that directory prior to node start.
|
||||
func (m *Manager) AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error) {
|
||||
func (m *Manager) AddressToDecryptedAccount(address, password string) (types.Account, *types.Key, error) {
|
||||
if m.keystore == nil {
|
||||
return accounts.Account{}, nil, ErrAccountKeyStoreMissing
|
||||
return types.Account{}, nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
|
||||
account, err := ParseAccountString(address)
|
||||
if err != nil {
|
||||
return accounts.Account{}, nil, ErrAddressToAccountMappingFailure
|
||||
return types.Account{}, nil, ErrAddressToAccountMappingFailure
|
||||
}
|
||||
|
||||
var key *keystore.Key
|
||||
account, key, err = m.keystore.AccountDecryptedKey(account, password)
|
||||
account, key, err := m.keystore.AccountDecryptedKey(account, password)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s: %s", ErrAccountToKeyMappingFailure, err)
|
||||
}
|
||||
|
@ -444,7 +411,7 @@ func (m *Manager) unlockExtendedKey(address, password string) (*SelectedExtKey,
|
|||
}
|
||||
|
||||
selectedExtendedKey := &SelectedExtKey{
|
||||
Address: types.Address(account.Address),
|
||||
Address: account.Address,
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// +build !nimbus
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
)
|
||||
|
||||
// GethManager represents account manager interface.
|
||||
type GethManager struct {
|
||||
Manager
|
||||
|
||||
manager *accounts.Manager
|
||||
}
|
||||
|
||||
// NewManager returns new node account manager.
|
||||
func NewManager() *GethManager {
|
||||
m := &GethManager{}
|
||||
m.accountsGenerator = generator.New(m)
|
||||
return m
|
||||
}
|
||||
|
||||
// InitKeystore sets key manager and key store.
|
||||
func (m *GethManager) InitKeystore(keydir string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
var err error
|
||||
m.manager, err = makeAccountManager(keydir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.keystore, err = makeKeyStore(m.manager)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *GethManager) GetManager() *accounts.Manager {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.manager
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// +build nimbus
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
)
|
||||
|
||||
// NewManager returns new node account manager.
|
||||
func NewManager() *Manager {
|
||||
m := &Manager{}
|
||||
m.accountsGenerator = generator.New(m)
|
||||
return m
|
||||
}
|
||||
|
||||
// InitKeystore sets key manager and key store.
|
||||
func (m *Manager) InitKeystore(keydir string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// TODO: Wire with the Nimbus keystore
|
||||
manager, err := makeAccountManager(keydir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.keystore, err = makeKeyStore(manager)
|
||||
return err
|
||||
}
|
|
@ -87,7 +87,7 @@ func TestVerifyAccountPassword(t *testing.T) {
|
|||
require.Fail(t, "no error reported, but account key is missing")
|
||||
}
|
||||
accountAddress := types.BytesToAddress(types.FromHex(testCase.address))
|
||||
if types.Address(accountKey.Address) != accountAddress {
|
||||
if accountKey.Address != accountAddress {
|
||||
require.Fail(t, "account mismatch: have %s, want %s", accountKey.Address.Hex(), accountAddress.Hex())
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ func TestManagerTestSuite(t *testing.T) {
|
|||
type ManagerTestSuite struct {
|
||||
suite.Suite
|
||||
testAccount
|
||||
accManager *Manager
|
||||
accManager *GethManager
|
||||
keydir string
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
|
@ -25,7 +24,7 @@ var (
|
|||
)
|
||||
|
||||
type AccountManager interface {
|
||||
AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error)
|
||||
AddressToDecryptedAccount(address, password string) (types.Account, *types.Key, error)
|
||||
ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error)
|
||||
ImportAccount(privateKey *ecdsa.PrivateKey, password string) (types.Address, error)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
|
@ -21,7 +21,7 @@ var (
|
|||
|
||||
// ValidateKeystoreExtendedKey validates the keystore keys, checking that
|
||||
// ExtendedKey is the extended key of PrivateKey.
|
||||
func ValidateKeystoreExtendedKey(key *keystore.Key) error {
|
||||
func ValidateKeystoreExtendedKey(key *types.Key) error {
|
||||
if key.ExtendedKey.IsZeroed() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package generator
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -25,7 +25,7 @@ func TestValidateKeystoreExtendedKey(t *testing.T) {
|
|||
extendedKey2 := generateTestKey(t)
|
||||
|
||||
// new keystore file format
|
||||
key := &keystore.Key{
|
||||
key := &types.Key{
|
||||
PrivateKey: extendedKey1.ToECDSA(),
|
||||
ExtendedKey: extendedKey1,
|
||||
}
|
||||
|
@ -33,14 +33,14 @@ func TestValidateKeystoreExtendedKey(t *testing.T) {
|
|||
|
||||
// old keystore file format where the extended key was
|
||||
// from another derivation path and not the same of the private key
|
||||
oldKey := &keystore.Key{
|
||||
oldKey := &types.Key{
|
||||
PrivateKey: extendedKey1.ToECDSA(),
|
||||
ExtendedKey: extendedKey2,
|
||||
}
|
||||
assert.Error(t, ValidateKeystoreExtendedKey(oldKey))
|
||||
|
||||
// normal key where we don't have an extended key
|
||||
normalKey := &keystore.Key{
|
||||
normalKey := &types.Key{
|
||||
PrivateKey: extendedKey1.ToECDSA(),
|
||||
ExtendedKey: nil,
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
)
|
||||
|
||||
// makeAccountManager creates ethereum accounts.Manager with single disk backend and lightweight kdf.
|
||||
// If keydir is empty new temporary directory with go-ethereum-keystore will be intialized.
|
||||
func makeAccountManager(keydir string) (manager *accounts.Manager, err error) {
|
||||
if keydir == "" {
|
||||
// There is no datadir.
|
||||
keydir, err = ioutil.TempDir("", "go-ethereum-keystore")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(keydir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := accounts.Config{InsecureUnlockAllowed: false}
|
||||
return accounts.NewManager(&config, keystore.NewKeyStore(keydir, keystore.LightScryptN, keystore.LightScryptP)), nil
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// TODO: Make independent version for Nimbus
|
||||
|
||||
package account
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
// makeAccountManager creates ethereum accounts.Manager with single disk backend and lightweight kdf.
|
||||
// If keydir is empty new temporary directory with go-ethereum-keystore will be intialized.
|
||||
func makeAccountManager(keydir string) (manager *accounts.Manager, err error) {
|
||||
if keydir == "" {
|
||||
// There is no datadir.
|
||||
keydir, err = ioutil.TempDir("", "go-ethereum-keystore")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(keydir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := accounts.Config{InsecureUnlockAllowed: false}
|
||||
return accounts.NewManager(&config, keystore.NewKeyStore(keydir, keystore.LightScryptN, keystore.LightScryptP)), nil
|
||||
}
|
||||
|
||||
func makeKeyStore(manager *accounts.Manager) (types.KeyStore, error) {
|
||||
backends := manager.Backends(keystore.KeyStoreType)
|
||||
if len(backends) == 0 {
|
||||
return nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
keyStore, ok := backends[0].(*keystore.KeyStore)
|
||||
if !ok {
|
||||
return nil, ErrAccountKeyStoreMissing
|
||||
}
|
||||
|
||||
return adaptGethKeyStore(keyStore), nil
|
||||
}
|
||||
|
||||
type gethKeyStoreAdapter struct {
|
||||
keystore *keystore.KeyStore
|
||||
}
|
||||
|
||||
func adaptGethKeyStore(keystore *keystore.KeyStore) types.KeyStore {
|
||||
return &gethKeyStoreAdapter{keystore: keystore}
|
||||
}
|
||||
|
||||
func (k *gethKeyStoreAdapter) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (types.Account, error) {
|
||||
gethAccount, err := k.keystore.ImportECDSA(priv, passphrase)
|
||||
return accountFrom(gethAccount), err
|
||||
}
|
||||
|
||||
func (k *gethKeyStoreAdapter) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (types.Account, error) {
|
||||
gethAccount, err := k.keystore.ImportSingleExtendedKey(extKey, passphrase)
|
||||
return accountFrom(gethAccount), err
|
||||
}
|
||||
|
||||
func (k *gethKeyStoreAdapter) ImportExtendedKeyForPurpose(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, passphrase string) (types.Account, error) {
|
||||
gethAccount, err := k.keystore.ImportExtendedKeyForPurpose(keyPurpose, extKey, passphrase)
|
||||
return accountFrom(gethAccount), err
|
||||
}
|
||||
|
||||
func (k *gethKeyStoreAdapter) AccountDecryptedKey(a types.Account, auth string) (types.Account, *types.Key, error) {
|
||||
gethAccount, err := gethAccountFrom(a)
|
||||
if err != nil {
|
||||
return types.Account{}, nil, err
|
||||
}
|
||||
|
||||
var gethKey *keystore.Key
|
||||
gethAccount, gethKey, err = k.keystore.AccountDecryptedKey(gethAccount, auth)
|
||||
return accountFrom(gethAccount), keyFrom(gethKey), err
|
||||
}
|
||||
|
||||
func (k *gethKeyStoreAdapter) Delete(a types.Account, auth string) error {
|
||||
gethAccount, err := gethAccountFrom(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.keystore.Delete(gethAccount, auth)
|
||||
}
|
||||
|
||||
// parseGethURL converts a user supplied URL into the accounts specific structure.
|
||||
func parseGethURL(url string) (accounts.URL, error) {
|
||||
parts := strings.Split(url, "://")
|
||||
if len(parts) != 2 || parts[0] == "" {
|
||||
return accounts.URL{}, errors.New("protocol scheme missing")
|
||||
}
|
||||
return accounts.URL{
|
||||
Scheme: parts[0],
|
||||
Path: parts[1],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func gethAccountFrom(account types.Account) (accounts.Account, error) {
|
||||
var (
|
||||
gethAccount accounts.Account
|
||||
err error
|
||||
)
|
||||
gethAccount.Address = common.Address(account.Address)
|
||||
if account.URL != "" {
|
||||
gethAccount.URL, err = parseGethURL(account.URL)
|
||||
}
|
||||
return gethAccount, err
|
||||
}
|
||||
|
||||
func accountFrom(gethAccount accounts.Account) types.Account {
|
||||
return types.Account{
|
||||
Address: types.Address(gethAccount.Address),
|
||||
URL: gethAccount.URL.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func keyFrom(k *keystore.Key) *types.Key {
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Key{
|
||||
Id: k.Id,
|
||||
Address: types.Address(k.Address),
|
||||
PrivateKey: k.PrivateKey,
|
||||
ExtendedKey: k.ExtendedKey,
|
||||
SubAccountIndex: k.SubAccountIndex,
|
||||
}
|
||||
}
|
|
@ -5,10 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
)
|
||||
|
||||
|
@ -75,8 +71,8 @@ type Info struct {
|
|||
// SelectedExtKey is a container for the selected (logged in) external account.
|
||||
type SelectedExtKey struct {
|
||||
Address types.Address
|
||||
AccountKey *keystore.Key
|
||||
SubAccounts []accounts.Account
|
||||
AccountKey *types.Key
|
||||
SubAccounts []types.Account
|
||||
}
|
||||
|
||||
// Hex dumps address of a given extended key as hex string.
|
||||
|
@ -88,25 +84,14 @@ func (k *SelectedExtKey) Hex() string {
|
|||
return k.Address.Hex()
|
||||
}
|
||||
|
||||
// ParseAccountString parses hex encoded string and returns is as accounts.Account.
|
||||
func ParseAccountString(account string) (accounts.Account, error) {
|
||||
// ParseAccountString parses hex encoded string and returns is as types.Account.
|
||||
func ParseAccountString(account string) (types.Account, error) {
|
||||
// valid address, convert to account
|
||||
if types.IsHexAddress(account) {
|
||||
return accounts.Account{Address: common.HexToAddress(account)}, nil
|
||||
return types.Account{Address: types.HexToAddress(account)}, nil
|
||||
}
|
||||
|
||||
return accounts.Account{}, ErrInvalidAccountAddressOrKey
|
||||
}
|
||||
|
||||
// GethFromAddress converts account address from string to common.Address.
|
||||
// The function is useful to format "From" field of send transaction struct.
|
||||
func GethFromAddress(accountAddress string) common.Address {
|
||||
from, err := ParseAccountString(accountAddress)
|
||||
if err != nil {
|
||||
return common.Address{}
|
||||
}
|
||||
|
||||
return from.Address
|
||||
return types.Account{}, ErrInvalidAccountAddressOrKey
|
||||
}
|
||||
|
||||
// FromAddress converts account address from string to types.Address.
|
||||
|
@ -117,12 +102,12 @@ func FromAddress(accountAddress string) types.Address {
|
|||
return types.Address{}
|
||||
}
|
||||
|
||||
return types.Address(from.Address)
|
||||
return from.Address
|
||||
}
|
||||
|
||||
// GethToAddress converts account address from string to *common.Address.
|
||||
// ToAddress converts account address from string to *common.Address.
|
||||
// The function is useful to format "To" field of send transaction struct.
|
||||
func GethToAddress(accountAddress string) *common.Address {
|
||||
func ToAddress(accountAddress string) *types.Address {
|
||||
to, err := ParseAccountString(accountAddress)
|
||||
if err != nil {
|
||||
return nil
|
||||
|
|
|
@ -4,9 +4,9 @@ package account
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
@ -21,13 +21,13 @@ func (suite *AccountUtilsTestSuite) SetupTest() {
|
|||
suite.validKey = "0xF35E0325dad87e2661c4eF951d58727e6d583d5c"
|
||||
}
|
||||
|
||||
func (suite *AccountUtilsTestSuite) TestGethToAddress() {
|
||||
addr := GethToAddress(suite.validKey)
|
||||
func (suite *AccountUtilsTestSuite) TestToAddress() {
|
||||
addr := ToAddress(suite.validKey)
|
||||
suite.Equal(suite.validKey, addr.String())
|
||||
}
|
||||
|
||||
func (suite *AccountUtilsTestSuite) TestGethToAddressInvalidAddress() {
|
||||
addr := GethToAddress("foobar")
|
||||
func (suite *AccountUtilsTestSuite) TestToAddressInvalidAddress() {
|
||||
addr := ToAddress("foobar")
|
||||
suite.Nil(addr)
|
||||
}
|
||||
|
||||
|
@ -46,21 +46,6 @@ func (suite *AccountUtilsTestSuite) TestFromAddress() {
|
|||
}
|
||||
}
|
||||
|
||||
func (suite *AccountUtilsTestSuite) TestGethFromAddress() {
|
||||
var flagtests = []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{suite.validKey, suite.validKey},
|
||||
{"foobar", "0x0000000000000000000000000000000000000000"},
|
||||
}
|
||||
|
||||
for _, tt := range flagtests {
|
||||
addr := GethFromAddress(tt.in)
|
||||
suite.Equal(tt.out, addr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AccountUtilsTestSuite) TestHex() {
|
||||
var addr *SelectedExtKey
|
||||
cr, _ := crypto.GenerateKey()
|
||||
|
@ -70,7 +55,7 @@ func (suite *AccountUtilsTestSuite) TestHex() {
|
|||
}{
|
||||
{&SelectedExtKey{
|
||||
Address: FromAddress(suite.validKey),
|
||||
AccountKey: &keystore.Key{PrivateKey: cr},
|
||||
AccountKey: &types.Key{PrivateKey: cr},
|
||||
}, suite.validKey},
|
||||
{addr, "0x0"},
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ type GethStatusBackend struct {
|
|||
personalAPI *personal.PublicAPI
|
||||
rpcFilters *rpcfilters.Service
|
||||
multiaccountsDB *multiaccounts.Database
|
||||
accountManager *account.Manager
|
||||
accountManager *account.GethManager
|
||||
transactor *transactions.Transactor
|
||||
connectionState connectionState
|
||||
appState appState
|
||||
|
@ -104,7 +104,7 @@ func (b *GethStatusBackend) StatusNode() *node.StatusNode {
|
|||
}
|
||||
|
||||
// AccountManager returns reference to account manager
|
||||
func (b *GethStatusBackend) AccountManager() *account.Manager {
|
||||
func (b *GethStatusBackend) AccountManager() *account.GethManager {
|
||||
return b.accountManager
|
||||
}
|
||||
|
||||
|
@ -386,7 +386,7 @@ func (b *GethStatusBackend) subscriptionService() gethnode.ServiceConstructor {
|
|||
|
||||
func (b *GethStatusBackend) accountsService(accountsFeed *event.Feed) gethnode.ServiceConstructor {
|
||||
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
|
||||
return accountssvc.NewService(accounts.NewDB(b.appDB), b.multiaccountsDB, b.accountManager, accountsFeed), nil
|
||||
return accountssvc.NewService(accounts.NewDB(b.appDB), b.multiaccountsDB, &b.accountManager.Manager, accountsFeed), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,7 +683,7 @@ func (b *GethStatusBackend) getVerifiedWalletAccount(address, password string) (
|
|||
}
|
||||
|
||||
return &account.SelectedExtKey{
|
||||
Address: types.Address(key.Address),
|
||||
Address: key.Address,
|
||||
AccountKey: key,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// Imported from github.com/ethereum/go-ethereum/accounts/keystore/keystore.go
|
||||
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
version = 3
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDecrypt = errors.New("could not decrypt key with given password")
|
||||
)
|
|
@ -0,0 +1,329 @@
|
|||
// Imported from github.com/ethereum/go-ethereum/accounts/keystore/passphrase.go
|
||||
// and github.com/ethereum/go-ethereum/accounts/keystore/presale.go
|
||||
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
keyHeaderKDF = "scrypt"
|
||||
)
|
||||
|
||||
type encryptedKeyJSONV3 struct {
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
Id string `json:"id"`
|
||||
Version int `json:"version"`
|
||||
ExtendedKey CryptoJSON `json:"extendedkey"`
|
||||
SubAccountIndex uint32 `json:"subaccountindex"`
|
||||
}
|
||||
|
||||
type encryptedKeyJSONV1 struct {
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
Id string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type CryptoJSON struct {
|
||||
Cipher string `json:"cipher"`
|
||||
CipherText string `json:"ciphertext"`
|
||||
CipherParams cipherparamsJSON `json:"cipherparams"`
|
||||
KDF string `json:"kdf"`
|
||||
KDFParams map[string]interface{} `json:"kdfparams"`
|
||||
MAC string `json:"mac"`
|
||||
}
|
||||
|
||||
type cipherparamsJSON struct {
|
||||
IV string `json:"iv"`
|
||||
}
|
||||
|
||||
// DecryptKey decrypts a key from a json blob, returning the private key itself.
|
||||
func DecryptKey(keyjson []byte, auth string) (*types.Key, error) {
|
||||
// Parse the json into a simple map to fetch the key version
|
||||
m := make(map[string]interface{})
|
||||
if err := json.Unmarshal(keyjson, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Depending on the version try to parse one way or another
|
||||
var (
|
||||
keyBytes, keyId []byte
|
||||
err error
|
||||
extKeyBytes []byte
|
||||
extKey *extkeys.ExtendedKey
|
||||
)
|
||||
|
||||
subAccountIndex, ok := m["subaccountindex"].(float64)
|
||||
if !ok {
|
||||
subAccountIndex = 0
|
||||
}
|
||||
|
||||
if version, ok := m["version"].(string); ok && version == "1" {
|
||||
k := new(encryptedKeyJSONV1)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBytes, keyId, err = decryptKeyV1(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extKey, err = extkeys.NewKeyFromString(extkeys.EmptyExtendedKeyString)
|
||||
} else {
|
||||
k := new(encryptedKeyJSONV3)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBytes, keyId, err = decryptKeyV3(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extKeyBytes, err = decryptExtendedKey(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extKey, err = extkeys.NewKeyFromString(string(extKeyBytes))
|
||||
}
|
||||
// Handle any decryption errors and return the key
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := crypto.ToECDSAUnsafe(keyBytes)
|
||||
|
||||
return &types.Key{
|
||||
Id: uuid.UUID(keyId),
|
||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||
PrivateKey: key,
|
||||
ExtendedKey: extKey,
|
||||
SubAccountIndex: uint32(subAccountIndex),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
||||
}
|
||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(cryptoJson.CipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(cryptoJson, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainText, err
|
||||
}
|
||||
|
||||
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||
if keyProtected.Version != version {
|
||||
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
||||
}
|
||||
keyId = uuid.Parse(keyProtected.Id)
|
||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||
keyId = uuid.Parse(keyProtected.Id)
|
||||
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
func decryptExtendedKey(keyProtected *encryptedKeyJSONV3, auth string) (plainText []byte, err error) {
|
||||
if len(keyProtected.ExtendedKey.CipherText) == 0 {
|
||||
return []byte(extkeys.EmptyExtendedKeyString), nil
|
||||
}
|
||||
|
||||
if keyProtected.Version != version {
|
||||
return nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
||||
}
|
||||
|
||||
if keyProtected.ExtendedKey.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.ExtendedKey.Cipher)
|
||||
}
|
||||
|
||||
mac, err := hex.DecodeString(keyProtected.ExtendedKey.MAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(keyProtected.ExtendedKey.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(keyProtected.ExtendedKey.CipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(keyProtected.ExtendedKey, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err = aesCTRXOR(derivedKey[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainText, err
|
||||
}
|
||||
|
||||
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||
authArray := []byte(auth)
|
||||
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
|
||||
|
||||
if cryptoJSON.KDF == keyHeaderKDF {
|
||||
n := ensureInt(cryptoJSON.KDFParams["n"])
|
||||
r := ensureInt(cryptoJSON.KDFParams["r"])
|
||||
p := ensureInt(cryptoJSON.KDFParams["p"])
|
||||
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
||||
|
||||
} else if cryptoJSON.KDF == "pbkdf2" {
|
||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||
if prf != "hmac-sha256" {
|
||||
return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
|
||||
}
|
||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
|
||||
}
|
||||
|
||||
// TODO: can we do without this when unmarshalling dynamic JSON?
|
||||
// why do integers in KDF params end up as float64 and not int after
|
||||
// unmarshal?
|
||||
func ensureInt(x interface{}) int {
|
||||
res, ok := x.(int)
|
||||
if !ok {
|
||||
res = int(x.(float64))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
|
||||
// AES-128 is selected due to size of encryptKey.
|
||||
aesBlock, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stream := cipher.NewCTR(aesBlock, iv)
|
||||
outText := make([]byte, len(inText))
|
||||
stream.XORKeyStream(outText, inText)
|
||||
return outText, err
|
||||
}
|
||||
|
||||
func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
|
||||
aesBlock, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
|
||||
paddedPlaintext := make([]byte, len(cipherText))
|
||||
decrypter.CryptBlocks(paddedPlaintext, cipherText)
|
||||
plaintext := pkcs7Unpad(paddedPlaintext)
|
||||
if plaintext == nil {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
|
||||
func pkcs7Unpad(in []byte) []byte {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
padding := in[len(in)-1]
|
||||
if int(padding) > len(in) || padding > aes.BlockSize {
|
||||
return nil
|
||||
} else if padding == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
|
||||
if in[i] != padding {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return in[:len(in)-int(padding)]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package types
|
||||
|
||||
// Account represents an Ethereum account located at a specific location defined
|
||||
// by the optional URL field.
|
||||
type Account struct {
|
||||
Address Address `json:"address"` // Ethereum account address derived from the key
|
||||
URL string `json:"url"` // Optional resource locator within a backend
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
Id uuid.UUID // Version 4 "random" for unique id not derived from key data
|
||||
// to simplify lookups we also store the address
|
||||
Address Address
|
||||
// we only store privkey as pubkey/address can be derived from it
|
||||
// privkey in this struct is always in plaintext
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
// ExtendedKey is the extended key of the PrivateKey itself, and it's used
|
||||
// to derive child keys.
|
||||
ExtendedKey *extkeys.ExtendedKey
|
||||
// SubAccountIndex is DEPRECATED
|
||||
// It was use in Status to keep track of the number of sub-account created
|
||||
// before having multi-account support.
|
||||
SubAccountIndex uint32
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
type KeyStore interface {
|
||||
// ImportAccount imports the account specified with privateKey.
|
||||
ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error)
|
||||
// ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields
|
||||
// of the Key struct.
|
||||
// ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0,
|
||||
// and ExtendedKey is the extended key of the BIP44 key at index 1.
|
||||
ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (Account, error)
|
||||
// ImportExtendedKeyForPurpose stores ECDSA key (obtained from extended key) along with CKD#2 (root for sub-accounts)
|
||||
// If key file is not found, it is created. Key is encrypted with the given passphrase.
|
||||
// Deprecated: status-go is now using ImportSingleExtendedKey
|
||||
ImportExtendedKeyForPurpose(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, passphrase string) (Account, error)
|
||||
// AccountDecryptedKey returns decrypted key for account (provided that password is correct).
|
||||
AccountDecryptedKey(a Account, auth string) (Account, *Key, error)
|
||||
// Delete deletes the key matched by account if the passphrase is correct.
|
||||
// If the account contains no filename, the address must match a unique key.
|
||||
Delete(a Account, auth string) error
|
||||
}
|
|
@ -23,12 +23,11 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/keystore"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/signal"
|
||||
|
@ -592,8 +591,8 @@ func testSendTransactionWithLogin(t *testing.T, feed *event.Feed) bool {
|
|||
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
||||
|
||||
args, err := json.Marshal(transactions.SendTxArgs{
|
||||
From: account.GethFromAddress(TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(TestConfig.Account2.WalletAddress),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -625,8 +624,8 @@ func testSendTransactionInvalidPassword(t *testing.T, feed *event.Feed) bool {
|
|||
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
||||
|
||||
args, err := json.Marshal(transactions.SendTxArgs{
|
||||
From: common.HexToAddress(acc.WalletAddress),
|
||||
To: account.GethToAddress(TestConfig.Account2.WalletAddress),
|
||||
From: types.HexToAddress(acc.WalletAddress),
|
||||
To: account.ToAddress(TestConfig.Account2.WalletAddress),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -653,8 +652,8 @@ func testFailedTransaction(t *testing.T, feed *event.Feed) bool {
|
|||
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
||||
|
||||
args, err := json.Marshal(transactions.SendTxArgs{
|
||||
From: *account.GethToAddress(TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(TestConfig.Account2.WalletAddress),
|
||||
From: *account.ToAddress(TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(TestConfig.Account2.WalletAddress),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -6,10 +6,9 @@ package status
|
|||
|
||||
import (
|
||||
ecdsa "crypto/ecdsa"
|
||||
accounts "github.com/ethereum/go-ethereum/accounts"
|
||||
keystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
account "github.com/status-im/status-go/account"
|
||||
types "github.com/status-im/status-go/eth-node/types"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
|
@ -75,11 +74,11 @@ func (m *MockAccountManager) EXPECT() *MockAccountManagerMockRecorder {
|
|||
}
|
||||
|
||||
// AddressToDecryptedAccount mocks base method
|
||||
func (m *MockAccountManager) AddressToDecryptedAccount(arg0, arg1 string) (accounts.Account, *keystore.Key, error) {
|
||||
func (m *MockAccountManager) AddressToDecryptedAccount(arg0, arg1 string) (types.Account, *types.Key, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddressToDecryptedAccount", arg0, arg1)
|
||||
ret0, _ := ret[0].(accounts.Account)
|
||||
ret1, _ := ret[1].(*keystore.Key)
|
||||
ret0, _ := ret[0].(types.Account)
|
||||
ret1, _ := ret[1].(*types.Key)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
|
@ -46,10 +44,10 @@ var logintests = []struct {
|
|||
expectedAddressKey: "addressKey",
|
||||
expectedError: nil,
|
||||
prepareExpectations: func(s *StatusSuite) {
|
||||
key := keystore.Key{
|
||||
key := types.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, nil)
|
||||
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("addressKey", nil)
|
||||
|
||||
loginParams := account.LoginParams{
|
||||
|
@ -65,10 +63,10 @@ var logintests = []struct {
|
|||
expectedAddressKey: "",
|
||||
expectedError: errors.New("foo"),
|
||||
prepareExpectations: func(s *StatusSuite) {
|
||||
key := keystore.Key{
|
||||
key := types.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, errors.New("foo"))
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, errors.New("foo"))
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -76,10 +74,10 @@ var logintests = []struct {
|
|||
expectedAddressKey: "",
|
||||
expectedError: errors.New("foo"),
|
||||
prepareExpectations: func(s *StatusSuite) {
|
||||
key := keystore.Key{
|
||||
key := types.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, nil)
|
||||
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", errors.New("foo"))
|
||||
},
|
||||
},
|
||||
|
@ -88,10 +86,10 @@ var logintests = []struct {
|
|||
expectedAddressKey: "",
|
||||
expectedError: errors.New("foo"),
|
||||
prepareExpectations: func(s *StatusSuite) {
|
||||
key := keystore.Key{
|
||||
key := types.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, nil)
|
||||
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", nil)
|
||||
|
||||
loginParams := account.LoginParams{
|
||||
|
|
|
@ -3,12 +3,11 @@ package status
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
)
|
||||
|
||||
// Make sure that Service implements node.Service interface.
|
||||
|
@ -21,7 +20,7 @@ type WhisperService interface {
|
|||
|
||||
// AccountManager interface to manage account actions
|
||||
type AccountManager interface {
|
||||
AddressToDecryptedAccount(string, string) (accounts.Account, *keystore.Key, error)
|
||||
AddressToDecryptedAccount(string, string) (types.Account, *types.Key, error)
|
||||
SelectAccount(account.LoginParams) error
|
||||
CreateAccount(password string) (accountInfo account.Info, mnemonic string, err error)
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ func (s *TransactionsTestSuite) TestEmptyToFieldPreserved() {
|
|||
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
|
||||
|
||||
args := transactions.SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
}
|
||||
|
||||
hash, err := s.Backend.SendTransaction(args, utils.TestConfig.Account1.Password)
|
||||
|
@ -135,7 +135,7 @@ func (s *TransactionsTestSuite) TestSendContractTxCompat() {
|
|||
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
|
||||
|
||||
initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
|
||||
args.Data = (hexutil.Bytes)(byteCode)
|
||||
args.Data = (types.HexBytes)(byteCode)
|
||||
}
|
||||
s.testSendContractTx(initFunc, nil, "")
|
||||
}
|
||||
|
@ -148,8 +148,8 @@ func (s *TransactionsTestSuite) TestSendContractTxCollision() {
|
|||
|
||||
// Scenario 1: Both fields are filled and have the same value, expect success
|
||||
initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
|
||||
args.Input = (hexutil.Bytes)(byteCode)
|
||||
args.Data = (hexutil.Bytes)(byteCode)
|
||||
args.Input = (types.HexBytes)(byteCode)
|
||||
args.Data = (types.HexBytes)(byteCode)
|
||||
}
|
||||
s.testSendContractTx(initFunc, nil, "")
|
||||
|
||||
|
@ -164,8 +164,8 @@ func (s *TransactionsTestSuite) TestSendContractTxCollision() {
|
|||
}
|
||||
|
||||
initFunc2 := func(byteCode []byte, args *transactions.SendTxArgs) {
|
||||
args.Input = (hexutil.Bytes)(byteCode)
|
||||
args.Data = (hexutil.Bytes)(inverted(byteCode))
|
||||
args.Input = (types.HexBytes)(byteCode)
|
||||
args.Data = (types.HexBytes)(inverted(byteCode))
|
||||
}
|
||||
s.testSendContractTx(initFunc2, transactions.ErrInvalidSendTxArgs, "expected error when invalid tx args are sent")
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ func (s *TransactionsTestSuite) TestSendContractTx() {
|
|||
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
|
||||
|
||||
initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
|
||||
args.Input = (hexutil.Bytes)(byteCode)
|
||||
args.Input = (types.HexBytes)(byteCode)
|
||||
}
|
||||
s.testSendContractTx(initFunc, nil, "")
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc
|
|||
|
||||
gas := uint64(params.DefaultGas)
|
||||
args := transactions.SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: nil, // marker, contract creation is expected
|
||||
//Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), common.Ether)),
|
||||
Gas: (*hexutil.Uint64)(&gas),
|
||||
|
@ -231,8 +231,8 @@ func (s *TransactionsTestSuite) TestSendEther() {
|
|||
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
|
||||
|
||||
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
}, utils.TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
@ -257,8 +257,8 @@ func (s *TransactionsTestSuite) TestSendEtherTxUpstream() {
|
|||
defer s.LogoutAndStop()
|
||||
|
||||
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
GasPrice: (*hexutil.Big)(big.NewInt(28000000000)),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
}, utils.TestConfig.Account1.Password)
|
||||
|
|
|
@ -5,21 +5,21 @@ package transactions
|
|||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
)
|
||||
|
||||
// AddrLocker provides locks for addresses
|
||||
type AddrLocker struct {
|
||||
mu sync.Mutex
|
||||
locks map[common.Address]*sync.Mutex
|
||||
locks map[types.Address]*sync.Mutex
|
||||
}
|
||||
|
||||
// lock returns the lock of the given address.
|
||||
func (l *AddrLocker) lock(address common.Address) *sync.Mutex {
|
||||
func (l *AddrLocker) lock(address types.Address) *sync.Mutex {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.locks == nil {
|
||||
l.locks = make(map[common.Address]*sync.Mutex)
|
||||
l.locks = make(map[types.Address]*sync.Mutex)
|
||||
}
|
||||
if _, ok := l.locks[address]; !ok {
|
||||
l.locks[address] = new(sync.Mutex)
|
||||
|
@ -30,11 +30,11 @@ func (l *AddrLocker) lock(address common.Address) *sync.Mutex {
|
|||
// LockAddr locks an account's mutex. This is used to prevent another tx getting the
|
||||
// same nonce until the lock is released. The mutex prevents the (an identical nonce) from
|
||||
// being read again during the time that the first transaction is being signed.
|
||||
func (l *AddrLocker) LockAddr(address common.Address) {
|
||||
func (l *AddrLocker) LockAddr(address types.Address) {
|
||||
l.lock(address).Lock()
|
||||
}
|
||||
|
||||
// UnlockAddr unlocks the mutex of the given account.
|
||||
func (l *AddrLocker) UnlockAddr(address common.Address) {
|
||||
func (l *AddrLocker) UnlockAddr(address types.Address) {
|
||||
l.lock(address).Unlock()
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ import (
|
|||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
|
||||
"github.com/status-im/status-go/rpc"
|
||||
)
|
||||
|
@ -57,12 +58,12 @@ func (w *rpcWrapper) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uin
|
|||
//
|
||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||
// contract address after the transaction has been mined.
|
||||
func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||
func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transaction) error {
|
||||
data, err := rlp.EncodeToBytes(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return w.rpcClient.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data))
|
||||
return w.rpcClient.CallContext(ctx, nil, "eth_sendRawTransaction", types.EncodeHex(data))
|
||||
}
|
||||
|
||||
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
|
@ -71,7 +72,7 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
|||
"to": msg.To,
|
||||
}
|
||||
if len(msg.Data) > 0 {
|
||||
arg["data"] = hexutil.Bytes(msg.Data)
|
||||
arg["data"] = types.HexBytes(msg.Data)
|
||||
}
|
||||
if msg.Value != nil {
|
||||
arg["value"] = (*hexutil.Big)(msg.Value)
|
||||
|
|
|
@ -10,12 +10,13 @@ import (
|
|||
"time"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
)
|
||||
|
@ -170,9 +171,18 @@ func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs,
|
|||
if args.Gas == nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
var (
|
||||
gethTo common.Address
|
||||
gethToPtr *common.Address
|
||||
)
|
||||
if args.To != nil {
|
||||
gethTo = common.Address(*args.To)
|
||||
gethToPtr = &gethTo
|
||||
}
|
||||
gas, err = t.gasCalculator.EstimateGas(ctx, ethereum.CallMsg{
|
||||
From: args.From,
|
||||
To: args.To,
|
||||
From: common.Address(args.From),
|
||||
To: gethToPtr,
|
||||
GasPrice: gasPrice,
|
||||
Value: value,
|
||||
Data: args.GetInput(),
|
||||
|
@ -238,7 +248,7 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
|
|||
}()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
nonce, err = t.pendingNonceProvider.PendingNonceAt(ctx, args.From)
|
||||
nonce, err = t.pendingNonceProvider.PendingNonceAt(ctx, common.Address(args.From))
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
@ -264,9 +274,18 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
|
|||
if args.Gas == nil {
|
||||
ctx, cancel = context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
var (
|
||||
gethTo common.Address
|
||||
gethToPtr *common.Address
|
||||
)
|
||||
if args.To != nil {
|
||||
gethTo = common.Address(*args.To)
|
||||
gethToPtr = &gethTo
|
||||
}
|
||||
gas, err = t.gasCalculator.EstimateGas(ctx, ethereum.CallMsg{
|
||||
From: args.From,
|
||||
To: args.To,
|
||||
From: common.Address(args.From),
|
||||
To: gethToPtr,
|
||||
GasPrice: gasPrice,
|
||||
Value: value,
|
||||
Data: args.GetInput(),
|
||||
|
@ -282,14 +301,7 @@ func (t *Transactor) validateAndPropagate(selectedAccount *account.SelectedExtKe
|
|||
gas = uint64(*args.Gas)
|
||||
}
|
||||
|
||||
var tx *gethtypes.Transaction
|
||||
if args.To != nil {
|
||||
tx = gethtypes.NewTransaction(nonce, *args.To, value, gas, gasPrice, args.GetInput())
|
||||
t.logNewTx(args, gas, gasPrice, value)
|
||||
} else {
|
||||
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput())
|
||||
t.logNewContract(args, gas, gasPrice, value, nonce)
|
||||
}
|
||||
tx := t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
||||
|
||||
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewEIP155Signer(chainID), selectedAccount.AccountKey.PrivateKey)
|
||||
if err != nil {
|
||||
|
@ -310,10 +322,14 @@ func (t *Transactor) buildTransaction(args SendTxArgs) *gethtypes.Transaction {
|
|||
gas := uint64(*args.Gas)
|
||||
gasPrice := (*big.Int)(args.GasPrice)
|
||||
|
||||
return t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
|
||||
}
|
||||
|
||||
func (t *Transactor) buildTransactionWithOverrides(nonce uint64, value *big.Int, gas uint64, gasPrice *big.Int, args SendTxArgs) *gethtypes.Transaction {
|
||||
var tx *gethtypes.Transaction
|
||||
|
||||
if args.To != nil {
|
||||
tx = gethtypes.NewTransaction(nonce, *args.To, value, gas, gasPrice, args.GetInput())
|
||||
tx = gethtypes.NewTransaction(nonce, common.Address(*args.To), value, gas, gasPrice, args.GetInput())
|
||||
t.logNewTx(args, gas, gasPrice, value)
|
||||
} else {
|
||||
tx = gethtypes.NewContractCreation(nonce, value, gas, gasPrice, args.GetInput())
|
||||
|
@ -337,7 +353,7 @@ func (t *Transactor) getTransactionNonce(args SendTxArgs) (newNonce uint64, err
|
|||
// get the remote nonce
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
remoteNonce, err = t.pendingNonceProvider.PendingNonceAt(ctx, args.From)
|
||||
remoteNonce, err = t.pendingNonceProvider.PendingNonceAt(ctx, common.Address(args.From))
|
||||
if err != nil {
|
||||
return newNonce, err
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
@ -23,6 +22,7 @@ import (
|
|||
"github.com/golang/mock/gomock"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts/ens/contract"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
|
@ -104,7 +104,7 @@ func (s *TransactorSuite) setupTransactionPoolAPI(args SendTxArgs, returnNonce,
|
|||
func (s *TransactorSuite) rlpEncodeTx(args SendTxArgs, config *params.NodeConfig, account *account.SelectedExtKey, nonce *hexutil.Uint64, gas hexutil.Uint64, gasPrice *big.Int) hexutil.Bytes {
|
||||
newTx := gethtypes.NewTransaction(
|
||||
uint64(*nonce),
|
||||
*args.To,
|
||||
common.Address(*args.To),
|
||||
args.Value.ToInt(),
|
||||
uint64(gas),
|
||||
gasPrice,
|
||||
|
@ -122,7 +122,7 @@ func (s *TransactorSuite) TestGasValues() {
|
|||
key, _ := gethcrypto.GenerateKey()
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
AccountKey: &keystore.Key{PrivateKey: key},
|
||||
AccountKey: &types.Key{PrivateKey: key},
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@ -155,8 +155,8 @@ func (s *TransactorSuite) TestGasValues() {
|
|||
s.T().Run(testCase.name, func(t *testing.T) {
|
||||
s.SetupTest()
|
||||
args := SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
Gas: testCase.gas,
|
||||
GasPrice: testCase.gasPrice,
|
||||
}
|
||||
|
@ -171,10 +171,10 @@ func (s *TransactorSuite) TestGasValues() {
|
|||
|
||||
func (s *TransactorSuite) TestArgsValidation() {
|
||||
args := SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
Data: hexutil.Bytes([]byte{0x01, 0x02}),
|
||||
Input: hexutil.Bytes([]byte{0x02, 0x01}),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
Data: types.HexBytes([]byte{0x01, 0x02}),
|
||||
Input: types.HexBytes([]byte{0x02, 0x01}),
|
||||
}
|
||||
s.False(args.Valid())
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
|
@ -186,8 +186,8 @@ func (s *TransactorSuite) TestArgsValidation() {
|
|||
|
||||
func (s *TransactorSuite) TestAccountMismatch() {
|
||||
args := SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
}
|
||||
|
||||
var err error
|
||||
|
@ -216,14 +216,14 @@ func (s *TransactorSuite) TestLocalNonce() {
|
|||
key, _ := gethcrypto.GenerateKey()
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
AccountKey: &keystore.Key{PrivateKey: key},
|
||||
AccountKey: &types.Key{PrivateKey: key},
|
||||
}
|
||||
nonce := hexutil.Uint64(0)
|
||||
|
||||
for i := 0; i < txCount; i++ {
|
||||
args := SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
}
|
||||
s.setupTransactionPoolAPI(args, nonce, hexutil.Uint64(i), selectedAccount, nil)
|
||||
|
||||
|
@ -235,8 +235,8 @@ func (s *TransactorSuite) TestLocalNonce() {
|
|||
|
||||
nonce = hexutil.Uint64(5)
|
||||
args := SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
}
|
||||
|
||||
s.setupTransactionPoolAPI(args, nonce, nonce, selectedAccount, nil)
|
||||
|
@ -250,8 +250,8 @@ func (s *TransactorSuite) TestLocalNonce() {
|
|||
testErr := errors.New("test")
|
||||
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), gomock.Eq(common.Address(selectedAccount.Address)), gethrpc.PendingBlockNumber).Return(nil, testErr)
|
||||
args = SendTxArgs{
|
||||
From: account.GethFromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.GethToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
|
||||
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
|
||||
}
|
||||
|
||||
_, err = s.manager.SendTransaction(args, selectedAccount)
|
||||
|
@ -269,14 +269,14 @@ func (s *TransactorSuite) TestContractCreation() {
|
|||
backend := backends.NewSimulatedBackend(genesis, math.MaxInt64)
|
||||
selectedAccount := &account.SelectedExtKey{
|
||||
Address: types.Address(testaddr),
|
||||
AccountKey: &keystore.Key{PrivateKey: key},
|
||||
AccountKey: &types.Key{PrivateKey: key},
|
||||
}
|
||||
s.manager.sender = backend
|
||||
s.manager.gasCalculator = backend
|
||||
s.manager.pendingNonceProvider = backend
|
||||
tx := SendTxArgs{
|
||||
From: testaddr,
|
||||
Input: hexutil.Bytes(common.FromHex(contract.ENSBin)),
|
||||
From: types.Address(testaddr),
|
||||
Input: types.FromHex(contract.ENSBin),
|
||||
}
|
||||
|
||||
hash, err := s.manager.SendTransaction(tx, selectedAccount)
|
||||
|
@ -288,9 +288,9 @@ func (s *TransactorSuite) TestContractCreation() {
|
|||
}
|
||||
|
||||
func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
||||
privKey, err := gethcrypto.GenerateKey()
|
||||
privKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
address := gethcrypto.PubkeyToAddress(privKey.PublicKey)
|
||||
address := crypto.PubkeyToAddress(privKey.PublicKey)
|
||||
|
||||
scenarios := []struct {
|
||||
localNonce hexutil.Uint64
|
||||
|
@ -340,7 +340,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
|||
|
||||
// simulate transaction signed externally
|
||||
signer := gethtypes.NewEIP155Signer(chainID)
|
||||
tx := gethtypes.NewTransaction(uint64(nonce), to, (*big.Int)(value), uint64(gas), (*big.Int)(gasPrice), data)
|
||||
tx := gethtypes.NewTransaction(uint64(nonce), common.Address(to), (*big.Int)(value), uint64(gas), (*big.Int)(gasPrice), data)
|
||||
hash := signer.Hash(tx)
|
||||
sig, err := gethcrypto.Sign(hash[:], privKey)
|
||||
s.Require().NoError(err)
|
||||
|
@ -350,7 +350,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
s.txServiceMock.EXPECT().
|
||||
GetTransactionCount(gomock.Any(), address, gethrpc.PendingBlockNumber).
|
||||
GetTransactionCount(gomock.Any(), common.Address(address), gethrpc.PendingBlockNumber).
|
||||
Return(&scenario.localNonce, nil)
|
||||
|
||||
if !scenario.expectError {
|
||||
|
@ -382,9 +382,9 @@ func (s *TransactorSuite) TestSendTransactionWithSignature_InvalidSignature() {
|
|||
}
|
||||
|
||||
func (s *TransactorSuite) TestHashTransaction() {
|
||||
privKey, err := gethcrypto.GenerateKey()
|
||||
privKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
address := gethcrypto.PubkeyToAddress(privKey.PublicKey)
|
||||
address := crypto.PubkeyToAddress(privKey.PublicKey)
|
||||
|
||||
remoteNonce := hexutil.Uint64(1)
|
||||
txNonce := hexutil.Uint64(0)
|
||||
|
@ -405,7 +405,7 @@ func (s *TransactorSuite) TestHashTransaction() {
|
|||
}
|
||||
|
||||
s.txServiceMock.EXPECT().
|
||||
GetTransactionCount(gomock.Any(), address, gethrpc.PendingBlockNumber).
|
||||
GetTransactionCount(gomock.Any(), common.Address(address), gethrpc.PendingBlockNumber).
|
||||
Return(&remoteNonce, nil)
|
||||
|
||||
newArgs, hash, err := s.manager.HashTransaction(args)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -36,8 +37,8 @@ type GasCalculator interface {
|
|||
// This struct is based on go-ethereum's type in internal/ethapi/api.go, but we have freedom
|
||||
// over the exact layout of this struct.
|
||||
type SendTxArgs struct {
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
From types.Address `json:"from"`
|
||||
To *types.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
|
@ -45,8 +46,8 @@ type SendTxArgs struct {
|
|||
// We keep both "input" and "data" for backward compatibility.
|
||||
// "input" is a preferred field.
|
||||
// see `vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go:1107`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
Input types.HexBytes `json:"input"`
|
||||
Data types.HexBytes `json:"data"`
|
||||
}
|
||||
|
||||
// Valid checks whether this structure is filled in correctly.
|
||||
|
@ -61,7 +62,7 @@ func (args SendTxArgs) Valid() bool {
|
|||
}
|
||||
|
||||
// GetInput returns either Input or Data field's value dependent on what is filled.
|
||||
func (args SendTxArgs) GetInput() hexutil.Bytes {
|
||||
func (args SendTxArgs) GetInput() types.HexBytes {
|
||||
if !isNilOrEmpty(args.Input) {
|
||||
return args.Input
|
||||
}
|
||||
|
@ -69,6 +70,6 @@ func (args SendTxArgs) GetInput() hexutil.Bytes {
|
|||
return args.Data
|
||||
}
|
||||
|
||||
func isNilOrEmpty(bytes hexutil.Bytes) bool {
|
||||
func isNilOrEmpty(bytes types.HexBytes) bool {
|
||||
return bytes == nil || len(bytes) == 0
|
||||
}
|
||||
|
|
|
@ -3,17 +3,17 @@ package transactions
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSendTxArgsValidity(t *testing.T) {
|
||||
// 1. If only data fields is set, valid and return data
|
||||
|
||||
bytes1 := hexutil.Bytes([]byte{0xAA, 0xBB, 0xCC, 0xDD})
|
||||
bytes2 := hexutil.Bytes([]byte{0x00, 0x01, 0x02})
|
||||
bytes1 := types.HexBytes([]byte{0xAA, 0xBB, 0xCC, 0xDD})
|
||||
bytes2 := types.HexBytes([]byte{0x00, 0x01, 0x02})
|
||||
|
||||
bytesEmpty := hexutil.Bytes([]byte{})
|
||||
bytesEmpty := types.HexBytes([]byte{})
|
||||
|
||||
doSendTxValidityTest(t, SendTxArgs{}, true, nil)
|
||||
doSendTxValidityTest(t, SendTxArgs{Input: bytes1}, true, bytes1)
|
||||
|
@ -25,7 +25,7 @@ func TestSendTxArgsValidity(t *testing.T) {
|
|||
doSendTxValidityTest(t, SendTxArgs{Input: bytesEmpty, Data: bytesEmpty}, true, bytesEmpty)
|
||||
}
|
||||
|
||||
func doSendTxValidityTest(t *testing.T, args SendTxArgs, expectValid bool, expectValue hexutil.Bytes) {
|
||||
func doSendTxValidityTest(t *testing.T, args SendTxArgs, expectValid bool, expectValue types.HexBytes) {
|
||||
assert.Equal(t, expectValid, args.Valid(), "Valid() returned unexpected value")
|
||||
if expectValid {
|
||||
assert.Equal(t, expectValue, args.GetInput(), "GetInput() returned unexpected value")
|
||||
|
|
15
vendor/github.com/status-im/status-go/eth-node/keystore/keystore.go
generated
vendored
Normal file
15
vendor/github.com/status-im/status-go/eth-node/keystore/keystore.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Imported from github.com/ethereum/go-ethereum/accounts/keystore/keystore.go
|
||||
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
version = 3
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDecrypt = errors.New("could not decrypt key with given password")
|
||||
)
|
329
vendor/github.com/status-im/status-go/eth-node/keystore/passphrase.go
generated
vendored
Normal file
329
vendor/github.com/status-im/status-go/eth-node/keystore/passphrase.go
generated
vendored
Normal file
|
@ -0,0 +1,329 @@
|
|||
// Imported from github.com/ethereum/go-ethereum/accounts/keystore/passphrase.go
|
||||
// and github.com/ethereum/go-ethereum/accounts/keystore/presale.go
|
||||
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
keyHeaderKDF = "scrypt"
|
||||
)
|
||||
|
||||
type encryptedKeyJSONV3 struct {
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
Id string `json:"id"`
|
||||
Version int `json:"version"`
|
||||
ExtendedKey CryptoJSON `json:"extendedkey"`
|
||||
SubAccountIndex uint32 `json:"subaccountindex"`
|
||||
}
|
||||
|
||||
type encryptedKeyJSONV1 struct {
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
Id string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type CryptoJSON struct {
|
||||
Cipher string `json:"cipher"`
|
||||
CipherText string `json:"ciphertext"`
|
||||
CipherParams cipherparamsJSON `json:"cipherparams"`
|
||||
KDF string `json:"kdf"`
|
||||
KDFParams map[string]interface{} `json:"kdfparams"`
|
||||
MAC string `json:"mac"`
|
||||
}
|
||||
|
||||
type cipherparamsJSON struct {
|
||||
IV string `json:"iv"`
|
||||
}
|
||||
|
||||
// DecryptKey decrypts a key from a json blob, returning the private key itself.
|
||||
func DecryptKey(keyjson []byte, auth string) (*types.Key, error) {
|
||||
// Parse the json into a simple map to fetch the key version
|
||||
m := make(map[string]interface{})
|
||||
if err := json.Unmarshal(keyjson, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Depending on the version try to parse one way or another
|
||||
var (
|
||||
keyBytes, keyId []byte
|
||||
err error
|
||||
extKeyBytes []byte
|
||||
extKey *extkeys.ExtendedKey
|
||||
)
|
||||
|
||||
subAccountIndex, ok := m["subaccountindex"].(float64)
|
||||
if !ok {
|
||||
subAccountIndex = 0
|
||||
}
|
||||
|
||||
if version, ok := m["version"].(string); ok && version == "1" {
|
||||
k := new(encryptedKeyJSONV1)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBytes, keyId, err = decryptKeyV1(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extKey, err = extkeys.NewKeyFromString(extkeys.EmptyExtendedKeyString)
|
||||
} else {
|
||||
k := new(encryptedKeyJSONV3)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBytes, keyId, err = decryptKeyV3(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extKeyBytes, err = decryptExtendedKey(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extKey, err = extkeys.NewKeyFromString(string(extKeyBytes))
|
||||
}
|
||||
// Handle any decryption errors and return the key
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := crypto.ToECDSAUnsafe(keyBytes)
|
||||
|
||||
return &types.Key{
|
||||
Id: uuid.UUID(keyId),
|
||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||
PrivateKey: key,
|
||||
ExtendedKey: extKey,
|
||||
SubAccountIndex: uint32(subAccountIndex),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
||||
}
|
||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(cryptoJson.CipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(cryptoJson, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainText, err
|
||||
}
|
||||
|
||||
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||
if keyProtected.Version != version {
|
||||
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
||||
}
|
||||
keyId = uuid.Parse(keyProtected.Id)
|
||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||
keyId = uuid.Parse(keyProtected.Id)
|
||||
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
func decryptExtendedKey(keyProtected *encryptedKeyJSONV3, auth string) (plainText []byte, err error) {
|
||||
if len(keyProtected.ExtendedKey.CipherText) == 0 {
|
||||
return []byte(extkeys.EmptyExtendedKeyString), nil
|
||||
}
|
||||
|
||||
if keyProtected.Version != version {
|
||||
return nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
||||
}
|
||||
|
||||
if keyProtected.ExtendedKey.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.ExtendedKey.Cipher)
|
||||
}
|
||||
|
||||
mac, err := hex.DecodeString(keyProtected.ExtendedKey.MAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(keyProtected.ExtendedKey.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(keyProtected.ExtendedKey.CipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(keyProtected.ExtendedKey, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err = aesCTRXOR(derivedKey[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainText, err
|
||||
}
|
||||
|
||||
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||
authArray := []byte(auth)
|
||||
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
|
||||
|
||||
if cryptoJSON.KDF == keyHeaderKDF {
|
||||
n := ensureInt(cryptoJSON.KDFParams["n"])
|
||||
r := ensureInt(cryptoJSON.KDFParams["r"])
|
||||
p := ensureInt(cryptoJSON.KDFParams["p"])
|
||||
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
||||
|
||||
} else if cryptoJSON.KDF == "pbkdf2" {
|
||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||
if prf != "hmac-sha256" {
|
||||
return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
|
||||
}
|
||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
|
||||
}
|
||||
|
||||
// TODO: can we do without this when unmarshalling dynamic JSON?
|
||||
// why do integers in KDF params end up as float64 and not int after
|
||||
// unmarshal?
|
||||
func ensureInt(x interface{}) int {
|
||||
res, ok := x.(int)
|
||||
if !ok {
|
||||
res = int(x.(float64))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
|
||||
// AES-128 is selected due to size of encryptKey.
|
||||
aesBlock, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stream := cipher.NewCTR(aesBlock, iv)
|
||||
outText := make([]byte, len(inText))
|
||||
stream.XORKeyStream(outText, inText)
|
||||
return outText, err
|
||||
}
|
||||
|
||||
func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
|
||||
aesBlock, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
|
||||
paddedPlaintext := make([]byte, len(cipherText))
|
||||
decrypter.CryptBlocks(paddedPlaintext, cipherText)
|
||||
plaintext := pkcs7Unpad(paddedPlaintext)
|
||||
if plaintext == nil {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
|
||||
func pkcs7Unpad(in []byte) []byte {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
padding := in[len(in)-1]
|
||||
if int(padding) > len(in) || padding > aes.BlockSize {
|
||||
return nil
|
||||
} else if padding == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
|
||||
if in[i] != padding {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return in[:len(in)-int(padding)]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package types
|
||||
|
||||
// Account represents an Ethereum account located at a specific location defined
|
||||
// by the optional URL field.
|
||||
type Account struct {
|
||||
Address Address `json:"address"` // Ethereum account address derived from the key
|
||||
URL string `json:"url"` // Optional resource locator within a backend
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
Id uuid.UUID // Version 4 "random" for unique id not derived from key data
|
||||
// to simplify lookups we also store the address
|
||||
Address Address
|
||||
// we only store privkey as pubkey/address can be derived from it
|
||||
// privkey in this struct is always in plaintext
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
// ExtendedKey is the extended key of the PrivateKey itself, and it's used
|
||||
// to derive child keys.
|
||||
ExtendedKey *extkeys.ExtendedKey
|
||||
// SubAccountIndex is DEPRECATED
|
||||
// It was use in Status to keep track of the number of sub-account created
|
||||
// before having multi-account support.
|
||||
SubAccountIndex uint32
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
type KeyStore interface {
|
||||
// ImportAccount imports the account specified with privateKey.
|
||||
ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (Account, error)
|
||||
// ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields
|
||||
// of the Key struct.
|
||||
// ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0,
|
||||
// and ExtendedKey is the extended key of the BIP44 key at index 1.
|
||||
ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (Account, error)
|
||||
// ImportExtendedKeyForPurpose stores ECDSA key (obtained from extended key) along with CKD#2 (root for sub-accounts)
|
||||
// If key file is not found, it is created. Key is encrypted with the given passphrase.
|
||||
// Deprecated: status-go is now using ImportSingleExtendedKey
|
||||
ImportExtendedKeyForPurpose(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, passphrase string) (Account, error)
|
||||
// AccountDecryptedKey returns decrypted key for account (provided that password is correct).
|
||||
AccountDecryptedKey(a Account, auth string) (Account, *Key, error)
|
||||
// Delete deletes the key matched by account if the passphrase is correct.
|
||||
// If the account contains no filename, the address must match a unique key.
|
||||
Delete(a Account, auth string) error
|
||||
}
|
|
@ -374,6 +374,7 @@ github.com/status-im/status-go/eth-node/bridge/geth
|
|||
github.com/status-im/status-go/eth-node/bridge/geth/ens
|
||||
github.com/status-im/status-go/eth-node/crypto
|
||||
github.com/status-im/status-go/eth-node/crypto/ecies
|
||||
github.com/status-im/status-go/eth-node/keystore
|
||||
github.com/status-im/status-go/eth-node/types
|
||||
github.com/status-im/status-go/eth-node/types/ens
|
||||
# github.com/status-im/status-go/extkeys v1.0.0 => ./extkeys
|
||||
|
|
|
@ -116,7 +116,7 @@ func TestPeerLimiterHandlerWithWhitelisting(t *testing.T) {
|
|||
LimitPerSecIP: 1,
|
||||
LimitPerSecPeerID: 1,
|
||||
WhitelistedIPs: []string{"<nil>"}, // no IP is represented as <nil> string
|
||||
WhitelistedPeerIDs: []enode.ID{enode.ID{0xaa, 0xbb, 0xcc}},
|
||||
WhitelistedPeerIDs: []enode.ID{{0xaa, 0xbb, 0xcc}},
|
||||
}, h)
|
||||
p := &Peer{
|
||||
peer: p2p.NewPeer(enode.ID{0xaa, 0xbb, 0xcc}, "test-peer", nil),
|
||||
|
|
Loading…
Reference in New Issue