erigon-pulse/cl/phase1/forkchoice/on_attester_slashing.go
2023-10-21 23:10:58 +02:00

125 lines
3.8 KiB
Go

package forkchoice
import (
"fmt"
"github.com/Giulio2002/bls"
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
"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/cltypes"
)
func (f *ForkChoiceStore) OnAttesterSlashing(attesterSlashing *cltypes.AttesterSlashing, test bool) error {
if f.operationsPool.AttesterSlashingsPool.Has(pool.ComputeKeyForAttesterSlashing(attesterSlashing)) {
return nil
}
f.mu.Lock()
// Check if this attestation is even slashable.
attestation1 := attesterSlashing.Attestation_1
attestation2 := attesterSlashing.Attestation_2
if !cltypes.IsSlashableAttestationData(attestation1.Data, attestation2.Data) {
f.mu.Unlock()
return fmt.Errorf("attestation data is not slashable")
}
// Retrieve justified state
s, err := f.forkGraph.GetState(f.justifiedCheckpoint.BlockRoot(), false)
if err != nil {
f.mu.Unlock()
return err
}
if s == nil {
f.mu.Unlock()
return fmt.Errorf("justified checkpoint state not accessible")
}
attestation1PublicKeys, err := getIndexedAttestationPublicKeys(s, attestation1)
if err != nil {
f.mu.Unlock()
return err
}
attestation2PublicKeys, err := getIndexedAttestationPublicKeys(s, attestation2)
if err != nil {
f.mu.Unlock()
return err
}
domain1, err := s.GetDomain(s.BeaconConfig().DomainBeaconAttester, attestation1.Data.Target().Epoch())
if err != nil {
return fmt.Errorf("unable to get the domain: %v", err)
}
domain2, err := s.GetDomain(s.BeaconConfig().DomainBeaconAttester, attestation2.Data.Target().Epoch())
if err != nil {
return fmt.Errorf("unable to get the domain: %v", err)
}
f.mu.Unlock()
if !test {
// Verify validity of slashings (1)
signingRoot, err := fork.ComputeSigningRoot(attestation1.Data, domain1)
if err != nil {
return fmt.Errorf("unable to get signing root: %v", err)
}
valid, err := bls.VerifyAggregate(attestation1.Signature[:], signingRoot[:], attestation1PublicKeys)
if err != nil {
return fmt.Errorf("error while validating signature: %v", err)
}
if !valid {
return fmt.Errorf("invalid aggregate signature")
}
// Verify validity of slashings (2)
signingRoot, err = fork.ComputeSigningRoot(attestation2.Data, domain2)
if err != nil {
return fmt.Errorf("unable to get signing root: %v", err)
}
valid, err = bls.VerifyAggregate(attestation2.Signature[:], signingRoot[:], attestation2PublicKeys)
if err != nil {
return fmt.Errorf("error while validating signature: %v", err)
}
if !valid {
return fmt.Errorf("invalid aggregate signature")
}
}
f.mu.Lock()
defer f.mu.Unlock()
var anySlashed bool
for _, index := range solid.IntersectionOfSortedSets(attestation1.AttestingIndices, attestation2.AttestingIndices) {
f.equivocatingIndicies[index] = struct{}{}
if !anySlashed {
v, err := s.ValidatorForValidatorIndex(int(index))
if err != nil {
return fmt.Errorf("unable to retrieve state: %v", err)
}
if v.IsSlashable(state.Epoch(s)) {
anySlashed = true
}
}
}
if anySlashed {
f.operationsPool.AttesterSlashingsPool.Insert(pool.ComputeKeyForAttesterSlashing(attesterSlashing), attesterSlashing)
}
return nil
}
func getIndexedAttestationPublicKeys(b *state.CachingBeaconState, att *cltypes.IndexedAttestation) ([][]byte, error) {
inds := att.AttestingIndices
if inds.Length() == 0 || !solid.IsUint64SortedSet(inds) {
return nil, fmt.Errorf("isValidIndexedAttestation: attesting indices are not sorted or are null")
}
pks := make([][]byte, 0, inds.Length())
if err := solid.RangeErr[uint64](inds, func(_ int, v uint64, _ int) error {
val, err := b.ValidatorForValidatorIndex(int(v))
if err != nil {
return err
}
pk := val.PublicKey()
pks = append(pks, pk[:])
return nil
}); err != nil {
return nil, err
}
return pks, nil
}