package transition import ( "errors" "fmt" "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon/cl/cltypes/solid" 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 solid.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() rawProof := []common.Hash{} deposit.Proof.Range(func(_ int, l common.Hash, _ int) bool { rawProof = append(rawProof, l) return true }) // Validate merkle proof for deposit leaf. if fullValidation && !utils.IsValidMerkleBranch( depositLeaf, rawProof, 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 *solid.ListSSZ[*types.Withdrawal], 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) != withdrawals.Len() { return fmt.Errorf("ProcessWithdrawals: expected %d withdrawals, but got %d", len(expectedWithdrawals), withdrawals.Len()) } if err := solid.RangeErr[*types.Withdrawal](withdrawals, func(i int, w *types.Withdrawal, _ int) error { if !expectedWithdrawals[i].Equal(w) { return fmt.Errorf("ProcessWithdrawals: withdrawal %d does not match expected withdrawal", i) } return nil }); err != nil { return err } } if err := solid.RangeErr[*types.Withdrawal](withdrawals, func(_ int, w *types.Withdrawal, _ int) error { if err := state2.DecreaseBalance(s.BeaconState, w.Validator, w.Amount); err != nil { return err } return nil }); err != nil { return err } // Update next withdrawal index based on number of withdrawals. if withdrawals.Len() > 0 { lastWithdrawalIndex := withdrawals.Get(withdrawals.Len() - 1).Index s.SetNextWithdrawalIndex(lastWithdrawalIndex + 1) } // Update next withdrawal validator index based on number of withdrawals. if withdrawals.Len() == int(beaconConfig.MaxWithdrawalsPerPayload) { lastWithdrawalValidatorIndex := withdrawals.Get(withdrawals.Len()-1).Validator + 1 s.SetNextWithdrawalValidatorIndex(lastWithdrawalValidatorIndex % numValidators) } else { nextIndex := s.NextWithdrawalValidatorIndex() + beaconConfig.MaxValidatorsPerWithdrawalsSweep s.SetNextWithdrawalValidatorIndex(nextIndex % numValidators) } return nil }