Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add remote keymanager #5133

Merged
merged 7 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -1650,3 +1650,11 @@ go_repository(
sum = "h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=",
version = "v1.20.0",
)

go_repository(
name = "com_github_wealdtech_eth2_signer_api",
build_file_proto_mode = "disable_global",
importpath = "github.com/wealdtech/eth2-signer-api",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this includes a ton of methods for admin and management that are irrelevant for the validator. I think maybe have the minimum for the remote wallet, anything else can be user side implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this being an issue, there is no requirement on a client to call all methods in the interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, I think we should keep this spec as simple and minimalist as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eth2-signer-api defines the endpoints for the remote server, not the client. The ability to unlock wallets and accounts (which I believe are the only endpoints unused by the validator) are not worth spinning out in to a separate repository; clients can just ignore the bits they don't use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can leave that I guess.
I just thin that once the wallet is an external service people might choose to implement their logic and calls (which are outside the minimum the validator needs).

sum = "h1:fqJYjKwG/FeUAJYYiZblIP6agiz3WWB+Hxpw85Fnr5I=",
version = "v1.0.1",
)
1 change: 1 addition & 0 deletions beacon-chain/core/helpers/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ go_library(
"//slasher:__subpackages__",
"//tools:__subpackages__",
"//validator:__subpackages__",
"//endtoend/evaluators:__pkg__",
],
deps = [
"//beacon-chain/cache:go_default_library",
Expand Down
21 changes: 21 additions & 0 deletions validator/client/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/validator/db"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -177,3 +181,20 @@ func (v *ValidatorService) Status() error {
}
return nil
}

// signObject signs a generic object, with protection if available.
func (v *validator) signObject(pubKey [48]byte, object interface{}, domain []byte) (*bls.Signature, error) {
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
root, err := ssz.HashTreeRoot(object)
if err != nil {
return nil, err
}
return protectingKeymanager.SignGeneric(pubKey, root, bytesutil.ToBytes32(domain))
}

root, err := helpers.ComputeSigningRoot(object, domain)
if err != nil {
return nil, err
}
return v.keyManager.Sign(pubKey, root)
}
6 changes: 1 addition & 5 deletions validator/client/validator_aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,7 @@ func (v *validator) signSlot(ctx context.Context, pubKey [48]byte, slot uint64)
return nil, err
}

root, err := helpers.ComputeSigningRoot(slot, domain.SignatureDomain)
if err != nil {
return nil, err
}
sig, err := v.keyManager.Sign(pubKey, root)
sig, err := v.signObject(pubKey, slot, domain.SignatureDomain)
if err != nil {
return nil, errors.Wrap(err, "Failed to sign slot")
}
Expand Down
2 changes: 1 addition & 1 deletion validator/client/validator_attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func (v *validator) signAtt(ctx context.Context, pubKey [48]byte, data *ethpb.At

var sig *bls.Signature
if protectingKeymanager, supported := v.keyManager.(keymanager.ProtectingKeyManager); supported {
sig, err = protectingKeymanager.SignAttestation(pubKey, domain.SignatureDomain, data)
sig, err = protectingKeymanager.SignAttestation(pubKey, bytesutil.ToBytes32(domain.SignatureDomain), data)
} else {
sig, err = v.keyManager.Sign(pubKey, root)
}
Expand Down
13 changes: 3 additions & 10 deletions validator/client/validator_propose.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package client
// Validator client proposer functions.
import (
"context"
"encoding/binary"
"fmt"

"github.com/pkg/errors"
Expand Down Expand Up @@ -176,17 +175,11 @@ func (v *validator) ProposeExit(ctx context.Context, exit *ethpb.VoluntaryExit)
// Sign randao reveal with randao domain and private key.
func (v *validator) signRandaoReveal(ctx context.Context, pubKey [48]byte, epoch uint64) ([]byte, error) {
domain, err := v.domainData(ctx, epoch, params.BeaconConfig().DomainRandao[:])

if err != nil {
return nil, errors.Wrap(err, "could not get domain data")
}
var buf [32]byte
binary.LittleEndian.PutUint64(buf[:], epoch)
sigRoot, err := helpers.ComputeSigningRoot(epoch, domain.SignatureDomain)
if err != nil {
return nil, errors.Wrap(err, "could not compute signing root")
}
randaoReveal, err := v.keyManager.Sign(pubKey, sigRoot)

randaoReveal, err := v.signObject(pubKey, epoch, domain.SignatureDomain)
if err != nil {
return nil, errors.Wrap(err, "could not sign reveal")
}
Expand All @@ -211,7 +204,7 @@ func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch uint64
ParentRoot: b.ParentRoot,
BodyRoot: bodyRoot[:],
}
sig, err = protectingKeymanager.SignProposal(pubKey, domain.SignatureDomain, blockHeader)
sig, err = protectingKeymanager.SignProposal(pubKey, bytesutil.ToBytes32(domain.SignatureDomain), blockHeader)
if err != nil {
return nil, errors.Wrap(err, "could not sign block proposal")
}
Expand Down
7 changes: 7 additions & 0 deletions validator/keymanager/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
"keymanager.go",
"log.go",
"opts.go",
"remote.go",
"wallet.go",
],
importpath = "github.com/prysmaticlabs/prysm/validator/keymanager",
Expand All @@ -19,11 +20,15 @@ go_library(
"//shared/bytesutil:go_default_library",
"//shared/interop:go_default_library",
"//validator/accounts:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_wealdtech_eth2_signer_api//pb/v1:go_default_library",
"@com_github_wealdtech_go_eth2_wallet//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_store_filesystem//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_types_v2//:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//credentials:go_default_library",
"@org_golang_x_crypto//ssh/terminal:go_default_library",
],
)
Expand All @@ -34,12 +39,14 @@ go_test(
"direct_interop_test.go",
"direct_test.go",
"opts_test.go",
"remote_test.go",
"wallet_test.go",
],
embed = [":go_default_library"],
deps = [
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_nd_v2//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_store_filesystem//:go_default_library",
Expand Down
13 changes: 9 additions & 4 deletions validator/keymanager/keymanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,27 @@ var ErrNoSuchKey = errors.New("no such key")
// ErrCannotSign is returned whenever a signing attempt fails.
var ErrCannotSign = errors.New("cannot sign")

// ErrCouldSlash is returned whenever a signing attempt is refused due to a potential slashing event.
var ErrCouldSlash = errors.New("could result in a slashing event")
// ErrDenied is returned whenever a signing attempt is denied.
var ErrDenied = errors.New("signing attempt denied")

// 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.
FetchValidatingKeys() ([][48]byte, error)
// Sign signs a message for the validator to broadcast.
// Note that the domain should already be part of the root, but it is passed along for security purposes.
Sign(pubKey [48]byte, root [32]byte) (*bls.Signature, error)
}

// ProtectingKeyManager provides access to a keymanager that protects its clients from slashing events.
type ProtectingKeyManager interface {
// SignGeneric signs a generic root.
// Note that the domain should already be part of the root, but it is provided for authorisation purposes.
SignGeneric(pubKey [48]byte, root [32]byte, domain [32]byte) (*bls.Signature, error)

// SignProposal signs a block proposal for the validator to broadcast.
SignProposal(pubKey [48]byte, domain []byte, data *ethpb.BeaconBlockHeader) (*bls.Signature, error)
SignProposal(pubKey [48]byte, domain [32]byte, data *ethpb.BeaconBlockHeader) (*bls.Signature, error)

// SignAttestation signs an attestation for the validator to broadcast.
SignAttestation(pubKey [48]byte, domain []byte, data *ethpb.AttestationData) (*bls.Signature, error)
SignAttestation(pubKey [48]byte, domain [32]byte, data *ethpb.AttestationData) (*bls.Signature, error)
}
Loading