erigon-pulse/cl/phase1/forkchoice/on_operations.go
Giulio rebuffo 57bcbaa21f
Adds flags to enable/disable backfilling and enable full historical beacon node (#8813)
* Correct naming of hash func in Eth2
* Customizable mode of operation for Caplin
2023-11-22 13:24:35 +01:00

217 lines
6.5 KiB
Go

package forkchoice
import (
"bytes"
"errors"
"fmt"
"github.com/Giulio2002/bls"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/fork"
"github.com/ledgerwatch/erigon/cl/phase1/core/state"
"github.com/ledgerwatch/erigon/cl/pool"
"github.com/ledgerwatch/erigon/cl/utils"
)
// NOTE: This file implements non-official handlers for other types of iterations. what it does is,using the forkchoices
// and verify external operations and eventually push them in the operations pool.
// OnVoluntaryExit is a non-official handler for voluntary exit operations. it pushes the voluntary exit in the pool.
func (f *ForkChoiceStore) OnVoluntaryExit(signedVoluntaryExit *cltypes.SignedVoluntaryExit, test bool) error {
voluntaryExit := signedVoluntaryExit.VoluntaryExit
if f.operationsPool.VoluntaryExistsPool.Has(voluntaryExit.ValidatorIndex) {
return nil
}
f.mu.Lock()
headHash, _, err := f.getHead()
if err != nil {
f.mu.Unlock()
return err
}
s, err := f.forkGraph.GetState(headHash, false)
if err != nil {
f.mu.Unlock()
return err
}
val, err := s.ValidatorForValidatorIndex(int(voluntaryExit.ValidatorIndex))
if err != nil {
f.mu.Unlock()
return err
}
if val.ExitEpoch() != f.beaconCfg.FarFutureEpoch {
f.mu.Unlock()
return nil
}
pk := val.PublicKey()
f.mu.Unlock()
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
}
if !test {
valid, err := bls.Verify(signedVoluntaryExit.Signature[:], signingRoot[:], pk[:])
if err != nil {
return err
}
if !valid {
return errors.New("ProcessVoluntaryExit: BLS verification failed")
}
}
f.operationsPool.VoluntaryExistsPool.Insert(voluntaryExit.ValidatorIndex, signedVoluntaryExit)
return nil
}
// OnProposerSlashing is a non-official handler for proposer slashing operations. it pushes the proposer slashing in the pool.
func (f *ForkChoiceStore) OnProposerSlashing(proposerSlashing *cltypes.ProposerSlashing, test bool) (err error) {
if f.operationsPool.ProposerSlashingsPool.Has(pool.ComputeKeyForProposerSlashing(proposerSlashing)) {
return nil
}
h1 := proposerSlashing.Header1.Header
h2 := proposerSlashing.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)
}
if *h1 == *h2 {
return fmt.Errorf("proposee slashing headers are the same")
}
// Take lock as we interact with state.
f.mu.Lock()
headHash, _, err := f.getHead()
if err != nil {
f.mu.Unlock()
return err
}
s, err := f.forkGraph.GetState(headHash, false)
if err != nil {
f.mu.Unlock()
return err
}
proposer, err := s.ValidatorForValidatorIndex(int(h1.ProposerIndex))
if err != nil {
f.mu.Unlock()
return fmt.Errorf("unable to retrieve state: %v", err)
}
if !proposer.IsSlashable(state.Epoch(s)) {
f.mu.Unlock()
return fmt.Errorf("proposer is not slashable: %v", proposer)
}
domain1, err := s.GetDomain(s.BeaconConfig().DomainBeaconProposer, state.GetEpochAtSlot(s.BeaconConfig(), h1.Slot))
if err != nil {
return fmt.Errorf("unable to get domain: %v", err)
}
domain2, err := s.GetDomain(s.BeaconConfig().DomainBeaconProposer, state.GetEpochAtSlot(s.BeaconConfig(), h2.Slot))
if err != nil {
return fmt.Errorf("unable to get domain: %v", err)
}
pk := proposer.PublicKey()
f.mu.Unlock()
if test {
f.operationsPool.ProposerSlashingsPool.Insert(pool.ComputeKeyForProposerSlashing(proposerSlashing), proposerSlashing)
return nil
}
signingRoot, err := fork.ComputeSigningRoot(h1, domain1)
if err != nil {
return fmt.Errorf("unable to compute signing root: %v", err)
}
valid, err := bls.Verify(proposerSlashing.Header1.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", proposerSlashing.Header1.Signature[:], signingRoot[:], pk)
}
signingRoot, err = fork.ComputeSigningRoot(h2, domain2)
if err != nil {
return fmt.Errorf("unable to compute signing root: %v", err)
}
valid, err = bls.Verify(proposerSlashing.Header2.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", proposerSlashing.Header2.Signature[:], signingRoot[:], pk)
}
f.operationsPool.ProposerSlashingsPool.Insert(pool.ComputeKeyForProposerSlashing(proposerSlashing), proposerSlashing)
return nil
}
func (f *ForkChoiceStore) OnBlsToExecutionChange(signedChange *cltypes.SignedBLSToExecutionChange, test bool) error {
if f.operationsPool.BLSToExecutionChangesPool.Has(signedChange.Signature) {
return nil
}
change := signedChange.Message
// Take lock as we interact with state.
f.mu.Lock()
headHash, _, err := f.getHead()
if err != nil {
f.mu.Unlock()
return err
}
s, err := f.forkGraph.GetState(headHash, false)
if err != nil {
f.mu.Unlock()
return err
}
validator, err := s.ValidatorForValidatorIndex(int(change.ValidatorIndex))
if err != nil {
f.mu.Unlock()
return fmt.Errorf("unable to retrieve state: %v", err)
}
wc := validator.WithdrawalCredentials()
if wc[0] != f.beaconCfg.BLSWithdrawalPrefixByte {
f.mu.Unlock()
return fmt.Errorf("invalid withdrawal credentials prefix")
}
genesisValidatorRoot := s.GenesisValidatorsRoot()
f.mu.Unlock()
// Perform full validation if requested.
if !test {
// Check the validator's withdrawal credentials against the provided message.
hashedFrom := utils.Sha256(change.From[:])
if !bytes.Equal(hashedFrom[1:], wc[1:]) {
return fmt.Errorf("invalid withdrawal credentials")
}
// Compute the signing domain and verify the message signature.
domain, err := fork.ComputeDomain(f.beaconCfg.DomainBLSToExecutionChange[:], utils.Uint32ToBytes4(f.beaconCfg.GenesisForkVersion), genesisValidatorRoot)
if err != nil {
return err
}
signedRoot, err := fork.ComputeSigningRoot(change, domain)
if err != nil {
return err
}
valid, err := bls.Verify(signedChange.Signature[:], signedRoot[:], change.From[:])
if err != nil {
return err
}
if !valid {
return fmt.Errorf("invalid signature")
}
}
f.operationsPool.BLSToExecutionChangesPool.Insert(signedChange.Signature, signedChange)
return nil
}