mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 07:58:22 -05:00
Add wallet keymanager (#4687)
* Add wallet keymanager * Read keymanageropts from file if not JSON Co-authored-by: Nishant Das <nish1993@hotmail.com> Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
This commit is contained in:
78
WORKSPACE
78
WORKSPACE
@@ -1486,3 +1486,81 @@ go_repository(
|
||||
sum = "h1:oq6BiN7v0MfWCRcJAxSV+hesVMAAV8COrQbTjYNnso4=",
|
||||
version = "v0.0.0-20190611015032-8a3d0352aa79",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet",
|
||||
commit = "6970d62e60d86fdae3c3e510e800e8a60d755a7d",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet_hd",
|
||||
commit = "ce0a252a01c621687e9786a64899cfbfe802ba73",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet-hd",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet_nd",
|
||||
commit = "12c8c41cdbd16797ff292e27f58e126bb89e9706",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet-nd",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet_store_filesystem",
|
||||
commit = "1eea6a48d75380047d2ebe7c8c4bd8985bcfdeca",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet-store-filesystem",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet_store_s3",
|
||||
commit = "1c821b5161f7bb0b3efa2030eff687eea5e70e53",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet-store-s3",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4",
|
||||
commit = "0c11c07b9544eb662210fadded94f40f309d8c8f",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_wallet_types",
|
||||
commit = "af67d8101be61e7c4dd8126d2b3eba20cff5dab2",
|
||||
importpath = "github.com/wealdtech/go-eth2-wallet-types",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_types",
|
||||
commit = "f9c31ddf180537dd5712d5998a3d56c45864d71f",
|
||||
importpath = "github.com/wealdtech/go-eth2-types",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_eth2_util",
|
||||
commit = "326ebb1755651131bb8f4506ea9a23be6d9ad1dd",
|
||||
importpath = "github.com/wealdtech/go-eth2-util",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_ecodec",
|
||||
commit = "7473d835445a3490e61a5fcf48fe4e9755a37957",
|
||||
importpath = "github.com/wealdtech/go-ecodec",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_bytesutil",
|
||||
commit = "e564d0ade555b9f97494f0f669196ddcc6bc531d",
|
||||
importpath = "github.com/wealdtech/go-bytesutil",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_wealdtech_go_indexer",
|
||||
commit = "334862c32b1e3a5c6738a2618f5c0a8ebeb8cd51",
|
||||
importpath = "github.com/wealdtech/go-indexer",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_shibukawa_configdir",
|
||||
commit = "e180dbdc8da04c4fa04272e875ce64949f38bd3e",
|
||||
importpath = "github.com/shibukawa/configdir",
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ go_library(
|
||||
"keymanager.go",
|
||||
"log.go",
|
||||
"opts.go",
|
||||
"wallet.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/validator/keymanager",
|
||||
visibility = ["//validator:__subpackages__"],
|
||||
@@ -19,6 +20,8 @@ go_library(
|
||||
"//shared/interop:go_default_library",
|
||||
"//validator/accounts:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_wealdtech_go_eth2_wallet//:go_default_library",
|
||||
"@com_github_wealdtech_go_eth2_wallet_types//:go_default_library",
|
||||
"@org_golang_x_crypto//ssh/terminal:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -9,6 +9,9 @@ import (
|
||||
// ErrNoSuchKey is returned whenever a request is made for a key of which a key manager is unaware.
|
||||
var ErrNoSuchKey = errors.New("no such key")
|
||||
|
||||
// ErrCannotSign is returned whenever a signing attempt fails.
|
||||
var ErrCannotSign = errors.New("cannot sign")
|
||||
|
||||
// KeyManager controls access to private keys by the validator.
|
||||
type KeyManager interface {
|
||||
// FetchValidatingKeys fetches the list of public keys that should be used to validate with.
|
||||
|
||||
110
validator/keymanager/wallet.go
Normal file
110
validator/keymanager/wallet.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package keymanager
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types"
|
||||
)
|
||||
|
||||
type walletOpts struct {
|
||||
Accounts []string `json:"accounts"`
|
||||
Passphrases []string `json:"passphrases"`
|
||||
}
|
||||
|
||||
var walletOptsHelp = `The wallet key manager stores keys in a local encrypted store. The options are:
|
||||
- accounts This is a list of account specifiers. An account specifier is of
|
||||
the form <wallet name>/[account name], where the account name can be a
|
||||
regular expression. If the account specifier is just <wallet name> all
|
||||
accounts in that wallet will be used. Multiple account specifiers can be
|
||||
supplied if required.
|
||||
- passphrase This is the passphrase used to encrypt the accounts when they
|
||||
were created. Multiple passphrases can be supplied if required.
|
||||
{
|
||||
"accounts": ["Validators/Account.*"], // Use all accounts in the 'Validators' wallet starting with 'Account'
|
||||
"passphrases": ["secret1","secret2"] // Use the passphrases 'secret1' and 'secret2' to decrypt accounts
|
||||
}`
|
||||
|
||||
// NewWallet creates a key manager populated with the keys from a wallet at the given path.
|
||||
func NewWallet(input string) (KeyManager, string, error) {
|
||||
opts := &walletOpts{}
|
||||
err := json.Unmarshal([]byte(input), opts)
|
||||
if err != nil {
|
||||
return nil, walletOptsHelp, err
|
||||
}
|
||||
|
||||
if len(opts.Accounts) == 0 {
|
||||
return nil, walletOptsHelp, errors.New("at least one account specifier is required")
|
||||
}
|
||||
|
||||
if len(opts.Passphrases) == 0 {
|
||||
return nil, walletOptsHelp, errors.New("at least one passphrase is required to decrypt accounts")
|
||||
}
|
||||
|
||||
km := &Wallet{
|
||||
accounts: make(map[[48]byte]e2wtypes.Account),
|
||||
}
|
||||
|
||||
for _, path := range opts.Accounts {
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts[0]) == 0 {
|
||||
return nil, walletOptsHelp, fmt.Errorf("did not understand account specifier %q", path)
|
||||
}
|
||||
wallet, err := e2wallet.OpenWallet(parts[0])
|
||||
if err != nil {
|
||||
return nil, walletOptsHelp, err
|
||||
}
|
||||
accountSpecifier := "^.*$"
|
||||
if len(parts) > 1 && len(parts[1]) > 0 {
|
||||
accountSpecifier = fmt.Sprintf("^%s$", parts[1])
|
||||
}
|
||||
re := regexp.MustCompile(accountSpecifier)
|
||||
for account := range wallet.Accounts() {
|
||||
if re.Match([]byte(account.Name())) {
|
||||
pubKey := bytesutil.ToBytes48(account.PublicKey().Marshal())
|
||||
for _, passphrase := range opts.Passphrases {
|
||||
if err := account.Unlock([]byte(passphrase)); err != nil {
|
||||
log.WithError(err).WithField("pubKey", fmt.Sprintf("%#x", pubKey)).Warn("Failed to unlock account with supplied passphrases; cannot validate")
|
||||
} else {
|
||||
km.accounts[pubKey] = account
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return km, walletOptsHelp, nil
|
||||
}
|
||||
|
||||
// Wallet is a key manager that loads keys from a local Ethereum 2 wallet.
|
||||
type Wallet struct {
|
||||
accounts map[[48]byte]e2wtypes.Account
|
||||
}
|
||||
|
||||
// FetchValidatingKeys fetches the list of public keys that should be used to validate with.
|
||||
func (km *Wallet) FetchValidatingKeys() ([][48]byte, error) {
|
||||
res := make([][48]byte, 0, len(km.accounts))
|
||||
for pubKey := range km.accounts {
|
||||
res = append(res, pubKey)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Sign signs a message for the validator to broadcast.
|
||||
func (km *Wallet) Sign(pubKey [48]byte, root [32]byte, domain uint64) (*bls.Signature, error) {
|
||||
account, exists := km.accounts[pubKey]
|
||||
if !exists {
|
||||
return nil, ErrNoSuchKey
|
||||
}
|
||||
sig, err := account.Sign(root[:], domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bls.SignatureFromBytes(sig.Marshal())
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package node
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
@@ -88,8 +89,13 @@ func NewValidatorClient(ctx *cli.Context) (*ValidatorClient, error) {
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to obtain public keys for validation")
|
||||
} else {
|
||||
for _, key := range pubKeys {
|
||||
log.WithField("pubKey", fmt.Sprintf("%#x", key)).Info("Validating for public key")
|
||||
if len(pubKeys) == 0 {
|
||||
log.Warn("No keys found; nothing to validate")
|
||||
} else {
|
||||
log.WithField("validators", len(pubKeys)).Info("Found validator keys")
|
||||
for _, key := range pubKeys {
|
||||
log.WithField("pubKey", fmt.Sprintf("%#x", key)).Info("Validating for public key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +205,12 @@ func selectKeyManager(ctx *cli.Context) (keymanager.KeyManager, error) {
|
||||
opts := ctx.String(flags.KeyManagerOpts.Name)
|
||||
if opts == "" {
|
||||
opts = "{}"
|
||||
} else if !strings.HasPrefix(opts, "{") {
|
||||
fileopts, err := ioutil.ReadFile(opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to read keymanager options file")
|
||||
}
|
||||
opts = string(fileopts)
|
||||
}
|
||||
|
||||
if manager == "" {
|
||||
@@ -238,6 +250,8 @@ func selectKeyManager(ctx *cli.Context) (keymanager.KeyManager, error) {
|
||||
km, help, err = keymanager.NewUnencrypted(opts)
|
||||
case "keystore":
|
||||
km, help, err = keymanager.NewKeystore(opts)
|
||||
case "wallet":
|
||||
km, help, err = keymanager.NewWallet(opts)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown keymanager %q", manager)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user