2020-07-06 22:27:42 +00:00
|
|
|
package blocks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2021-04-14 01:32:49 +00:00
|
|
|
types "github.com/prysmaticlabs/eth2-types"
|
2020-07-06 22:27:42 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2021-09-27 16:19:20 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
|
2021-09-30 19:00:14 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
|
2021-07-23 16:11:21 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
2021-09-21 19:59:25 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/config/params"
|
2021-07-21 21:34:07 +00:00
|
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
2021-09-16 09:46:29 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/runtime/version"
|
2021-10-01 20:17:57 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/time/slots"
|
2021-05-17 18:32:04 +00:00
|
|
|
"google.golang.org/protobuf/proto"
|
2020-07-06 22:27:42 +00:00
|
|
|
)
|
|
|
|
|
2021-09-26 15:27:57 +00:00
|
|
|
type slashValidatorFunc func(ctx context.Context, st state.BeaconState, vid types.ValidatorIndex, penaltyQuotient, proposerRewardQuotient uint64) (state.BeaconState, error)
|
2021-04-14 01:32:49 +00:00
|
|
|
|
2020-07-06 22:27:42 +00:00
|
|
|
// ProcessProposerSlashings is one of the operations performed
|
|
|
|
// on each processed beacon block to slash proposers based on
|
|
|
|
// slashing conditions if any slashable events occurred.
|
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
|
|
|
// def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
2021-04-13 05:35:22 +00:00
|
|
|
// header_1 = proposer_slashing.signed_header_1.message
|
|
|
|
// header_2 = proposer_slashing.signed_header_2.message
|
|
|
|
//
|
|
|
|
// # Verify header slots match
|
|
|
|
// assert header_1.slot == header_2.slot
|
|
|
|
// # Verify header proposer indices match
|
|
|
|
// assert header_1.proposer_index == header_2.proposer_index
|
|
|
|
// # Verify the headers are different
|
|
|
|
// assert header_1 != header_2
|
|
|
|
// # Verify the proposer is slashable
|
|
|
|
// proposer = state.validators[header_1.proposer_index]
|
2020-07-06 22:27:42 +00:00
|
|
|
// assert is_slashable_validator(proposer, get_current_epoch(state))
|
2021-04-13 05:35:22 +00:00
|
|
|
// # Verify signatures
|
|
|
|
// for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
|
|
|
|
// domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot))
|
|
|
|
// signing_root = compute_signing_root(signed_header.message, domain)
|
|
|
|
// assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature)
|
2020-07-06 22:27:42 +00:00
|
|
|
//
|
2021-04-13 05:35:22 +00:00
|
|
|
// slash_validator(state, header_1.proposer_index)
|
2020-07-06 22:27:42 +00:00
|
|
|
func ProcessProposerSlashings(
|
2021-09-26 15:27:57 +00:00
|
|
|
ctx context.Context,
|
2021-07-23 16:11:21 +00:00
|
|
|
beaconState state.BeaconState,
|
2021-04-15 13:58:54 +00:00
|
|
|
slashings []*ethpb.ProposerSlashing,
|
2021-04-14 01:32:49 +00:00
|
|
|
slashFunc slashValidatorFunc,
|
2021-07-23 16:11:21 +00:00
|
|
|
) (state.BeaconState, error) {
|
2020-07-06 22:27:42 +00:00
|
|
|
var err error
|
2021-04-15 13:58:54 +00:00
|
|
|
for idx, slashing := range slashings {
|
2020-07-06 22:27:42 +00:00
|
|
|
if slashing == nil {
|
|
|
|
return nil, errors.New("nil proposer slashings in block body")
|
|
|
|
}
|
|
|
|
if err = VerifyProposerSlashing(beaconState, slashing); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not verify proposer slashing %d", idx)
|
|
|
|
}
|
2021-08-06 04:06:49 +00:00
|
|
|
cfg := params.BeaconConfig()
|
2021-08-18 18:44:17 +00:00
|
|
|
slashingQuotient := cfg.MinSlashingPenaltyQuotient
|
|
|
|
if beaconState.Version() == version.Altair {
|
|
|
|
slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair
|
|
|
|
}
|
2021-09-26 15:27:57 +00:00
|
|
|
beaconState, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex, slashingQuotient, cfg.ProposerRewardQuotient)
|
2020-07-06 22:27:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not slash proposer index %d", slashing.Header_1.Header.ProposerIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return beaconState, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyProposerSlashing verifies that the data provided from slashing is valid.
|
|
|
|
func VerifyProposerSlashing(
|
2021-09-29 02:27:21 +00:00
|
|
|
beaconState state.ReadOnlyBeaconState,
|
2020-07-06 22:27:42 +00:00
|
|
|
slashing *ethpb.ProposerSlashing,
|
|
|
|
) error {
|
|
|
|
if slashing.Header_1 == nil || slashing.Header_1.Header == nil || slashing.Header_2 == nil || slashing.Header_2.Header == nil {
|
|
|
|
return errors.New("nil header cannot be verified")
|
|
|
|
}
|
2020-07-28 16:49:47 +00:00
|
|
|
hSlot := slashing.Header_1.Header.Slot
|
|
|
|
if hSlot != slashing.Header_2.Header.Slot {
|
2020-07-06 22:27:42 +00:00
|
|
|
return fmt.Errorf("mismatched header slots, received %d == %d", slashing.Header_1.Header.Slot, slashing.Header_2.Header.Slot)
|
|
|
|
}
|
2020-07-28 16:49:47 +00:00
|
|
|
pIdx := slashing.Header_1.Header.ProposerIndex
|
|
|
|
if pIdx != slashing.Header_2.Header.ProposerIndex {
|
2020-07-06 22:27:42 +00:00
|
|
|
return fmt.Errorf("mismatched indices, received %d == %d", slashing.Header_1.Header.ProposerIndex, slashing.Header_2.Header.ProposerIndex)
|
|
|
|
}
|
2020-09-18 09:10:14 +00:00
|
|
|
if proto.Equal(slashing.Header_1.Header, slashing.Header_2.Header) {
|
2020-07-06 22:27:42 +00:00
|
|
|
return errors.New("expected slashing headers to differ")
|
|
|
|
}
|
|
|
|
proposer, err := beaconState.ValidatorAtIndexReadOnly(slashing.Header_1.Header.ProposerIndex)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-09-30 19:00:14 +00:00
|
|
|
if !helpers.IsSlashableValidatorUsingTrie(proposer, time.CurrentEpoch(beaconState)) {
|
2020-07-06 22:27:42 +00:00
|
|
|
return fmt.Errorf("validator with key %#x is not slashable", proposer.PublicKey())
|
|
|
|
}
|
|
|
|
headers := []*ethpb.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2}
|
|
|
|
for _, header := range headers {
|
2021-10-01 20:17:57 +00:00
|
|
|
if err := signing.ComputeDomainVerifySigningRoot(beaconState, pIdx, slots.ToEpoch(hSlot),
|
2020-07-28 16:49:47 +00:00
|
|
|
header.Header, params.BeaconConfig().DomainBeaconProposer, header.Signature); err != nil {
|
2020-07-06 22:27:42 +00:00
|
|
|
return errors.Wrap(err, "could not verify beacon block header")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|