2020-07-21 02:05:23 +00:00
|
|
|
package derived
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
2022-03-22 03:04:09 +00:00
|
|
|
"github.com/logrusorgru/aurora"
|
2020-07-21 02:05:23 +00:00
|
|
|
"github.com/pkg/errors"
|
2021-09-18 17:26:11 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/async/event"
|
2022-01-06 17:33:08 +00:00
|
|
|
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
2021-09-15 22:55:11 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/crypto/bls"
|
2021-11-19 04:11:54 +00:00
|
|
|
ethpbservice "github.com/prysmaticlabs/prysm/proto/eth/service"
|
2021-08-18 21:24:01 +00:00
|
|
|
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
|
2020-10-15 22:31:52 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/validator/accounts/iface"
|
2020-11-16 22:26:04 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/validator/keymanager"
|
2022-02-01 19:54:19 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/validator/keymanager/local"
|
2020-11-12 21:16:41 +00:00
|
|
|
util "github.com/wealdtech/go-eth2-util"
|
2020-07-21 02:05:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-11-17 06:00:20 +00:00
|
|
|
// DerivationPathFormat describes the structure of how keys are derived from a master key.
|
|
|
|
DerivationPathFormat = "m / purpose / coin_type / account_index / withdrawal_key / validating_key"
|
2020-07-21 02:05:23 +00:00
|
|
|
// ValidatingKeyDerivationPathTemplate defining the hierarchical path for validating
|
2021-06-26 19:00:33 +00:00
|
|
|
// keys for Prysm Ethereum validators. According to EIP-2334, the format is as follows:
|
2020-07-21 02:05:23 +00:00
|
|
|
// m / purpose / coin_type / account_index / withdrawal_key / validating_key
|
|
|
|
ValidatingKeyDerivationPathTemplate = "m/12381/3600/%d/0/0"
|
2020-08-10 18:54:40 +00:00
|
|
|
)
|
|
|
|
|
2020-08-31 19:46:45 +00:00
|
|
|
// SetupConfig includes configuration values for initializing
|
|
|
|
// a keymanager, such as passwords, the wallet, and more.
|
|
|
|
type SetupConfig struct {
|
2021-02-24 18:05:46 +00:00
|
|
|
Wallet iface.Wallet
|
|
|
|
ListenForChanges bool
|
2020-07-21 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Keymanager implementation for derived, HD keymanager using EIP-2333 and EIP-2334.
|
|
|
|
type Keymanager struct {
|
2022-02-01 19:54:19 +00:00
|
|
|
localKM *local.Keymanager
|
2020-07-21 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewKeymanager instantiates a new derived keymanager from configuration options.
|
|
|
|
func NewKeymanager(
|
2020-08-31 19:46:45 +00:00
|
|
|
ctx context.Context,
|
|
|
|
cfg *SetupConfig,
|
2020-07-21 02:05:23 +00:00
|
|
|
) (*Keymanager, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
localKM, err := local.NewKeymanager(ctx, &local.SetupConfig{
|
2021-02-24 18:05:46 +00:00
|
|
|
Wallet: cfg.Wallet,
|
|
|
|
ListenForChanges: cfg.ListenForChanges,
|
2020-11-16 22:26:04 +00:00
|
|
|
})
|
2020-10-15 20:33:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-08-10 18:54:40 +00:00
|
|
|
}
|
2020-11-16 22:26:04 +00:00
|
|
|
return &Keymanager{
|
2022-02-01 19:54:19 +00:00
|
|
|
localKM: localKM,
|
2020-11-16 22:26:04 +00:00
|
|
|
}, nil
|
2020-08-25 19:30:26 +00:00
|
|
|
}
|
|
|
|
|
2020-11-16 22:26:04 +00:00
|
|
|
// RecoverAccountsFromMnemonic given a mnemonic phrase, is able to regenerate N accounts
|
|
|
|
// from a derived seed, encrypt them according to the EIP-2334 JSON standard, and write them
|
|
|
|
// to disk. Then, the mnemonic is never stored nor used by the validator.
|
2021-01-20 14:39:07 +00:00
|
|
|
func (km *Keymanager) RecoverAccountsFromMnemonic(
|
2020-11-16 22:26:04 +00:00
|
|
|
ctx context.Context, mnemonic, mnemonicPassphrase string, numAccounts int,
|
2020-10-27 20:51:29 +00:00
|
|
|
) error {
|
2020-11-16 22:26:04 +00:00
|
|
|
seed, err := seedFromMnemonic(mnemonic, mnemonicPassphrase)
|
2020-08-10 18:54:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not initialize new wallet seed file")
|
|
|
|
}
|
2020-11-16 22:26:04 +00:00
|
|
|
privKeys := make([][]byte, numAccounts)
|
|
|
|
pubKeys := make([][]byte, numAccounts)
|
|
|
|
for i := 0; i < numAccounts; i++ {
|
|
|
|
privKey, err := util.PrivateKeyFromSeedAndPath(
|
|
|
|
seed, fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
privKeys[i] = privKey.Marshal()
|
|
|
|
pubKeys[i] = privKey.PublicKey().Marshal()
|
2020-08-10 18:54:40 +00:00
|
|
|
}
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.ImportKeypairs(ctx, privKeys, pubKeys)
|
2020-08-10 18:54:40 +00:00
|
|
|
}
|
|
|
|
|
2020-11-16 22:26:04 +00:00
|
|
|
// ExtractKeystores retrieves the secret keys for specified public keys
|
|
|
|
// in the function input, encrypts them using the specified password,
|
|
|
|
// and returns their respective EIP-2335 keystores.
|
2021-01-20 14:39:07 +00:00
|
|
|
func (km *Keymanager) ExtractKeystores(
|
2020-11-16 22:26:04 +00:00
|
|
|
ctx context.Context, publicKeys []bls.PublicKey, password string,
|
|
|
|
) ([]*keymanager.Keystore, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.ExtractKeystores(ctx, publicKeys, password)
|
2020-07-21 05:06:11 +00:00
|
|
|
}
|
|
|
|
|
2020-11-16 22:26:04 +00:00
|
|
|
// ValidatingAccountNames for the derived keymanager.
|
2021-01-20 14:39:07 +00:00
|
|
|
func (km *Keymanager) ValidatingAccountNames(_ context.Context) ([]string, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.ValidatingAccountNames()
|
2020-07-21 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sign signs a message using a validator key.
|
2021-01-20 14:39:07 +00:00
|
|
|
func (km *Keymanager) Sign(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.Sign(ctx, req)
|
2020-07-21 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 05:06:11 +00:00
|
|
|
// FetchValidatingPublicKeys fetches the list of validating public keys from the keymanager.
|
2022-01-06 17:33:08 +00:00
|
|
|
func (km *Keymanager) FetchValidatingPublicKeys(ctx context.Context) ([][fieldparams.BLSPubkeyLength]byte, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.FetchValidatingPublicKeys(ctx)
|
2020-07-21 05:06:11 +00:00
|
|
|
}
|
|
|
|
|
2020-10-15 16:08:52 +00:00
|
|
|
// FetchValidatingPrivateKeys fetches the list of validating private keys from the keymanager.
|
2021-01-20 14:39:07 +00:00
|
|
|
func (km *Keymanager) FetchValidatingPrivateKeys(ctx context.Context) ([][32]byte, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.FetchValidatingPrivateKeys(ctx)
|
2020-08-10 18:54:40 +00:00
|
|
|
}
|
|
|
|
|
2021-11-24 15:40:49 +00:00
|
|
|
// ImportKeystores for a derived keymanager.
|
|
|
|
func (km *Keymanager) ImportKeystores(
|
|
|
|
ctx context.Context, keystores []*keymanager.Keystore, passwords []string,
|
|
|
|
) ([]*ethpbservice.ImportedKeystoreStatus, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.ImportKeystores(ctx, keystores, passwords)
|
2021-11-24 15:40:49 +00:00
|
|
|
}
|
|
|
|
|
2021-11-19 04:11:54 +00:00
|
|
|
// DeleteKeystores for a derived keymanager.
|
|
|
|
func (km *Keymanager) DeleteKeystores(
|
|
|
|
ctx context.Context, publicKeys [][]byte,
|
|
|
|
) ([]*ethpbservice.DeletedKeystoreStatus, error) {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.DeleteKeystores(ctx, publicKeys)
|
2020-08-10 18:54:40 +00:00
|
|
|
}
|
2021-01-22 20:21:34 +00:00
|
|
|
|
|
|
|
// SubscribeAccountChanges creates an event subscription for a channel
|
|
|
|
// to listen for public key changes at runtime, such as when new validator accounts
|
|
|
|
// are imported into the keymanager while the validator process is running.
|
2022-01-06 17:33:08 +00:00
|
|
|
func (km *Keymanager) SubscribeAccountChanges(pubKeysChan chan [][fieldparams.BLSPubkeyLength]byte) event.Subscription {
|
2022-02-01 19:54:19 +00:00
|
|
|
return km.localKM.SubscribeAccountChanges(pubKeysChan)
|
2021-01-22 20:21:34 +00:00
|
|
|
}
|
2022-03-22 03:04:09 +00:00
|
|
|
|
|
|
|
func (km *Keymanager) ListKeymanagerAccounts(ctx context.Context, cfg keymanager.ListKeymanagerAccountConfig) error {
|
|
|
|
au := aurora.NewAurora(true)
|
|
|
|
fmt.Printf("(keymanager kind) %s\n", au.BrightGreen("derived, (HD) hierarchical-deterministic").Bold())
|
|
|
|
fmt.Printf("(derivation format) %s\n", au.BrightGreen(DerivationPathFormat).Bold())
|
|
|
|
validatingPubKeys, err := km.FetchValidatingPublicKeys(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not fetch validating public keys")
|
|
|
|
}
|
|
|
|
var validatingPrivateKeys [][32]byte
|
|
|
|
if cfg.ShowPrivateKeys {
|
|
|
|
validatingPrivateKeys, err = km.FetchValidatingPrivateKeys(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not fetch validating private keys")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
accountNames, err := km.ValidatingAccountNames(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(accountNames) == 1 {
|
|
|
|
fmt.Print("Showing 1 validator account\n")
|
|
|
|
} else if len(accountNames) == 0 {
|
|
|
|
fmt.Print("No accounts found\n")
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
fmt.Printf("Showing %d validator accounts\n", len(accountNames))
|
|
|
|
}
|
|
|
|
for i := 0; i < len(accountNames); i++ {
|
|
|
|
fmt.Println("")
|
|
|
|
validatingKeyPath := fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i)
|
|
|
|
|
|
|
|
// Retrieve the withdrawal key account metadata.
|
|
|
|
fmt.Printf("%s | %s\n", au.BrightBlue(fmt.Sprintf("Account %d", i)).Bold(), au.BrightGreen(accountNames[i]).Bold())
|
|
|
|
// Retrieve the validating key account metadata.
|
|
|
|
fmt.Printf("%s %#x\n", au.BrightCyan("[validating public key]").Bold(), validatingPubKeys[i])
|
|
|
|
if cfg.ShowPrivateKeys && validatingPrivateKeys != nil {
|
|
|
|
fmt.Printf("%s %#x\n", au.BrightRed("[validating private key]").Bold(), validatingPrivateKeys[i])
|
|
|
|
}
|
|
|
|
fmt.Printf("%s %s\n", au.BrightCyan("[derivation path]").Bold(), validatingKeyPath)
|
|
|
|
fmt.Println(" ")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|