Skip to content

Commit 586416c

Browse files
authored
check attestation signature (#10018)
1 parent 190cbfa commit 586416c

File tree

8 files changed

+234
-26
lines changed

8 files changed

+234
-26
lines changed

cl/beacon/synced_data/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "github.com/ledgerwatch/erigon/cl/phase1/core/state"
66
type SyncedData interface {
77
OnHeadState(newState *state.CachingBeaconState) (err error)
88
HeadState() *state.CachingBeaconState
9+
HeadStateReader() state.BeaconStateReader
910
Syncing() bool
1011
HeadSlot() uint64
1112
}

cl/beacon/synced_data/mock_services/synced_data_mock.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cl/beacon/synced_data/synced_data.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ func (s *SyncedDataManager) HeadState() *state.CachingBeaconState {
4343
return nil
4444
}
4545

46+
func (s *SyncedDataManager) HeadStateReader() state.BeaconStateReader {
47+
headstate := s.HeadState()
48+
if headstate == nil {
49+
return nil
50+
}
51+
return headstate
52+
}
53+
4654
func (s *SyncedDataManager) Syncing() bool {
4755
if !s.enabled {
4856
return false

cl/phase1/core/state/interface.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package state
2+
3+
import libcommon "github.com/ledgerwatch/erigon-lib/common"
4+
5+
// BeaconStateReader is an interface for reading the beacon state.
6+
//
7+
//go:generate mockgen -destination=./mock_services/beacon_state_reader.go -package=mock_services . BeaconStateReader
8+
type BeaconStateReader interface {
9+
ValidatorPublicKey(index int) (libcommon.Bytes48, error)
10+
GetDomain(domainType [4]byte, epoch uint64) ([]byte, error)
11+
CommitteeCount(epoch uint64) uint64
12+
}

cl/phase1/core/state/mock_services/beacon_state_reader.go

Lines changed: 84 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cl/phase1/network/services/attestation_service.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import (
55
"fmt"
66
"time"
77

8+
"github.com/Giulio2002/bls"
89
"github.com/ledgerwatch/erigon/cl/beacon/synced_data"
910
"github.com/ledgerwatch/erigon/cl/clparams"
1011
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
12+
"github.com/ledgerwatch/erigon/cl/fork"
1113
"github.com/ledgerwatch/erigon/cl/phase1/core/state/lru"
1214
"github.com/ledgerwatch/erigon/cl/phase1/forkchoice"
1315
"github.com/ledgerwatch/erigon/cl/phase1/network/subnets"
@@ -19,6 +21,8 @@ import (
1921
var (
2022
computeSubnetForAttestation = subnets.ComputeSubnetForAttestation
2123
computeCommitteeCountPerSlot = subnets.ComputeCommitteeCountPerSlot
24+
computeSigningRoot = fork.ComputeSigningRoot
25+
blsVerify = bls.Verify
2226
)
2327

2428
type attestationService struct {
@@ -59,7 +63,7 @@ func (s *attestationService) ProcessMessage(ctx context.Context, subnet *uint64,
5963
committeeIndex = att.AttestantionData().CommitteeIndex()
6064
targetEpoch = att.AttestantionData().Target().Epoch()
6165
)
62-
headState := s.syncedDataManager.HeadState()
66+
headState := s.syncedDataManager.HeadStateReader()
6367
if headState == nil {
6468
return ErrIgnore
6569
}
@@ -117,7 +121,6 @@ func (s *attestationService) ProcessMessage(ctx context.Context, subnet *uint64,
117121
if setBits != 1 {
118122
return fmt.Errorf("attestation does not have exactly one participating validator")
119123
}
120-
121124
// [IGNORE] There has been no other valid attestation seen on an attestation subnet that has an identical attestation.data.target.epoch and participating validator index.
122125
if err != nil {
123126
return err
@@ -133,6 +136,26 @@ func (s *attestationService) ProcessMessage(ctx context.Context, subnet *uint64,
133136
}
134137
s.validatorAttestationSeen.Add(vIndex, targetEpoch)
135138

139+
// [REJECT] The signature of attestation is valid.
140+
signature := att.Signature()
141+
pubKey, err := headState.ValidatorPublicKey(int(beaconCommittee[onBitIndex]))
142+
if err != nil {
143+
return fmt.Errorf("unable to get public key: %v", err)
144+
}
145+
domain, err := headState.GetDomain(s.beaconCfg.DomainBeaconAttester, targetEpoch)
146+
if err != nil {
147+
return fmt.Errorf("unable to get the domain: %v", err)
148+
}
149+
signingRoot, err := computeSigningRoot(att.AttestantionData(), domain)
150+
if err != nil {
151+
return fmt.Errorf("unable to get signing root: %v", err)
152+
}
153+
if valid, err := blsVerify(signature[:], signingRoot[:], pubKey[:]); err != nil {
154+
return err
155+
} else if !valid {
156+
return fmt.Errorf("invalid signature")
157+
}
158+
136159
// [IGNORE] The block being voted for (attestation.data.beacon_block_root) has been seen (via both gossip and non-gossip sources)
137160
// (a client MAY queue attestations for processing once block is retrieved).
138161
if _, ok := s.forkchoiceStore.GetHeader(root); !ok {

0 commit comments

Comments
 (0)