mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 19:40:37 +00:00
Refactor block operations for validating exits slightly (#4612)
* Refactor block operations for validating exits slightly so that we don't have to advance state in a pubsub validator * current slot * remove duplicated validation for exits * nil request check Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
parent
9a395530b7
commit
cc58b5aca6
@ -960,7 +960,10 @@ func ProcessVoluntaryExits(ctx context.Context, beaconState *pb.BeaconState, bod
|
||||
exits := body.VoluntaryExits
|
||||
|
||||
for idx, exit := range exits {
|
||||
if err := VerifyExit(beaconState, exit); err != nil {
|
||||
if int(exit.Exit.ValidatorIndex) >= len(beaconState.Validators) {
|
||||
return nil, fmt.Errorf("validator index out of bound %d > %d", exit.Exit.ValidatorIndex, len(beaconState.Validators))
|
||||
}
|
||||
if err := VerifyExit(beaconState.Validators[exit.Exit.ValidatorIndex], beaconState.Slot, beaconState.Fork, exit); err != nil {
|
||||
return nil, errors.Wrapf(err, "could not verify exit %d", idx)
|
||||
}
|
||||
beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex)
|
||||
@ -1008,18 +1011,14 @@ func ProcessVoluntaryExitsNoVerify(
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
|
||||
// assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain)
|
||||
func VerifyExit(beaconState *pb.BeaconState, signed *ethpb.SignedVoluntaryExit) error {
|
||||
func VerifyExit(validator *ethpb.Validator, currentSlot uint64, fork *pb.Fork, signed *ethpb.SignedVoluntaryExit) error {
|
||||
if signed == nil || signed.Exit == nil {
|
||||
return errors.New("nil exit")
|
||||
}
|
||||
|
||||
exit := signed.Exit
|
||||
if int(exit.ValidatorIndex) >= len(beaconState.Validators) {
|
||||
return fmt.Errorf("validator index out of bound %d > %d", exit.ValidatorIndex, len(beaconState.Validators))
|
||||
}
|
||||
|
||||
validator := beaconState.Validators[exit.ValidatorIndex]
|
||||
currentEpoch := helpers.CurrentEpoch(beaconState)
|
||||
currentEpoch := helpers.SlotToEpoch(currentSlot)
|
||||
// Verify the validator is active.
|
||||
if !helpers.IsActiveValidator(validator, currentEpoch) {
|
||||
return errors.New("non-active validator cannot exit")
|
||||
@ -1040,7 +1039,7 @@ func VerifyExit(beaconState *pb.BeaconState, signed *ethpb.SignedVoluntaryExit)
|
||||
validator.ActivationEpoch+params.BeaconConfig().PersistentCommitteePeriod,
|
||||
)
|
||||
}
|
||||
domain := helpers.Domain(beaconState.Fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit)
|
||||
domain := helpers.Domain(fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit)
|
||||
if err := verifySigningRoot(exit, validator.PublicKey, signed.Signature, domain); err != nil {
|
||||
return ErrSigFailedToVerify
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["validation.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/exit",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/roughtime:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["validation_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
],
|
||||
)
|
@ -1,66 +0,0 @@
|
||||
package exit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/roughtime"
|
||||
)
|
||||
|
||||
// ValidateVoluntaryExit validates the voluntary exit.
|
||||
// If it is invalid for some reason an error, if valid it will return no error.
|
||||
func ValidateVoluntaryExit(state *pb.BeaconState, genesisTime time.Time, signed *ethpb.SignedVoluntaryExit) error {
|
||||
if signed == nil || signed.Exit == nil {
|
||||
return errors.New("nil signed voluntary exit")
|
||||
}
|
||||
ve := signed.Exit
|
||||
if ve.ValidatorIndex >= uint64(len(state.Validators)) {
|
||||
return fmt.Errorf("unknown validator index %d", ve.ValidatorIndex)
|
||||
}
|
||||
validator := state.Validators[ve.ValidatorIndex]
|
||||
|
||||
if !helpers.IsActiveValidator(validator, ve.Epoch) {
|
||||
return fmt.Errorf("validator %d not active at epoch %d", ve.ValidatorIndex, ve.Epoch)
|
||||
}
|
||||
if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
|
||||
return fmt.Errorf("validator %d already exiting or exited", ve.ValidatorIndex)
|
||||
}
|
||||
|
||||
secondsPerEpoch := params.BeaconConfig().SecondsPerSlot * params.BeaconConfig().SlotsPerEpoch
|
||||
currentEpoch := uint64(roughtime.Now().Unix()-genesisTime.Unix()) / secondsPerEpoch
|
||||
earliestRequestedExitEpoch := mathutil.Max(ve.Epoch, currentEpoch)
|
||||
earliestExitEpoch := validator.ActivationEpoch + params.BeaconConfig().PersistentCommitteePeriod
|
||||
if earliestRequestedExitEpoch < earliestExitEpoch {
|
||||
return fmt.Errorf("validator %d cannot exit before epoch %d", ve.ValidatorIndex, earliestExitEpoch)
|
||||
}
|
||||
|
||||
// Confirm signature is valid
|
||||
root, err := ssz.HashTreeRoot(ve)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot confirm signature")
|
||||
}
|
||||
sig, err := bls.SignatureFromBytes(signed.Signature)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "malformed signature")
|
||||
}
|
||||
validatorPubKey, err := bls.PublicKeyFromBytes(validator.PublicKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid validator public key")
|
||||
}
|
||||
domain := bls.ComputeDomain(params.BeaconConfig().DomainVoluntaryExit)
|
||||
verified := sig.Verify(root[:], validatorPubKey, domain)
|
||||
if !verified {
|
||||
return errors.New("incorrect signature")
|
||||
}
|
||||
|
||||
// Parameters are valid.
|
||||
return nil
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
package exit_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
blk "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/exit"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
// Set genesis to a small set for faster test processing.
|
||||
func init() {
|
||||
p := params.BeaconConfig()
|
||||
p.MinGenesisActiveValidatorCount = 8
|
||||
params.OverrideBeaconConfig(p)
|
||||
}
|
||||
|
||||
func TestValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
epoch uint64
|
||||
validatorIndex uint64
|
||||
signature []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "MissingValidator",
|
||||
epoch: 2048,
|
||||
validatorIndex: 16,
|
||||
err: errors.New("unknown validator index 16"),
|
||||
},
|
||||
{
|
||||
name: "EarlyExit",
|
||||
epoch: 2047,
|
||||
validatorIndex: 0,
|
||||
err: errors.New("validator 0 cannot exit before epoch 2048"),
|
||||
},
|
||||
{
|
||||
name: "NoSignature",
|
||||
epoch: 2048,
|
||||
validatorIndex: 0,
|
||||
err: errors.New("malformed signature: signature must be 96 bytes"),
|
||||
},
|
||||
{
|
||||
name: "InvalidSignature",
|
||||
epoch: 2048,
|
||||
validatorIndex: 0,
|
||||
signature: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
err: errors.New("malformed signature: could not unmarshal bytes into signature: err blsSignatureDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
name: "IncorrectSignature",
|
||||
epoch: 2048,
|
||||
validatorIndex: 0,
|
||||
signature: []byte{0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84},
|
||||
err: errors.New("incorrect signature"),
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
epoch: 2048,
|
||||
validatorIndex: 0,
|
||||
signature: []byte{0xb3, 0xe1, 0x9d, 0xc6, 0x7c, 0x78, 0x6c, 0xcf, 0x33, 0x1d, 0xb9, 0x6f, 0x59, 0x64, 0x44, 0xe1, 0x29, 0xd0, 0x87, 0x03, 0x26, 0x6e, 0x49, 0x1c, 0x05, 0xae, 0x16, 0x7b, 0x04, 0x0f, 0x3f, 0xf8, 0x82, 0x77, 0x60, 0xfc, 0xcf, 0x2f, 0x59, 0xc7, 0x40, 0x0b, 0x2c, 0xa9, 0x23, 0x8a, 0x6c, 0x8d, 0x01, 0x21, 0x5e, 0xa8, 0xac, 0x36, 0x70, 0x31, 0xb0, 0xe1, 0xa8, 0xb8, 0x8f, 0x93, 0x8c, 0x1c, 0xa2, 0x86, 0xe7, 0x22, 0x00, 0x6a, 0x7d, 0x36, 0xc0, 0x2b, 0x86, 0x2c, 0xf5, 0xf9, 0x10, 0xb9, 0xf2, 0xbd, 0x5e, 0xa6, 0x5f, 0x12, 0x86, 0x43, 0x20, 0x4d, 0xa2, 0x9d, 0x8b, 0xe6, 0x6f, 0x09},
|
||||
},
|
||||
}
|
||||
|
||||
db := dbutil.SetupDB(t)
|
||||
defer dbutil.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
deposits, _, _ := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block := blk.NewGenesisBlock([]byte{})
|
||||
if err := db.SaveBlock(ctx, block); err != nil {
|
||||
t.Fatalf("Could not save genesis block: %v", err)
|
||||
}
|
||||
genesisRoot, err := ssz.HashTreeRoot(block.Block)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get signing root %v", err)
|
||||
}
|
||||
|
||||
// Set genesis time to be 100 epochs ago
|
||||
genesisTime := time.Now().Add(time.Duration(-100*int64(params.BeaconConfig().SecondsPerSlot*params.BeaconConfig().SlotsPerEpoch)) * time.Second)
|
||||
mockChainService := &mockChain.ChainService{State: beaconState, Root: genesisRoot[:], Genesis: genesisTime}
|
||||
headState, err := mockChainService.HeadState(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal("Failed to obtain head state")
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: test.epoch,
|
||||
ValidatorIndex: test.validatorIndex,
|
||||
},
|
||||
Signature: test.signature,
|
||||
}
|
||||
|
||||
err := exit.ValidateVoluntaryExit(headState, genesisTime, req)
|
||||
if test.err == nil {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: received %v", err)
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Error("Failed to receive expected error")
|
||||
}
|
||||
if err.Error() != test.err.Error() {
|
||||
t.Errorf("Unexpected error: expected %s, received %s", test.err.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ go_library(
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/exit:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/operation:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
|
@ -5,23 +5,29 @@ import (
|
||||
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/exit"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// ProposeExit proposes an exit for a validator.
|
||||
func (vs *Server) ProposeExit(ctx context.Context, req *ethpb.SignedVoluntaryExit) (*ptypes.Empty, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "nil request")
|
||||
}
|
||||
s, err := vs.HeadFetcher.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err)
|
||||
}
|
||||
|
||||
// Confirm the validator is eligible to exit with the parameters provided.
|
||||
err = exit.ValidateVoluntaryExit(s, vs.GenesisTime, req)
|
||||
if err != nil {
|
||||
if int(req.Exit.ValidatorIndex) >= len(s.Validators) {
|
||||
return nil, status.Error(codes.InvalidArgument, "validator index exceeds validator set length")
|
||||
}
|
||||
if err := blocks.VerifyExit(s.Validators[req.Exit.ValidatorIndex], helpers.StartSlot(req.Exit.Epoch), s.Fork, req); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
"go.opencensus.io/trace"
|
||||
@ -42,22 +41,16 @@ func (r *Service) validateVoluntaryExit(ctx context.Context, pid peer.ID, msg *p
|
||||
return false
|
||||
}
|
||||
|
||||
// Retrieve head state, advance state to the epoch slot used specified in exit message.
|
||||
s, err := r.chain.HeadState(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
exitedEpochSlot := exit.Exit.Epoch * params.BeaconConfig().SlotsPerEpoch
|
||||
if s.Slot < exitedEpochSlot {
|
||||
var err error
|
||||
s, err = state.ProcessSlots(ctx, s, exitedEpochSlot)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if int(exit.Exit.ValidatorIndex) >= len(s.Validators) {
|
||||
return false
|
||||
}
|
||||
|
||||
if err := blocks.VerifyExit(s, exit); err != nil {
|
||||
if err := blocks.VerifyExit(s.Validators[exit.Exit.ValidatorIndex], exitedEpochSlot, s.Fork, exit); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ func setupValidExit(t *testing.T) (*ethpb.SignedVoluntaryExit, *pb.BeaconState)
|
||||
exit := ðpb.SignedVoluntaryExit{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
ValidatorIndex: 0,
|
||||
Epoch: 0,
|
||||
Epoch: 1+params.BeaconConfig().PersistentCommitteePeriod,
|
||||
},
|
||||
}
|
||||
registry := []*ethpb.Validator{
|
||||
|
Loading…
Reference in New Issue
Block a user