erigon-pulse/cl/phase1/core/transition/operations.go
a cda14447ad
[caplin] pkg refactor (#7507)
Co-authored-by: Giulio <giulio.rebuffo@gmail.com>
2023-05-13 23:44:07 +02:00

269 lines
9.1 KiB
Go

package transition
import (
"errors"
"fmt"
state2 "github.com/ledgerwatch/erigon/cl/phase1/core/state"
"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/core/types"
)
func ProcessProposerSlashing(s *state2.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(state2.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, state2.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)
}
pk := proposer.PublicKey()
valid, err := bls.Verify(signedHeader.Signature[:], signingRoot[:], pk[:])
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[:], pk)
}
}
// Set whistleblower index to 0 so current proposer gets reward.
s.SlashValidator(h1.ProposerIndex, nil)
return nil
}
func ProcessAttesterSlashing(s *state2.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 := state2.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 = state2.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 := state2.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 *state2.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(state2.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 state2.IncreaseBalance(s.BeaconState, validatorIndex, amount)
}
// ProcessVoluntaryExit takes a voluntary exit and applies state transition.
func ProcessVoluntaryExit(s *state2.BeaconState, signedVoluntaryExit *cltypes.SignedVoluntaryExit, fullValidation bool) error {
// Sanity checks so that we know it is good.
voluntaryExit := signedVoluntaryExit.VolunaryExit
currentEpoch := state2.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
}
pk := validator.PublicKey()
valid, err := bls.Verify(signedVoluntaryExit.Signature[:], signingRoot[:], pk[:])
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 *state2.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(s.ValidatorLength())
// Check if full validation is required and verify expected withdrawals.
if fullValidation {
expectedWithdrawals := state2.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 := state2.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
}