Allow to create an empty imported wallet (#8251)

* reload keys into empty wallet

# Conflicts:
#	validator/accounts/accounts.go

* removed warning on wallet creation

* export AccountsKeystoreRepresentation type

* rename error message variable

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Radosław Kapka
2021-01-12 17:52:01 +01:00
committed by GitHub
parent 5dda2ca328
commit aa69e5edcc
17 changed files with 77 additions and 83 deletions

View File

@@ -7,6 +7,11 @@ import (
var msgKeymanagerNotSupported = "keymanager kind not supported: %s"
var (
// ErrCouldNotInitializeKeymanager informs about failed keymanager initialization
ErrCouldNotInitializeKeymanager = "could not initialize keymanager"
)
// AccountsConfig specifies parameters to run to delete, enable, disable accounts.
type AccountsConfig struct {
Wallet *wallet.Wallet

View File

@@ -53,7 +53,7 @@ func BackupAccountsCli(cliCtx *cli.Context) error {
}
km, err := w.InitializeKeymanager(cliCtx.Context)
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
return errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
pubKeys, err := km.FetchAllValidatingPublicKeys(cliCtx.Context)
if err != nil {

View File

@@ -29,7 +29,7 @@ func DeleteAccountCli(cliCtx *cli.Context) error {
}
keymanager, err := w.InitializeKeymanager(cliCtx.Context)
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
return errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
validatingPublicKeys, err := keymanager.FetchAllValidatingPublicKeys(cliCtx.Context)
if err != nil {

View File

@@ -25,7 +25,7 @@ func DisableAccountsCli(cliCtx *cli.Context) error {
}
km, err := w.InitializeKeymanager(cliCtx.Context)
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
return errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
validatingPublicKeys, err := km.FetchValidatingPublicKeys(cliCtx.Context)
if err != nil {
@@ -102,7 +102,7 @@ func EnableAccountsCli(cliCtx *cli.Context) error {
}
km, err := w.InitializeKeymanager(cliCtx.Context)
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
return errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
importedKM, ok := km.(*imported.Keymanager)
if !ok {

View File

@@ -84,7 +84,7 @@ func prepareWallet(cliCtx *cli.Context) (validatingPublicKeys [][48]byte, km key
km, err = w.InitializeKeymanager(cliCtx.Context)
if err != nil {
return nil, nil, errors.Wrap(err, "could not initialize keymanager")
return nil, nil, errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
validatingPublicKeys, err = km.FetchValidatingPublicKeys(cliCtx.Context)
if err != nil {

View File

@@ -116,7 +116,7 @@ func TestPrepareWallet_EmptyWalletReturnsError(t *testing.T) {
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Imported,
WalletPassword: "Passwordz0320$",
WalletPassword: password,
},
})
require.NoError(t, err)

View File

@@ -32,7 +32,7 @@ func ListAccountsCli(cliCtx *cli.Context) error {
return errors.New("wrong wallet password entered")
}
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
return errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
showDepositData := cliCtx.Bool(flags.ShowDepositDataFlag.Name)
showPrivateKeys := cliCtx.Bool(flags.ShowPrivateKeysFlag.Name)

View File

@@ -2,6 +2,7 @@ package accounts
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
@@ -14,6 +15,7 @@ import (
"github.com/prysmaticlabs/prysm/validator/flags"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/prysmaticlabs/prysm/validator/keymanager/derived"
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
"github.com/prysmaticlabs/prysm/validator/keymanager/remote"
"github.com/urfave/cli/v2"
)
@@ -35,10 +37,6 @@ func CreateAndSaveWalletCli(cliCtx *cli.Context) (*wallet.Wallet, error) {
if err != nil {
return nil, err
}
if keymanagerKind == keymanager.Imported {
log.Warning("Creating an imported wallet does not provide anything beneficial. " +
"This option will be removed in v1.0.0. Please use `validator accounts import` instead.")
}
createWalletConfig, err := extractWalletCreationConfigFromCli(cliCtx, keymanagerKind)
if err != nil {
return nil, err
@@ -74,6 +72,26 @@ func CreateWalletWithKeymanager(ctx context.Context, cfg *CreateWalletConfig) (*
if err = createImportedKeymanagerWallet(ctx, w); err != nil {
return nil, errors.Wrap(err, "could not initialize wallet")
}
km, err := w.InitializeKeymanager(ctx)
if err != nil {
return nil, errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
importedKm, ok := km.(*imported.Keymanager)
if !ok {
return nil, errors.Wrap(err, ErrCouldNotInitializeKeymanager)
}
accountsKeystore, err := importedKm.CreateAccountsKeystore(ctx, make([][]byte, 0), make([][]byte, 0))
if err != nil {
return nil, err
}
encodedAccounts, err := json.MarshalIndent(accountsKeystore, "", "\t")
if err != nil {
return nil, err
}
if err = w.WriteFileAtPath(ctx, imported.AccountsPath, imported.AccountsKeystoreFileName, encodedAccounts); err != nil {
return nil, err
}
log.WithField("--wallet-dir", cfg.WalletCfg.WalletDir).Info(
"Successfully created wallet with ability to import keystores",
)

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/validator/flags"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
"github.com/prysmaticlabs/prysm/validator/keymanager/remote"
"github.com/sirupsen/logrus"
logTest "github.com/sirupsen/logrus/hooks/test"
@@ -159,10 +160,12 @@ func TestCreateWallet_Imported(t *testing.T) {
require.NoError(t, err)
// We attempt to open the newly created wallet.
_, err = wallet.OpenWallet(cliCtx.Context, &wallet.Config{
w, err := wallet.OpenWallet(cliCtx.Context, &wallet.Config{
WalletDir: walletDir,
})
assert.NoError(t, err)
_, err = w.ReadFileAtPath(cliCtx.Context, imported.AccountsPath, imported.AccountsKeystoreFileName)
require.NoError(t, err)
}
func TestCreateWallet_Derived(t *testing.T) {

View File

@@ -38,11 +38,11 @@ func (dr *Keymanager) EnableAccounts(ctx context.Context, pubKeys [][]byte) erro
}
func (dr *Keymanager) rewriteDisabledKeysToDisk(ctx context.Context) error {
encoded, err := dr.wallet.ReadFileAtPath(ctx, AccountsPath, accountsKeystoreFileName)
encoded, err := dr.wallet.ReadFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName)
if err != nil {
return errors.Wrap(err, "could not read keystore file for accounts")
}
keystore := &accountsKeystoreRepresentation{}
keystore := &AccountsKeystoreRepresentation{}
if err := json.Unmarshal(encoded, keystore); err != nil {
return err
}
@@ -55,7 +55,7 @@ func (dr *Keymanager) rewriteDisabledKeysToDisk(ctx context.Context) error {
if err != nil {
return err
}
if err := dr.wallet.WriteFileAtPath(ctx, AccountsPath, accountsKeystoreFileName, encoded); err != nil {
if err := dr.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encoded); err != nil {
return errors.Wrap(err, "could not write keystore file for accounts")
}
return nil

View File

@@ -86,7 +86,7 @@ func TestKeymanager_DisableAccounts(t *testing.T) {
}
// First we write the accounts store file.
ctx := context.Background()
store, err := dr.createAccountsKeystore(ctx, randomPrivateKeys, randomPublicKeys)
store, err := dr.CreateAccountsKeystore(ctx, randomPrivateKeys, randomPublicKeys)
require.NoError(t, err)
existingDisabledKeysStr := make([]string, len(tt.existingDisabledKeys))
for i := 0; i < len(tt.existingDisabledKeys); i++ {
@@ -95,7 +95,7 @@ func TestKeymanager_DisableAccounts(t *testing.T) {
store.DisabledPublicKeys = existingDisabledKeysStr
encoded, err := json.Marshal(store)
require.NoError(t, err)
err = dr.wallet.WriteFileAtPath(ctx, AccountsPath, accountsKeystoreFileName, encoded)
err = dr.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encoded)
require.NoError(t, err)
if err := dr.DisableAccounts(ctx, tt.keysToDisable); (err != nil) != tt.wantErr {
@@ -107,9 +107,9 @@ func TestKeymanager_DisableAccounts(t *testing.T) {
wanted[bytesutil.ToBytes48(pubKey)] = true
}
// We verify that the updated disabled keys are reflected on disk as well.
encoded, err := wallet.ReadFileAtPath(ctx, AccountsPath, accountsKeystoreFileName)
encoded, err := wallet.ReadFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName)
require.NoError(t, err)
keystore := &accountsKeystoreRepresentation{}
keystore := &AccountsKeystoreRepresentation{}
require.NoError(t, json.Unmarshal(encoded, keystore))
require.Equal(t, len(wanted), len(keystore.DisabledPublicKeys))
@@ -197,7 +197,7 @@ func TestKeymanager_EnableAccounts(t *testing.T) {
}
// First we write the accounts store file.
ctx := context.Background()
store, err := dr.createAccountsKeystore(ctx, randomPrivateKeys, randomPublicKeys)
store, err := dr.CreateAccountsKeystore(ctx, randomPrivateKeys, randomPublicKeys)
require.NoError(t, err)
existingDisabledKeysStr := make([]string, len(tt.existingDisabledKeys))
for i := 0; i < len(tt.existingDisabledKeys); i++ {
@@ -206,7 +206,7 @@ func TestKeymanager_EnableAccounts(t *testing.T) {
store.DisabledPublicKeys = existingDisabledKeysStr
encoded, err := json.Marshal(store)
require.NoError(t, err)
err = dr.wallet.WriteFileAtPath(ctx, AccountsPath, accountsKeystoreFileName, encoded)
err = dr.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encoded)
require.NoError(t, err)
if err := dr.EnableAccounts(ctx, tt.keysToEnable); (err != nil) != tt.wantErr {
@@ -223,9 +223,9 @@ func TestKeymanager_EnableAccounts(t *testing.T) {
}
}
// We verify that the updated disabled keys are reflected on disk as well.
encoded, err := wallet.ReadFileAtPath(ctx, AccountsPath, accountsKeystoreFileName)
encoded, err := wallet.ReadFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName)
require.NoError(t, err)
keystore := &accountsKeystoreRepresentation{}
keystore := &AccountsKeystoreRepresentation{}
require.NoError(t, json.Unmarshal(encoded, keystore))
require.Equal(t, len(wanted), len(keystore.DisabledPublicKeys))

View File

@@ -50,7 +50,7 @@ func (dr *Keymanager) ImportKeystores(
}
// Write the accounts to disk into a single keystore.
accountsKeystore, err := dr.createAccountsKeystore(ctx, privKeys, pubKeys)
accountsKeystore, err := dr.CreateAccountsKeystore(ctx, privKeys, pubKeys)
if err != nil {
return err
}
@@ -58,13 +58,13 @@ func (dr *Keymanager) ImportKeystores(
if err != nil {
return err
}
return dr.wallet.WriteFileAtPath(ctx, AccountsPath, accountsKeystoreFileName, encodedAccounts)
return dr.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encodedAccounts)
}
// ImportKeypairs directly into the keymanager.
func (dr *Keymanager) ImportKeypairs(ctx context.Context, privKeys, pubKeys [][]byte) error {
// Write the accounts to disk into a single keystore.
accountsKeystore, err := dr.createAccountsKeystore(ctx, privKeys, pubKeys)
accountsKeystore, err := dr.CreateAccountsKeystore(ctx, privKeys, pubKeys)
if err != nil {
return errors.Wrap(err, "could not import account keypairs")
}
@@ -72,7 +72,7 @@ func (dr *Keymanager) ImportKeypairs(ctx context.Context, privKeys, pubKeys [][]
if err != nil {
return errors.Wrap(err, "could not marshal accounts keystore into JSON")
}
return dr.wallet.WriteFileAtPath(ctx, AccountsPath, accountsKeystoreFileName, encodedAccounts)
return dr.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encodedAccounts)
}
// Retrieves the private key and public key from an EIP-2335 keystore file

View File

@@ -52,7 +52,7 @@ func TestImportedKeymanager_CreateAccountsKeystore_NoDuplicates(t *testing.T) {
wallet: wallet,
}
ctx := context.Background()
_, err := dr.createAccountsKeystore(ctx, privKeys, pubKeys)
_, err := dr.CreateAccountsKeystore(ctx, privKeys, pubKeys)
require.NoError(t, err)
// We expect the 50 keys in the account store to match.
@@ -65,7 +65,7 @@ func TestImportedKeymanager_CreateAccountsKeystore_NoDuplicates(t *testing.T) {
}
// Re-run the create accounts keystore function with the same pubkeys.
_, err = dr.createAccountsKeystore(ctx, privKeys, pubKeys)
_, err = dr.CreateAccountsKeystore(ctx, privKeys, pubKeys)
require.NoError(t, err)
// We expect nothing to change.
@@ -84,7 +84,7 @@ func TestImportedKeymanager_CreateAccountsKeystore_NoDuplicates(t *testing.T) {
privKeys = append(privKeys, privKey.Marshal())
pubKeys = append(pubKeys, privKey.PublicKey().Marshal())
_, err = dr.createAccountsKeystore(ctx, privKeys, pubKeys)
_, err = dr.CreateAccountsKeystore(ctx, privKeys, pubKeys)
require.NoError(t, err)
require.Equal(t, len(dr.accountsStore.PublicKeys), len(dr.accountsStore.PrivateKeys))

View File

@@ -33,8 +33,9 @@ const (
// KeystoreFileNameFormat exposes the filename the keystore should be formatted in.
KeystoreFileNameFormat = "keystore-%d.json"
// AccountsPath where all imported keymanager keystores are kept.
AccountsPath = "accounts"
accountsKeystoreFileName = "all-accounts.keystore.json"
AccountsPath = "accounts"
// AccountsKeystoreFileName exposes the name of the keystore file.
AccountsKeystoreFileName = "all-accounts.keystore.json"
)
// Keymanager implementation for imported keystores utilizing EIP-2335.
@@ -58,10 +59,10 @@ type accountStore struct {
PublicKeys [][]byte `json:"public_keys"`
}
// Defines an internal Prysm representation
// AccountsKeystoreRepresentation defines an internal Prysm representation
// of validator accounts, encrypted according to the EIP-2334 standard
// but containing extra fields such as markers for disabled public keys.
type accountsKeystoreRepresentation struct {
type AccountsKeystoreRepresentation struct {
Crypto map[string]interface{} `json:"crypto"`
ID string `json:"uuid"`
Version uint `json:"version"`
@@ -189,7 +190,7 @@ func (dr *Keymanager) DeleteAccounts(ctx context.Context, publicKeys [][]byte) e
dr.accountsStore.PrivateKeys = append(dr.accountsStore.PrivateKeys[:index], dr.accountsStore.PrivateKeys[index+1:]...)
dr.accountsStore.PublicKeys = append(dr.accountsStore.PublicKeys[:index], dr.accountsStore.PublicKeys[index+1:]...)
newStore, err := dr.createAccountsKeystore(ctx, dr.accountsStore.PrivateKeys, dr.accountsStore.PublicKeys)
newStore, err := dr.CreateAccountsKeystore(ctx, dr.accountsStore.PrivateKeys, dr.accountsStore.PublicKeys)
if err != nil {
return errors.Wrap(err, "could not rewrite accounts keystore")
}
@@ -199,7 +200,7 @@ func (dr *Keymanager) DeleteAccounts(ctx context.Context, publicKeys [][]byte) e
if err != nil {
return err
}
if err := dr.wallet.WriteFileAtPath(ctx, AccountsPath, accountsKeystoreFileName, encoded); err != nil {
if err := dr.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encoded); err != nil {
return errors.Wrap(err, "could not write keystore file for accounts")
}
@@ -285,16 +286,16 @@ func (dr *Keymanager) Sign(ctx context.Context, req *validatorpb.SignRequest) (b
}
func (dr *Keymanager) initializeAccountKeystore(ctx context.Context) error {
encoded, err := dr.wallet.ReadFileAtPath(ctx, AccountsPath, accountsKeystoreFileName)
encoded, err := dr.wallet.ReadFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName)
if err != nil && strings.Contains(err.Error(), "no files found") {
// If there are no keys to initialize at all, just exit.
return nil
} else if err != nil {
return errors.Wrapf(err, "could not read keystore file for accounts %s", accountsKeystoreFileName)
return errors.Wrapf(err, "could not read keystore file for accounts %s", AccountsKeystoreFileName)
}
keystoreFile := &accountsKeystoreRepresentation{}
keystoreFile := &AccountsKeystoreRepresentation{}
if err := json.Unmarshal(encoded, keystoreFile); err != nil {
return errors.Wrapf(err, "could not decode keystore file for accounts %s", accountsKeystoreFileName)
return errors.Wrapf(err, "could not decode keystore file for accounts %s", AccountsKeystoreFileName)
}
// We extract the validator signing private key from the keystore
// by utilizing the password and initialize a new BLS secret key from
@@ -337,10 +338,11 @@ func (dr *Keymanager) initializeAccountKeystore(ctx context.Context) error {
return err
}
func (dr *Keymanager) createAccountsKeystore(
// CreateAccountsKeystore creates a new keystore holding the provided keys.
func (dr *Keymanager) CreateAccountsKeystore(
_ context.Context,
privateKeys, publicKeys [][]byte,
) (*accountsKeystoreRepresentation, error) {
) (*AccountsKeystoreRepresentation, error) {
encryptor := keystorev4.New()
id, err := uuid.NewRandom()
if err != nil {
@@ -395,7 +397,7 @@ func (dr *Keymanager) createAccountsKeystore(
for pubKey := range dr.disabledPublicKeys {
disabledPubKeys = append(disabledPubKeys, fmt.Sprintf("%x", pubKey))
}
return &accountsKeystoreRepresentation{
return &AccountsKeystoreRepresentation{
Crypto: cryptoFields,
ID: id.String(),
Version: encryptor.Version(),

View File

@@ -25,7 +25,7 @@ import (
// ensure we can handle thousands of events fired in a short time-span.
func (dr *Keymanager) listenForAccountChanges(ctx context.Context) {
debounceFileChangesInterval := featureconfig.Get().KeystoreImportDebounceInterval
accountsFilePath := filepath.Join(dr.wallet.AccountsDir(), AccountsPath, accountsKeystoreFileName)
accountsFilePath := filepath.Join(dr.wallet.AccountsDir(), AccountsPath, AccountsKeystoreFileName)
if !fileutil.FileExists(accountsFilePath) {
return
}
@@ -66,7 +66,7 @@ func (dr *Keymanager) listenForAccountChanges(ctx context.Context) {
log.WithError(err).Errorf("Loaded in an empty file: %s", ev.Name)
return
}
accountsKeystore := &accountsKeystoreRepresentation{}
accountsKeystore := &AccountsKeystoreRepresentation{}
if err := json.Unmarshal(fileBytes, accountsKeystore); err != nil {
log.WithError(
err,
@@ -95,7 +95,7 @@ func (dr *Keymanager) listenForAccountChanges(ctx context.Context) {
// Replaces the accounts store struct in the imported keymanager with
// the contents of a keystore file by decrypting it with the accounts password.
func (dr *Keymanager) reloadAccountsFromKeystore(keystore *accountsKeystoreRepresentation) error {
func (dr *Keymanager) reloadAccountsFromKeystore(keystore *AccountsKeystoreRepresentation) error {
decryptor := keystorev4.New()
encodedAccounts, err := decryptor.Decrypt(keystore.Crypto, dr.wallet.Password())
if err != nil {
@@ -105,9 +105,6 @@ func (dr *Keymanager) reloadAccountsFromKeystore(keystore *accountsKeystoreRepre
if err := json.Unmarshal(encodedAccounts, newAccountsStore); err != nil {
return err
}
if len(newAccountsStore.PublicKeys) == 0 || len(newAccountsStore.PrivateKeys) == 0 {
return errors.New("attempted to reload a keystore with 0 public/private keys")
}
if len(newAccountsStore.PublicKeys) != len(newAccountsStore.PrivateKeys) {
return errors.New("number of public and private keys in keystore do not match")
}

View File

@@ -15,37 +15,6 @@ import (
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
)
func TestImportedKeymanager_reloadAccountsFromKeystore_NoKeys(t *testing.T) {
password := "Passw03rdz293**%#2"
wallet := &mock.Wallet{
Files: make(map[string]map[string][]byte),
AccountPasswords: make(map[string]string),
WalletPassword: password,
}
dr := &Keymanager{
wallet: wallet,
}
accountsStore := &accountStore{
PrivateKeys: nil,
PublicKeys: nil,
}
encodedStore, err := json.MarshalIndent(accountsStore, "", "\t")
require.NoError(t, err)
encryptor := keystorev4.New()
cryptoFields, err := encryptor.Encrypt(encodedStore, dr.wallet.Password())
require.NoError(t, err)
id, err := uuid.NewRandom()
require.NoError(t, err)
keystore := &accountsKeystoreRepresentation{
Crypto: cryptoFields,
ID: id.String(),
Version: encryptor.Version(),
Name: encryptor.Name(),
}
err = dr.reloadAccountsFromKeystore(keystore)
assert.ErrorContains(t, "0 public/private keys", err)
}
func TestImportedKeymanager_reloadAccountsFromKeystore_MismatchedNumKeys(t *testing.T) {
password := "Passw03rdz293**%#2"
wallet := &mock.Wallet{
@@ -67,7 +36,7 @@ func TestImportedKeymanager_reloadAccountsFromKeystore_MismatchedNumKeys(t *test
require.NoError(t, err)
id, err := uuid.NewRandom()
require.NoError(t, err)
keystore := &accountsKeystoreRepresentation{
keystore := &AccountsKeystoreRepresentation{
Crypto: cryptoFields,
ID: id.String(),
Version: encryptor.Version(),
@@ -99,7 +68,7 @@ func TestImportedKeymanager_reloadAccountsFromKeystore(t *testing.T) {
pubKeys[i] = privKey.PublicKey().Marshal()
}
accountsStore, err := dr.createAccountsKeystore(context.Background(), privKeys, pubKeys)
accountsStore, err := dr.CreateAccountsKeystore(context.Background(), privKeys, pubKeys)
require.NoError(t, err)
require.NoError(t, dr.reloadAccountsFromKeystore(accountsStore))

View File

@@ -265,7 +265,7 @@ func (s *Server) initializeWallet(ctx context.Context, cfg *wallet.Config) error
s.walletInitialized = true
km, err := w.InitializeKeymanager(ctx)
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
return errors.Wrap(err, accounts.ErrCouldNotInitializeKeymanager)
}
s.keymanager = km
s.wallet = w