mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-11 14:28:09 -05:00
Compare commits
175 Commits
hackyPropo
...
feature/sl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dd1ce957f | ||
|
|
673a845918 | ||
|
|
15a024f171 | ||
|
|
618e7d8f89 | ||
|
|
df33ce3309 | ||
|
|
86efa87101 | ||
|
|
ff625d55df | ||
|
|
0678e9f718 | ||
|
|
10251c4191 | ||
|
|
861c2f5120 | ||
|
|
26de0f1358 | ||
|
|
5f38167cd9 | ||
|
|
a0193ca90c | ||
|
|
0fc5b27195 | ||
|
|
2a9a978fc6 | ||
|
|
9a78ce007a | ||
|
|
16febfe6d9 | ||
|
|
db57c45ec9 | ||
|
|
0d99862e42 | ||
|
|
a4f35f4b09 | ||
|
|
ebb1fe80f9 | ||
|
|
ab071834fb | ||
|
|
e27fb164a3 | ||
|
|
7b807a6c01 | ||
|
|
33e6908c71 | ||
|
|
bac54f53ab | ||
|
|
960d39c859 | ||
|
|
fc951b4416 | ||
|
|
4d2a01e6df | ||
|
|
3121501e80 | ||
|
|
65656045f9 | ||
|
|
59b38ed293 | ||
|
|
963f61f1fb | ||
|
|
1e8720057e | ||
|
|
2fa80c35dc | ||
|
|
96852134ab | ||
|
|
2606935c42 | ||
|
|
c6ca50e766 | ||
|
|
3433002bae | ||
|
|
ce9df503b3 | ||
|
|
b93d925047 | ||
|
|
1df73a698b | ||
|
|
ca2461491c | ||
|
|
83fb66b9d5 | ||
|
|
ab3ffb5d38 | ||
|
|
28fbbbdf7f | ||
|
|
eb612d81b1 | ||
|
|
601fbbfb3a | ||
|
|
f4d3eec431 | ||
|
|
b3ffac459a | ||
|
|
2e1b8190eb | ||
|
|
bf14b28ca9 | ||
|
|
b4b6f25386 | ||
|
|
f5f95d8f99 | ||
|
|
d93369294c | ||
|
|
ad0332ba41 | ||
|
|
18d00724b2 | ||
|
|
a21b98bfd3 | ||
|
|
35b853c958 | ||
|
|
401f73d341 | ||
|
|
403389a0d8 | ||
|
|
8c5d577636 | ||
|
|
f4d54506a0 | ||
|
|
44f6ec48b8 | ||
|
|
2958c84872 | ||
|
|
e9d670f16f | ||
|
|
7194d025ba | ||
|
|
0776639772 | ||
|
|
68d43a9e90 | ||
|
|
e18cca94c9 | ||
|
|
08df279330 | ||
|
|
c80fc63c9f | ||
|
|
1df189339a | ||
|
|
c0246bd82e | ||
|
|
12ed20f341 | ||
|
|
9824e17c83 | ||
|
|
f4e247808a | ||
|
|
8dcfa40807 | ||
|
|
89ee961354 | ||
|
|
ad30274a2d | ||
|
|
91ce227966 | ||
|
|
dd4154aed9 | ||
|
|
6f97ff2219 | ||
|
|
ac44476977 | ||
|
|
6cecb79988 | ||
|
|
67ac7a7455 | ||
|
|
4e6981374e | ||
|
|
21b33d3883 | ||
|
|
ca5527e331 | ||
|
|
cb0450eb23 | ||
|
|
9a75bb9d5a | ||
|
|
71f9b8be87 | ||
|
|
03b1bc12a6 | ||
|
|
3a7ce95dc2 | ||
|
|
dfb0209f01 | ||
|
|
5a22c5c2b0 | ||
|
|
7734ae1006 | ||
|
|
71ac917ba8 | ||
|
|
8836445dff | ||
|
|
661bf55961 | ||
|
|
48d1b8766e | ||
|
|
1127851899 | ||
|
|
bcbfa26fbd | ||
|
|
dd5b19f3de | ||
|
|
c3084a2cfe | ||
|
|
c0e49a4217 | ||
|
|
425816a2b4 | ||
|
|
dd8e4edb41 | ||
|
|
7e3dae42c0 | ||
|
|
d629297b80 | ||
|
|
abbc66e617 | ||
|
|
905f5c3c88 | ||
|
|
980fab5439 | ||
|
|
a2b80eceb6 | ||
|
|
3c4b858c99 | ||
|
|
082bb19c6d | ||
|
|
10f6f06bb8 | ||
|
|
dd31cfb929 | ||
|
|
23cbd504ba | ||
|
|
be711a820e | ||
|
|
376f661f8a | ||
|
|
5cb12099e9 | ||
|
|
a430a4b8ae | ||
|
|
4526483392 | ||
|
|
0efb3ec54e | ||
|
|
254cd6def0 | ||
|
|
4943d635cd | ||
|
|
5184d63801 | ||
|
|
8d5c44ff77 | ||
|
|
8901cbc574 | ||
|
|
8fff0b6ca7 | ||
|
|
e7e62f5234 | ||
|
|
8f7c4cea4e | ||
|
|
d7af14128e | ||
|
|
87bf2296a2 | ||
|
|
963d42567c | ||
|
|
daa575b87c | ||
|
|
346629b205 | ||
|
|
f5f97c3d1f | ||
|
|
5259a4b698 | ||
|
|
746315815e | ||
|
|
5678fbe591 | ||
|
|
a82fc98453 | ||
|
|
30fec47d57 | ||
|
|
fd33c70a5c | ||
|
|
ec678939a8 | ||
|
|
9553b82ed3 | ||
|
|
0bb61cf3fe | ||
|
|
f7cc07c750 | ||
|
|
c56d09dff3 | ||
|
|
8cdbcd9700 | ||
|
|
19ae9b5590 | ||
|
|
2913947c24 | ||
|
|
c342d35bbf | ||
|
|
b285722f5c | ||
|
|
16c6ea9489 | ||
|
|
72bfb94e53 | ||
|
|
976297725a | ||
|
|
9b6f93184d | ||
|
|
a6fde55646 | ||
|
|
a48de9ab69 | ||
|
|
429c94534e | ||
|
|
12defb914d | ||
|
|
ce9cf8cf1f | ||
|
|
5596f8b0d4 | ||
|
|
2864d5f070 | ||
|
|
38d681341c | ||
|
|
90f2ef801a | ||
|
|
d9bbbe5a4e | ||
|
|
24dde8fb82 | ||
|
|
b6b5bb0c51 | ||
|
|
971a15f907 | ||
|
|
3e2c6a9522 | ||
|
|
939a36df58 | ||
|
|
de89d816ad |
@@ -27,6 +27,7 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//async:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/tracing"
|
||||
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/attestation"
|
||||
@@ -106,6 +107,33 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
return err
|
||||
}
|
||||
|
||||
// If slasher is configured, forward the attestations in the block via
|
||||
// an event feed for processing.
|
||||
if features.Get().EnableSlasher {
|
||||
// Feed the indexed attestation to slasher if enabled. This action
|
||||
// is done in the background to avoid adding more load to this critical code path.
|
||||
go func() {
|
||||
// Using a different context to prevent timeouts as this operation can be expensive
|
||||
// and we want to avoid affecting the critical code path.
|
||||
ctx := context.TODO()
|
||||
for _, att := range signed.Block().Body().Attestations() {
|
||||
committee, err := helpers.BeaconCommitteeFromState(ctx, preState, att.Data.Slot, att.Data.CommitteeIndex)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get attestation committee")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not convert to indexed attestation")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
s.cfg.SlasherAttestationsFeed.Send(indexedAtt)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Update justified check point.
|
||||
currJustifiedEpoch := s.justifiedCheckpt.Epoch
|
||||
if postState.CurrentJustifiedCheckpoint().Epoch > currJustifiedEpoch {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
@@ -82,6 +83,7 @@ type Config struct {
|
||||
ForkChoiceStore f.ForkChoicer
|
||||
AttService *attestations.Service
|
||||
StateGen *stategen.State
|
||||
SlasherAttestationsFeed *event.Feed
|
||||
WeakSubjectivityCheckpt *ethpb.Checkpoint
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,11 @@ go_library(
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__pkg__",
|
||||
"//crypto/keystore:__pkg__",
|
||||
"//fuzz:__pkg__",
|
||||
"//network/forks:__pkg__",
|
||||
"//proto/prysm/v1alpha1/attestation:__pkg__",
|
||||
"//runtime/interop:__pkg__",
|
||||
"//shared/attestationutil:__pkg__",
|
||||
"//shared/keystore:__pkg__",
|
||||
"//slasher:__subpackages__",
|
||||
"//testing/altair:__pkg__",
|
||||
"//testing/benchmark/benchmark_files:__subpackages__",
|
||||
|
||||
@@ -18,12 +18,12 @@ go_library(
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__pkg__",
|
||||
"//crypto/keystore:__pkg__",
|
||||
"//fuzz:__pkg__",
|
||||
"//network/forks:__pkg__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/prysm/v1alpha1/attestation:__pkg__",
|
||||
"//runtime/interop:__pkg__",
|
||||
"//shared/attestationutil:__pkg__",
|
||||
"//shared/keystore:__pkg__",
|
||||
"//slasher:__subpackages__",
|
||||
"//testing/altair:__pkg__",
|
||||
"//testing/benchmark/benchmark_files:__subpackages__",
|
||||
|
||||
@@ -21,6 +21,7 @@ go_library(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/db/slasherkv:go_default_library",
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/protoarray:go_default_library",
|
||||
"//beacon-chain/gateway:go_default_library",
|
||||
@@ -34,6 +35,7 @@ go_library(
|
||||
"//beacon-chain/powchain:go_default_library",
|
||||
"//beacon-chain/rpc:go_default_library",
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//beacon-chain/slasher:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync:go_default_library",
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/slasherkv"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/gateway"
|
||||
@@ -37,6 +38,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/slasher"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
regularsync "github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
initialsync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync"
|
||||
@@ -64,24 +66,27 @@ const debugGrpcMaxMsgSize = 1 << 27
|
||||
// full PoS node. It handles the lifecycle of the entire system and registers
|
||||
// services to a service registry.
|
||||
type BeaconNode struct {
|
||||
cliCtx *cli.Context
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
services *runtime.ServiceRegistry
|
||||
lock sync.RWMutex
|
||||
stop chan struct{} // Channel to wait for termination notifications.
|
||||
db db.Database
|
||||
attestationPool attestations.Pool
|
||||
exitPool voluntaryexits.PoolManager
|
||||
slashingsPool slashings.PoolManager
|
||||
syncCommitteePool synccommittee.Pool
|
||||
depositCache *depositcache.DepositCache
|
||||
stateFeed *event.Feed
|
||||
blockFeed *event.Feed
|
||||
opFeed *event.Feed
|
||||
forkChoiceStore forkchoice.ForkChoicer
|
||||
stateGen *stategen.State
|
||||
collector *bcnodeCollector
|
||||
cliCtx *cli.Context
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
services *runtime.ServiceRegistry
|
||||
lock sync.RWMutex
|
||||
stop chan struct{} // Channel to wait for termination notifications.
|
||||
db db.Database
|
||||
slasherDB db.SlasherDatabase
|
||||
attestationPool attestations.Pool
|
||||
exitPool voluntaryexits.PoolManager
|
||||
slashingsPool slashings.PoolManager
|
||||
syncCommitteePool synccommittee.Pool
|
||||
depositCache *depositcache.DepositCache
|
||||
stateFeed *event.Feed
|
||||
blockFeed *event.Feed
|
||||
opFeed *event.Feed
|
||||
forkChoiceStore forkchoice.ForkChoicer
|
||||
stateGen *stategen.State
|
||||
collector *bcnodeCollector
|
||||
slasherBlockHeadersFeed *event.Feed
|
||||
slasherAttestationsFeed *event.Feed
|
||||
}
|
||||
|
||||
// New creates a new node instance, sets up configuration options, and registers
|
||||
@@ -108,18 +113,20 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(cliCtx.Context)
|
||||
beacon := &BeaconNode{
|
||||
cliCtx: cliCtx,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
services: registry,
|
||||
stop: make(chan struct{}),
|
||||
stateFeed: new(event.Feed),
|
||||
blockFeed: new(event.Feed),
|
||||
opFeed: new(event.Feed),
|
||||
attestationPool: attestations.NewPool(),
|
||||
exitPool: voluntaryexits.NewPool(),
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
cliCtx: cliCtx,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
services: registry,
|
||||
stop: make(chan struct{}),
|
||||
stateFeed: new(event.Feed),
|
||||
blockFeed: new(event.Feed),
|
||||
opFeed: new(event.Feed),
|
||||
attestationPool: attestations.NewPool(),
|
||||
exitPool: voluntaryexits.NewPool(),
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
slasherBlockHeadersFeed: new(event.Feed),
|
||||
slasherAttestationsFeed: new(event.Feed),
|
||||
}
|
||||
|
||||
depositAddress, err := registration.DepositContractAddress()
|
||||
@@ -130,6 +137,10 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := beacon.startSlasherDB(cliCtx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
beacon.startStateGen()
|
||||
|
||||
if err := beacon.registerP2P(cliCtx); err != nil {
|
||||
@@ -162,6 +173,10 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := beacon.registerSlasherService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := beacon.registerRPCService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -329,11 +344,9 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error {
|
||||
return errors.Wrap(err, "could not load genesis from file")
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.db.EnsureEmbeddedGenesis(b.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
knownContract, err := b.db.DepositContractAddress(b.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -351,7 +364,53 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error {
|
||||
knownContract, addr.Bytes())
|
||||
}
|
||||
log.Infof("Deposit contract: %#x", addr.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BeaconNode) startSlasherDB(cliCtx *cli.Context) error {
|
||||
if !features.Get().EnableSlasher {
|
||||
return nil
|
||||
}
|
||||
baseDir := cliCtx.String(cmd.DataDirFlag.Name)
|
||||
dbPath := filepath.Join(baseDir, kv.BeaconNodeDbDirName)
|
||||
clearDB := cliCtx.Bool(cmd.ClearDB.Name)
|
||||
forceClearDB := cliCtx.Bool(cmd.ForceClearDB.Name)
|
||||
|
||||
log.WithField("database-path", dbPath).Info("Checking DB")
|
||||
|
||||
d, err := slasherkv.NewKVStore(b.ctx, dbPath, &slasherkv.Config{
|
||||
InitialMMapSize: cliCtx.Int(cmd.BoltMMapInitialSizeFlag.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clearDBConfirmed := false
|
||||
if clearDB && !forceClearDB {
|
||||
actionText := "This will delete your beacon chain database stored in your data directory. " +
|
||||
"Your database backups will not be removed - do you want to proceed? (Y/N)"
|
||||
deniedText := "Database will not be deleted. No changes have been made."
|
||||
clearDBConfirmed, err = cmd.ConfirmAction(actionText, deniedText)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if clearDBConfirmed || forceClearDB {
|
||||
log.Warning("Removing database")
|
||||
if err := d.Close(); err != nil {
|
||||
return errors.Wrap(err, "could not close db prior to clearing")
|
||||
}
|
||||
if err := d.ClearDB(); err != nil {
|
||||
return errors.Wrap(err, "could not clear database")
|
||||
}
|
||||
d, err = slasherkv.NewKVStore(b.ctx, dbPath, &slasherkv.Config{
|
||||
InitialMMapSize: cliCtx.Int(cmd.BoltMMapInitialSizeFlag.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create new database")
|
||||
}
|
||||
}
|
||||
|
||||
b.slasherDB = d
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -441,6 +500,7 @@ func (b *BeaconNode) registerBlockchainService() error {
|
||||
ForkChoiceStore: b.forkChoiceStore,
|
||||
AttService: attService,
|
||||
StateGen: b.stateGen,
|
||||
SlasherAttestationsFeed: b.slasherAttestationsFeed,
|
||||
WeakSubjectivityCheckpt: wsCheckpt,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -500,18 +560,21 @@ func (b *BeaconNode) registerSyncService() error {
|
||||
}
|
||||
|
||||
rs := regularsync.NewService(b.ctx, ®ularsync.Config{
|
||||
DB: b.db,
|
||||
P2P: b.fetchP2P(),
|
||||
Chain: chainService,
|
||||
InitialSync: initSync,
|
||||
StateNotifier: b,
|
||||
BlockNotifier: b,
|
||||
OperationNotifier: b,
|
||||
AttPool: b.attestationPool,
|
||||
ExitPool: b.exitPool,
|
||||
SlashingPool: b.slashingsPool,
|
||||
SyncCommsPool: b.syncCommitteePool,
|
||||
StateGen: b.stateGen,
|
||||
DB: b.db,
|
||||
P2P: b.fetchP2P(),
|
||||
Chain: chainService,
|
||||
InitialSync: initSync,
|
||||
StateNotifier: b,
|
||||
BlockNotifier: b,
|
||||
AttestationNotifier: b,
|
||||
OperationNotifier: b,
|
||||
AttPool: b.attestationPool,
|
||||
ExitPool: b.exitPool,
|
||||
SlashingPool: b.slashingsPool,
|
||||
SyncCommsPool: b.syncCommitteePool,
|
||||
StateGen: b.stateGen,
|
||||
SlasherAttestationsFeed: b.slasherAttestationsFeed,
|
||||
SlasherBlockHeadersFeed: b.slasherBlockHeadersFeed,
|
||||
})
|
||||
|
||||
return b.services.RegisterService(rs)
|
||||
@@ -533,6 +596,36 @@ func (b *BeaconNode) registerInitialSyncService() error {
|
||||
return b.services.RegisterService(is)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerSlasherService() error {
|
||||
if !features.Get().EnableSlasher {
|
||||
return nil
|
||||
}
|
||||
var chainService *blockchain.Service
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
return err
|
||||
}
|
||||
var syncService *initialsync.Service
|
||||
if err := b.services.FetchService(&syncService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slasherSrv, err := slasher.New(b.ctx, &slasher.ServiceConfig{
|
||||
IndexedAttestationsFeed: b.slasherAttestationsFeed,
|
||||
BeaconBlockHeadersFeed: b.slasherBlockHeadersFeed,
|
||||
Database: b.slasherDB,
|
||||
StateNotifier: b,
|
||||
AttestationStateFetcher: chainService,
|
||||
StateGen: b.stateGen,
|
||||
SlashingPoolInserter: b.slashingsPool,
|
||||
SyncChecker: syncService,
|
||||
HeadStateFetcher: chainService,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.services.RegisterService(slasherSrv)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerRPCService() error {
|
||||
var chainService *blockchain.Service
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
@@ -549,6 +642,13 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var slasherService *slasher.Service
|
||||
if features.Get().EnableSlasher {
|
||||
if err := b.services.FetchService(&slasherService); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
genesisValidators := b.cliCtx.Uint64(flags.InteropNumValidatorsFlag.Name)
|
||||
genesisStatePath := b.cliCtx.String(flags.InteropGenesisStateFlag.Name)
|
||||
var depositFetcher depositcache.DepositFetcher
|
||||
@@ -604,6 +704,7 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
AttestationsPool: b.attestationPool,
|
||||
ExitPool: b.exitPool,
|
||||
SlashingsPool: b.slashingsPool,
|
||||
SlashingChecker: slasherService,
|
||||
SyncCommitteeObjectPool: b.syncCommitteePool,
|
||||
POWChainService: web3Service,
|
||||
ChainStartFetcher: chainStartFetcher,
|
||||
|
||||
@@ -51,6 +51,10 @@ var refreshRate = slots.DivideSlotBy(2)
|
||||
// maxBadResponses is the maximum number of bad responses from a peer before we stop talking to it.
|
||||
const maxBadResponses = 5
|
||||
|
||||
// pubsubQueueSize is the size that we assign to our validation queue and outbound message queue for
|
||||
// gossipsub.
|
||||
const pubsubQueueSize = 600
|
||||
|
||||
// maxDialTimeout is the timeout for a single peer dial.
|
||||
var maxDialTimeout = params.BeaconNetworkConfig().RespTimeout
|
||||
|
||||
@@ -141,8 +145,8 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
|
||||
return MsgID(s.genesisValidatorsRoot, pmsg)
|
||||
}),
|
||||
pubsub.WithSubscriptionFilter(s),
|
||||
pubsub.WithPeerOutboundQueueSize(256),
|
||||
pubsub.WithValidateQueueSize(256),
|
||||
pubsub.WithPeerOutboundQueueSize(pubsubQueueSize),
|
||||
pubsub.WithValidateQueueSize(pubsubQueueSize),
|
||||
pubsub.WithPeerScore(peerScoringParams()),
|
||||
pubsub.WithPeerScoreInspect(s.peerInspector, time.Minute),
|
||||
pubsub.WithGossipSubParams(pubsubGossipParam()),
|
||||
|
||||
@@ -76,6 +76,7 @@ go_test(
|
||||
deps = [
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
@@ -84,6 +85,7 @@ go_test(
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//beacon-chain/powchain/testing:go_default_library",
|
||||
"//beacon-chain/powchain/types:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
@@ -93,6 +95,7 @@ go_test(
|
||||
"//network:go_default_library",
|
||||
"//network/authorization:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
|
||||
@@ -616,6 +616,16 @@ func (s *Service) initDepositCaches(ctx context.Context, ctrs []*protodb.Deposit
|
||||
}
|
||||
// Set deposit index to the one in the current archived state.
|
||||
currIndex = fState.Eth1DepositIndex()
|
||||
|
||||
// when a node pauses for some time and starts again, the deposits to finalize
|
||||
// accumulates. we finalize them here before we are ready to receive a block.
|
||||
// Otherwise, the first few blocks will be slower to compute as we will
|
||||
// hold the lock and be busy finalizing the deposits.
|
||||
s.cfg.DepositCache.InsertFinalizedDeposits(ctx, int64(currIndex))
|
||||
// Deposit proofs are only used during state transition and can be safely removed to save space.
|
||||
if err = s.cfg.DepositCache.PruneProofs(ctx, int64(currIndex)); err != nil {
|
||||
return errors.Wrap(err, "could not prune deposit proofs")
|
||||
}
|
||||
}
|
||||
validDepositsCount.Add(float64(currIndex))
|
||||
// Only add pending deposits if the container slice length
|
||||
|
||||
@@ -17,14 +17,18 @@ import (
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/clientstats"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
protodb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
@@ -463,6 +467,82 @@ func TestInitDepositCache_OK(t *testing.T) {
|
||||
require.Equal(t, 3, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil)))
|
||||
}
|
||||
|
||||
func TestInitDepositCacheWithFinalization_OK(t *testing.T) {
|
||||
ctrs := []*protodb.DepositContainer{
|
||||
{
|
||||
Index: 0,
|
||||
Eth1BlockHeight: 2,
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{0}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Eth1BlockHeight: 4,
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{1}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Eth1BlockHeight: 6,
|
||||
Deposit: ðpb.Deposit{
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: bytesutil.PadTo([]byte{2}, 48),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
gs, _ := util.DeterministicGenesisState(t, 1)
|
||||
beaconDB := dbutil.SetupDB(t)
|
||||
s := &Service{
|
||||
chainStartData: &protodb.ChainStartData{Chainstarted: false},
|
||||
preGenesisState: gs,
|
||||
cfg: &Web3ServiceConfig{BeaconDB: beaconDB},
|
||||
}
|
||||
var err error
|
||||
s.cfg.DepositCache, err = depositcache.New()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
||||
|
||||
require.Equal(t, 0, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil)))
|
||||
|
||||
headBlock := util.NewBeaconBlock()
|
||||
headRoot, err := headBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
stateGen := stategen.New(beaconDB)
|
||||
|
||||
emptyState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.cfg.BeaconDB.SaveGenesisBlockRoot(context.Background(), headRoot))
|
||||
require.NoError(t, s.cfg.BeaconDB.SaveState(context.Background(), emptyState, headRoot))
|
||||
require.NoError(t, stateGen.SaveState(context.Background(), headRoot, emptyState))
|
||||
s.cfg.StateGen = stateGen
|
||||
require.NoError(t, emptyState.SetEth1DepositIndex(2))
|
||||
|
||||
ctx := context.Background()
|
||||
require.NoError(t, stateGen.SaveState(ctx, headRoot, emptyState))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, emptyState, headRoot))
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(headBlock)))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: core.SlotToEpoch(0), Root: headRoot[:]}))
|
||||
|
||||
s.chainStartData.Chainstarted = true
|
||||
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
||||
|
||||
deps := s.cfg.DepositCache.NonFinalizedDeposits(context.Background(), nil)
|
||||
assert.Equal(t, 0, len(deps))
|
||||
}
|
||||
|
||||
func TestNewService_EarliestVotingBlock(t *testing.T) {
|
||||
testAcc, err := contracts.Setup()
|
||||
require.NoError(t, err, "Unable to set up simulated backend")
|
||||
|
||||
@@ -30,8 +30,10 @@ go_library(
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/beacon:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/debug:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/node:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/slasher:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
|
||||
"//beacon-chain/rpc/statefetcher:go_default_library",
|
||||
"//beacon-chain/slasher:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
|
||||
29
beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel
Normal file
29
beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel
Normal file
@@ -0,0 +1,29 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attestations.go",
|
||||
"blocks.go",
|
||||
"server.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/slasher",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/slasher:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["server_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/slasher:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
],
|
||||
)
|
||||
34
beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go
Normal file
34
beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// IsSlashableAttestation returns an attester slashing if an input
|
||||
// attestation is found to be slashable.
|
||||
func (s *Server) IsSlashableAttestation(
|
||||
ctx context.Context, req *ethpb.IndexedAttestation,
|
||||
) (*ethpb.AttesterSlashingResponse, error) {
|
||||
attesterSlashings, err := s.SlashingChecker.IsSlashableAttestation(ctx, req)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not determine if attestation is slashable: %v", err)
|
||||
}
|
||||
if len(attesterSlashings) > 0 {
|
||||
return ðpb.AttesterSlashingResponse{
|
||||
AttesterSlashings: attesterSlashings,
|
||||
}, nil
|
||||
}
|
||||
return ðpb.AttesterSlashingResponse{}, nil
|
||||
}
|
||||
|
||||
// HighestAttestations returns the highest source and target epochs attested for
|
||||
// validator indices that have been observed by slasher.
|
||||
func (_ *Server) HighestAttestations(
|
||||
ctx context.Context, req *ethpb.HighestAttestationRequest,
|
||||
) (*ethpb.HighestAttestationResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "Unimplemented")
|
||||
}
|
||||
28
beacon-chain/rpc/prysm/v1alpha1/slasher/blocks.go
Normal file
28
beacon-chain/rpc/prysm/v1alpha1/slasher/blocks.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// IsSlashableBlock returns a proposer slashing if an input
|
||||
// signed beacon block header is found to be slashable.
|
||||
func (s *Server) IsSlashableBlock(
|
||||
ctx context.Context, req *ethpb.SignedBeaconBlockHeader,
|
||||
) (*ethpb.ProposerSlashingResponse, error) {
|
||||
proposerSlashing, err := s.SlashingChecker.IsSlashableBlock(ctx, req)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not determine if block is slashable: %v", err)
|
||||
}
|
||||
if proposerSlashing == nil {
|
||||
return ðpb.ProposerSlashingResponse{
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{},
|
||||
}, nil
|
||||
}
|
||||
return ðpb.ProposerSlashingResponse{
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{proposerSlashing},
|
||||
}, nil
|
||||
}
|
||||
12
beacon-chain/rpc/prysm/v1alpha1/slasher/server.go
Normal file
12
beacon-chain/rpc/prysm/v1alpha1/slasher/server.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package slasher defines a gRPC server implementation of a slasher service
|
||||
// which allows for checking if attestations or blocks are slashable.
|
||||
package slasher
|
||||
|
||||
import (
|
||||
slasherservice "github.com/prysmaticlabs/prysm/beacon-chain/slasher"
|
||||
)
|
||||
|
||||
// Server defines a server implementation of the gRPC slasher service.
|
||||
type Server struct {
|
||||
SlashingChecker slasherservice.SlashingChecker
|
||||
}
|
||||
54
beacon-chain/rpc/prysm/v1alpha1/slasher/server_test.go
Normal file
54
beacon-chain/rpc/prysm/v1alpha1/slasher/server_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/slasher"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestServer_IsSlashableAttestation_SlashingFound(t *testing.T) {
|
||||
mockSlasher := &slasher.MockSlashingChecker{
|
||||
AttesterSlashingFound: true,
|
||||
}
|
||||
s := Server{SlashingChecker: mockSlasher}
|
||||
ctx := context.Background()
|
||||
slashing, err := s.IsSlashableAttestation(ctx, ðpb.IndexedAttestation{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(slashing.AttesterSlashings) > 0)
|
||||
}
|
||||
|
||||
func TestServer_IsSlashableAttestation_SlashingNotFound(t *testing.T) {
|
||||
mockSlasher := &slasher.MockSlashingChecker{
|
||||
AttesterSlashingFound: false,
|
||||
}
|
||||
s := Server{SlashingChecker: mockSlasher}
|
||||
ctx := context.Background()
|
||||
slashing, err := s.IsSlashableAttestation(ctx, ðpb.IndexedAttestation{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(slashing.AttesterSlashings) == 0)
|
||||
}
|
||||
|
||||
func TestServer_IsSlashableBlock_SlashingFound(t *testing.T) {
|
||||
mockSlasher := &slasher.MockSlashingChecker{
|
||||
ProposerSlashingFound: true,
|
||||
}
|
||||
s := Server{SlashingChecker: mockSlasher}
|
||||
ctx := context.Background()
|
||||
slashing, err := s.IsSlashableBlock(ctx, ðpb.SignedBeaconBlockHeader{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(slashing.ProposerSlashings) > 0)
|
||||
}
|
||||
|
||||
func TestServer_IsSlashableBlock_SlashingNotFound(t *testing.T) {
|
||||
mockSlasher := &slasher.MockSlashingChecker{
|
||||
ProposerSlashingFound: false,
|
||||
}
|
||||
s := Server{SlashingChecker: mockSlasher}
|
||||
ctx := context.Background()
|
||||
slashing, err := s.IsSlashableBlock(ctx, ðpb.SignedBeaconBlockHeader{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(slashing.ProposerSlashings) == 0)
|
||||
}
|
||||
@@ -34,8 +34,10 @@ import (
|
||||
beaconv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/beacon"
|
||||
debugv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/debug"
|
||||
nodev1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/node"
|
||||
slasherv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/slasher"
|
||||
validatorv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
|
||||
slasherservice "github.com/prysmaticlabs/prysm/beacon-chain/slasher"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
chainSync "github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
@@ -93,6 +95,7 @@ type Config struct {
|
||||
AttestationsPool attestations.Pool
|
||||
ExitPool voluntaryexits.PoolManager
|
||||
SlashingsPool slashings.PoolManager
|
||||
SlashingChecker slasherservice.SlashingChecker
|
||||
SyncCommitteeObjectPool synccommittee.Pool
|
||||
SyncService chainSync.Checker
|
||||
Broadcaster p2p.Broadcaster
|
||||
@@ -234,6 +237,10 @@ func (s *Service) Start() {
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
}
|
||||
|
||||
slasherServer := &slasherv1alpha1.Server{
|
||||
SlashingChecker: s.cfg.SlashingChecker,
|
||||
}
|
||||
|
||||
beaconChainServer := &beaconv1alpha1.Server{
|
||||
Ctx: s.ctx,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
@@ -280,6 +287,7 @@ func (s *Service) Start() {
|
||||
ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer)
|
||||
ethpbservice.RegisterBeaconNodeServer(s.grpcServer, nodeServerV1)
|
||||
ethpbv1alpha1.RegisterHealthServer(s.grpcServer, nodeServer)
|
||||
ethpbv1alpha1.RegisterSlasherServer(s.grpcServer, slasherServer)
|
||||
ethpbv1alpha1.RegisterBeaconChainServer(s.grpcServer, beaconChainServer)
|
||||
ethpbservice.RegisterBeaconChainServer(s.grpcServer, beaconChainServerV1)
|
||||
ethpbservice.RegisterEventsServer(s.grpcServer, &events.Server{
|
||||
|
||||
@@ -10,10 +10,12 @@ go_library(
|
||||
"helpers.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
"mock_slashing_checker.go",
|
||||
"params.go",
|
||||
"process_slashings.go",
|
||||
"queue.go",
|
||||
"receive.go",
|
||||
"rpc.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/slasher",
|
||||
@@ -46,6 +48,8 @@ go_library(
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -60,17 +64,22 @@ go_test(
|
||||
"process_slashings_test.go",
|
||||
"queue_test.go",
|
||||
"receive_test.go",
|
||||
"rpc_test.go",
|
||||
"service_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/slasher/types:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync/testing:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
@@ -78,8 +87,10 @@ go_test(
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -147,3 +147,24 @@ func Test_processQueuedBlocks_NotSlashable(t *testing.T) {
|
||||
<-exitChan
|
||||
require.LogsDoNotContain(t, hook, "Proposer slashing detected")
|
||||
}
|
||||
|
||||
func createProposalWrapper(t *testing.T, slot types.Slot, proposerIndex types.ValidatorIndex, signingRoot []byte) *slashertypes.SignedBlockHeaderWrapper {
|
||||
header := ðpb.BeaconBlockHeader{
|
||||
Slot: slot,
|
||||
ProposerIndex: proposerIndex,
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: bytesutil.PadTo(signingRoot, 32),
|
||||
BodyRoot: params.BeaconConfig().ZeroHash[:],
|
||||
}
|
||||
signRoot, err := header.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
fakeSig := make([]byte, 96)
|
||||
copy(fakeSig, "hello")
|
||||
return &slashertypes.SignedBlockHeaderWrapper{
|
||||
SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{
|
||||
Header: header,
|
||||
Signature: fakeSig,
|
||||
},
|
||||
SigningRoot: signRoot,
|
||||
}
|
||||
}
|
||||
|
||||
57
beacon-chain/slasher/mock_slashing_checker.go
Normal file
57
beacon-chain/slasher/mock_slashing_checker.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
type MockSlashingChecker struct {
|
||||
AttesterSlashingFound bool
|
||||
ProposerSlashingFound bool
|
||||
}
|
||||
|
||||
func (s *MockSlashingChecker) IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) {
|
||||
if s.ProposerSlashingFound {
|
||||
return ðpb.ProposerSlashing{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 0,
|
||||
ProposerIndex: 0,
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: params.BeaconConfig().ZeroHash[:],
|
||||
BodyRoot: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 0,
|
||||
ProposerIndex: 0,
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: params.BeaconConfig().ZeroHash[:],
|
||||
BodyRoot: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *MockSlashingChecker) IsSlashableAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error) {
|
||||
if s.AttesterSlashingFound {
|
||||
return []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{},
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -308,26 +308,3 @@ func TestService_processQueuedBlocks(t *testing.T) {
|
||||
<-exitChan
|
||||
assert.LogsContain(t, hook, "New slot, processing queued")
|
||||
}
|
||||
|
||||
func createProposalWrapper(t *testing.T, slot types.Slot, proposerIndex types.ValidatorIndex, signingRoot []byte) *slashertypes.SignedBlockHeaderWrapper {
|
||||
header := ðpb.BeaconBlockHeader{
|
||||
Slot: slot,
|
||||
ProposerIndex: proposerIndex,
|
||||
ParentRoot: params2.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: bytesutil.PadTo(signingRoot, 32),
|
||||
BodyRoot: params2.BeaconConfig().ZeroHash[:],
|
||||
}
|
||||
signRoot, err := header.HashTreeRoot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
someSig := make([]byte, params2.BeaconConfig().BLSSignatureLength)
|
||||
copy(someSig, "foobar")
|
||||
return &slashertypes.SignedBlockHeaderWrapper{
|
||||
SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{
|
||||
Header: header,
|
||||
Signature: someSig,
|
||||
},
|
||||
SigningRoot: signRoot,
|
||||
}
|
||||
}
|
||||
|
||||
64
beacon-chain/slasher/rpc.go
Normal file
64
beacon-chain/slasher/rpc.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// IsSlashableBlock checks if an input block header is slashable
|
||||
// with respect to historical block proposal data.
|
||||
func (s *Service) IsSlashableBlock(
|
||||
ctx context.Context, block *ethpb.SignedBeaconBlockHeader,
|
||||
) (*ethpb.ProposerSlashing, error) {
|
||||
dataRoot, err := block.Header.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get block header hash tree root: %v", err)
|
||||
}
|
||||
signedBlockWrapper := &slashertypes.SignedBlockHeaderWrapper{
|
||||
SignedBeaconBlockHeader: block,
|
||||
SigningRoot: dataRoot,
|
||||
}
|
||||
proposerSlashings, err := s.detectProposerSlashings(ctx, []*slashertypes.SignedBlockHeaderWrapper{signedBlockWrapper})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if proposal is slashable: %v", err)
|
||||
}
|
||||
if len(proposerSlashings) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return proposerSlashings[0], nil
|
||||
}
|
||||
|
||||
// IsSlashableAttestation checks if an input indexed attestation is slashable
|
||||
// with respect to historical attestation data.
|
||||
func (s *Service) IsSlashableAttestation(
|
||||
ctx context.Context, attestation *ethpb.IndexedAttestation,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
dataRoot, err := attestation.Data.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not get attestation data hash tree root: %v", err)
|
||||
}
|
||||
indexedAttWrapper := &slashertypes.IndexedAttestationWrapper{
|
||||
IndexedAttestation: attestation,
|
||||
SigningRoot: dataRoot,
|
||||
}
|
||||
|
||||
attesterSlashings, err := s.checkSlashableAttestations(ctx, []*slashertypes.IndexedAttestationWrapper{indexedAttWrapper})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if attestation is slashable: %v", err)
|
||||
}
|
||||
if len(attesterSlashings) == 0 {
|
||||
// If the incoming attestations are not slashable, we mark them as saved in
|
||||
// slasher's DB storage to help us with future detection.
|
||||
if err := s.serviceCfg.Database.SaveAttestationRecordsForValidators(
|
||||
ctx, []*slashertypes.IndexedAttestationWrapper{indexedAttWrapper},
|
||||
); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not save attestation records to DB: %v", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return attesterSlashings, nil
|
||||
}
|
||||
137
beacon-chain/slasher/rpc_test.go
Normal file
137
beacon-chain/slasher/rpc_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestIsSlashableBlock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
},
|
||||
params: DefaultParams(),
|
||||
blksQueue: newBlocksQueue(),
|
||||
}
|
||||
err := slasherDB.SaveBlockProposals(ctx, []*slashertypes.SignedBlockHeaderWrapper{
|
||||
createProposalWrapper(t, 2, 3, []byte{1}),
|
||||
createProposalWrapper(t, 3, 3, []byte{1}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
blockToCheck *slashertypes.SignedBlockHeaderWrapper
|
||||
shouldBeSlashable bool
|
||||
}{
|
||||
{
|
||||
name: "should not detect if same signing root",
|
||||
blockToCheck: createProposalWrapper(t, 2, 3, []byte{1}),
|
||||
shouldBeSlashable: false,
|
||||
},
|
||||
{
|
||||
name: "should not detect if different slot",
|
||||
blockToCheck: createProposalWrapper(t, 1, 3, []byte{2}),
|
||||
shouldBeSlashable: false,
|
||||
},
|
||||
{
|
||||
name: "should not detect if different validator index",
|
||||
blockToCheck: createProposalWrapper(t, 2, 4, []byte{2}),
|
||||
shouldBeSlashable: false,
|
||||
},
|
||||
{
|
||||
name: "detects differing signing root",
|
||||
blockToCheck: createProposalWrapper(t, 2, 3, []byte{2}),
|
||||
shouldBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "should detect another slot",
|
||||
blockToCheck: createProposalWrapper(t, 3, 3, []byte{2}),
|
||||
shouldBeSlashable: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
proposerSlashing, err := s.IsSlashableBlock(ctx, tt.blockToCheck.SignedBeaconBlockHeader)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.shouldBeSlashable, proposerSlashing != nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSlashableAttestation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
|
||||
currentEpoch := types.Epoch(3)
|
||||
currentTime := time.Now()
|
||||
totalSlots := uint64(currentEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot)
|
||||
genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second)
|
||||
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
},
|
||||
params: DefaultParams(),
|
||||
blksQueue: newBlocksQueue(),
|
||||
genesisTime: genesisTime,
|
||||
}
|
||||
prevAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 2, 3, []uint64{0}, []byte{1}),
|
||||
createAttestationWrapper(t, 2, 3, []uint64{1}, []byte{1}),
|
||||
}
|
||||
err := slasherDB.SaveAttestationRecordsForValidators(ctx, prevAtts)
|
||||
require.NoError(t, err)
|
||||
attesterSlashings, err := s.checkSlashableAttestations(ctx, prevAtts)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(attesterSlashings))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attToCheck *slashertypes.IndexedAttestationWrapper
|
||||
amtSlashable uint64
|
||||
}{
|
||||
{
|
||||
name: "should not detect if same attestation data",
|
||||
attToCheck: createAttestationWrapper(t, 2, 3, []uint64{1}, []byte{1}),
|
||||
amtSlashable: 0,
|
||||
},
|
||||
{
|
||||
name: "should not detect if different index",
|
||||
attToCheck: createAttestationWrapper(t, 0, 3, []uint64{2}, []byte{2}),
|
||||
amtSlashable: 0,
|
||||
},
|
||||
{
|
||||
name: "should detect double if same index",
|
||||
attToCheck: createAttestationWrapper(t, 0, 3, []uint64{0}, []byte{2}),
|
||||
amtSlashable: 1,
|
||||
},
|
||||
{
|
||||
name: "should detect multiple double if multiple same indices",
|
||||
attToCheck: createAttestationWrapper(t, 0, 3, []uint64{0, 1}, []byte{2}),
|
||||
amtSlashable: 2,
|
||||
},
|
||||
{
|
||||
name: "should detect multiple surround if multiple same indices",
|
||||
attToCheck: createAttestationWrapper(t, 1, 4, []uint64{0, 1}, []byte{2}),
|
||||
amtSlashable: 2,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
attesterSlashings, err = s.IsSlashableAttestation(ctx, tt.attToCheck.IndexedAttestation)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.amtSlashable, uint64(len(attesterSlashings)))
|
||||
})
|
||||
}
|
||||
}
|
||||
99
beacon-chain/slasher/service_test.go
Normal file
99
beacon-chain/slasher/service_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
var _ = SlashingChecker(&Service{})
|
||||
var _ = SlashingChecker(&MockSlashingChecker{})
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestService_StartStop_ChainStartEvent(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
currentSlot := types.Slot(4)
|
||||
require.NoError(t, beaconState.SetSlot(currentSlot))
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
Slot: ¤tSlot,
|
||||
}
|
||||
|
||||
srv, err := New(context.Background(), &ServiceConfig{
|
||||
IndexedAttestationsFeed: new(event.Feed),
|
||||
BeaconBlockHeadersFeed: new(event.Feed),
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
Database: slasherDB,
|
||||
HeadStateFetcher: mockChain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
go srv.Start()
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
srv.serviceCfg.StateNotifier.StateFeed().Send(&feed.Event{
|
||||
Type: statefeed.ChainStarted,
|
||||
Data: &statefeed.ChainStartedData{StartTime: time.Now()},
|
||||
})
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
srv.slotTicker = &slots.SlotTicker{}
|
||||
require.NoError(t, srv.Stop())
|
||||
require.NoError(t, srv.Status())
|
||||
require.LogsContain(t, hook, "received chain start event")
|
||||
}
|
||||
|
||||
func TestService_StartStop_ChainAlreadyInitialized(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
hook := logTest.NewGlobal()
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
currentSlot := types.Slot(4)
|
||||
require.NoError(t, beaconState.SetSlot(currentSlot))
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
Slot: ¤tSlot,
|
||||
}
|
||||
srv, err := New(context.Background(), &ServiceConfig{
|
||||
IndexedAttestationsFeed: new(event.Feed),
|
||||
BeaconBlockHeadersFeed: new(event.Feed),
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
Database: slasherDB,
|
||||
HeadStateFetcher: mockChain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
go srv.Start()
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
srv.serviceCfg.StateNotifier.StateFeed().Send(&feed.Event{
|
||||
Type: statefeed.Initialized,
|
||||
Data: &statefeed.InitializedData{StartTime: time.Now()},
|
||||
})
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
srv.slotTicker = &slots.SlotTicker{}
|
||||
require.NoError(t, srv.Stop())
|
||||
require.NoError(t, srv.Status())
|
||||
require.LogsContain(t, hook, "chain already initialized")
|
||||
}
|
||||
@@ -10,8 +10,8 @@ go_library(
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//shared/aggregation:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/endtoend:__subpackages__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
],
|
||||
|
||||
@@ -6,8 +6,10 @@ go_library(
|
||||
"array_root.go",
|
||||
"block_header_root.go",
|
||||
"eth1_root.go",
|
||||
"participation_bit_root.go",
|
||||
"pending_attestation_root.go",
|
||||
"reference.go",
|
||||
"sync_committee.root.go",
|
||||
"trie_helpers.go",
|
||||
"validator_map_handler.go",
|
||||
"validator_root.go",
|
||||
@@ -16,8 +18,8 @@ go_library(
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//shared/blockutil:__subpackages__",
|
||||
"//slasher:__subpackages__",
|
||||
"//testing:__subpackages__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package v2
|
||||
package stateutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/encoding/ssz"
|
||||
)
|
||||
|
||||
// participationBitsRoot computes the HashTreeRoot merkleization of
|
||||
// ParticipationBitsRoot computes the HashTreeRoot merkleization of
|
||||
// participation roots.
|
||||
func participationBitsRoot(bits []byte) ([32]byte, error) {
|
||||
func ParticipationBitsRoot(bits []byte) ([32]byte, error) {
|
||||
hasher := hash.CustomSHA256Hasher()
|
||||
chunkedRoots, err := packParticipationBits(bits)
|
||||
if err != nil {
|
||||
@@ -1,4 +1,4 @@
|
||||
package v2
|
||||
package stateutil
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/crypto/hash"
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// syncCommitteeRoot computes the HashTreeRoot Merkleization of a commitee root.
|
||||
// SyncCommitteeRoot computes the HashTreeRoot Merkleization of a commitee root.
|
||||
// a SyncCommitteeRoot struct according to the eth2
|
||||
// Simple Serialize specification.
|
||||
func syncCommitteeRoot(committee *ethpb.SyncCommittee) ([32]byte, error) {
|
||||
func SyncCommitteeRoot(committee *ethpb.SyncCommittee) ([32]byte, error) {
|
||||
hasher := hash.CustomSHA256Hasher()
|
||||
var fieldRoots [][32]byte
|
||||
if committee == nil {
|
||||
@@ -32,9 +32,9 @@ go_library(
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//runtime/interop:__subpackages__",
|
||||
"//shared/aggregation:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
|
||||
@@ -6,8 +6,6 @@ go_library(
|
||||
"deprecated_getters.go",
|
||||
"deprecated_setters.go",
|
||||
"field_root_eth1.go",
|
||||
"field_root_participation_bit.go",
|
||||
"field_root_sync_committee.go",
|
||||
"field_root_validator.go",
|
||||
"field_root_vector.go",
|
||||
"field_roots.go",
|
||||
|
||||
@@ -163,14 +163,14 @@ func (h *stateRootHasher) computeFieldRootsWithHasher(ctx context.Context, state
|
||||
fieldRoots[14] = slashingsRootsRoot[:]
|
||||
|
||||
// PreviousEpochParticipation slice root.
|
||||
prevParticipationRoot, err := participationBitsRoot(state.PreviousEpochParticipation)
|
||||
prevParticipationRoot, err := stateutil.ParticipationBitsRoot(state.PreviousEpochParticipation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute previous epoch participation merkleization")
|
||||
}
|
||||
fieldRoots[15] = prevParticipationRoot[:]
|
||||
|
||||
// CurrentEpochParticipation slice root.
|
||||
currParticipationRoot, err := participationBitsRoot(state.CurrentEpochParticipation)
|
||||
currParticipationRoot, err := stateutil.ParticipationBitsRoot(state.CurrentEpochParticipation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute current epoch participation merkleization")
|
||||
}
|
||||
@@ -209,14 +209,14 @@ func (h *stateRootHasher) computeFieldRootsWithHasher(ctx context.Context, state
|
||||
fieldRoots[21] = inactivityScoresRoot[:]
|
||||
|
||||
// Current sync committee root.
|
||||
currentSyncCommitteeRoot, err := syncCommitteeRoot(state.CurrentSyncCommittee)
|
||||
currentSyncCommitteeRoot, err := stateutil.SyncCommitteeRoot(state.CurrentSyncCommittee)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute sync committee merkleization")
|
||||
}
|
||||
fieldRoots[22] = currentSyncCommitteeRoot[:]
|
||||
|
||||
// Next sync committee root.
|
||||
nextSyncCommitteeRoot, err := syncCommitteeRoot(state.NextSyncCommittee)
|
||||
nextSyncCommitteeRoot, err := stateutil.SyncCommitteeRoot(state.NextSyncCommittee)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute sync committee merkleization")
|
||||
}
|
||||
|
||||
@@ -338,9 +338,9 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
|
||||
case slashings:
|
||||
return ssz.SlashingsRoot(b.state.Slashings)
|
||||
case previousEpochParticipationBits:
|
||||
return participationBitsRoot(b.state.PreviousEpochParticipation)
|
||||
return stateutil.ParticipationBitsRoot(b.state.PreviousEpochParticipation)
|
||||
case currentEpochParticipationBits:
|
||||
return participationBitsRoot(b.state.CurrentEpochParticipation)
|
||||
return stateutil.ParticipationBitsRoot(b.state.CurrentEpochParticipation)
|
||||
case justificationBits:
|
||||
return bytesutil.ToBytes32(b.state.JustificationBits), nil
|
||||
case previousJustifiedCheckpoint:
|
||||
@@ -352,9 +352,9 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
|
||||
case inactivityScores:
|
||||
return stateutil.Uint64ListRootWithRegistryLimit(b.state.InactivityScores)
|
||||
case currentSyncCommittee:
|
||||
return syncCommitteeRoot(b.state.CurrentSyncCommittee)
|
||||
return stateutil.SyncCommitteeRoot(b.state.CurrentSyncCommittee)
|
||||
case nextSyncCommittee:
|
||||
return syncCommitteeRoot(b.state.NextSyncCommittee)
|
||||
return stateutil.SyncCommitteeRoot(b.state.NextSyncCommittee)
|
||||
}
|
||||
return [32]byte{}, errors.New("invalid field index provided")
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ go_library(
|
||||
deps = [
|
||||
"//async:go_default_library",
|
||||
"//async/abool:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
@@ -89,6 +90,7 @@ go_library(
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//network/forks:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//proto/prysm/v1alpha1/metadata:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
|
||||
@@ -83,7 +83,6 @@ func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorRoot())
|
||||
require.NoError(t, err)
|
||||
hashTreeRoot, err := signing.ComputeSigningRoot(att.Data, attesterDomain)
|
||||
@@ -123,15 +122,11 @@ func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) {
|
||||
seenUnAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
|
||||
sb = util.NewBeaconBlock()
|
||||
r32, err := sb.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb)))
|
||||
s, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32))
|
||||
require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, root))
|
||||
|
||||
r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}}
|
||||
r.blkRootToPendingAtts[root] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}}
|
||||
require.NoError(t, r.processPendingAtts(context.Background()))
|
||||
|
||||
atts, err := r.cfg.AttPool.UnaggregatedAttestations()
|
||||
@@ -199,7 +194,6 @@ func TestProcessPendingAtts_NoBroadcastWithBadSignature(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
attesterDomain, err := signing.Domain(s.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, s.GenesisValidatorRoot())
|
||||
require.NoError(t, err)
|
||||
hashTreeRoot, err := signing.ComputeSigningRoot(att.Data, attesterDomain)
|
||||
@@ -273,7 +267,6 @@ func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorRoot())
|
||||
require.NoError(t, err)
|
||||
hashTreeRoot, err := signing.ComputeSigningRoot(att.Data, attesterDomain)
|
||||
@@ -316,15 +309,11 @@ func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) {
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
|
||||
sb = util.NewBeaconBlock()
|
||||
r32, err := sb.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb)))
|
||||
s, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32))
|
||||
require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, root))
|
||||
|
||||
r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}}
|
||||
r.blkRootToPendingAtts[root] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}}
|
||||
require.NoError(t, r.processPendingAtts(context.Background()))
|
||||
|
||||
assert.Equal(t, 1, len(r.cfg.AttPool.AggregatedAttestations()), "Did not save aggregated att")
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/async"
|
||||
"github.com/prysmaticlabs/prysm/async/abool"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
@@ -68,18 +69,21 @@ type validationFn func(ctx context.Context) (pubsub.ValidationResult, error)
|
||||
|
||||
// Config to set up the regular sync service.
|
||||
type Config struct {
|
||||
P2P p2p.P2P
|
||||
DB db.NoHeadAccessDatabase
|
||||
AttPool attestations.Pool
|
||||
ExitPool voluntaryexits.PoolManager
|
||||
SlashingPool slashings.PoolManager
|
||||
SyncCommsPool synccommittee.Pool
|
||||
Chain blockchainService
|
||||
InitialSync Checker
|
||||
StateNotifier statefeed.Notifier
|
||||
BlockNotifier blockfeed.Notifier
|
||||
OperationNotifier operation.Notifier
|
||||
StateGen *stategen.State
|
||||
AttestationNotifier operation.Notifier
|
||||
P2P p2p.P2P
|
||||
DB db.NoHeadAccessDatabase
|
||||
AttPool attestations.Pool
|
||||
ExitPool voluntaryexits.PoolManager
|
||||
SlashingPool slashings.PoolManager
|
||||
SyncCommsPool synccommittee.Pool
|
||||
Chain blockchainService
|
||||
InitialSync Checker
|
||||
StateNotifier statefeed.Notifier
|
||||
BlockNotifier blockfeed.Notifier
|
||||
OperationNotifier operation.Notifier
|
||||
StateGen *stategen.State
|
||||
SlasherAttestationsFeed *event.Feed
|
||||
SlasherBlockHeadersFeed *event.Feed
|
||||
}
|
||||
|
||||
// This defines the interface for interacting with block chain service
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
func TestBeaconAggregateProofSubscriber_CanSaveAggregatedAttestation(t *testing.T) {
|
||||
r := &Service{
|
||||
cfg: &Config{
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenUnAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
@@ -39,8 +39,8 @@ func TestBeaconAggregateProofSubscriber_CanSaveAggregatedAttestation(t *testing.
|
||||
func TestBeaconAggregateProofSubscriber_CanSaveUnaggregatedAttestation(t *testing.T) {
|
||||
r := &Service{
|
||||
cfg: &Config{
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenUnAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, ms
|
||||
|
||||
// Broadcast the aggregated attestation on a feed to notify other services in the beacon node
|
||||
// of a received aggregated attestation.
|
||||
s.cfg.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.AggregatedAttReceived,
|
||||
Data: &operation.AggregatedAttReceivedData{
|
||||
Attestation: m.Message,
|
||||
|
||||
@@ -189,8 +189,8 @@ func TestValidateAggregateAndProof_NotWithinSlotRange(t *testing.T) {
|
||||
Genesis: time.Now(),
|
||||
State: beaconState,
|
||||
},
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
@@ -271,7 +271,7 @@ func TestValidateAggregateAndProof_ExistedInPool(t *testing.T) {
|
||||
InitialSync: &mockSync.Sync{IsSyncing: false},
|
||||
Chain: &mock.ChainService{Genesis: time.Now(),
|
||||
State: beaconState},
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
|
||||
@@ -364,8 +364,8 @@ func TestValidateAggregateAndProof_CanValidate(t *testing.T) {
|
||||
Epoch: 0,
|
||||
Root: att.Data.BeaconBlockRoot,
|
||||
}},
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
@@ -459,8 +459,8 @@ func TestVerifyIndexInCommittee_SeenAggregatorEpoch(t *testing.T) {
|
||||
Root: signedAggregateAndProof.Message.Aggregate.Data.BeaconBlockRoot,
|
||||
}},
|
||||
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
@@ -570,8 +570,8 @@ func TestValidateAggregateAndProof_BadBlock(t *testing.T) {
|
||||
FinalizedCheckPoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
}},
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
@@ -661,8 +661,8 @@ func TestValidateAggregateAndProof_RejectWhenAttEpochDoesntEqualTargetEpoch(t *t
|
||||
Epoch: 0,
|
||||
Root: att.Data.BeaconBlockRoot,
|
||||
}},
|
||||
AttPool: attestations.NewPool(),
|
||||
OperationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
AttPool: attestations.NewPool(),
|
||||
AttestationNotifier: (&mock.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
seenAggregatedAttestationCache: lruwrpr.New(10),
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/tracing"
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/attestation"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -63,7 +64,7 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
|
||||
// Broadcast the unaggregated attestation on a feed to notify other services in the beacon node
|
||||
// of a received unaggregated attestation.
|
||||
s.cfg.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.UnaggregatedAttReceived,
|
||||
Data: &operation.UnAggregatedAttReceivedData{
|
||||
Attestation: att,
|
||||
@@ -81,6 +82,35 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
if features.Get().EnableSlasher {
|
||||
// Feed the indexed attestation to slasher if enabled. This action
|
||||
// is done in the background to avoid adding more load to this critical code path.
|
||||
go func() {
|
||||
// Using a different context to prevent timeouts as this operation can be expensive
|
||||
// and we want to avoid affecting the critical code path.
|
||||
ctx := context.TODO()
|
||||
preState, err := s.cfg.Chain.AttestationTargetState(ctx, att.Data.Target)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not retrieve pre state")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
committee, err := helpers.BeaconCommitteeFromState(ctx, preState, att.Data.Slot, att.Data.CommitteeIndex)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get attestation committee")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not convert to indexed attestation")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
s.cfg.SlasherAttestationsFeed.Send(indexedAtt)
|
||||
}()
|
||||
}
|
||||
|
||||
// Verify this the first attestation received for the participating validator for the slot.
|
||||
if s.hasSeenCommitteeIndicesSlot(att.Data.Slot, att.Data.CommitteeIndex, att.AggregationBits) {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
|
||||
@@ -38,11 +38,11 @@ func TestService_validateCommitteeIndexBeaconAttestation(t *testing.T) {
|
||||
|
||||
s := &Service{
|
||||
cfg: &Config{
|
||||
InitialSync: &mockSync.Sync{IsSyncing: false},
|
||||
P2P: p,
|
||||
DB: db,
|
||||
Chain: chain,
|
||||
OperationNotifier: (&mockChain.ChainService{}).OperationNotifier(),
|
||||
InitialSync: &mockSync.Sync{IsSyncing: false},
|
||||
P2P: p,
|
||||
DB: db,
|
||||
Chain: chain,
|
||||
AttestationNotifier: (&mockChain.ChainService{}).OperationNotifier(),
|
||||
},
|
||||
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
|
||||
seenUnAggregatedAttestationCache: lruwrpr.New(10),
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/tracing"
|
||||
@@ -70,6 +71,18 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
|
||||
},
|
||||
})
|
||||
|
||||
if features.Get().EnableSlasher {
|
||||
// Feed the block header to slasher if enabled. This action
|
||||
// is done in the background to avoid adding more load to this critical code path.
|
||||
go func() {
|
||||
blockHeader, err := block.SignedBeaconBlockHeaderFromBlockInterface(blk)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("blockSlot", blk.Block().Slot()).Warn("Could not extract block header")
|
||||
}
|
||||
s.cfg.SlasherBlockHeadersFeed.Send(blockHeader)
|
||||
}()
|
||||
}
|
||||
|
||||
// Verify the block is the first block received for the proposer for the slot.
|
||||
if s.hasSeenBlockIndexSlot(blk.Block().Slot(), blk.Block().ProposerIndex()) {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
@@ -149,6 +162,7 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
|
||||
if err := s.validateBeaconBlock(ctx, blk, blockRoot); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
// Record attribute of valid block.
|
||||
span.AddAttributes(trace.Int64Attribute("slotInEpoch", int64(blk.Block().Slot()%params.BeaconConfig().SlotsPerEpoch)))
|
||||
msg.ValidatorData = blk.Proto() // Used in downstream subscriber
|
||||
|
||||
@@ -39,9 +39,9 @@ type Flags struct {
|
||||
PyrmontTestnet bool // PyrmontTestnet defines the flag through which we can enable the node to run on the Pyrmont testnet.
|
||||
|
||||
// Feature related flags.
|
||||
RemoteSlasherProtection bool // RemoteSlasherProtection utilizes a beacon node with --slasher mode for validator slashing protection.
|
||||
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
|
||||
SkipBLSVerify bool // Skips BLS verification across the runtime.
|
||||
SlasherProtection bool // SlasherProtection protects validator fron sending over a slashable offense over the network using external slasher.
|
||||
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
|
||||
EnableLargerGossipHistory bool // EnableLargerGossipHistory increases the gossip history we store in our caches.
|
||||
WriteWalletPasswordOnWebOnboarding bool // WriteWalletPasswordOnWebOnboarding writes the password to disk after Prysm web signup.
|
||||
@@ -66,6 +66,7 @@ type Flags struct {
|
||||
// Bug fixes related flags.
|
||||
AttestTimely bool // AttestTimely fixes #8185. It is gated behind a flag to ensure beacon node's fix can safely roll out first. We'll invert this in v1.1.0.
|
||||
|
||||
EnableSlasher bool // Enable slasher in the beacon node runtime.
|
||||
// EnableSlashingProtectionPruning for the validator client.
|
||||
EnableSlashingProtectionPruning bool
|
||||
|
||||
@@ -180,6 +181,10 @@ func ConfigureBeaconChain(ctx *cli.Context) {
|
||||
logDisabled(disableNextSlotStateCache)
|
||||
cfg.EnableNextSlotStateCache = false
|
||||
}
|
||||
if ctx.Bool(enableSlasherFlag.Name) {
|
||||
log.WithField(enableSlasherFlag.Name, enableSlasherFlag.Usage).Warn(enabledFeatureFlag)
|
||||
cfg.EnableSlasher = true
|
||||
}
|
||||
cfg.ProposerAttsSelectionUsingMaxCover = true
|
||||
if ctx.Bool(disableProposerAttsSelectionUsingMaxCover.Name) {
|
||||
logDisabled(disableProposerAttsSelectionUsingMaxCover)
|
||||
@@ -223,8 +228,8 @@ func ConfigureValidator(ctx *cli.Context) {
|
||||
cfg := &Flags{}
|
||||
configureTestnet(ctx, cfg)
|
||||
if ctx.Bool(enableExternalSlasherProtectionFlag.Name) {
|
||||
logEnabled(enableExternalSlasherProtectionFlag)
|
||||
cfg.SlasherProtection = true
|
||||
log.WithField(enableExternalSlasherProtectionFlag.Name, enableExternalSlasherProtectionFlag.Usage).Warn(enabledFeatureFlag)
|
||||
cfg.RemoteSlasherProtection = true
|
||||
}
|
||||
if ctx.Bool(writeWalletPasswordOnWebOnboarding.Name) {
|
||||
logEnabled(writeWalletPasswordOnWebOnboarding)
|
||||
|
||||
@@ -33,8 +33,8 @@ var (
|
||||
}
|
||||
enableExternalSlasherProtectionFlag = &cli.BoolFlag{
|
||||
Name: "enable-external-slasher-protection",
|
||||
Usage: "Enables the validator to connect to external slasher to prevent it from " +
|
||||
"transmitting a slashable offence over the network.",
|
||||
Usage: "Enables the validator to connect to a beacon node using the --slasher flag" +
|
||||
"for remote slashing protection",
|
||||
}
|
||||
disableLookbackFlag = &cli.BoolFlag{
|
||||
Name: "disable-lookback",
|
||||
@@ -93,6 +93,10 @@ var (
|
||||
Name: "disable-next-slot-state-cache",
|
||||
Usage: "Disable next slot cache which improves attesting and proposing efficiency by caching the next slot state at the end of the current slot",
|
||||
}
|
||||
enableSlasherFlag = &cli.BoolFlag{
|
||||
Name: "slasher",
|
||||
Usage: "Enables a slasher in the beacon node for detecting slashable offenses",
|
||||
}
|
||||
disableProposerAttsSelectionUsingMaxCover = &cli.BoolFlag{
|
||||
Name: "disable-proposer-atts-selection-using-max-cover",
|
||||
Usage: "Disable max-cover algorithm when selecting attestations for proposer",
|
||||
@@ -178,6 +182,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{
|
||||
disableBroadcastSlashingFlag,
|
||||
disableNextSlotStateCache,
|
||||
forceOptMaxCoverAggregationStategy,
|
||||
enableSlasherFlag,
|
||||
disableProposerAttsSelectionUsingMaxCover,
|
||||
disableOptimizedBalanceUpdate,
|
||||
enableHistoricalSpaceRepresentation,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
mock_path="shared/mock"
|
||||
mocks=(
|
||||
"$mock_path/beacon_service_mock.go BeaconChainClient,BeaconChain_StreamChainHeadClient,BeaconChain_StreamAttestationsClient,BeaconChain_StreamBlocksClient,BeaconChain_StreamValidatorsInfoClient,BeaconChain_StreamIndexedAttestationsClient"
|
||||
"$mock_path/slasher_service_mock.go SlasherClient"
|
||||
"$mock_path/beacon_chain_service_mock.go BeaconChain_StreamChainHeadServer,BeaconChain_StreamAttestationsServer,BeaconChain_StreamBlocksServer,BeaconChain_StreamValidatorsInfoServer,BeaconChain_StreamIndexedAttestationsServer"
|
||||
"$mock_path/beacon_validator_server_mock.go BeaconNodeValidatorServer,BeaconNodeValidator_WaitForActivationServer,BeaconNodeValidator_WaitForChainStartServer,BeaconNodeValidator_StreamDutiesServer"
|
||||
"$mock_path/beacon_validator_client_mock.go BeaconNodeValidatorClient,BeaconNodeValidator_WaitForChainStartClient,BeaconNodeValidator_WaitForActivationClient,BeaconNodeValidator_StreamDutiesClient"
|
||||
@@ -19,7 +20,6 @@ for ((i = 0; i < ${#mocks[@]}; i++)); do
|
||||
interfaces=${mocks[i]#* };
|
||||
echo "generating $file for interfaces: $interfaces";
|
||||
GO11MODULE=on mockgen -package=mock -destination="$file" github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1 "$interfaces"
|
||||
GO11MODULE=on mockgen -package=mock -destination="$file" github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1 "$interfaces"
|
||||
done
|
||||
|
||||
goimports -w "$mock_path/."
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"external/.*": "Third party code",
|
||||
"rules_go_work-.*": "Third party code",
|
||||
"config/params/config.go": "This config struct needs to be organized for now",
|
||||
"shared/featureconfig/config.go": "This config struct needs to be organized for now",
|
||||
"config/features/config.go": "This config struct needs to be organized for now",
|
||||
"proto/.*": "Excluding protobuf objects for now"
|
||||
}
|
||||
},
|
||||
|
||||
153
proto/prysm/v1alpha1/slasher.pb.go
generated
153
proto/prysm/v1alpha1/slasher.pb.go
generated
@@ -541,7 +541,7 @@ var file_proto_prysm_v1alpha1_slasher_proto_rawDesc = []byte{
|
||||
0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x32, 0xf1, 0x05, 0x0a, 0x07, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12,
|
||||
0x02, 0x38, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x07, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12,
|
||||
0xad, 0x01, 0x0a, 0x16, 0x49, 0x73, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
@@ -563,42 +563,28 @@ var file_proto_prysm_v1alpha1_slasher_proto_rawDesc = []byte{
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f,
|
||||
0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x6c, 0x61,
|
||||
0x73, 0x68, 0x65, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x73, 0x6c, 0x61, 0x73,
|
||||
0x68, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x1e, 0x49, 0x73, 0x53, 0x6c, 0x61, 0x73, 0x68,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e,
|
||||
0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x1a, 0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x6c, 0x61, 0x73, 0x68,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x6b, 0x0a, 0x18, 0x49, 0x73, 0x53,
|
||||
0x6c, 0x61, 0x73, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x6f, 0x55,
|
||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65,
|
||||
0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a,
|
||||
0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0xae, 0x01, 0x0a, 0x13, 0x48, 0x69, 0x67, 0x68, 0x65,
|
||||
0x73, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
|
||||
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x41, 0x74,
|
||||
0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74,
|
||||
0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x65, 0x74,
|
||||
0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68,
|
||||
0x65, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f,
|
||||
0x68, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x42, 0x94, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e,
|
||||
0x68, 0x61, 0x62, 0x6c, 0x65, 0x12, 0xae, 0x01, 0x0a, 0x13, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73,
|
||||
0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x50, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f,
|
||||
0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73,
|
||||
0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02,
|
||||
0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31,
|
||||
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x41, 0x74, 0x74,
|
||||
0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x65, 0x74, 0x68,
|
||||
0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x65,
|
||||
0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x68,
|
||||
0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x42, 0x94, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c,
|
||||
0x70, 0x68, 0x61, 0x31, 0x42, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x50, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70,
|
||||
0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d,
|
||||
0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x15,
|
||||
0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0x61,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -628,7 +614,6 @@ var file_proto_prysm_v1alpha1_slasher_proto_goTypes = []interface{}{
|
||||
(*ProposerSlashing)(nil), // 10: ethereum.eth.v1alpha1.ProposerSlashing
|
||||
(*IndexedAttestation)(nil), // 11: ethereum.eth.v1alpha1.IndexedAttestation
|
||||
(*SignedBeaconBlockHeader)(nil), // 12: ethereum.eth.v1alpha1.SignedBeaconBlockHeader
|
||||
(*BeaconBlockHeader)(nil), // 13: ethereum.eth.v1alpha1.BeaconBlockHeader
|
||||
}
|
||||
var file_proto_prysm_v1alpha1_slasher_proto_depIdxs = []int32{
|
||||
9, // 0: ethereum.eth.v1alpha1.AttesterSlashingResponse.attester_slashings:type_name -> ethereum.eth.v1alpha1.AttesterSlashing
|
||||
@@ -637,16 +622,12 @@ var file_proto_prysm_v1alpha1_slasher_proto_depIdxs = []int32{
|
||||
8, // 3: ethereum.eth.v1alpha1.AttestationHistory.target_to_source:type_name -> ethereum.eth.v1alpha1.AttestationHistory.TargetToSourceEntry
|
||||
11, // 4: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestation:input_type -> ethereum.eth.v1alpha1.IndexedAttestation
|
||||
12, // 5: ethereum.eth.v1alpha1.Slasher.IsSlashableBlock:input_type -> ethereum.eth.v1alpha1.SignedBeaconBlockHeader
|
||||
11, // 6: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestationNoUpdate:input_type -> ethereum.eth.v1alpha1.IndexedAttestation
|
||||
13, // 7: ethereum.eth.v1alpha1.Slasher.IsSlashableBlockNoUpdate:input_type -> ethereum.eth.v1alpha1.BeaconBlockHeader
|
||||
2, // 8: ethereum.eth.v1alpha1.Slasher.HighestAttestations:input_type -> ethereum.eth.v1alpha1.HighestAttestationRequest
|
||||
0, // 9: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestation:output_type -> ethereum.eth.v1alpha1.AttesterSlashingResponse
|
||||
1, // 10: ethereum.eth.v1alpha1.Slasher.IsSlashableBlock:output_type -> ethereum.eth.v1alpha1.ProposerSlashingResponse
|
||||
6, // 11: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestationNoUpdate:output_type -> ethereum.eth.v1alpha1.Slashable
|
||||
6, // 12: ethereum.eth.v1alpha1.Slasher.IsSlashableBlockNoUpdate:output_type -> ethereum.eth.v1alpha1.Slashable
|
||||
3, // 13: ethereum.eth.v1alpha1.Slasher.HighestAttestations:output_type -> ethereum.eth.v1alpha1.HighestAttestationResponse
|
||||
9, // [9:14] is the sub-list for method output_type
|
||||
4, // [4:9] is the sub-list for method input_type
|
||||
2, // 6: ethereum.eth.v1alpha1.Slasher.HighestAttestations:input_type -> ethereum.eth.v1alpha1.HighestAttestationRequest
|
||||
0, // 7: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestation:output_type -> ethereum.eth.v1alpha1.AttesterSlashingResponse
|
||||
1, // 8: ethereum.eth.v1alpha1.Slasher.IsSlashableBlock:output_type -> ethereum.eth.v1alpha1.ProposerSlashingResponse
|
||||
3, // 9: ethereum.eth.v1alpha1.Slasher.HighestAttestations:output_type -> ethereum.eth.v1alpha1.HighestAttestationResponse
|
||||
7, // [7:10] is the sub-list for method output_type
|
||||
4, // [4:7] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
@@ -790,10 +771,6 @@ const _ = grpc.SupportPackageIsVersion6
|
||||
type SlasherClient interface {
|
||||
IsSlashableAttestation(ctx context.Context, in *IndexedAttestation, opts ...grpc.CallOption) (*AttesterSlashingResponse, error)
|
||||
IsSlashableBlock(ctx context.Context, in *SignedBeaconBlockHeader, opts ...grpc.CallOption) (*ProposerSlashingResponse, error)
|
||||
// Deprecated: Do not use.
|
||||
IsSlashableAttestationNoUpdate(ctx context.Context, in *IndexedAttestation, opts ...grpc.CallOption) (*Slashable, error)
|
||||
// Deprecated: Do not use.
|
||||
IsSlashableBlockNoUpdate(ctx context.Context, in *BeaconBlockHeader, opts ...grpc.CallOption) (*Slashable, error)
|
||||
HighestAttestations(ctx context.Context, in *HighestAttestationRequest, opts ...grpc.CallOption) (*HighestAttestationResponse, error)
|
||||
}
|
||||
|
||||
@@ -823,26 +800,6 @@ func (c *slasherClient) IsSlashableBlock(ctx context.Context, in *SignedBeaconBl
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (c *slasherClient) IsSlashableAttestationNoUpdate(ctx context.Context, in *IndexedAttestation, opts ...grpc.CallOption) (*Slashable, error) {
|
||||
out := new(Slashable)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.Slasher/IsSlashableAttestationNoUpdate", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (c *slasherClient) IsSlashableBlockNoUpdate(ctx context.Context, in *BeaconBlockHeader, opts ...grpc.CallOption) (*Slashable, error) {
|
||||
out := new(Slashable)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.Slasher/IsSlashableBlockNoUpdate", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *slasherClient) HighestAttestations(ctx context.Context, in *HighestAttestationRequest, opts ...grpc.CallOption) (*HighestAttestationResponse, error) {
|
||||
out := new(HighestAttestationResponse)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.Slasher/HighestAttestations", in, out, opts...)
|
||||
@@ -856,10 +813,6 @@ func (c *slasherClient) HighestAttestations(ctx context.Context, in *HighestAtte
|
||||
type SlasherServer interface {
|
||||
IsSlashableAttestation(context.Context, *IndexedAttestation) (*AttesterSlashingResponse, error)
|
||||
IsSlashableBlock(context.Context, *SignedBeaconBlockHeader) (*ProposerSlashingResponse, error)
|
||||
// Deprecated: Do not use.
|
||||
IsSlashableAttestationNoUpdate(context.Context, *IndexedAttestation) (*Slashable, error)
|
||||
// Deprecated: Do not use.
|
||||
IsSlashableBlockNoUpdate(context.Context, *BeaconBlockHeader) (*Slashable, error)
|
||||
HighestAttestations(context.Context, *HighestAttestationRequest) (*HighestAttestationResponse, error)
|
||||
}
|
||||
|
||||
@@ -873,12 +826,6 @@ func (*UnimplementedSlasherServer) IsSlashableAttestation(context.Context, *Inde
|
||||
func (*UnimplementedSlasherServer) IsSlashableBlock(context.Context, *SignedBeaconBlockHeader) (*ProposerSlashingResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method IsSlashableBlock not implemented")
|
||||
}
|
||||
func (*UnimplementedSlasherServer) IsSlashableAttestationNoUpdate(context.Context, *IndexedAttestation) (*Slashable, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method IsSlashableAttestationNoUpdate not implemented")
|
||||
}
|
||||
func (*UnimplementedSlasherServer) IsSlashableBlockNoUpdate(context.Context, *BeaconBlockHeader) (*Slashable, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method IsSlashableBlockNoUpdate not implemented")
|
||||
}
|
||||
func (*UnimplementedSlasherServer) HighestAttestations(context.Context, *HighestAttestationRequest) (*HighestAttestationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method HighestAttestations not implemented")
|
||||
}
|
||||
@@ -923,42 +870,6 @@ func _Slasher_IsSlashableBlock_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Slasher_IsSlashableAttestationNoUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(IndexedAttestation)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SlasherServer).IsSlashableAttestationNoUpdate(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ethereum.eth.v1alpha1.Slasher/IsSlashableAttestationNoUpdate",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SlasherServer).IsSlashableAttestationNoUpdate(ctx, req.(*IndexedAttestation))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Slasher_IsSlashableBlockNoUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BeaconBlockHeader)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SlasherServer).IsSlashableBlockNoUpdate(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ethereum.eth.v1alpha1.Slasher/IsSlashableBlockNoUpdate",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SlasherServer).IsSlashableBlockNoUpdate(ctx, req.(*BeaconBlockHeader))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Slasher_HighestAttestations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HighestAttestationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -989,14 +900,6 @@ var _Slasher_serviceDesc = grpc.ServiceDesc{
|
||||
MethodName: "IsSlashableBlock",
|
||||
Handler: _Slasher_IsSlashableBlock_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "IsSlashableAttestationNoUpdate",
|
||||
Handler: _Slasher_IsSlashableAttestationNoUpdate_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "IsSlashableBlockNoUpdate",
|
||||
Handler: _Slasher_IsSlashableBlockNoUpdate_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "HighestAttestations",
|
||||
Handler: _Slasher_HighestAttestations_Handler,
|
||||
|
||||
@@ -49,22 +49,6 @@ service Slasher {
|
||||
};
|
||||
}
|
||||
|
||||
// Returns if a given indexed attestation could be slashable when compared to
|
||||
// the slashers history for the attesters. This function is read-only, and
|
||||
// does not need the indexed attestation to be signed.
|
||||
rpc IsSlashableAttestationNoUpdate(ethereum.eth.v1alpha1.IndexedAttestation)
|
||||
returns (Slashable) {
|
||||
option deprecated = true;
|
||||
};
|
||||
|
||||
// Returns if a given beacon block header could be slashable when compared to
|
||||
// the slashers history for the proposer. This function is read-only, and does
|
||||
// not need the beacon block header to be signed.
|
||||
rpc IsSlashableBlockNoUpdate(ethereum.eth.v1alpha1.BeaconBlockHeader)
|
||||
returns (Slashable) {
|
||||
option deprecated = true;
|
||||
};
|
||||
|
||||
// Returns the highest source and target attestation for validator indices
|
||||
// that have been observed by slasher.
|
||||
rpc HighestAttestations(HighestAttestationRequest)
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//cache/lru:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
@@ -53,7 +54,6 @@ go_library(
|
||||
"//validator/keymanager:go_default_library",
|
||||
"//validator/keymanager/imported:go_default_library",
|
||||
"//validator/keymanager/remote:go_default_library",
|
||||
"//validator/slashing-protection/iface:go_default_library",
|
||||
"@com_github_dgraph_io_ristretto//:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library",
|
||||
|
||||
@@ -81,8 +81,12 @@ func (v *validator) slashableAttestationCheck(
|
||||
return errors.Wrap(err, "could not save attestation history for validator public key")
|
||||
}
|
||||
|
||||
if features.Get().SlasherProtection && v.protector != nil {
|
||||
if !v.protector.CommitAttestation(ctx, indexedAtt) {
|
||||
if features.Get().RemoteSlasherProtection {
|
||||
slashing, err := v.slashingProtectionClient.IsSlashableAttestation(ctx, indexedAtt)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if attestation is slashable")
|
||||
}
|
||||
if slashing != nil && len(slashing.AttesterSlashings) > 0 {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorAttestFailVecSlasher.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
|
||||
@@ -10,16 +10,15 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
mockSlasher "github.com/prysmaticlabs/prysm/validator/testing"
|
||||
)
|
||||
|
||||
func Test_slashableAttestationCheck(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: true,
|
||||
RemoteSlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
pubKey := [48]byte{}
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
@@ -39,18 +38,30 @@ func Test_slashableAttestationCheck(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
mockProtector := &mockSlasher.MockProtector{AllowAttestation: false}
|
||||
validator.protector = mockProtector
|
||||
|
||||
m.slasherClient.EXPECT().IsSlashableAttestation(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.AttesterSlashingResponse{AttesterSlashings: []*ethpb.AttesterSlashing{{
|
||||
Attestation_1: ðpb.IndexedAttestation{},
|
||||
Attestation_2: ðpb.IndexedAttestation{},
|
||||
}}}, nil /*err*/)
|
||||
|
||||
err := validator.slashableAttestationCheck(context.Background(), att, pubKey, [32]byte{1})
|
||||
require.ErrorContains(t, failedPostAttSignExternalErr, err)
|
||||
mockProtector.AllowAttestation = true
|
||||
|
||||
m.slasherClient.EXPECT().IsSlashableAttestation(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/)
|
||||
|
||||
err = validator.slashableAttestationCheck(context.Background(), att, pubKey, [32]byte{1})
|
||||
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
||||
}
|
||||
|
||||
func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: true,
|
||||
RemoteSlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
@@ -75,18 +86,23 @@ func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
mockProtector := &mockSlasher.MockProtector{AllowAttestation: false}
|
||||
validator.protector = mockProtector
|
||||
|
||||
m.slasherClient.EXPECT().IsSlashableAttestation(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.DomainRequest{Epoch: 10, Domain: []byte{1, 0, 0, 0}},
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
_, sr, err := validator.getDomainAndSigningRoot(ctx, att.Data)
|
||||
require.NoError(t, err)
|
||||
mockProtector.AllowAttestation = true
|
||||
|
||||
err = validator.slashableAttestationCheck(context.Background(), att, pubKey, sr)
|
||||
require.NoError(t, err)
|
||||
differentSigningRoot := [32]byte{2}
|
||||
|
||||
err = validator.slashableAttestationCheck(context.Background(), att, pubKey, differentSigningRoot)
|
||||
require.ErrorContains(t, "could not sign attestation", err)
|
||||
|
||||
@@ -102,12 +118,12 @@ func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) {
|
||||
|
||||
func Test_slashableAttestationCheck_OK(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: false,
|
||||
RemoteSlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
ctx := context.Background()
|
||||
validator, _, _, finish := setup(t)
|
||||
validator, mocks, _, finish := setup(t)
|
||||
defer finish()
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
@@ -127,18 +143,24 @@ func Test_slashableAttestationCheck_OK(t *testing.T) {
|
||||
}
|
||||
sr := [32]byte{1}
|
||||
fakePubkey := bytesutil.ToBytes48([]byte("test"))
|
||||
|
||||
mocks.slasherClient.EXPECT().IsSlashableAttestation(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/)
|
||||
|
||||
err := validator.slashableAttestationCheck(ctx, att, fakePubkey, sr)
|
||||
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
||||
}
|
||||
|
||||
func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: false,
|
||||
RemoteSlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
ctx := context.Background()
|
||||
validator, _, _, finish := setup(t)
|
||||
validator, mocks, _, finish := setup(t)
|
||||
defer finish()
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
@@ -156,6 +178,12 @@ func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mocks.slasherClient.EXPECT().IsSlashableAttestation(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/)
|
||||
|
||||
fakePubkey := bytesutil.ToBytes48([]byte("test"))
|
||||
err := validator.slashableAttestationCheck(ctx, att, fakePubkey, [32]byte{})
|
||||
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
||||
|
||||
@@ -36,7 +36,6 @@ type Validator interface {
|
||||
WaitForChainStart(ctx context.Context) error
|
||||
WaitForSync(ctx context.Context) error
|
||||
WaitForActivation(ctx context.Context, accountsChangedChan chan [][48]byte) error
|
||||
SlasherReady(ctx context.Context) error
|
||||
CanonicalHeadSlot(ctx context.Context) (types.Slot, error)
|
||||
NextSlot() <-chan types.Slot
|
||||
SlotDeadline(slot types.Slot) time.Time
|
||||
|
||||
@@ -119,20 +119,13 @@ func (v *validator) proposeBlockPhase0(ctx context.Context, slot types.Slot, pub
|
||||
return
|
||||
}
|
||||
|
||||
if err := v.preBlockSignValidations(ctx, pubKey, wrapper.WrappedPhase0BeaconBlock(b), signingRoot); err != nil {
|
||||
if err := v.slashableProposalCheck(ctx, pubKey, wrapper.WrappedPhase0SignedBeaconBlock(blk), signingRoot); err != nil {
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, wrapper.WrappedPhase0BeaconBlock(b), nil),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
return
|
||||
}
|
||||
|
||||
if err := v.postBlockSignUpdate(ctx, pubKey, wrapper.WrappedPhase0SignedBeaconBlock(blk), signingRoot); err != nil {
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, wrapper.WrappedPhase0BeaconBlock(b), sig),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
return
|
||||
}
|
||||
|
||||
// Propose and broadcast block via beacon node
|
||||
blkResp, err := v.validatorClient.ProposeBlock(ctx, blk)
|
||||
if err != nil {
|
||||
@@ -252,16 +245,6 @@ func (v *validator) proposeBlockAltair(ctx context.Context, slot types.Slot, pub
|
||||
return
|
||||
}
|
||||
|
||||
if err := v.preBlockSignValidations(ctx, pubKey, wb, signingRoot); err != nil {
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, wb, nil),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to wrap signed block")
|
||||
@@ -270,9 +253,10 @@ func (v *validator) proposeBlockAltair(ctx context.Context, slot types.Slot, pub
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := v.postBlockSignUpdate(ctx, pubKey, wsb, signingRoot); err != nil {
|
||||
|
||||
if err := v.slashableProposalCheck(ctx, pubKey, wsb, signingRoot); err != nil {
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, wb, sig),
|
||||
blockLogFields(pubKey, wb, nil),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
|
||||
@@ -5,22 +5,23 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var failedPreBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection"
|
||||
var failedPreBlockSignExternalErr = "attempted a double proposal, block rejected by remote slashing protection"
|
||||
var failedPostBlockSignErr = "made a double proposal, considered slashable by remote slashing protection"
|
||||
var failedBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection"
|
||||
var failedBlockSignExternalErr = "attempted a double proposal, block rejected by remote slashing protection"
|
||||
|
||||
func (v *validator) preBlockSignValidations(
|
||||
ctx context.Context, pubKey [48]byte, blk block.BeaconBlock, signingRoot [32]byte,
|
||||
func (v *validator) slashableProposalCheck(
|
||||
ctx context.Context, pubKey [48]byte, signedBlock block.SignedBeaconBlock, signingRoot [32]byte,
|
||||
) error {
|
||||
fmtKey := fmt.Sprintf("%#x", pubKey[:])
|
||||
|
||||
prevSigningRoot, proposalAtSlotExists, err := v.db.ProposalHistoryForSlot(ctx, pubKey, blk.Slot())
|
||||
block := signedBlock.Block()
|
||||
prevSigningRoot, proposalAtSlotExists, err := v.db.ProposalHistoryForSlot(ctx, pubKey, block.Slot())
|
||||
if err != nil {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
@@ -42,7 +43,7 @@ func (v *validator) preBlockSignValidations(
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.New(failedPreBlockSignLocalErr)
|
||||
return errors.New(failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
// Based on EIP3076, validator should refuse to sign any proposal with slot less
|
||||
@@ -57,46 +58,23 @@ func (v *validator) preBlockSignValidations(
|
||||
)
|
||||
}
|
||||
|
||||
if features.Get().SlasherProtection && v.protector != nil {
|
||||
blockHdr, err := block.BeaconBlockHeaderFromBlockInterface(blk)
|
||||
if features.Get().RemoteSlasherProtection {
|
||||
blockHdr, err := blocks.SignedBeaconBlockHeaderFromBlockInterface(signedBlock)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get block header from block")
|
||||
}
|
||||
if !v.protector.CheckBlockSafety(ctx, blockHdr) {
|
||||
slashing, err := v.slashingProtectionClient.IsSlashableBlock(ctx, blockHdr)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if block is slashable")
|
||||
}
|
||||
if slashing != nil && len(slashing.ProposerSlashings) > 0 {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVecSlasher.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.New(failedPreBlockSignExternalErr)
|
||||
return errors.New(failedBlockSignExternalErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validator) postBlockSignUpdate(
|
||||
ctx context.Context,
|
||||
pubKey [48]byte,
|
||||
blk block.SignedBeaconBlock,
|
||||
signingRoot [32]byte,
|
||||
) error {
|
||||
fmtKey := fmt.Sprintf("%#x", pubKey[:])
|
||||
if features.Get().SlasherProtection && v.protector != nil {
|
||||
sbh, err := block.SignedBeaconBlockHeaderFromBlockInterface(blk)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get block header from block")
|
||||
}
|
||||
valid, err := v.protector.CommitBlock(ctx, sbh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !valid {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVecSlasher.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return fmt.Errorf(failedPostBlockSignErr)
|
||||
}
|
||||
}
|
||||
if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, blk.Block().Slot(), signingRoot[:]); err != nil {
|
||||
if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, block.Slot(), signingRoot[:]); err != nil {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
@@ -13,7 +15,7 @@ import (
|
||||
mockSlasher "github.com/prysmaticlabs/prysm/validator/testing"
|
||||
)
|
||||
|
||||
func TestPreBlockSignLocalValidation_PreventsLowerThanMinProposal(t *testing.T) {
|
||||
func Test_slashableProposalCheck_PreventsLowerThanMinProposal(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
@@ -28,55 +30,64 @@ func TestPreBlockSignLocalValidation_PreventsLowerThanMinProposal(t *testing.T)
|
||||
|
||||
// We expect the same block with a slot lower than the lowest
|
||||
// signed slot to fail validation.
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot - 1,
|
||||
ProposerIndex: 0,
|
||||
block := ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot - 1,
|
||||
ProposerIndex: 0,
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{4})
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{4})
|
||||
require.ErrorContains(t, "could not sign block with slot <= lowest signed", err)
|
||||
|
||||
// We expect the same block with a slot equal to the lowest
|
||||
// signed slot to pass validation if signing roots are equal.
|
||||
block = ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot,
|
||||
ProposerIndex: 0,
|
||||
block = ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot,
|
||||
ProposerIndex: 0,
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{1})
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block with a slot equal to the lowest
|
||||
// signed slot to fail validation if signing roots are different.
|
||||
block = ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot,
|
||||
ProposerIndex: 0,
|
||||
}
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{4})
|
||||
require.ErrorContains(t, failedPreBlockSignLocalErr, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{4})
|
||||
require.ErrorContains(t, failedBlockSignLocalErr, err)
|
||||
|
||||
// We expect the same block with a slot > than the lowest
|
||||
// signed slot to pass validation.
|
||||
block = ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot + 1,
|
||||
ProposerIndex: 0,
|
||||
block = ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot + 1,
|
||||
ProposerIndex: 0,
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{3})
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{3})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPreBlockSignLocalValidation(t *testing.T) {
|
||||
func Test_slashableProposalCheck(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &features.Flags{
|
||||
SlasherProtection: false,
|
||||
RemoteSlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
validator, mocks, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 10,
|
||||
ProposerIndex: 0,
|
||||
}
|
||||
block := util.HydrateSignedBeaconBlock(ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: 10,
|
||||
ProposerIndex: 0,
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
})
|
||||
|
||||
pubKeyBytes := [48]byte{}
|
||||
copy(pubKeyBytes[:], validatorKey.PublicKey().Marshal())
|
||||
|
||||
@@ -91,71 +102,63 @@ func TestPreBlockSignLocalValidation(t *testing.T) {
|
||||
pubKey := [48]byte{}
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
|
||||
mock.slasherClient.EXPECT().IsSlashableBlock(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Times(2).Return(ðpb.ProposerSlashingResponse{}, nil /*err*/)
|
||||
|
||||
// We expect the same block sent out with the same root should not be slasahble.
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), dummySigningRoot)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(block), dummySigningRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block sent out with a different signing root should be slasahble.
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{2})
|
||||
require.ErrorContains(t, failedPreBlockSignLocalErr, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{2})
|
||||
require.ErrorContains(t, failedBlockSignLocalErr, err)
|
||||
|
||||
// We save a proposal at slot 11 with a nil signing root.
|
||||
block.Slot = 11
|
||||
err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, block.Slot, nil)
|
||||
block.Block.Slot = 11
|
||||
err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, block.Block.Slot, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block sent out should return slashable error even
|
||||
// if we had a nil signing root stored in the database.
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{2})
|
||||
require.ErrorContains(t, failedPreBlockSignLocalErr, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{2})
|
||||
require.ErrorContains(t, failedBlockSignLocalErr, err)
|
||||
|
||||
// A block with a different slot for which we do not have a proposing history
|
||||
// should not be failing validation.
|
||||
block.Slot = 9
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{3})
|
||||
block.Block.Slot = 9
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{3})
|
||||
require.NoError(t, err, "Expected allowed block not to throw error")
|
||||
}
|
||||
|
||||
func TestPreBlockSignValidation(t *testing.T) {
|
||||
func Test_slashableProposalCheck_RemoteProtection(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: true,
|
||||
RemoteSlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
pubKey := [48]byte{}
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
|
||||
block := util.NewBeaconBlock()
|
||||
block.Block.Slot = 10
|
||||
mockProtector := &mockSlasher.MockProtector{AllowBlock: false}
|
||||
validator.protector = mockProtector
|
||||
err := validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block.Block), [32]byte{2})
|
||||
require.ErrorContains(t, failedPreBlockSignExternalErr, err)
|
||||
mockProtector.AllowBlock = true
|
||||
err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block.Block), [32]byte{2})
|
||||
require.NoError(t, err, "Expected allowed block not to throw error")
|
||||
}
|
||||
|
||||
func TestPostBlockSignUpdate(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
pubKey := [48]byte{}
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
emptyBlock := util.NewBeaconBlock()
|
||||
emptyBlock.Block.Slot = 10
|
||||
emptyBlock.Block.ProposerIndex = 0
|
||||
mockProtector := &mockSlasher.MockProtector{AllowBlock: false}
|
||||
validator.protector = mockProtector
|
||||
err := validator.postBlockSignUpdate(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(emptyBlock), [32]byte{})
|
||||
require.ErrorContains(t, failedPostBlockSignErr, err, "Expected error when post signature update is detected as slashable")
|
||||
mockProtector.AllowBlock = true
|
||||
err = validator.postBlockSignUpdate(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(emptyBlock), [32]byte{})
|
||||
m.nodeClient.EXPECT().IsSlashableBlock(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.ProposerSlashingResponse{ProposerSlashings: []*ethpb.ProposerSlashing{{}}}, nil /*err*/)
|
||||
|
||||
err := validator.slashableProposalCheck(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{2})
|
||||
require.ErrorContains(t, failedBlockSignExternalErr, err)
|
||||
|
||||
m.slasherClient.EXPECT().IsSlashableBlock(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(),
|
||||
).Return(ðpb.ProposerSlashingResponse{}, nil /*err*/)
|
||||
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{2})
|
||||
require.NoError(t, err, "Expected allowed block not to throw error")
|
||||
}
|
||||
|
||||
@@ -296,10 +296,10 @@ func TestProposeBlock_BlocksDoubleProposal(t *testing.T) {
|
||||
).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/)
|
||||
|
||||
validator.ProposeBlock(context.Background(), slot, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
|
||||
validator.ProposeBlock(context.Background(), slot, pubKey)
|
||||
require.LogsContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsContain(t, hook, failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
func TestProposeBlockAltair_BlocksDoubleProposal(t *testing.T) {
|
||||
@@ -356,10 +356,10 @@ func TestProposeBlockAltair_BlocksDoubleProposal(t *testing.T) {
|
||||
).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/)
|
||||
|
||||
validator.ProposeBlock(context.Background(), slot, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
|
||||
validator.ProposeBlock(context.Background(), slot, pubKey)
|
||||
require.LogsContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsContain(t, hook, failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) {
|
||||
@@ -408,10 +408,10 @@ func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) {
|
||||
).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/)
|
||||
|
||||
validator.ProposeBlock(context.Background(), farFuture, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
|
||||
validator.ProposeBlock(context.Background(), farFuture, pubKey)
|
||||
require.LogsContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsContain(t, hook, failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
func TestProposeBlock_AllowsPastProposals(t *testing.T) {
|
||||
@@ -449,7 +449,7 @@ func TestProposeBlock_AllowsPastProposals(t *testing.T) {
|
||||
).Times(2).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/)
|
||||
|
||||
validator.ProposeBlock(context.Background(), farAhead, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
|
||||
past := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().WeakSubjectivityPeriod - 400))
|
||||
blk2 := util.NewBeaconBlock()
|
||||
@@ -459,7 +459,7 @@ func TestProposeBlock_AllowsPastProposals(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(blk2.Block, nil /*err*/)
|
||||
validator.ProposeBlock(context.Background(), past, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
func TestProposeBlock_AllowsSameEpoch(t *testing.T) {
|
||||
@@ -497,7 +497,7 @@ func TestProposeBlock_AllowsSameEpoch(t *testing.T) {
|
||||
).Times(2).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/)
|
||||
|
||||
validator.ProposeBlock(context.Background(), farAhead, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
|
||||
blk2 := util.NewBeaconBlock()
|
||||
blk2.Block.Slot = farAhead - 4
|
||||
@@ -507,7 +507,7 @@ func TestProposeBlock_AllowsSameEpoch(t *testing.T) {
|
||||
).Return(blk2.Block, nil /*err*/)
|
||||
|
||||
validator.ProposeBlock(context.Background(), farAhead-4, pubKey)
|
||||
require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr)
|
||||
require.LogsDoNotContain(t, hook, failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
func TestProposeBlock_BroadcastsBlock(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/validator/client/iface"
|
||||
@@ -40,11 +39,6 @@ func run(ctx context.Context, v iface.Validator) {
|
||||
cleanup()
|
||||
log.Fatalf("Wallet is not ready: %v", err)
|
||||
}
|
||||
if features.Get().SlasherProtection {
|
||||
if err := v.SlasherReady(ctx); err != nil {
|
||||
log.Fatalf("Slasher is not ready: %v", err)
|
||||
}
|
||||
}
|
||||
ticker := time.NewTicker(backOffPeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/validator/client/iface"
|
||||
@@ -62,17 +61,6 @@ func TestCancelledContext_WaitsForActivation(t *testing.T) {
|
||||
assert.Equal(t, 1, v.WaitForActivationCalled, "Expected WaitForActivation() to be called")
|
||||
}
|
||||
|
||||
func TestCancelledContext_ChecksSlasherReady(t *testing.T) {
|
||||
v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
|
||||
cfg := &features.Flags{
|
||||
SlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(cfg)
|
||||
defer reset()
|
||||
run(cancelledContext(), v)
|
||||
assert.Equal(t, true, v.SlasherReadyCalled, "Expected SlasherReady() to be called")
|
||||
}
|
||||
|
||||
func TestUpdateDuties_NextSlot(t *testing.T) {
|
||||
v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/validator/graffiti"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
|
||||
slashingiface "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface"
|
||||
"go.opencensus.io/plugin/ocgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@@ -64,7 +63,6 @@ type ValidatorService struct {
|
||||
withCert string
|
||||
endpoint string
|
||||
validator iface.Validator
|
||||
protector slashingiface.Protector
|
||||
ctx context.Context
|
||||
keyManager keymanager.IKeymanager
|
||||
grpcHeaders []string
|
||||
@@ -82,7 +80,6 @@ type Config struct {
|
||||
GrpcRetriesFlag uint
|
||||
GrpcRetryDelay time.Duration
|
||||
GrpcMaxCallRecvMsgSizeFlag int
|
||||
Protector slashingiface.Protector
|
||||
Endpoint string
|
||||
Validator iface.Validator
|
||||
ValDB db.Database
|
||||
@@ -112,7 +109,6 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e
|
||||
grpcRetries: cfg.GrpcRetriesFlag,
|
||||
grpcRetryDelay: cfg.GrpcRetryDelay,
|
||||
grpcHeaders: strings.Split(cfg.GrpcHeadersFlag, ","),
|
||||
protector: cfg.Protector,
|
||||
validator: cfg.Validator,
|
||||
db: cfg.ValDB,
|
||||
walletInitializedFeed: cfg.WalletInitializedFeed,
|
||||
@@ -188,7 +184,6 @@ func (v *ValidatorService) Start() {
|
||||
attLogs: make(map[[32]byte]*attSubmitted),
|
||||
domainDataCache: cache,
|
||||
aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache,
|
||||
protector: v.protector,
|
||||
voteStats: voteStats{startEpoch: types.Epoch(^uint64(0))},
|
||||
useWeb: v.useWeb,
|
||||
walletInitializedFeed: v.walletInitializedFeed,
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/io/file"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
@@ -76,12 +75,6 @@ func setupEIP3076SpecTests(t *testing.T) []*eip3076TestCase {
|
||||
}
|
||||
|
||||
func TestEIP3076SpecTests(t *testing.T) {
|
||||
config := &features.Flags{
|
||||
SlasherProtection: true,
|
||||
}
|
||||
reset := features.InitWithReset(config)
|
||||
defer reset()
|
||||
|
||||
testCases := setupEIP3076SpecTests(t)
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
@@ -130,22 +123,12 @@ func TestEIP3076SpecTests(t *testing.T) {
|
||||
copy(signingRoot[:], signingRootBytes)
|
||||
}
|
||||
|
||||
err = validator.preBlockSignValidations(context.Background(), pk, wrapper.WrappedPhase0BeaconBlock(b.Block), signingRoot)
|
||||
err = validator.slashableProposalCheck(context.Background(), pk, wrapper.WrappedPhase0SignedBeaconBlock(b), signingRoot)
|
||||
if sb.ShouldSucceed {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.NotEqual(t, nil, err, "pre validation should have failed for block")
|
||||
}
|
||||
|
||||
// Only proceed post update if pre validation did not error.
|
||||
if err == nil {
|
||||
err = validator.postBlockSignUpdate(context.Background(), pk, wrapper.WrappedPhase0SignedBeaconBlock(b), signingRoot)
|
||||
if sb.ShouldSucceed {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.NotEqual(t, nil, err, "post validation should have failed for block")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This loops through a list of attestation signings to attempt after importing the interchange data above.
|
||||
|
||||
@@ -36,20 +36,17 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/validator/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/validator/graffiti"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager"
|
||||
slashingiface "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// reconnectPeriod is the frequency that we try to restart our
|
||||
// slasher connection when the slasher client connection is not ready.
|
||||
var reconnectPeriod = 5 * time.Second
|
||||
|
||||
// keyFetchPeriod is the frequency that we try to refetch validating keys
|
||||
// in case no keys were fetched previously.
|
||||
var keyRefetchPeriod = 30 * time.Second
|
||||
var (
|
||||
keyRefetchPeriod = 30 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
msgCouldNotFetchKeys = "could not fetch validating keys"
|
||||
@@ -81,7 +78,7 @@ type validator struct {
|
||||
keyManager keymanager.IKeymanager
|
||||
beaconClient ethpb.BeaconChainClient
|
||||
validatorClient ethpb.BeaconNodeValidatorClient
|
||||
protector slashingiface.Protector
|
||||
slashingProtectionClient ethpb.SlasherClient
|
||||
db vdb.Database
|
||||
graffiti []byte
|
||||
voteStats voteStats
|
||||
@@ -224,12 +221,14 @@ func (v *validator) WaitForSync(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
// SlasherReady checks if slasher that was configured as external protection
|
||||
// is reachable.
|
||||
func (v *validator) SlasherReady(ctx context.Context) error {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.SlasherReady")
|
||||
defer span.End()
|
||||
if features.Get().SlasherProtection {
|
||||
if features.Get().RemoteSlasherProtection {
|
||||
err := v.protector.Status()
|
||||
if err == nil {
|
||||
return nil
|
||||
@@ -255,6 +254,7 @@ func (v *validator) SlasherReady(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
>>>>>>> develop
|
||||
// ReceiveBlocks starts a gRPC client stream listener to obtain
|
||||
// blocks from the beacon node. Upon receiving a block, the service
|
||||
// broadcasts it to a feed for other usages to subscribe to.
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestPruneAttestations_NoPruning(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPruneAttestations_OK(t *testing.T) {
|
||||
numKeys := uint64(2048)
|
||||
numKeys := uint64(64)
|
||||
pks := make([][48]byte, 0, numKeys)
|
||||
for i := uint64(0); i < numKeys; i++ {
|
||||
pks = append(pks, bytesutil.ToBytes48(bytesutil.ToBytes(i, 48)))
|
||||
|
||||
@@ -52,8 +52,6 @@ go_library(
|
||||
"//validator/keymanager:go_default_library",
|
||||
"//validator/keymanager/imported:go_default_library",
|
||||
"//validator/rpc:go_default_library",
|
||||
"//validator/slashing-protection:go_default_library",
|
||||
"//validator/slashing-protection/iface:go_default_library",
|
||||
"//validator/web:go_default_library",
|
||||
"@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
|
||||
@@ -40,8 +40,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
|
||||
"github.com/prysmaticlabs/prysm/validator/rpc"
|
||||
slashingprotection "github.com/prysmaticlabs/prysm/validator/slashing-protection"
|
||||
"github.com/prysmaticlabs/prysm/validator/slashing-protection/iface"
|
||||
"github.com/prysmaticlabs/prysm/validator/web"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -251,11 +249,6 @@ func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if features.Get().SlasherProtection {
|
||||
if err := c.registerSlasherService(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := c.registerValidatorService(keyManager); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -340,11 +333,6 @@ func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if features.Get().SlasherProtection {
|
||||
if err := c.registerSlasherService(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := c.registerValidatorService(keyManager); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -395,12 +383,6 @@ func (c *ValidatorClient) registerValidatorService(
|
||||
maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
|
||||
grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name)
|
||||
grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name)
|
||||
var sp *slashingprotection.Service
|
||||
var protector iface.Protector
|
||||
if err := c.services.FetchService(&sp); err == nil {
|
||||
protector = sp
|
||||
}
|
||||
|
||||
gStruct := &g.Graffiti{}
|
||||
var err error
|
||||
if c.cliCtx.IsSet(flags.GraffitiFileFlag.Name) {
|
||||
@@ -423,7 +405,6 @@ func (c *ValidatorClient) registerValidatorService(
|
||||
GrpcRetriesFlag: grpcRetries,
|
||||
GrpcRetryDelay: grpcRetryDelay,
|
||||
GrpcHeadersFlag: c.cliCtx.String(flags.GrpcHeadersFlag.Name),
|
||||
Protector: protector,
|
||||
ValDB: c.db,
|
||||
UseWeb: c.cliCtx.Bool(flags.EnableWebFlag.Name),
|
||||
WalletInitializedFeed: c.walletInitialized,
|
||||
@@ -436,29 +417,6 @@ func (c *ValidatorClient) registerValidatorService(
|
||||
|
||||
return c.services.RegisterService(v)
|
||||
}
|
||||
func (c *ValidatorClient) registerSlasherService() error {
|
||||
endpoint := c.cliCtx.String(flags.SlasherRPCProviderFlag.Name)
|
||||
if endpoint == "" {
|
||||
return errors.New("external slasher feature flag is set but no slasher endpoint is configured")
|
||||
|
||||
}
|
||||
cert := c.cliCtx.String(flags.SlasherCertFlag.Name)
|
||||
maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
|
||||
grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name)
|
||||
grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name)
|
||||
sp, err := slashingprotection.NewService(c.cliCtx.Context, &slashingprotection.Config{
|
||||
Endpoint: endpoint,
|
||||
CertFlag: cert,
|
||||
GrpcMaxCallRecvMsgSizeFlag: maxCallRecvMsgSize,
|
||||
GrpcRetriesFlag: grpcRetries,
|
||||
GrpcRetryDelay: grpcRetryDelay,
|
||||
GrpcHeadersFlag: c.cliCtx.String(flags.GrpcHeadersFlag.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize slasher service")
|
||||
}
|
||||
return c.services.RegisterService(sp)
|
||||
}
|
||||
|
||||
func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context, km keymanager.IKeymanager) error {
|
||||
var vs *client.ValidatorService
|
||||
|
||||
@@ -5,9 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"cli_export.go",
|
||||
"cli_import.go",
|
||||
"external.go",
|
||||
"log.go",
|
||||
"slasher_client.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/validator/slashing-protection",
|
||||
visibility = [
|
||||
@@ -15,42 +13,28 @@ go_library(
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//api/grpc:go_default_library",
|
||||
"//cmd:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//validator/accounts/userprompt:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
"//validator/slashing-protection/local/standard-protection-format:go_default_library",
|
||||
"@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library",
|
||||
"@com_github_grpc_ecosystem_go_grpc_middleware//retry:go_default_library",
|
||||
"@com_github_grpc_ecosystem_go_grpc_middleware//tracing/opentracing:go_default_library",
|
||||
"@com_github_grpc_ecosystem_go_grpc_prometheus//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
"@io_opencensus_go//plugin/ocgrpc:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
"@org_golang_google_grpc//connectivity:go_default_library",
|
||||
"@org_golang_google_grpc//credentials:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cli_import_export_test.go",
|
||||
"external_test.go",
|
||||
"slasher_client_test.go",
|
||||
],
|
||||
srcs = ["cli_import_export_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
@@ -58,6 +42,5 @@ go_test(
|
||||
"//validator/slashing-protection/local/standard-protection-format/format:go_default_library",
|
||||
"//validator/testing:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
"@org_golang_google_grpc//metadata:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package slashingprotection
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// CheckBlockSafety this function is part of slashing protection for block proposals it performs
|
||||
// validation without db update. To be used before the block is signed.
|
||||
func (s *Service) CheckBlockSafety(ctx context.Context, blockHeader *ethpb.BeaconBlockHeader) bool {
|
||||
slashable, err := s.slasherClient.IsSlashableBlockNoUpdate(ctx, blockHeader)
|
||||
if err != nil {
|
||||
log.Errorf("External slashing block protection returned an error: %v", err)
|
||||
return false
|
||||
}
|
||||
if slashable != nil && slashable.Slashable {
|
||||
log.Warn("External slashing proposal protection found the block to be slashable")
|
||||
}
|
||||
return !slashable.Slashable
|
||||
}
|
||||
|
||||
// CommitBlock this function is part of slashing protection for block proposals it performs
|
||||
// validation and db update. To be used after the block is proposed.
|
||||
func (s *Service) CommitBlock(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) (bool, error) {
|
||||
ps, err := s.slasherClient.IsSlashableBlock(ctx, blockHeader)
|
||||
if err != nil {
|
||||
log.Errorf("External slashing block protection returned an error: %v", err)
|
||||
return false, err
|
||||
}
|
||||
if ps != nil && len(ps.ProposerSlashings) != 0 {
|
||||
log.Warn("External slashing proposal protection found the block to be slashable")
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckAttestationSafety implements the slashing protection for attestations without db update.
|
||||
// To be used before signing.
|
||||
func (s *Service) CheckAttestationSafety(ctx context.Context, attestation *ethpb.IndexedAttestation) bool {
|
||||
slashable, err := s.slasherClient.IsSlashableAttestationNoUpdate(ctx, attestation)
|
||||
if err != nil {
|
||||
log.Errorf("External slashing attestation protection returned an error: %v", err)
|
||||
return false
|
||||
}
|
||||
if slashable.Slashable {
|
||||
log.Warn("External slashing attestation protection found the attestation to be slashable")
|
||||
}
|
||||
return !slashable.Slashable
|
||||
}
|
||||
|
||||
// CommitAttestation implements the slashing protection for attestations it performs
|
||||
// validation and db update. To be used after the attestation is proposed.
|
||||
func (s *Service) CommitAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) bool {
|
||||
as, err := s.slasherClient.IsSlashableAttestation(ctx, attestation)
|
||||
if err != nil {
|
||||
log.Errorf("External slashing attestation protection returned an error: %v", err)
|
||||
return false
|
||||
}
|
||||
if as != nil && len(as.AttesterSlashings) != 0 {
|
||||
log.Warnf("External slashing attestation protection found the attestation to be slashable: %v", as)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package slashingprotection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
mockSlasher "github.com/prysmaticlabs/prysm/validator/testing"
|
||||
)
|
||||
|
||||
func TestService_VerifyAttestation(t *testing.T) {
|
||||
s := &Service{slasherClient: mockSlasher.MockSlasher{SlashAttestation: true}}
|
||||
att := ð.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
Data: ð.AttestationData{
|
||||
Slot: 5,
|
||||
CommitteeIndex: 2,
|
||||
BeaconBlockRoot: []byte("great block"),
|
||||
Source: ð.Checkpoint{
|
||||
Epoch: 4,
|
||||
Root: []byte("good source"),
|
||||
},
|
||||
Target: ð.Checkpoint{
|
||||
Epoch: 10,
|
||||
Root: []byte("good target"),
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, false, s.CheckAttestationSafety(context.Background(), att), "Expected verify attestation to fail verification")
|
||||
s = &Service{slasherClient: mockSlasher.MockSlasher{SlashAttestation: false}}
|
||||
assert.Equal(t, true, s.CheckAttestationSafety(context.Background(), att), "Expected verify attestation to pass verification")
|
||||
}
|
||||
|
||||
func TestService_CommitAttestation(t *testing.T) {
|
||||
s := &Service{slasherClient: mockSlasher.MockSlasher{SlashAttestation: true}}
|
||||
att := ð.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
Data: ð.AttestationData{
|
||||
Slot: 5,
|
||||
CommitteeIndex: 2,
|
||||
BeaconBlockRoot: []byte("great block"),
|
||||
Source: ð.Checkpoint{
|
||||
Epoch: 4,
|
||||
Root: []byte("good source"),
|
||||
},
|
||||
Target: ð.Checkpoint{
|
||||
Epoch: 10,
|
||||
Root: []byte("good target"),
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, false, s.CommitAttestation(context.Background(), att), "Expected commit attestation to fail verification")
|
||||
s = &Service{slasherClient: mockSlasher.MockSlasher{SlashAttestation: false}}
|
||||
assert.Equal(t, true, s.CommitAttestation(context.Background(), att), "Expected commit attestation to pass verification")
|
||||
}
|
||||
|
||||
func TestService_CommitBlock(t *testing.T) {
|
||||
s := &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: true}}
|
||||
blk := ð.SignedBeaconBlockHeader{
|
||||
Header: ð.BeaconBlockHeader{
|
||||
Slot: 0,
|
||||
ProposerIndex: 0,
|
||||
ParentRoot: bytesutil.PadTo([]byte("parent"), 32),
|
||||
StateRoot: bytesutil.PadTo([]byte("state"), 32),
|
||||
BodyRoot: bytesutil.PadTo([]byte("body"), 32),
|
||||
},
|
||||
}
|
||||
slashable, err := s.CommitBlock(context.Background(), blk)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, slashable, "Expected commit block to fail verification")
|
||||
s = &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: false}}
|
||||
slashable, err = s.CommitBlock(context.Background(), blk)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, slashable, "Expected commit block to pass verification")
|
||||
}
|
||||
|
||||
func TestService_VerifyBlock(t *testing.T) {
|
||||
s := &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: true}}
|
||||
blk := ð.BeaconBlockHeader{
|
||||
Slot: 0,
|
||||
ProposerIndex: 0,
|
||||
ParentRoot: bytesutil.PadTo([]byte("parent"), 32),
|
||||
StateRoot: bytesutil.PadTo([]byte("state"), 32),
|
||||
BodyRoot: bytesutil.PadTo([]byte("body"), 32),
|
||||
}
|
||||
assert.Equal(t, false, s.CheckBlockSafety(context.Background(), blk), "Expected verify block to fail verification")
|
||||
s = &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: false}}
|
||||
assert.Equal(t, true, s.CheckBlockSafety(context.Background(), blk), "Expected verify block to pass verification")
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["protector.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface",
|
||||
visibility = ["//validator:__subpackages__"],
|
||||
deps = ["//proto/prysm/v1alpha1:go_default_library"],
|
||||
)
|
||||
@@ -1,16 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// Protector interface defines the methods of the service that provides slashing protection.
|
||||
type Protector interface {
|
||||
CheckAttestationSafety(ctx context.Context, attestation *eth.IndexedAttestation) bool
|
||||
CommitAttestation(ctx context.Context, attestation *eth.IndexedAttestation) bool
|
||||
CheckBlockSafety(ctx context.Context, blockHeader *eth.BeaconBlockHeader) bool
|
||||
CommitBlock(ctx context.Context, blockHeader *eth.SignedBeaconBlockHeader) (bool, error)
|
||||
Status() error
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package slashingprotection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
|
||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
grpcutil "github.com/prysmaticlabs/prysm/api/grpc"
|
||||
ethsl "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"go.opencensus.io/plugin/ocgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
// Service represents a service to manage the validator
|
||||
// slashing protection.
|
||||
type Service struct {
|
||||
cfg *Config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
conn *grpc.ClientConn
|
||||
grpcHeaders []string
|
||||
slasherClient ethsl.SlasherClient
|
||||
}
|
||||
|
||||
// Config for the validator service.
|
||||
type Config struct {
|
||||
Endpoint string
|
||||
CertFlag string
|
||||
GrpcMaxCallRecvMsgSizeFlag int
|
||||
GrpcRetriesFlag uint
|
||||
GrpcRetryDelay time.Duration
|
||||
GrpcHeadersFlag string
|
||||
}
|
||||
|
||||
// NewService creates a new validator service for the service
|
||||
// registry.
|
||||
func NewService(ctx context.Context, cfg *Config) (*Service, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &Service{
|
||||
cfg: cfg,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
grpcHeaders: strings.Split(cfg.GrpcHeadersFlag, ","),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start the slasher protection service and grpc client.
|
||||
func (s *Service) Start() {
|
||||
if s.cfg.Endpoint != "" {
|
||||
s.slasherClient = s.startSlasherClient()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) startSlasherClient() ethsl.SlasherClient {
|
||||
var dialOpt grpc.DialOption
|
||||
|
||||
if s.cfg.CertFlag != "" {
|
||||
creds, err := credentials.NewClientTLSFromFile(s.cfg.CertFlag, "")
|
||||
if err != nil {
|
||||
log.Errorf("Could not get valid slasher credentials: %v", err)
|
||||
return nil
|
||||
}
|
||||
dialOpt = grpc.WithTransportCredentials(creds)
|
||||
} else {
|
||||
dialOpt = grpc.WithInsecure()
|
||||
log.Warn("You are using an insecure slasher gRPC connection! Please provide a certificate and key to use a secure connection.")
|
||||
}
|
||||
|
||||
s.ctx = grpcutil.AppendHeaders(s.ctx, s.grpcHeaders)
|
||||
|
||||
opts := []grpc.DialOption{
|
||||
dialOpt,
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc_retry.WithMax(s.cfg.GrpcRetriesFlag),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(s.cfg.GrpcRetryDelay)),
|
||||
),
|
||||
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
|
||||
grpc.WithStreamInterceptor(middleware.ChainStreamClient(
|
||||
grpc_opentracing.StreamClientInterceptor(),
|
||||
grpc_prometheus.StreamClientInterceptor,
|
||||
grpc_retry.StreamClientInterceptor(),
|
||||
)),
|
||||
grpc.WithUnaryInterceptor(middleware.ChainUnaryClient(
|
||||
grpc_opentracing.UnaryClientInterceptor(),
|
||||
grpc_prometheus.UnaryClientInterceptor,
|
||||
grpc_retry.UnaryClientInterceptor(),
|
||||
grpcutil.LogRequests,
|
||||
)),
|
||||
}
|
||||
conn, err := grpc.DialContext(s.ctx, s.cfg.Endpoint, opts...)
|
||||
if err != nil {
|
||||
log.Errorf("Could not dial slasher endpoint: %s, %v", s.cfg.Endpoint, err)
|
||||
return nil
|
||||
}
|
||||
log.Debug("Successfully started slasher gRPC connection")
|
||||
s.conn = conn
|
||||
return ethsl.NewSlasherClient(s.conn)
|
||||
|
||||
}
|
||||
|
||||
// Stop the validator service.
|
||||
func (s *Service) Stop() error {
|
||||
s.cancel()
|
||||
log.Info("Stopping slashing protection service")
|
||||
if s.conn != nil {
|
||||
return s.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status checks if the connection to slasher server is ready,
|
||||
// returns error otherwise.
|
||||
func (s *Service) Status() error {
|
||||
if s.conn == nil {
|
||||
return errors.New("no connection to slasher RPC")
|
||||
}
|
||||
if s.conn.GetState() != connectivity.Ready {
|
||||
return fmt.Errorf("can`t connect to slasher server at: %v connection status: %v ", s.cfg.Endpoint, s.conn.GetState())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package slashingprotection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TestGrpcHeaders(t *testing.T) {
|
||||
s := &Service{
|
||||
cfg: &Config{},
|
||||
ctx: context.Background(),
|
||||
grpcHeaders: []string{"first=value1", "second=value2"},
|
||||
}
|
||||
s.startSlasherClient()
|
||||
md, _ := metadata.FromOutgoingContext(s.ctx)
|
||||
require.Equal(t, 2, md.Len(), "MetadataV0 contains wrong number of values")
|
||||
assert.Equal(t, "value1", md.Get("first")[0])
|
||||
assert.Equal(t, "value2", md.Get("second")[0])
|
||||
}
|
||||
@@ -11,9 +11,7 @@ type MockProtector struct {
|
||||
AllowAttestation bool
|
||||
AllowBlock bool
|
||||
VerifyAttestationCalled bool
|
||||
CommitAttestationCalled bool
|
||||
VerifyBlockCalled bool
|
||||
CommitBlockCalled bool
|
||||
StatusCalled bool
|
||||
}
|
||||
|
||||
@@ -23,24 +21,12 @@ func (mp MockProtector) CheckAttestationSafety(_ context.Context, _ *eth.Indexed
|
||||
return mp.AllowAttestation
|
||||
}
|
||||
|
||||
// CommitAttestation returns bool with allow attestation value.
|
||||
func (mp MockProtector) CommitAttestation(_ context.Context, _ *eth.IndexedAttestation) bool {
|
||||
mp.CommitAttestationCalled = true
|
||||
return mp.AllowAttestation
|
||||
}
|
||||
|
||||
// CheckBlockSafety returns bool with allow block value.
|
||||
func (mp MockProtector) CheckBlockSafety(_ context.Context, _ *eth.BeaconBlockHeader) bool {
|
||||
func (mp MockProtector) CheckBlockSafety(_ context.Context, _ *eth.SignedBeaconBlockHeader) bool {
|
||||
mp.VerifyBlockCalled = true
|
||||
return mp.AllowBlock
|
||||
}
|
||||
|
||||
// CommitBlock returns bool with allow block value.
|
||||
func (mp MockProtector) CommitBlock(_ context.Context, _ *eth.SignedBeaconBlockHeader) (bool, error) {
|
||||
mp.CommitBlockCalled = true
|
||||
return mp.AllowBlock, nil
|
||||
}
|
||||
|
||||
// Status returns nil.
|
||||
func (mp MockProtector) Status() error {
|
||||
mp.StatusCalled = true
|
||||
|
||||
@@ -5,30 +5,27 @@ import (
|
||||
"errors"
|
||||
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
slashpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// MockSlasher mocks the slasher rpc server.
|
||||
type MockSlasher struct {
|
||||
SlashAttestation bool
|
||||
SlashBlock bool
|
||||
IsSlashableAttestationCalled bool
|
||||
IsSlashableAttestationNoUpdateCalled bool
|
||||
IsSlashableBlockCalled bool
|
||||
IsSlashableBlockNoUpdateCalled bool
|
||||
SlashAttestation bool
|
||||
SlashBlock bool
|
||||
IsSlashableAttestationCalled bool
|
||||
IsSlashableBlockCalled bool
|
||||
}
|
||||
|
||||
// HighestAttestations will return an empty array of attestations.
|
||||
func (ms MockSlasher) HighestAttestations(ctx context.Context, req *slashpb.HighestAttestationRequest, _ ...grpc.CallOption) (*slashpb.HighestAttestationResponse, error) {
|
||||
return &slashpb.HighestAttestationResponse{
|
||||
func (_ MockSlasher) HighestAttestations(ctx context.Context, req *eth.HighestAttestationRequest, _ ...grpc.CallOption) (*eth.HighestAttestationResponse, error) {
|
||||
return ð.HighestAttestationResponse{
|
||||
Attestations: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsSlashableAttestation returns slashbale attestation if slash attestation is set to true.
|
||||
func (ms MockSlasher) IsSlashableAttestation(_ context.Context, in *eth.IndexedAttestation, _ ...grpc.CallOption) (*slashpb.AttesterSlashingResponse, error) {
|
||||
func (ms MockSlasher) IsSlashableAttestation(_ context.Context, in *eth.IndexedAttestation, _ ...grpc.CallOption) (*eth.AttesterSlashingResponse, error) {
|
||||
ms.IsSlashableAttestationCalled = true
|
||||
if ms.SlashAttestation {
|
||||
|
||||
@@ -42,24 +39,15 @@ func (ms MockSlasher) IsSlashableAttestation(_ context.Context, in *eth.IndexedA
|
||||
Attestation_2: slashingAtt,
|
||||
},
|
||||
}
|
||||
return &slashpb.AttesterSlashingResponse{
|
||||
return ð.AttesterSlashingResponse{
|
||||
AttesterSlashings: slashings,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// IsSlashableAttestationNoUpdate returns slashbale if slash attestation is set to true.
|
||||
func (ms MockSlasher) IsSlashableAttestationNoUpdate(_ context.Context, _ *eth.IndexedAttestation, _ ...grpc.CallOption) (*slashpb.Slashable, error) {
|
||||
ms.IsSlashableAttestationNoUpdateCalled = true
|
||||
return &slashpb.Slashable{
|
||||
Slashable: ms.SlashAttestation,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// IsSlashableBlock returns proposer slashing if slash block is set to true.
|
||||
func (ms MockSlasher) IsSlashableBlock(_ context.Context, in *eth.SignedBeaconBlockHeader, _ ...grpc.CallOption) (*slashpb.ProposerSlashingResponse, error) {
|
||||
func (ms MockSlasher) IsSlashableBlock(_ context.Context, in *eth.SignedBeaconBlockHeader, _ ...grpc.CallOption) (*eth.ProposerSlashingResponse, error) {
|
||||
ms.IsSlashableBlockCalled = true
|
||||
if ms.SlashBlock {
|
||||
slashingBlk, ok := proto.Clone(in).(*eth.SignedBeaconBlockHeader)
|
||||
@@ -72,17 +60,9 @@ func (ms MockSlasher) IsSlashableBlock(_ context.Context, in *eth.SignedBeaconBl
|
||||
Header_2: slashingBlk,
|
||||
},
|
||||
}
|
||||
return &slashpb.ProposerSlashingResponse{
|
||||
return ð.ProposerSlashingResponse{
|
||||
ProposerSlashings: slashings,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// IsSlashableBlockNoUpdate returns slashbale if slash block is set to true.
|
||||
func (ms MockSlasher) IsSlashableBlockNoUpdate(_ context.Context, _ *eth.BeaconBlockHeader, _ ...grpc.CallOption) (*slashpb.Slashable, error) {
|
||||
ms.IsSlashableBlockNoUpdateCalled = true
|
||||
return &slashpb.Slashable{
|
||||
Slashable: ms.SlashBlock,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user