mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-10 21:11:20 +00:00
30430d585a
this first major move separates the transient beacon state cache from the underlying tree. leaf updates are enforced in the setters, which should make programming easier. all exported methods of the raw.BeaconState should be safe to call (without disrupting internal state) changes many functions to consume *raw.BeaconState in perparation for interface beyond refactor it also: adds a pool for the leaves of the validator ssz hash adds a pool for the snappy writers removed the parallel hash experiment (high memory use)
267 lines
9.0 KiB
Go
267 lines
9.0 KiB
Go
package transition
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/Giulio2002/bls"
|
|
"github.com/ledgerwatch/log/v3"
|
|
|
|
"github.com/ledgerwatch/erigon/cl/clparams"
|
|
"github.com/ledgerwatch/erigon/cl/cltypes"
|
|
"github.com/ledgerwatch/erigon/cl/fork"
|
|
"github.com/ledgerwatch/erigon/cl/utils"
|
|
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
|
|
"github.com/ledgerwatch/erigon/core/types"
|
|
)
|
|
|
|
func ProcessProposerSlashing(s *state.BeaconState, propSlashing *cltypes.ProposerSlashing) error {
|
|
h1 := propSlashing.Header1.Header
|
|
h2 := propSlashing.Header2.Header
|
|
|
|
if h1.Slot != h2.Slot {
|
|
return fmt.Errorf("non-matching slots on proposer slashing: %d != %d", h1.Slot, h2.Slot)
|
|
}
|
|
|
|
if h1.ProposerIndex != h2.ProposerIndex {
|
|
return fmt.Errorf("non-matching proposer indices proposer slashing: %d != %d", h1.ProposerIndex, h2.ProposerIndex)
|
|
}
|
|
|
|
h1Root, err := h1.HashSSZ()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to hash header1: %v", err)
|
|
}
|
|
h2Root, err := h2.HashSSZ()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to hash header2: %v", err)
|
|
}
|
|
if h1Root == h2Root {
|
|
return fmt.Errorf("propose slashing headers are the same: %v == %v", h1Root, h2Root)
|
|
}
|
|
|
|
proposer, err := s.ValidatorForValidatorIndex(int(h1.ProposerIndex))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !proposer.IsSlashable(state.Epoch(s.BeaconState)) {
|
|
return fmt.Errorf("proposer is not slashable: %v", proposer)
|
|
}
|
|
|
|
for _, signedHeader := range []*cltypes.SignedBeaconBlockHeader{propSlashing.Header1, propSlashing.Header2} {
|
|
domain, err := s.GetDomain(s.BeaconConfig().DomainBeaconProposer, state.GetEpochAtSlot(s.BeaconConfig(), signedHeader.Header.Slot))
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get domain: %v", err)
|
|
}
|
|
signingRoot, err := fork.ComputeSigningRoot(signedHeader.Header, domain)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to compute signing root: %v", err)
|
|
}
|
|
valid, err := bls.Verify(signedHeader.Signature[:], signingRoot[:], proposer.PublicKey[:])
|
|
if err != nil {
|
|
return fmt.Errorf("unable to verify signature: %v", err)
|
|
}
|
|
if !valid {
|
|
return fmt.Errorf("invalid signature: signature %v, root %v, pubkey %v", signedHeader.Signature[:], signingRoot[:], proposer.PublicKey[:])
|
|
}
|
|
}
|
|
|
|
// Set whistleblower index to 0 so current proposer gets reward.
|
|
s.SlashValidator(h1.ProposerIndex, nil)
|
|
return nil
|
|
}
|
|
|
|
func ProcessAttesterSlashing(s *state.BeaconState, attSlashing *cltypes.AttesterSlashing) error {
|
|
att1 := attSlashing.Attestation_1
|
|
att2 := attSlashing.Attestation_2
|
|
|
|
if !cltypes.IsSlashableAttestationData(att1.Data, att2.Data) {
|
|
return fmt.Errorf("attestation data not slashable: %+v; %+v", att1.Data, att2.Data)
|
|
}
|
|
|
|
valid, err := state.IsValidIndexedAttestation(s.BeaconState, att1)
|
|
if err != nil {
|
|
return fmt.Errorf("error calculating indexed attestation 1 validity: %v", err)
|
|
}
|
|
if !valid {
|
|
return fmt.Errorf("invalid indexed attestation 1")
|
|
}
|
|
|
|
valid, err = state.IsValidIndexedAttestation(s.BeaconState, att2)
|
|
if err != nil {
|
|
return fmt.Errorf("error calculating indexed attestation 2 validity: %v", err)
|
|
}
|
|
if !valid {
|
|
return fmt.Errorf("invalid indexed attestation 2")
|
|
}
|
|
|
|
slashedAny := false
|
|
currentEpoch := state.GetEpochAtSlot(s.BeaconConfig(), s.Slot())
|
|
for _, ind := range utils.IntersectionOfSortedSets(att1.AttestingIndices, att2.AttestingIndices) {
|
|
validator, err := s.ValidatorForValidatorIndex(int(ind))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if validator.IsSlashable(currentEpoch) {
|
|
err := s.SlashValidator(ind, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to slash validator: %d", ind)
|
|
}
|
|
slashedAny = true
|
|
}
|
|
}
|
|
|
|
if !slashedAny {
|
|
return fmt.Errorf("no validators slashed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ProcessDeposit(s *state.BeaconState, deposit *cltypes.Deposit, fullValidation bool) error {
|
|
if deposit == nil {
|
|
return nil
|
|
}
|
|
depositLeaf, err := deposit.Data.HashSSZ()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
depositIndex := s.Eth1DepositIndex()
|
|
eth1Data := s.Eth1Data()
|
|
// Validate merkle proof for deposit leaf.
|
|
if fullValidation && !utils.IsValidMerkleBranch(
|
|
depositLeaf,
|
|
deposit.Proof,
|
|
s.BeaconConfig().DepositContractTreeDepth+1,
|
|
depositIndex,
|
|
eth1Data.Root,
|
|
) {
|
|
return fmt.Errorf("processDepositForAltair: Could not validate deposit root")
|
|
}
|
|
|
|
// Increment index
|
|
s.SetEth1DepositIndex(depositIndex + 1)
|
|
publicKey := deposit.Data.PubKey
|
|
amount := deposit.Data.Amount
|
|
// Check if pub key is in validator set
|
|
validatorIndex, has := s.ValidatorIndexByPubkey(publicKey)
|
|
if !has {
|
|
// Agnostic domain.
|
|
domain, err := fork.ComputeDomain(s.BeaconConfig().DomainDeposit[:], utils.Uint32ToBytes4(s.BeaconConfig().GenesisForkVersion), [32]byte{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
depositMessageRoot, err := deposit.Data.MessageHash()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
signedRoot := utils.Keccak256(depositMessageRoot[:], domain)
|
|
// Perform BLS verification and if successful noice.
|
|
valid, err := bls.Verify(deposit.Data.Signature[:], signedRoot[:], publicKey[:])
|
|
// Literally you can input it trash.
|
|
if !valid || err != nil {
|
|
log.Debug("Validator BLS verification failed", "valid", valid, "err", err)
|
|
return nil
|
|
}
|
|
// Append validator
|
|
s.AddValidator(state.ValidatorFromDeposit(s.BeaconConfig(), deposit), amount)
|
|
// Altair forward
|
|
if s.Version() >= clparams.AltairVersion {
|
|
s.AddCurrentEpochParticipationFlags(cltypes.ParticipationFlags(0))
|
|
s.AddPreviousEpochParticipationFlags(cltypes.ParticipationFlags(0))
|
|
s.AddInactivityScore(0)
|
|
}
|
|
return nil
|
|
}
|
|
// Increase the balance if exists already
|
|
return state.IncreaseBalance(s.BeaconState, validatorIndex, amount)
|
|
}
|
|
|
|
// ProcessVoluntaryExit takes a voluntary exit and applies state transition.
|
|
func ProcessVoluntaryExit(s *state.BeaconState, signedVoluntaryExit *cltypes.SignedVoluntaryExit, fullValidation bool) error {
|
|
// Sanity checks so that we know it is good.
|
|
voluntaryExit := signedVoluntaryExit.VolunaryExit
|
|
currentEpoch := state.Epoch(s.BeaconState)
|
|
validator, err := s.ValidatorForValidatorIndex(int(voluntaryExit.ValidatorIndex))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !validator.Active(currentEpoch) {
|
|
return errors.New("ProcessVoluntaryExit: validator is not active")
|
|
}
|
|
if validator.ExitEpoch != s.BeaconConfig().FarFutureEpoch {
|
|
return errors.New("ProcessVoluntaryExit: another exit for the same validator is already getting processed")
|
|
}
|
|
if currentEpoch < voluntaryExit.Epoch {
|
|
return errors.New("ProcessVoluntaryExit: exit is happening in the future")
|
|
}
|
|
if currentEpoch < validator.ActivationEpoch+s.BeaconConfig().ShardCommitteePeriod {
|
|
return errors.New("ProcessVoluntaryExit: exit is happening too fast")
|
|
}
|
|
|
|
// We can skip it in some instances if we want to optimistically sync up.
|
|
if fullValidation {
|
|
domain, err := s.GetDomain(s.BeaconConfig().DomainVoluntaryExit, voluntaryExit.Epoch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
signingRoot, err := fork.ComputeSigningRoot(voluntaryExit, domain)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
valid, err := bls.Verify(signedVoluntaryExit.Signature[:], signingRoot[:], validator.PublicKey[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !valid {
|
|
return errors.New("ProcessVoluntaryExit: BLS verification failed")
|
|
}
|
|
}
|
|
// Do the exit (same process in slashing).
|
|
return s.InitiateValidatorExit(voluntaryExit.ValidatorIndex)
|
|
}
|
|
|
|
// ProcessWithdrawals processes withdrawals by decreasing the balance of each validator
|
|
// and updating the next withdrawal index and validator index.
|
|
func ProcessWithdrawals(s *state.BeaconState, withdrawals types.Withdrawals, fullValidation bool) error {
|
|
// Get the list of withdrawals, the expected withdrawals (if performing full validation),
|
|
// and the beacon configuration.
|
|
beaconConfig := s.BeaconConfig()
|
|
numValidators := uint64(len(s.Validators()))
|
|
|
|
// Check if full validation is required and verify expected withdrawals.
|
|
if fullValidation {
|
|
expectedWithdrawals := state.ExpectedWithdrawals(s.BeaconState)
|
|
if len(expectedWithdrawals) != len(withdrawals) {
|
|
return fmt.Errorf("ProcessWithdrawals: expected %d withdrawals, but got %d", len(expectedWithdrawals), len(withdrawals))
|
|
}
|
|
for i, withdrawal := range withdrawals {
|
|
if !expectedWithdrawals[i].Equal(withdrawal) {
|
|
return fmt.Errorf("ProcessWithdrawals: withdrawal %d does not match expected withdrawal", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decrease the balance of each validator for the corresponding withdrawal.
|
|
for _, withdrawal := range withdrawals {
|
|
if err := state.DecreaseBalance(s.BeaconState, withdrawal.Validator, withdrawal.Amount); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Update next withdrawal index based on number of withdrawals.
|
|
if len(withdrawals) > 0 {
|
|
lastWithdrawalIndex := withdrawals[len(withdrawals)-1].Index
|
|
s.SetNextWithdrawalIndex(lastWithdrawalIndex + 1)
|
|
}
|
|
|
|
// Update next withdrawal validator index based on number of withdrawals.
|
|
if len(withdrawals) == int(beaconConfig.MaxWithdrawalsPerPayload) {
|
|
lastWithdrawalValidatorIndex := withdrawals[len(withdrawals)-1].Validator + 1
|
|
s.SetNextWithdrawalValidatorIndex(lastWithdrawalValidatorIndex % numValidators)
|
|
} else {
|
|
nextIndex := s.NextWithdrawalValidatorIndex() + beaconConfig.MaxValidatorsPerWithdrawalsSweep
|
|
s.SetNextWithdrawalValidatorIndex(nextIndex % numValidators)
|
|
}
|
|
|
|
return nil
|
|
}
|