mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 22:07:59 -05:00
Compare commits
186 Commits
slasher-de
...
feature/sl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dd1ce957f | ||
|
|
673a845918 | ||
|
|
15a024f171 | ||
|
|
618e7d8f89 | ||
|
|
df33ce3309 | ||
|
|
86efa87101 | ||
|
|
ff625d55df | ||
|
|
0678e9f718 | ||
|
|
10251c4191 | ||
|
|
861c2f5120 | ||
|
|
bfc821d03a | ||
|
|
26de0f1358 | ||
|
|
5f38167cd9 | ||
|
|
9edba29f64 | ||
|
|
0edb3b9e65 | ||
|
|
6c5bf70021 | ||
|
|
393549ad19 | ||
|
|
8f8ccf11e4 | ||
|
|
806bcf1d29 | ||
|
|
a0193ca90c | ||
|
|
2a2239d937 | ||
|
|
c94ba40db2 | ||
|
|
1816906bc7 | ||
|
|
0fc5b27195 | ||
|
|
2a9a978fc6 | ||
|
|
c32090aae5 | ||
|
|
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 |
@@ -10,7 +10,7 @@
|
||||
|
||||
# Prysm specific remote-cache properties.
|
||||
#build:remote-cache --disk_cache=
|
||||
build:remote-cache --remote_download_minimal
|
||||
build:remote-cache --remote_download_toplevel
|
||||
build:remote-cache --remote_cache=grpc://bazel-remote-cache:9092
|
||||
build:remote-cache --experimental_remote_downloader=grpc://bazel-remote-cache:9092
|
||||
build:remote-cache --remote_local_fallback
|
||||
@@ -46,4 +46,4 @@ test:fuzz --flaky_test_attempts=1
|
||||
|
||||
# Better caching
|
||||
build:nostamp --nostamp
|
||||
build:nostamp --workspace_status_command=./hack/workspace_status_ci.sh
|
||||
build:nostamp --workspace_status_command=./hack/workspace_status_ci.sh
|
||||
|
||||
@@ -59,8 +59,8 @@ type CustomHandler = func(m *ApiProxyMiddleware, endpoint Endpoint, w http.Respo
|
||||
|
||||
// HookCollection contains hooks that can be used to amend the default request/response cycle with custom logic for a specific endpoint.
|
||||
type HookCollection struct {
|
||||
OnPreDeserializeRequestBodyIntoContainer func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) (RunDefault, ErrorJson)
|
||||
OnPostDeserializeRequestBodyIntoContainer func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson
|
||||
OnPreDeserializeRequestBodyIntoContainer func(endpoint *Endpoint, w http.ResponseWriter, req *http.Request) (RunDefault, ErrorJson)
|
||||
OnPostDeserializeRequestBodyIntoContainer func(endpoint *Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson
|
||||
OnPreDeserializeGrpcResponseBodyIntoContainer func([]byte, interface{}) (RunDefault, ErrorJson)
|
||||
OnPreSerializeMiddlewareResponseIntoJson func(interface{}) (RunDefault, []byte, ErrorJson)
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func (m *ApiProxyMiddleware) handleApiPath(gatewayRouter *mux.Router, path strin
|
||||
func deserializeRequestBodyIntoContainerWrapped(endpoint *Endpoint, req *http.Request, w http.ResponseWriter) ErrorJson {
|
||||
runDefault := true
|
||||
if endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer != nil {
|
||||
run, errJson := endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer(*endpoint, w, req)
|
||||
run, errJson := endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer(endpoint, w, req)
|
||||
if errJson != nil {
|
||||
return errJson
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func deserializeRequestBodyIntoContainerWrapped(endpoint *Endpoint, req *http.Re
|
||||
}
|
||||
}
|
||||
if endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer != nil {
|
||||
if errJson := endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer(*endpoint, w, req); errJson != nil {
|
||||
if errJson := endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer(endpoint, w, req); errJson != nil {
|
||||
return errJson
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ go_library(
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
],
|
||||
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,22 +107,36 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
return err
|
||||
}
|
||||
|
||||
// Updating next slot state cache can happen in the background. It shouldn't block rest of the process.
|
||||
if features.Get().EnableNextSlotStateCache {
|
||||
// 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() {
|
||||
// Use a custom deadline here, since this method runs asynchronously.
|
||||
// We ignore the parent method's context and instead create a new one
|
||||
// with a custom deadline, therefore using the background context instead.
|
||||
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
|
||||
defer cancel()
|
||||
if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil {
|
||||
log.WithError(err).Debug("could not update next slot state cache")
|
||||
// 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.
|
||||
if postState.CurrentJustifiedCheckpoint().Epoch > s.justifiedCheckpt.Epoch {
|
||||
currJustifiedEpoch := s.justifiedCheckpt.Epoch
|
||||
if postState.CurrentJustifiedCheckpoint().Epoch > currJustifiedEpoch {
|
||||
if err := s.updateJustified(ctx, postState); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -155,6 +170,27 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
},
|
||||
})
|
||||
|
||||
// Updating next slot state cache can happen in the background. It shouldn't block rest of the process.
|
||||
if features.Get().EnableNextSlotStateCache {
|
||||
go func() {
|
||||
// Use a custom deadline here, since this method runs asynchronously.
|
||||
// We ignore the parent method's context and instead create a new one
|
||||
// with a custom deadline, therefore using the background context instead.
|
||||
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
|
||||
defer cancel()
|
||||
if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil {
|
||||
log.WithError(err).Debug("could not update next slot state cache")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Save justified check point to db.
|
||||
if postState.CurrentJustifiedCheckpoint().Epoch > currJustifiedEpoch {
|
||||
if err := s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, postState.CurrentJustifiedCheckpoint()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update finalized check point.
|
||||
if newFinalized {
|
||||
if err := s.updateFinalized(ctx, postState.FinalizedCheckpoint()); err != nil {
|
||||
|
||||
@@ -212,7 +212,7 @@ func (s *Service) updateJustified(ctx context.Context, state state.ReadOnlyBeaco
|
||||
}
|
||||
}
|
||||
|
||||
return s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, cpt)
|
||||
return nil
|
||||
}
|
||||
|
||||
// This caches input checkpoint as justified for the service struct. It rotates current justified to previous justified,
|
||||
|
||||
@@ -957,6 +957,14 @@ func TestOnBlock_CanFinalize(t *testing.T) {
|
||||
}
|
||||
require.Equal(t, types.Epoch(3), service.CurrentJustifiedCheckpt().Epoch)
|
||||
require.Equal(t, types.Epoch(2), service.FinalizedCheckpt().Epoch)
|
||||
|
||||
// The update should persist in DB.
|
||||
j, err := service.cfg.BeaconDB.JustifiedCheckpoint(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, j.Epoch, service.CurrentJustifiedCheckpt().Epoch)
|
||||
f, err := service.cfg.BeaconDB.FinalizedCheckpoint(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, f.Epoch, service.FinalizedCheckpt().Epoch)
|
||||
}
|
||||
|
||||
func TestInsertFinalizedDeposits(t *testing.T) {
|
||||
|
||||
@@ -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,6 +7,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing:__subpackages__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
3
beacon-chain/cache/committees.go
vendored
3
beacon-chain/cache/committees.go
vendored
@@ -10,9 +10,6 @@ import (
|
||||
// a Committee struct.
|
||||
var ErrNotCommittee = errors.New("object is not a committee struct")
|
||||
|
||||
// ErrNonCommitteeKey will be returned when the committee key does not exist in cache.
|
||||
var ErrNonCommitteeKey = errors.New("committee key does not exist")
|
||||
|
||||
// Committees defines the shuffled committees seed.
|
||||
type Committees struct {
|
||||
CommitteeCount uint64
|
||||
|
||||
@@ -7,17 +7,17 @@ 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__",
|
||||
"//testing/endtoend/evaluators:__pkg__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
"//testing/slasher/simulator:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools:__subpackages__",
|
||||
|
||||
@@ -98,7 +98,10 @@ func ProcessInactivityScores(
|
||||
v.InactivityScore -= 1
|
||||
}
|
||||
} else {
|
||||
v.InactivityScore += bias
|
||||
v.InactivityScore, err = math.Add64(v.InactivityScore, bias)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
|
||||
@@ -200,7 +203,10 @@ func ProcessRewardsAndPenaltiesPrecompute(
|
||||
|
||||
// Compute the post balance of the validator after accounting for the
|
||||
// attester and proposer rewards and penalties.
|
||||
balances[i] = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i])
|
||||
balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
balances[i] = helpers.DecreaseBalanceWithVal(balances[i], attsPenalties[i])
|
||||
|
||||
vals[i].AfterEpochTransitionBalance = balances[i]
|
||||
@@ -230,7 +236,10 @@ func AttestationsDelta(beaconState state.BeaconStateAltair, bal *precompute.Bala
|
||||
inactivityDenominator := cfg.InactivityScoreBias * cfg.InactivityPenaltyQuotientAltair
|
||||
|
||||
for i, v := range vals {
|
||||
rewards[i], penalties[i] = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak)
|
||||
rewards[i], penalties[i], err = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return rewards, penalties, nil
|
||||
@@ -240,11 +249,11 @@ func attestationDelta(
|
||||
bal *precompute.Balance,
|
||||
val *precompute.Validator,
|
||||
baseRewardMultiplier, inactivityDenominator uint64,
|
||||
inactivityLeak bool) (reward, penalty uint64) {
|
||||
inactivityLeak bool) (reward, penalty uint64, err error) {
|
||||
eligible := val.IsActivePrevEpoch || (val.IsSlashed && !val.IsWithdrawableCurrentEpoch)
|
||||
// Per spec `ActiveCurrentEpoch` can't be 0 to process attestation delta.
|
||||
if !eligible || bal.ActiveCurrentEpoch == 0 {
|
||||
return 0, 0
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
@@ -289,9 +298,12 @@ func attestationDelta(
|
||||
// Process finality delay penalty
|
||||
// Apply an additional penalty to validators that did not vote on the correct target or slashed
|
||||
if !val.IsPrevEpochTargetAttester || val.IsSlashed {
|
||||
n := effectiveBalance * val.InactivityScore
|
||||
n, err := math.Mul64(effectiveBalance, val.InactivityScore)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
penalty += n / inactivityDenominator
|
||||
}
|
||||
|
||||
return reward, penalty
|
||||
return reward, penalty, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package altair_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -181,3 +182,16 @@ func TestProcessSlashings_SlashedLess(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessSlashings_BadValue(t *testing.T) {
|
||||
base := ðpb.BeaconStateAltair{
|
||||
Slot: 0,
|
||||
Validators: []*ethpb.Validator{{Slashed: true}},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{math.MaxUint64, 1e9},
|
||||
}
|
||||
s, err := stateAltair.InitializeFromProto(base)
|
||||
require.NoError(t, err)
|
||||
_, err = epoch.ProcessSlashings(s, params.BeaconConfig().ProportionalSlashingMultiplierAltair)
|
||||
require.ErrorContains(t, "addition overflows", err)
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func ProcessProposerSlashings(
|
||||
|
||||
// VerifyProposerSlashing verifies that the data provided from slashing is valid.
|
||||
func VerifyProposerSlashing(
|
||||
beaconState state.BeaconState,
|
||||
beaconState state.ReadOnlyBeaconState,
|
||||
slashing *ethpb.ProposerSlashing,
|
||||
) error {
|
||||
if slashing.Header_1 == nil || slashing.Header_1.Header == nil || slashing.Header_2 == nil || slashing.Header_2.Header == nil {
|
||||
|
||||
@@ -180,7 +180,10 @@ func ProcessSlashings(state state.BeaconState, slashingMultiplier uint64) (state
|
||||
slashings := state.Slashings()
|
||||
totalSlashing := uint64(0)
|
||||
for _, slashing := range slashings {
|
||||
totalSlashing += slashing
|
||||
totalSlashing, err = math.Add64(totalSlashing, slashing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// a callback is used here to apply the following actions to all validators
|
||||
|
||||
@@ -3,6 +3,7 @@ package epoch_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -440,3 +441,16 @@ func buildState(t testing.TB, slot types.Slot, validatorCount uint64) state.Beac
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func TestProcessSlashings_BadValue(t *testing.T) {
|
||||
base := ðpb.BeaconState{
|
||||
Slot: 0,
|
||||
Validators: []*ethpb.Validator{{Slashed: true}},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{math.MaxUint64, 1e9},
|
||||
}
|
||||
s, err := v1.InitializeFromProto(base)
|
||||
require.NoError(t, err)
|
||||
_, err = epoch.ProcessSlashings(s, params.BeaconConfig().ProportionalSlashingMultiplier)
|
||||
require.ErrorContains(t, "addition overflows", err)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,10 @@ func ProcessRewardsAndPenaltiesPrecompute(
|
||||
|
||||
// Compute the post balance of the validator after accounting for the
|
||||
// attester and proposer rewards and penalties.
|
||||
validatorBals[i] = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i])
|
||||
validatorBals[i], err = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validatorBals[i] = helpers.DecreaseBalanceWithVal(validatorBals[i], attsPenalties[i])
|
||||
|
||||
vp[i].AfterEpochTransitionBalance = validatorBals[i]
|
||||
|
||||
@@ -6,6 +6,6 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/feed",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//shared:__subpackages__",
|
||||
"//testing/slasher/simulator:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -7,7 +7,10 @@ go_library(
|
||||
"notifier.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/slasher/simulator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//async/event:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
|
||||
@@ -18,17 +18,18 @@ 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__",
|
||||
"//testing/endtoend/evaluators:__pkg__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools:__subpackages__",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
mathutil "github.com/prysmaticlabs/prysm/math"
|
||||
)
|
||||
|
||||
var balanceCache = cache.NewEffectiveBalanceCache()
|
||||
@@ -95,7 +96,11 @@ func IncreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta ui
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return state.UpdateBalancesAtIndex(idx, IncreaseBalanceWithVal(balAtIdx, delta))
|
||||
newBal, err := IncreaseBalanceWithVal(balAtIdx, delta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return state.UpdateBalancesAtIndex(idx, newBal)
|
||||
}
|
||||
|
||||
// IncreaseBalanceWithVal increases validator with the given 'index' balance by 'delta' in Gwei.
|
||||
@@ -108,8 +113,8 @@ func IncreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta ui
|
||||
// Increase the validator balance at index ``index`` by ``delta``.
|
||||
// """
|
||||
// state.balances[index] += delta
|
||||
func IncreaseBalanceWithVal(currBalance, delta uint64) uint64 {
|
||||
return currBalance + delta
|
||||
func IncreaseBalanceWithVal(currBalance, delta uint64) (uint64, error) {
|
||||
return mathutil.Add64(currBalance, delta)
|
||||
}
|
||||
|
||||
// DecreaseBalance decreases validator with the given 'index' balance by 'delta' in Gwei.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -238,3 +239,23 @@ func buildState(slot types.Slot, validatorCount uint64) *ethpb.BeaconState {
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)},
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncreaseBadBalance_NotOK(t *testing.T) {
|
||||
tests := []struct {
|
||||
i types.ValidatorIndex
|
||||
b []uint64
|
||||
nb uint64
|
||||
}{
|
||||
{i: 0, b: []uint64{math.MaxUint64, math.MaxUint64, math.MaxUint64}, nb: 1},
|
||||
{i: 2, b: []uint64{math.MaxUint64, math.MaxUint64, math.MaxUint64}, nb: 33 * 1e9},
|
||||
}
|
||||
for _, test := range tests {
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{EffectiveBalance: 4}, {EffectiveBalance: 4}, {EffectiveBalance: 4}},
|
||||
Balances: test.b,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.ErrorContains(t, "addition overflows", IncreaseBalance(state, test.i, test.nb))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func signingData(rootFunc func() ([32]byte, error), domain []byte) ([32]byte, er
|
||||
}
|
||||
|
||||
// ComputeDomainVerifySigningRoot computes domain and verifies signing root of an object given the beacon state, validator index and signature.
|
||||
func ComputeDomainVerifySigningRoot(st state.BeaconState, index types.ValidatorIndex, epoch types.Epoch, obj fssz.HashRoot, domain [4]byte, sig []byte) error {
|
||||
func ComputeDomainVerifySigningRoot(st state.ReadOnlyBeaconState, index types.ValidatorIndex, epoch types.Epoch, obj fssz.HashRoot, domain [4]byte, sig []byte) error {
|
||||
v, err := st.ValidatorAtIndex(index)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,6 +2,7 @@ package transition_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
@@ -207,6 +208,24 @@ func TestExecuteStateTransitionNoVerifyAnySig_PassesProcessingConditions(t *test
|
||||
require.Equal(t, true, verified, "Could not verify signature set")
|
||||
}
|
||||
|
||||
func TestProcessEpoch_BadBalanceAltair(t *testing.T) {
|
||||
s, _ := util.DeterministicGenesisStateAltair(t, 100)
|
||||
assert.NoError(t, s.SetSlot(63))
|
||||
assert.NoError(t, s.UpdateBalancesAtIndex(0, math.MaxUint64))
|
||||
participation := byte(0)
|
||||
participation = altair.AddValidatorFlag(participation, params.BeaconConfig().TimelyHeadFlagIndex)
|
||||
participation = altair.AddValidatorFlag(participation, params.BeaconConfig().TimelySourceFlagIndex)
|
||||
participation = altair.AddValidatorFlag(participation, params.BeaconConfig().TimelyTargetFlagIndex)
|
||||
|
||||
epochParticipation, err := s.CurrentEpochParticipation()
|
||||
assert.NoError(t, err)
|
||||
epochParticipation[0] = participation
|
||||
assert.NoError(t, s.SetCurrentParticipationBits(epochParticipation))
|
||||
assert.NoError(t, s.SetPreviousParticipationBits(epochParticipation))
|
||||
_, err = altair.ProcessEpoch(context.Background(), s)
|
||||
assert.ErrorContains(t, "addition overflows", err)
|
||||
}
|
||||
|
||||
func createFullAltairBlockWithOperations(t *testing.T) (state.BeaconStateAltair,
|
||||
*ethpb.SignedBeaconBlockAltair) {
|
||||
beaconState, privKeys := util.DeterministicGenesisStateAltair(t, 32)
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//cmd/beacon-chain:__subpackages__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//tools:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -7,7 +7,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/testing",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/endtoend:__subpackages__",
|
||||
"//testing:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/db:go_default_library",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -13,7 +13,9 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/endtoend:__subpackages__",
|
||||
"//testing/fuzz:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core:go_default_library",
|
||||
|
||||
@@ -30,17 +30,17 @@ func (m *PoolMock) InsertAttesterSlashing(_ context.Context, _ state.ReadOnlyBea
|
||||
}
|
||||
|
||||
// InsertProposerSlashing --
|
||||
func (m *PoolMock) InsertProposerSlashing(_ context.Context, _ state.BeaconState, slashing *ethpb.ProposerSlashing) error {
|
||||
func (m *PoolMock) InsertProposerSlashing(_ context.Context, _ state.ReadOnlyBeaconState, slashing *ethpb.ProposerSlashing) error {
|
||||
m.PendingPropSlashings = append(m.PendingPropSlashings, slashing)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkIncludedAttesterSlashing --
|
||||
func (m *PoolMock) MarkIncludedAttesterSlashing(_ *ethpb.AttesterSlashing) {
|
||||
func (_ *PoolMock) MarkIncludedAttesterSlashing(_ *ethpb.AttesterSlashing) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// MarkIncludedProposerSlashing --
|
||||
func (m *PoolMock) MarkIncludedProposerSlashing(_ *ethpb.ProposerSlashing) {
|
||||
func (_ *PoolMock) MarkIncludedProposerSlashing(_ *ethpb.ProposerSlashing) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ func (p *Pool) InsertAttesterSlashing(
|
||||
// has been included recently, the validator is already exited, or the validator was already slashed.
|
||||
func (p *Pool) InsertProposerSlashing(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slashing *ethpb.ProposerSlashing,
|
||||
) error {
|
||||
p.lock.Lock()
|
||||
|
||||
@@ -6,6 +6,13 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = PoolManager(&Pool{})
|
||||
_ = PoolInserter(&Pool{})
|
||||
_ = PoolManager(&PoolMock{})
|
||||
_ = PoolInserter(&PoolMock{})
|
||||
)
|
||||
|
||||
func TestPool_validatorSlashingPreconditionCheck_requiresLock(t *testing.T) {
|
||||
p := &Pool{}
|
||||
_, err := p.validatorSlashingPreconditionCheck(nil, 0)
|
||||
|
||||
@@ -9,11 +9,8 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// PoolManager maintains a pool of pending and recently included attester and proposer slashings.
|
||||
// This pool is used by proposers to insert data into new blocks.
|
||||
type PoolManager interface {
|
||||
PendingAttesterSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.AttesterSlashing
|
||||
PendingProposerSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing
|
||||
// PoolInserter is capable of inserting new slashing objects into the operations pool.
|
||||
type PoolInserter interface {
|
||||
InsertAttesterSlashing(
|
||||
ctx context.Context,
|
||||
state state.ReadOnlyBeaconState,
|
||||
@@ -21,9 +18,17 @@ type PoolManager interface {
|
||||
) error
|
||||
InsertProposerSlashing(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
state state.ReadOnlyBeaconState,
|
||||
slashing *ethpb.ProposerSlashing,
|
||||
) error
|
||||
}
|
||||
|
||||
// PoolManager maintains a pool of pending and recently included attester and proposer slashings.
|
||||
// This pool is used by proposers to insert data into new blocks.
|
||||
type PoolManager interface {
|
||||
PoolInserter
|
||||
PendingAttesterSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.AttesterSlashing
|
||||
PendingProposerSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing
|
||||
MarkIncludedAttesterSlashing(as *ethpb.AttesterSlashing)
|
||||
MarkIncludedProposerSlashing(ps *ethpb.ProposerSlashing)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// GossipTypeMapping.
|
||||
var ErrMessageNotMapped = errors.New("message type is not mapped to a PubSub topic")
|
||||
|
||||
// Broadcasts a message to the p2p network, the message is assumed to be
|
||||
// Broadcast a message to the p2p network, the message is assumed to be
|
||||
// broadcasted to the current fork.
|
||||
func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error {
|
||||
ctx, span := trace.StartSpan(ctx, "p2p.Broadcast")
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
metadata "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/metadata"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/metadata"
|
||||
)
|
||||
|
||||
// MockMetadataProvider is a fake implementation of the MetadataProvider interface.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -14,11 +14,14 @@ go_library(
|
||||
deps = [
|
||||
"//api/gateway/apimiddleware:go_default_library",
|
||||
"//api/grpc:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/rpc/eth/events:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/eth/v2:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_r3labs_sse//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -34,7 +37,9 @@ go_test(
|
||||
deps = [
|
||||
"//api/gateway/apimiddleware:go_default_library",
|
||||
"//api/grpc:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/rpc/eth/events:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/eth/v2:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
|
||||
)
|
||||
@@ -17,7 +21,7 @@ import (
|
||||
// https://ethereum.github.io/beacon-apis/#/Beacon/submitPoolAttestations expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapAttestationsArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -39,7 +43,7 @@ func wrapAttestationsArray(
|
||||
// Some endpoints e.g. https://ethereum.github.io/beacon-apis/#/Validator/getAttesterDuties expect posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with an 'Index' field.
|
||||
func wrapValidatorIndicesArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -61,7 +65,7 @@ func wrapValidatorIndicesArray(
|
||||
// https://ethereum.github.io/beacon-apis/#/Validator/publishAggregateAndProofs expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapSignedAggregateAndProofArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -83,7 +87,7 @@ func wrapSignedAggregateAndProofArray(
|
||||
// https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconCommitteeSubnet expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapBeaconCommitteeSubscriptionsArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -105,7 +109,7 @@ func wrapBeaconCommitteeSubscriptionsArray(
|
||||
// https://ethereum.github.io/beacon-APIs/#/Validator/prepareSyncCommitteeSubnets expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapSyncCommitteeSubscriptionsArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -127,7 +131,7 @@ func wrapSyncCommitteeSubscriptionsArray(
|
||||
// https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolSyncCommitteeSignatures expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapSyncCommitteeSignaturesArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -149,7 +153,7 @@ func wrapSyncCommitteeSignaturesArray(
|
||||
// https://ethereum.github.io/beacon-APIs/#/Validator/publishContributionAndProofs expects posting a top-level array.
|
||||
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
||||
func wrapSignedContributionAndProofsArray(
|
||||
endpoint apimiddleware.Endpoint,
|
||||
endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
@@ -168,15 +172,82 @@ func wrapSignedContributionAndProofsArray(
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Posted graffiti needs to have length of 32 bytes, but client is allowed to send data of any length.
|
||||
func prepareGraffiti(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson {
|
||||
type phase0PublishBlockRequestJson struct {
|
||||
Phase0Block *beaconBlockJson `json:"phase0_block"`
|
||||
Signature string `json:"signature" hex:"true"`
|
||||
}
|
||||
|
||||
type altairPublishBlockRequestJson struct {
|
||||
AltairBlock *beaconBlockAltairJson `json:"altair_block"`
|
||||
Signature string `json:"signature" hex:"true"`
|
||||
}
|
||||
|
||||
// setInitialPublishBlockPostRequest is triggered before we deserialize the request JSON into a struct.
|
||||
// We don't know which version of the block got posted, but we can determine it from the slot.
|
||||
// We know that both Phase 0 and Altair blocks have a Message field with a Slot field,
|
||||
// so we deserialize the request into a struct s, which has the right fields, to obtain the slot.
|
||||
// Once we know the slot, we can determine what the PostRequest field of the endpoint should be, and we set it appropriately.
|
||||
func setInitialPublishBlockPostRequest(endpoint *apimiddleware.Endpoint,
|
||||
_ http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
||||
s := struct {
|
||||
Message struct {
|
||||
Slot string
|
||||
}
|
||||
}{}
|
||||
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not read body")
|
||||
}
|
||||
if err := json.Unmarshal(buf, &s); err != nil {
|
||||
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not read slot from body")
|
||||
}
|
||||
slot, err := strconv.ParseUint(s.Message.Slot, 10, 64)
|
||||
if err != nil {
|
||||
return false, apimiddleware.InternalServerErrorWithMessage(err, "slot is not an unsigned integer")
|
||||
}
|
||||
if core.SlotToEpoch(types.Slot(slot)) < params.BeaconConfig().AltairForkEpoch {
|
||||
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
|
||||
} else {
|
||||
endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{}
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// In preparePublishedBlock we transform the PostRequest.
|
||||
// gRPC expects either a phase0_block or an altair_block field in the JSON object, but we have a message field at this point.
|
||||
// We do a simple conversion depending on the type of endpoint.PostRequest (which was filled out previously in setInitialPublishBlockPostRequest)
|
||||
func preparePublishedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson {
|
||||
if block, ok := endpoint.PostRequest.(*signedBeaconBlockContainerJson); ok {
|
||||
b := bytesutil.ToBytes32([]byte(block.Message.Body.Graffiti))
|
||||
block.Message.Body.Graffiti = hexutil.Encode(b[:])
|
||||
// Prepare post request that can be properly decoded on gRPC side.
|
||||
actualPostReq := &phase0PublishBlockRequestJson{
|
||||
Phase0Block: block.Message,
|
||||
Signature: block.Signature,
|
||||
}
|
||||
endpoint.PostRequest = actualPostReq
|
||||
block.Message.Body.Graffiti = prepareGraffiti(block.Message.Body.Graffiti)
|
||||
}
|
||||
if block, ok := endpoint.PostRequest.(*signedBeaconBlockAltairContainerJson); ok {
|
||||
// Prepare post request that can be properly decoded on gRPC side.
|
||||
actualPostReq := &altairPublishBlockRequestJson{
|
||||
AltairBlock: block.Message,
|
||||
Signature: block.Signature,
|
||||
}
|
||||
endpoint.PostRequest = actualPostReq
|
||||
block.Message.Body.Graffiti = prepareGraffiti(block.Message.Body.Graffiti)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Posted graffiti needs to have length of 32 bytes, but client is allowed to send data of any length.
|
||||
func prepareGraffiti(graffiti string) string {
|
||||
b := bytesutil.ToBytes32([]byte(graffiti))
|
||||
return hexutil.Encode(b[:])
|
||||
}
|
||||
|
||||
type tempSyncCommitteesResponseJson struct {
|
||||
Data *tempSyncCommitteeValidatorsJson `json:"data"`
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
@@ -18,7 +22,7 @@ import (
|
||||
|
||||
func TestWrapAttestationArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitAttestationRequestJson{},
|
||||
}
|
||||
unwrappedAtts := []*attestationJson{{AggregationBits: "1010"}}
|
||||
@@ -40,7 +44,7 @@ func TestWrapAttestationArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitAttestationRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -58,7 +62,7 @@ func TestWrapAttestationArray(t *testing.T) {
|
||||
|
||||
func TestWrapValidatorIndicesArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &dutiesRequestJson{},
|
||||
}
|
||||
unwrappedIndices := []string{"1", "2"}
|
||||
@@ -81,7 +85,7 @@ func TestWrapValidatorIndicesArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &dutiesRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -99,7 +103,7 @@ func TestWrapValidatorIndicesArray(t *testing.T) {
|
||||
|
||||
func TestWrapSignedAggregateAndProofArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitAggregateAndProofsRequestJson{},
|
||||
}
|
||||
unwrappedAggs := []*signedAggregateAttestationAndProofJson{{Signature: "sig"}}
|
||||
@@ -121,7 +125,7 @@ func TestWrapSignedAggregateAndProofArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitAggregateAndProofsRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -139,7 +143,7 @@ func TestWrapSignedAggregateAndProofArray(t *testing.T) {
|
||||
|
||||
func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitBeaconCommitteeSubscriptionsRequestJson{},
|
||||
}
|
||||
unwrappedSubs := []*beaconCommitteeSubscribeJson{{
|
||||
@@ -171,7 +175,7 @@ func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitBeaconCommitteeSubscriptionsRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -189,7 +193,7 @@ func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) {
|
||||
|
||||
func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitSyncCommitteeSubscriptionRequestJson{},
|
||||
}
|
||||
unwrappedSubs := []*syncCommitteeSubscriptionJson{
|
||||
@@ -226,7 +230,7 @@ func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitSyncCommitteeSubscriptionRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -244,7 +248,7 @@ func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) {
|
||||
|
||||
func TestWrapSyncCommitteeSignaturesArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitSyncCommitteeSignaturesRequestJson{},
|
||||
}
|
||||
unwrappedSigs := []*syncCommitteeMessageJson{{
|
||||
@@ -274,7 +278,7 @@ func TestWrapSyncCommitteeSignaturesArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitSyncCommitteeSignaturesRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -292,7 +296,7 @@ func TestWrapSyncCommitteeSignaturesArray(t *testing.T) {
|
||||
|
||||
func TestWrapSignedContributionAndProofsArray(t *testing.T) {
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitContributionAndProofsRequestJson{},
|
||||
}
|
||||
unwrapped := []*signedContributionAndProofJson{
|
||||
@@ -343,7 +347,7 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid_body", func(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &submitContributionAndProofsRequestJson{},
|
||||
}
|
||||
var body bytes.Buffer
|
||||
@@ -359,45 +363,96 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrepareGraffiti(t *testing.T) {
|
||||
endpoint := apimiddleware.Endpoint{
|
||||
PostRequest: &signedBeaconBlockContainerJson{
|
||||
Message: &beaconBlockJson{
|
||||
Body: &beaconBlockBodyJson{},
|
||||
func TestSetInitialPublishBlockPostRequest(t *testing.T) {
|
||||
endpoint := &apimiddleware.Endpoint{}
|
||||
s := struct {
|
||||
Message struct {
|
||||
Slot string
|
||||
}
|
||||
}{}
|
||||
t.Run("Phase 0", func(t *testing.T) {
|
||||
s.Message = struct{ Slot string }{Slot: "0"}
|
||||
j, err := json.Marshal(s)
|
||||
require.NoError(t, err)
|
||||
var body bytes.Buffer
|
||||
_, err = body.Write(j)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", "http://foo.example", &body)
|
||||
runDefault, errJson := setInitialPublishBlockPostRequest(endpoint, nil, request)
|
||||
require.Equal(t, true, errJson == nil)
|
||||
assert.Equal(t, apimiddleware.RunDefault(true), runDefault)
|
||||
assert.Equal(t, reflect.TypeOf(signedBeaconBlockContainerJson{}).Name(), reflect.Indirect(reflect.ValueOf(endpoint.PostRequest)).Type().Name())
|
||||
})
|
||||
t.Run("Altair", func(t *testing.T) {
|
||||
slot, err := core.StartSlot(params.BeaconConfig().AltairForkEpoch)
|
||||
require.NoError(t, err)
|
||||
s.Message = struct{ Slot string }{Slot: strconv.FormatUint(uint64(slot), 10)}
|
||||
j, err := json.Marshal(s)
|
||||
require.NoError(t, err)
|
||||
var body bytes.Buffer
|
||||
_, err = body.Write(j)
|
||||
require.NoError(t, err)
|
||||
request := httptest.NewRequest("POST", "http://foo.example", &body)
|
||||
runDefault, errJson := setInitialPublishBlockPostRequest(endpoint, nil, request)
|
||||
require.Equal(t, true, errJson == nil)
|
||||
assert.Equal(t, apimiddleware.RunDefault(true), runDefault)
|
||||
assert.Equal(t, reflect.TypeOf(signedBeaconBlockAltairContainerJson{}).Name(), reflect.Indirect(reflect.ValueOf(endpoint.PostRequest)).Type().Name())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPreparePublishedBlock(t *testing.T) {
|
||||
t.Run("Phase 0", func(t *testing.T) {
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &signedBeaconBlockContainerJson{
|
||||
Message: &beaconBlockJson{
|
||||
Body: &beaconBlockBodyJson{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
preparePublishedBlock(endpoint, nil, nil)
|
||||
_, ok := endpoint.PostRequest.(*phase0PublishBlockRequestJson)
|
||||
assert.Equal(t, true, ok)
|
||||
})
|
||||
|
||||
t.Run("Altair", func(t *testing.T) {
|
||||
endpoint := &apimiddleware.Endpoint{
|
||||
PostRequest: &signedBeaconBlockAltairContainerJson{
|
||||
Message: &beaconBlockAltairJson{
|
||||
Body: &beaconBlockBodyAltairJson{},
|
||||
},
|
||||
},
|
||||
}
|
||||
preparePublishedBlock(endpoint, nil, nil)
|
||||
_, ok := endpoint.PostRequest.(*altairPublishBlockRequestJson)
|
||||
assert.Equal(t, true, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrepareGraffiti(t *testing.T) {
|
||||
t.Run("32_bytes", func(t *testing.T) {
|
||||
endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti = string(bytesutil.PadTo([]byte("foo"), 32))
|
||||
|
||||
prepareGraffiti(endpoint, nil, nil)
|
||||
result := prepareGraffiti(string(bytesutil.PadTo([]byte("foo"), 32)))
|
||||
assert.Equal(
|
||||
t,
|
||||
"0x666f6f0000000000000000000000000000000000000000000000000000000000",
|
||||
endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti,
|
||||
result,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("less_than_32_bytes", func(t *testing.T) {
|
||||
endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti = "foo"
|
||||
|
||||
prepareGraffiti(endpoint, nil, nil)
|
||||
t.Run("graffiti_less_than_32_bytes", func(t *testing.T) {
|
||||
result := prepareGraffiti("foo")
|
||||
assert.Equal(
|
||||
t,
|
||||
"0x666f6f0000000000000000000000000000000000000000000000000000000000",
|
||||
endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti,
|
||||
result,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("more_than_32_bytes", func(t *testing.T) {
|
||||
endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti = string(bytesutil.PadTo([]byte("foo"), 33))
|
||||
|
||||
prepareGraffiti(endpoint, nil, nil)
|
||||
t.Run("graffiti_more_than_32_bytes", func(t *testing.T) {
|
||||
result := prepareGraffiti(string(bytesutil.PadTo([]byte("foo"), 33)))
|
||||
assert.Equal(
|
||||
t,
|
||||
"0x666f6f0000000000000000000000000000000000000000000000000000000000",
|
||||
endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti,
|
||||
result,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ func (f *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
|
||||
case "/eth/v1/beacon/headers/{block_id}":
|
||||
endpoint.GetResponse = &blockHeaderResponseJson{}
|
||||
case "/eth/v1/beacon/blocks":
|
||||
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
|
||||
endpoint.Hooks = apimiddleware.HookCollection{
|
||||
OnPostDeserializeRequestBodyIntoContainer: prepareGraffiti,
|
||||
OnPreDeserializeRequestBodyIntoContainer: setInitialPublishBlockPostRequest,
|
||||
OnPostDeserializeRequestBodyIntoContainer: preparePublishedBlock,
|
||||
}
|
||||
case "/eth/v1/beacon/blocks/{block_id}":
|
||||
endpoint.GetResponse = &blockResponseJson{}
|
||||
|
||||
@@ -144,39 +144,80 @@ func (bs *Server) ListBlockHeaders(ctx context.Context, req *ethpbv1.BlockHeader
|
||||
// response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the
|
||||
// new block into its state, and therefore validate the block internally, however blocks which fail the validation are
|
||||
// still broadcast but a different status code is returned (202).
|
||||
func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv1.BeaconBlockContainer) (*emptypb.Empty, error) {
|
||||
func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBlockContainerV2) (*emptypb.Empty, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlock")
|
||||
defer span.End()
|
||||
|
||||
blk := req.Message
|
||||
rBlock, err := migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: blk, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
|
||||
}
|
||||
v1alpha1Block := wrapper.WrappedPhase0SignedBeaconBlock(rBlock)
|
||||
phase0BlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block)
|
||||
if ok {
|
||||
phase0Blk := phase0BlkContainer.Phase0Block
|
||||
v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
|
||||
}
|
||||
wrappedPhase0Blk := wrapper.WrappedPhase0SignedBeaconBlock(v1alpha1Blk)
|
||||
|
||||
root, err := blk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
|
||||
}
|
||||
root, err := phase0Blk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
// Do not block proposal critical path with debug logging or block feed updates.
|
||||
defer func() {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: v1alpha1Block},
|
||||
})
|
||||
}()
|
||||
// Do not block proposal critical path with debug logging or block feed updates.
|
||||
defer func() {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk},
|
||||
})
|
||||
}()
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, v1alpha1Block.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, wrappedPhase0Blk.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, v1alpha1Block, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedPhase0Blk, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
}
|
||||
} else {
|
||||
altairBlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock)
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not get Altair block from request")
|
||||
}
|
||||
altairBlk := altairBlkContainer.AltairBlock
|
||||
v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: req.Signature})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
|
||||
}
|
||||
wrappedAltairBlk, err := wrapper.WrappedAltairSignedBeaconBlock(v1alpha1Blk)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not prepare Altair block")
|
||||
}
|
||||
|
||||
root, err := altairBlk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
|
||||
}
|
||||
|
||||
// Do not block proposal critical path with debug logging or block feed updates.
|
||||
defer func() {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk},
|
||||
})
|
||||
}()
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
if err := bs.Broadcaster.Broadcast(ctx, wrappedAltairBlk.Proto()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
|
||||
}
|
||||
|
||||
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedAltairBlk, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
@@ -245,7 +286,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
|
||||
return ðpbv2.BlockResponseV2{
|
||||
Version: ethpbv2.Version_PHASE0,
|
||||
Data: ðpbv2.SignedBeaconBlockContainerV2{
|
||||
Block: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Blk.Block},
|
||||
Message: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Blk.Block},
|
||||
Signature: v1Blk.Signature,
|
||||
},
|
||||
}, nil
|
||||
@@ -261,7 +302,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
|
||||
return ðpbv2.BlockResponseV2{
|
||||
Version: ethpbv2.Version_ALTAIR,
|
||||
Data: ðpbv2.SignedBeaconBlockContainerV2{
|
||||
Block: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Blk},
|
||||
Message: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Blk},
|
||||
Signature: blk.Signature(),
|
||||
},
|
||||
}, nil
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
|
||||
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
|
||||
@@ -290,42 +289,83 @@ func TestServer_ListBlockHeaders(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_ProposeBlock_OK(t *testing.T) {
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
params.SetupTestConfigCleanup(t)
|
||||
params.OverrideBeaconConfig(params.MainnetConfig())
|
||||
t.Run("Phase 0", func(t *testing.T) {
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
genesis := util.NewBeaconBlock()
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genesis)), "Could not save genesis block")
|
||||
genesis := util.NewBeaconBlock()
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genesis)), "Could not save genesis block")
|
||||
|
||||
numDeposits := uint64(64)
|
||||
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
|
||||
bsRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesisRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state")
|
||||
numDeposits := uint64(64)
|
||||
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
|
||||
bsRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesisRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state")
|
||||
|
||||
c := &mock.ChainService{Root: bsRoot[:], State: beaconState}
|
||||
beaconChainServer := &Server{
|
||||
BeaconDB: beaconDB,
|
||||
BlockReceiver: c,
|
||||
ChainInfoFetcher: c,
|
||||
BlockNotifier: c.BlockNotifier(),
|
||||
Broadcaster: mockp2p.NewTestP2P(t),
|
||||
}
|
||||
req := util.NewBeaconBlock()
|
||||
req.Block.Slot = 5
|
||||
req.Block.ParentRoot = bsRoot[:]
|
||||
v1Block, err := migration.V1Alpha1ToV1SignedBlock(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(req)))
|
||||
blockReq := ðpbv1.BeaconBlockContainer{
|
||||
Message: v1Block.Block,
|
||||
Signature: v1Block.Signature,
|
||||
}
|
||||
_, err = beaconChainServer.SubmitBlock(context.Background(), blockReq)
|
||||
assert.NoError(t, err, "Could not propose block correctly")
|
||||
c := &mock.ChainService{Root: bsRoot[:], State: beaconState}
|
||||
beaconChainServer := &Server{
|
||||
BeaconDB: beaconDB,
|
||||
BlockReceiver: c,
|
||||
ChainInfoFetcher: c,
|
||||
BlockNotifier: c.BlockNotifier(),
|
||||
Broadcaster: mockp2p.NewTestP2P(t),
|
||||
}
|
||||
req := util.NewBeaconBlock()
|
||||
req.Block.Slot = 5
|
||||
req.Block.ParentRoot = bsRoot[:]
|
||||
v1Block, err := migration.V1Alpha1ToV1SignedBlock(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(req)))
|
||||
blockReq := ðpbv2.SignedBeaconBlockContainerV2{
|
||||
Message: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Block.Block},
|
||||
Signature: v1Block.Signature,
|
||||
}
|
||||
_, err = beaconChainServer.SubmitBlock(context.Background(), blockReq)
|
||||
assert.NoError(t, err, "Could not propose block correctly")
|
||||
})
|
||||
|
||||
t.Run("Altair", func(t *testing.T) {
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
genesis := util.NewBeaconBlockAltair()
|
||||
wrapped, err := wrapper.WrappedAltairSignedBeaconBlock(genesis)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapped), "Could not save genesis block")
|
||||
|
||||
numDeposits := uint64(64)
|
||||
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
|
||||
bsRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
genesisRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state")
|
||||
|
||||
c := &mock.ChainService{Root: bsRoot[:], State: beaconState}
|
||||
beaconChainServer := &Server{
|
||||
BeaconDB: beaconDB,
|
||||
BlockReceiver: c,
|
||||
ChainInfoFetcher: c,
|
||||
BlockNotifier: c.BlockNotifier(),
|
||||
Broadcaster: mockp2p.NewTestP2P(t),
|
||||
}
|
||||
req := util.NewBeaconBlockAltair()
|
||||
req.Block.Slot = 5
|
||||
req.Block.ParentRoot = bsRoot[:]
|
||||
v2Block, err := migration.V1Alpha1BeaconBlockAltairToV2(req.Block)
|
||||
require.NoError(t, err)
|
||||
wrapped, err = wrapper.WrappedAltairSignedBeaconBlock(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapped))
|
||||
blockReq := ðpbv2.SignedBeaconBlockContainerV2{
|
||||
Message: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Block},
|
||||
Signature: req.Signature,
|
||||
}
|
||||
_, err = beaconChainServer.SubmitBlock(context.Background(), blockReq)
|
||||
assert.NoError(t, err, "Could not propose block correctly")
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_GetBlock(t *testing.T) {
|
||||
@@ -538,7 +578,7 @@ func TestServer_GetBlockV2(t *testing.T) {
|
||||
v1Block, err := migration.V1Alpha1ToV1SignedBlock(tt.want)
|
||||
require.NoError(t, err)
|
||||
|
||||
phase0Block, ok := blk.Data.Block.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block)
|
||||
phase0Block, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block)
|
||||
require.Equal(t, true, ok)
|
||||
if !reflect.DeepEqual(phase0Block.Phase0Block, v1Block.Block) {
|
||||
t.Error("Expected blocks to equal")
|
||||
@@ -655,7 +695,7 @@ func TestServer_GetBlockV2(t *testing.T) {
|
||||
v2Block, err := migration.V1Alpha1BeaconBlockAltairToV2(tt.want.Block)
|
||||
require.NoError(t, err)
|
||||
|
||||
altairBlock, ok := blk.Data.Block.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock)
|
||||
altairBlock, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock)
|
||||
require.Equal(t, true, ok)
|
||||
if !reflect.DeepEqual(altairBlock.AltairBlock, v2Block) {
|
||||
t.Error("Expected blocks to equal")
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -48,11 +48,6 @@ type eth1DataSingleVote struct {
|
||||
blockHeight *big.Int
|
||||
}
|
||||
|
||||
type eth1DataAggregatedVote struct {
|
||||
data eth1DataSingleVote
|
||||
votes int
|
||||
}
|
||||
|
||||
// blockData required to create a beacon block.
|
||||
type blockData struct {
|
||||
ParentRoot []byte
|
||||
@@ -707,23 +702,15 @@ func (vs *Server) validateDepositTrie(trie *trie.SparseMerkleTrie, canonicalEth1
|
||||
}
|
||||
|
||||
// This filters the input attestations to return a list of valid attestations to be packaged inside a beacon block.
|
||||
func (vs *Server) filterAttestationsForBlockInclusion(ctx context.Context, st state.BeaconState, atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "ProposerServer.filterAttestationsForBlockInclusion")
|
||||
func (vs *Server) validateAndDeleteAttsInPool(ctx context.Context, st state.BeaconState, atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "ProposerServer.validateAndDeleteAttsInPool")
|
||||
defer span.End()
|
||||
|
||||
validAtts, invalidAtts := proposerAtts(atts).filter(ctx, st)
|
||||
if err := vs.deleteAttsInPool(ctx, invalidAtts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deduped, err := validAtts.dedup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sorted, err := deduped.sortByProfitability()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sorted.limitToMaxAttestations(), nil
|
||||
return validAtts, nil
|
||||
}
|
||||
|
||||
// The input attestations are processed and seen by the node, this deletes them from pool
|
||||
@@ -766,50 +753,53 @@ func (vs *Server) packAttestations(ctx context.Context, latestState state.Beacon
|
||||
defer span.End()
|
||||
|
||||
atts := vs.AttPool.AggregatedAttestations()
|
||||
atts, err := vs.filterAttestationsForBlockInclusion(ctx, latestState, atts)
|
||||
atts, err := vs.validateAndDeleteAttsInPool(ctx, latestState, atts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not filter attestations")
|
||||
}
|
||||
|
||||
// If there is any room left in the block, consider unaggregated attestations as well.
|
||||
numAtts := uint64(len(atts))
|
||||
if numAtts < params.BeaconConfig().MaxAttestations {
|
||||
uAtts, err := vs.AttPool.UnaggregatedAttestations()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get unaggregated attestations")
|
||||
}
|
||||
uAtts, err = vs.filterAttestationsForBlockInclusion(ctx, latestState, uAtts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not filter attestations")
|
||||
}
|
||||
atts = append(atts, uAtts...)
|
||||
|
||||
attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(atts))
|
||||
for _, att := range atts {
|
||||
attDataRoot, err := att.Data.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att)
|
||||
}
|
||||
|
||||
attsForInclusion := proposerAtts(make([]*ethpb.Attestation, 0))
|
||||
for _, as := range attsByDataRoot {
|
||||
as, err := attaggregation.Aggregate(as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attsForInclusion = append(attsForInclusion, as...)
|
||||
}
|
||||
deduped, err := attsForInclusion.dedup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sorted, err := deduped.sortByProfitability()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
atts = sorted.limitToMaxAttestations()
|
||||
uAtts, err := vs.AttPool.UnaggregatedAttestations()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get unaggregated attestations")
|
||||
}
|
||||
uAtts, err = vs.validateAndDeleteAttsInPool(ctx, latestState, uAtts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not filter attestations")
|
||||
}
|
||||
atts = append(atts, uAtts...)
|
||||
|
||||
// Remove duplicates from both aggregated/unaggregated attestations. This
|
||||
// prevents inefficient aggregates being created.
|
||||
atts, err = proposerAtts(atts).dedup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(atts))
|
||||
for _, att := range atts {
|
||||
attDataRoot, err := att.Data.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att)
|
||||
}
|
||||
|
||||
attsForInclusion := proposerAtts(make([]*ethpb.Attestation, 0))
|
||||
for _, as := range attsByDataRoot {
|
||||
as, err := attaggregation.Aggregate(as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attsForInclusion = append(attsForInclusion, as...)
|
||||
}
|
||||
deduped, err := attsForInclusion.dedup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sorted, err := deduped.sortByProfitability()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
atts = sorted.limitToMaxAttestations()
|
||||
return atts, nil
|
||||
}
|
||||
|
||||
@@ -1874,7 +1874,7 @@ func TestProposer_FilterAttestation(t *testing.T) {
|
||||
HeadFetcher: &mock.ChainService{State: state, Root: genesisRoot[:]},
|
||||
}
|
||||
atts := tt.inputAtts()
|
||||
received, err := proposerServer.filterAttestationsForBlockInclusion(context.Background(), state, atts)
|
||||
received, err := proposerServer.validateAndDeleteAttsInPool(context.Background(), state, atts)
|
||||
if tt.wantedErr != "" {
|
||||
assert.ErrorContains(t, tt.wantedErr, err)
|
||||
assert.Equal(t, nil, received)
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -4,38 +4,52 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"chunks.go",
|
||||
"detect_attestations.go",
|
||||
"detect_blocks.go",
|
||||
"doc.go",
|
||||
"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",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/slasher/simulator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/core:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/slasher/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/slice:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -43,21 +57,29 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"chunks_test.go",
|
||||
"detect_attestations_test.go",
|
||||
"detect_blocks_test.go",
|
||||
"helpers_test.go",
|
||||
"params_test.go",
|
||||
"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",
|
||||
@@ -65,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",
|
||||
],
|
||||
)
|
||||
|
||||
474
beacon-chain/slasher/detect_attestations.go
Normal file
474
beacon-chain/slasher/detect_attestations.go
Normal file
@@ -0,0 +1,474 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// Takes in a list of indexed attestation wrappers and returns any
|
||||
// found attester slashings to the caller.
|
||||
func (s *Service) checkSlashableAttestations(
|
||||
ctx context.Context, atts []*slashertypes.IndexedAttestationWrapper,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
currentEpoch := slots.EpochsSinceGenesis(s.genesisTime)
|
||||
slashings := make([]*ethpb.AttesterSlashing, 0)
|
||||
indices := make([]types.ValidatorIndex, 0)
|
||||
|
||||
// TODO(#8331): Consider using goroutines and wait groups here.
|
||||
groupedAtts := s.groupByValidatorChunkIndex(atts)
|
||||
for validatorChunkIdx, batch := range groupedAtts {
|
||||
attSlashings, err := s.detectAllAttesterSlashings(ctx, &chunkUpdateArgs{
|
||||
validatorChunkIndex: validatorChunkIdx,
|
||||
currentEpoch: currentEpoch,
|
||||
}, batch)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not detect slashable attestations")
|
||||
}
|
||||
slashings = append(slashings, attSlashings...)
|
||||
indices = append(indices, s.params.validatorIndicesInChunk(validatorChunkIdx)...)
|
||||
}
|
||||
if err := s.serviceCfg.Database.SaveLastEpochWrittenForValidators(ctx, indices, currentEpoch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return slashings, nil
|
||||
}
|
||||
|
||||
// Given a list of attestations all corresponding to a validator chunk index as well
|
||||
// as the current epoch in time, we perform slashing detection.
|
||||
// The process is as follows given a list of attestations:
|
||||
//
|
||||
// 1. Check for attester double votes using the list of attestations.
|
||||
// 2. Group the attestations by chunk index.
|
||||
// 3. Update the min and max spans for those grouped attestations, check if any slashings are
|
||||
// found in the process
|
||||
// 4. Update the latest written epoch for all validators involved to the current epoch.
|
||||
//
|
||||
// This function performs a lot of critical actions and is split into smaller helpers for cleanliness.
|
||||
func (s *Service) detectAllAttesterSlashings(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
attestations []*slashertypes.IndexedAttestationWrapper,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
// Check for double votes.
|
||||
doubleVoteSlashings, err := s.checkDoubleVotes(ctx, attestations)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check slashable double votes")
|
||||
}
|
||||
|
||||
// Group attestations by chunk index.
|
||||
groupedAtts := s.groupByChunkIndex(attestations)
|
||||
|
||||
// Update min and max spans and retrieve any detected slashable offenses.
|
||||
surroundingSlashings, err := s.updateSpans(ctx, &chunkUpdateArgs{
|
||||
kind: slashertypes.MinSpan,
|
||||
validatorChunkIndex: args.validatorChunkIndex,
|
||||
currentEpoch: args.currentEpoch,
|
||||
}, groupedAtts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not update min attestation spans for validator chunk index %d",
|
||||
args.validatorChunkIndex,
|
||||
)
|
||||
}
|
||||
|
||||
surroundedSlashings, err := s.updateSpans(ctx, &chunkUpdateArgs{
|
||||
kind: slashertypes.MaxSpan,
|
||||
validatorChunkIndex: args.validatorChunkIndex,
|
||||
currentEpoch: args.currentEpoch,
|
||||
}, groupedAtts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not update max attestation spans for validator chunk index %d",
|
||||
args.validatorChunkIndex,
|
||||
)
|
||||
}
|
||||
|
||||
// Consolidate all slashings into a slice.
|
||||
slashings := make([]*ethpb.AttesterSlashing, 0, len(doubleVoteSlashings)+len(surroundingSlashings)+len(surroundedSlashings))
|
||||
slashings = append(slashings, doubleVoteSlashings...)
|
||||
slashings = append(slashings, surroundingSlashings...)
|
||||
slashings = append(slashings, surroundedSlashings...)
|
||||
if len(slashings) > 0 {
|
||||
log.WithField("numSlashings", len(slashings)).Info("Slashable attestation offenses found")
|
||||
}
|
||||
return slashings, nil
|
||||
}
|
||||
|
||||
// Check for attester slashing double votes by looking at every single validator index
|
||||
// in each attestation's attesting indices and checking if there already exist records for such
|
||||
// attestation's target epoch. If so, we append a double vote slashing object to a list of slashings
|
||||
// we return to the caller.
|
||||
func (s *Service) checkDoubleVotes(
|
||||
ctx context.Context, attestations []*slashertypes.IndexedAttestationWrapper,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.checkDoubleVotes")
|
||||
defer span.End()
|
||||
// We check if there are any slashable double votes in the input list
|
||||
// of attestations with respect to each other.
|
||||
slashings := make([]*ethpb.AttesterSlashing, 0)
|
||||
existingAtts := make(map[string]*slashertypes.IndexedAttestationWrapper)
|
||||
for _, att := range attestations {
|
||||
for _, valIdx := range att.IndexedAttestation.AttestingIndices {
|
||||
key := uintToString(uint64(att.IndexedAttestation.Data.Target.Epoch)) + ":" + uintToString(valIdx)
|
||||
existingAtt, ok := existingAtts[key]
|
||||
if !ok {
|
||||
existingAtts[key] = att
|
||||
continue
|
||||
}
|
||||
if att.SigningRoot != existingAtt.SigningRoot {
|
||||
doubleVotesTotal.Inc()
|
||||
slashings = append(slashings, ðpb.AttesterSlashing{
|
||||
Attestation_1: existingAtt.IndexedAttestation,
|
||||
Attestation_2: att.IndexedAttestation,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We check if there are any slashable double votes in the input list
|
||||
// of attestations with respect to our database.
|
||||
moreSlashings, err := s.checkDoubleVotesOnDisk(ctx, attestations)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check attestation double votes on disk")
|
||||
}
|
||||
return append(slashings, moreSlashings...), nil
|
||||
}
|
||||
|
||||
// Check for double votes in our database given a list of incoming attestations.
|
||||
func (s *Service) checkDoubleVotesOnDisk(
|
||||
ctx context.Context, attestations []*slashertypes.IndexedAttestationWrapper,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.checkDoubleVotesOnDisk")
|
||||
defer span.End()
|
||||
doubleVotes, err := s.serviceCfg.Database.CheckAttesterDoubleVotes(
|
||||
ctx, attestations,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve potential double votes from disk")
|
||||
}
|
||||
doubleVoteSlashings := make([]*ethpb.AttesterSlashing, 0)
|
||||
for _, doubleVote := range doubleVotes {
|
||||
doubleVotesTotal.Inc()
|
||||
doubleVoteSlashings = append(doubleVoteSlashings, ðpb.AttesterSlashing{
|
||||
Attestation_1: doubleVote.PrevAttestationWrapper.IndexedAttestation,
|
||||
Attestation_2: doubleVote.AttestationWrapper.IndexedAttestation,
|
||||
})
|
||||
}
|
||||
return doubleVoteSlashings, nil
|
||||
}
|
||||
|
||||
// Updates spans and detects any slashable attester offenses along the way.
|
||||
// 1. Determine the chunks we need to use for updating for the validator indices
|
||||
// in a validator chunk index, then retrieve those chunks from the database.
|
||||
// 2. Using the chunks from step (1):
|
||||
// for every attestation by chunk index:
|
||||
// for each validator in the attestation's attesting indices:
|
||||
// - Check if the attestation is slashable, if so return a slashing object.
|
||||
// 3. Save the updated chunks to disk.
|
||||
func (s *Service) updateSpans(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
attestationsByChunkIdx map[uint64][]*slashertypes.IndexedAttestationWrapper,
|
||||
) ([]*ethpb.AttesterSlashing, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.updateSpans")
|
||||
defer span.End()
|
||||
// Determine the chunk indices we need to use for slashing detection.
|
||||
validatorIndices := s.params.validatorIndicesInChunk(args.validatorChunkIndex)
|
||||
chunkIndices, err := s.determineChunksToUpdateForValidators(ctx, args, validatorIndices)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not determine chunks to update for validator indices %v",
|
||||
validatorIndices,
|
||||
)
|
||||
}
|
||||
// Load the required chunks from disk.
|
||||
chunksByChunkIdx, err := s.loadChunks(ctx, args, chunkIndices)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not load chunks for chunk indices %v",
|
||||
chunkIndices,
|
||||
)
|
||||
}
|
||||
|
||||
// Apply the attestations to the related chunks and find any
|
||||
// slashings along the way.
|
||||
slashings := make([]*ethpb.AttesterSlashing, 0)
|
||||
for _, attestationBatch := range attestationsByChunkIdx {
|
||||
for _, att := range attestationBatch {
|
||||
for _, validatorIdx := range att.IndexedAttestation.AttestingIndices {
|
||||
validatorIndex := types.ValidatorIndex(validatorIdx)
|
||||
computedValidatorChunkIdx := s.params.validatorChunkIndex(validatorIndex)
|
||||
|
||||
// Every validator chunk index represents a range of validators.
|
||||
// If it possible that the validator index in this loop iteration is
|
||||
// not part of the validator chunk index we are updating chunks for.
|
||||
//
|
||||
// For example, if there are 4 validators per validator chunk index,
|
||||
// then validator chunk index 0 contains validator indices [0, 1, 2, 3]
|
||||
// If we see an attestation with attesting indices [3, 4, 5] and we are updating
|
||||
// chunks for validator chunk index 0, only validator index 3 should make
|
||||
// it past this line.
|
||||
if args.validatorChunkIndex != computedValidatorChunkIdx {
|
||||
continue
|
||||
}
|
||||
slashing, err := s.applyAttestationForValidator(
|
||||
ctx,
|
||||
args,
|
||||
validatorIndex,
|
||||
chunksByChunkIdx,
|
||||
att,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not apply attestation for validator index %d",
|
||||
validatorIndex,
|
||||
)
|
||||
}
|
||||
if slashing != nil {
|
||||
slashings = append(slashings, slashing)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write the updated chunks to disk.
|
||||
return slashings, s.saveUpdatedChunks(ctx, args, chunksByChunkIdx)
|
||||
}
|
||||
|
||||
// For a list of validator indices, we retrieve their latest written epoch. Then, for each
|
||||
// (validator, latest epoch written) pair, we determine the chunks we need to update and
|
||||
// perform slashing detection with.
|
||||
func (s *Service) determineChunksToUpdateForValidators(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
validatorIndices []types.ValidatorIndex,
|
||||
) (chunkIndices []uint64, err error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.determineChunksToUpdateForValidators")
|
||||
defer span.End()
|
||||
lastCurrentEpochs, err := s.serviceCfg.Database.LastEpochWrittenForValidators(ctx, validatorIndices)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "could not get latest epoch attested for validators")
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize the last epoch written for each validator to 0.
|
||||
lastCurrentEpochByValidator := make(map[types.ValidatorIndex]types.Epoch, len(validatorIndices))
|
||||
for _, valIdx := range validatorIndices {
|
||||
lastCurrentEpochByValidator[valIdx] = 0
|
||||
}
|
||||
for _, lastEpoch := range lastCurrentEpochs {
|
||||
lastCurrentEpochByValidator[lastEpoch.ValidatorIndex] = lastEpoch.Epoch
|
||||
}
|
||||
|
||||
// For every single validator and their last written current epoch, we determine
|
||||
// the chunk indices we need to update based on all the chunks between the last
|
||||
// epoch written and the current epoch, inclusive.
|
||||
chunkIndicesToUpdate := make(map[uint64]bool)
|
||||
|
||||
for _, epoch := range lastCurrentEpochByValidator {
|
||||
latestEpochWritten := epoch
|
||||
for latestEpochWritten <= args.currentEpoch {
|
||||
chunkIdx := s.params.chunkIndex(latestEpochWritten)
|
||||
chunkIndicesToUpdate[chunkIdx] = true
|
||||
latestEpochWritten++
|
||||
}
|
||||
}
|
||||
chunkIndices = make([]uint64, 0, len(chunkIndicesToUpdate))
|
||||
for chunkIdx := range chunkIndicesToUpdate {
|
||||
chunkIndices = append(chunkIndices, chunkIdx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Checks if an incoming attestation is slashable based on the validator chunk it
|
||||
// corresponds to. If a slashable offense is found, we return it to the caller.
|
||||
// If not, then update every single chunk the attestation covers, starting from its
|
||||
// source epoch up to its target.
|
||||
func (s *Service) applyAttestationForValidator(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
validatorIndex types.ValidatorIndex,
|
||||
chunksByChunkIdx map[uint64]Chunker,
|
||||
attestation *slashertypes.IndexedAttestationWrapper,
|
||||
) (*ethpb.AttesterSlashing, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.applyAttestationForValidator")
|
||||
defer span.End()
|
||||
sourceEpoch := attestation.IndexedAttestation.Data.Source.Epoch
|
||||
targetEpoch := attestation.IndexedAttestation.Data.Target.Epoch
|
||||
|
||||
attestationDistance.Observe(float64(targetEpoch) - float64(sourceEpoch))
|
||||
|
||||
chunkIdx := s.params.chunkIndex(sourceEpoch)
|
||||
chunk, err := s.getChunk(ctx, args, chunksByChunkIdx, chunkIdx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get chunk at index %d", chunkIdx)
|
||||
}
|
||||
|
||||
// Check slashable, if so, return the slashing.
|
||||
slashing, err := chunk.CheckSlashable(
|
||||
ctx,
|
||||
s.serviceCfg.Database,
|
||||
validatorIndex,
|
||||
attestation,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not check if attestation for validator index %d is slashable",
|
||||
validatorIndex,
|
||||
)
|
||||
}
|
||||
if slashing != nil {
|
||||
return slashing, nil
|
||||
}
|
||||
|
||||
// Get the first start epoch for the chunk. If it does not exist or
|
||||
// is not possible based on the input arguments, do not continue with the update.
|
||||
startEpoch, exists := chunk.StartEpoch(sourceEpoch, args.currentEpoch)
|
||||
if !exists {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Given a single attestation could span across multiple chunks
|
||||
// for a validator min or max span, we attempt to update the current chunk
|
||||
// for the source epoch of the attestation. If the update function tells
|
||||
// us we need to proceed to the next chunk, we continue by determining
|
||||
// the start epoch of the next chunk. We exit once no longer need to
|
||||
// keep updating chunks.
|
||||
for {
|
||||
chunkIdx = s.params.chunkIndex(startEpoch)
|
||||
chunk, err := s.getChunk(ctx, args, chunksByChunkIdx, chunkIdx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get chunk at index %d", chunkIdx)
|
||||
}
|
||||
keepGoing, err := chunk.Update(
|
||||
&chunkUpdateArgs{
|
||||
chunkIndex: chunkIdx,
|
||||
currentEpoch: args.currentEpoch,
|
||||
},
|
||||
validatorIndex,
|
||||
startEpoch,
|
||||
targetEpoch,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not update chunk at chunk index %d for validator index %d and current epoch %d",
|
||||
chunkIdx,
|
||||
validatorIndex,
|
||||
args.currentEpoch,
|
||||
)
|
||||
}
|
||||
// We update the chunksByChunkIdx map with the chunk we just updated.
|
||||
chunksByChunkIdx[chunkIdx] = chunk
|
||||
if !keepGoing {
|
||||
break
|
||||
}
|
||||
// Move to first epoch of next chunk if needed.
|
||||
startEpoch = chunk.NextChunkStartEpoch(startEpoch)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Retrieves a chunk at a chunk index from a map. If such chunk does not exist, which
|
||||
// should be rare (occurring when we receive an attestation with source and target epochs
|
||||
// that span multiple chunk indices), then we fallback to fetching from disk.
|
||||
func (s *Service) getChunk(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
chunksByChunkIdx map[uint64]Chunker,
|
||||
chunkIdx uint64,
|
||||
) (Chunker, error) {
|
||||
chunk, ok := chunksByChunkIdx[chunkIdx]
|
||||
if ok {
|
||||
return chunk, nil
|
||||
}
|
||||
// We can ensure we load the appropriate chunk we need by fetching from the DB.
|
||||
diskChunks, err := s.loadChunks(ctx, args, []uint64{chunkIdx})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not load chunk at index %d", chunkIdx)
|
||||
}
|
||||
if chunk, ok := diskChunks[chunkIdx]; ok {
|
||||
return chunk, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not retrieve chunk at chunk index %d from disk", chunkIdx)
|
||||
}
|
||||
|
||||
// Load chunks for a specified list of chunk indices. We attempt to load it from the database.
|
||||
// If the data exists, then we initialize a chunk of a specified kind. Otherwise, we create
|
||||
// an empty chunk, add it to our map, and then return it to the caller.
|
||||
func (s *Service) loadChunks(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
chunkIndices []uint64,
|
||||
) (map[uint64]Chunker, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.loadChunks")
|
||||
defer span.End()
|
||||
chunkKeys := make([][]byte, 0, len(chunkIndices))
|
||||
for _, chunkIdx := range chunkIndices {
|
||||
chunkKeys = append(chunkKeys, s.params.flatSliceID(args.validatorChunkIndex, chunkIdx))
|
||||
}
|
||||
rawChunks, chunksExist, err := s.serviceCfg.Database.LoadSlasherChunks(ctx, args.kind, chunkKeys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err,
|
||||
"could not load slasher chunk index",
|
||||
)
|
||||
}
|
||||
chunksByChunkIdx := make(map[uint64]Chunker, len(rawChunks))
|
||||
for i := 0; i < len(rawChunks); i++ {
|
||||
// If the chunk exists in the database, we initialize it from the raw bytes data.
|
||||
// If it does not exist, we initialize an empty chunk.
|
||||
var chunk Chunker
|
||||
switch args.kind {
|
||||
case slashertypes.MinSpan:
|
||||
if chunksExist[i] {
|
||||
chunk, err = MinChunkSpansSliceFrom(s.params, rawChunks[i])
|
||||
} else {
|
||||
chunk = EmptyMinSpanChunksSlice(s.params)
|
||||
}
|
||||
case slashertypes.MaxSpan:
|
||||
if chunksExist[i] {
|
||||
chunk, err = MaxChunkSpansSliceFrom(s.params, rawChunks[i])
|
||||
} else {
|
||||
chunk = EmptyMaxSpanChunksSlice(s.params)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not initialize chunk")
|
||||
}
|
||||
chunksByChunkIdx[chunkIndices[i]] = chunk
|
||||
}
|
||||
return chunksByChunkIdx, nil
|
||||
}
|
||||
|
||||
// Saves updated chunks to disk given the required database schema.
|
||||
func (s *Service) saveUpdatedChunks(
|
||||
ctx context.Context,
|
||||
args *chunkUpdateArgs,
|
||||
updatedChunksByChunkIdx map[uint64]Chunker,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "Slasher.saveUpdatedChunks")
|
||||
defer span.End()
|
||||
chunkKeys := make([][]byte, 0, len(updatedChunksByChunkIdx))
|
||||
chunks := make([][]uint16, 0, len(updatedChunksByChunkIdx))
|
||||
for chunkIdx, chunk := range updatedChunksByChunkIdx {
|
||||
chunkKeys = append(chunkKeys, s.params.flatSliceID(args.validatorChunkIndex, chunkIdx))
|
||||
chunks = append(chunks, chunk.Chunk())
|
||||
}
|
||||
chunksSavedTotal.Add(float64(len(chunks)))
|
||||
return s.serviceCfg.Database.SaveSlasherChunks(ctx, args.kind, chunkKeys, chunks)
|
||||
}
|
||||
917
beacon-chain/slasher/detect_attestations_test.go
Normal file
917
beacon-chain/slasher/detect_attestations_test.go
Normal file
@@ -0,0 +1,917 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
|
||||
dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func Test_processQueuedAttestations(t *testing.T) {
|
||||
type args struct {
|
||||
attestationQueue []*slashertypes.IndexedAttestationWrapper
|
||||
currentEpoch types.Epoch
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
shouldNotBeSlashable bool
|
||||
}{
|
||||
{
|
||||
name: "Detects surrounding vote (source 1, target 2), (source 0, target 3)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Detects surrounding vote (source 50, target 51), (source 0, target 1000)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 50, 51, []uint64{0}, nil),
|
||||
createAttestationWrapper(t, 0, 1000, []uint64{0}, nil),
|
||||
},
|
||||
currentEpoch: 1000,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Detects surrounded vote (source 0, target 3), (source 1, target 2)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Detects double vote, (source 1, target 2), (source 0, target 2)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 0, 2, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Not slashable, surrounding but non-overlapping attesting indices within same validator chunk index",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 1, 2, []uint64{0}, nil),
|
||||
createAttestationWrapper(t, 0, 3, []uint64{1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, surrounded but non-overlapping attesting indices within same validator chunk index",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 1, 2, []uint64{2, 3}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, surrounding but non-overlapping attesting indices in different validator chunk index",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0}, nil),
|
||||
createAttestationWrapper(
|
||||
t,
|
||||
1,
|
||||
2,
|
||||
[]uint64{params.BeaconConfig().MinGenesisActiveValidatorCount - 1},
|
||||
nil,
|
||||
),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, surrounded but non-overlapping attesting indices in different validator chunk index",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0}, nil),
|
||||
createAttestationWrapper(
|
||||
t,
|
||||
1,
|
||||
2,
|
||||
[]uint64{params.BeaconConfig().MinGenesisActiveValidatorCount - 1},
|
||||
nil,
|
||||
),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, (source 1, target 2), (source 2, target 3)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 2, 3, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, (source 0, target 3), (source 2, target 4)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 2, 4, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, (source 0, target 2), (source 0, target 3)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 2, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
{
|
||||
name: "Not slashable, (source 0, target 3), (source 0, target 2)",
|
||||
args: args{
|
||||
attestationQueue: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil),
|
||||
createAttestationWrapper(t, 0, 2, []uint64{0, 1}, nil),
|
||||
},
|
||||
currentEpoch: 4,
|
||||
},
|
||||
shouldNotBeSlashable: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
defer hook.Reset()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
currentTime := time.Now()
|
||||
totalSlots := uint64(tt.args.currentEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot)
|
||||
genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second)
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
slot, err := core.StartSlot(tt.args.currentEpoch)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetSlot(slot))
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
Slot: &slot,
|
||||
}
|
||||
|
||||
// Initialize validators in the state.
|
||||
numVals := params.BeaconConfig().MinGenesisActiveValidatorCount
|
||||
validators := make([]*ethpb.Validator, numVals)
|
||||
privKeys := make([]bls.SecretKey, numVals)
|
||||
for i := range validators {
|
||||
privKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
privKeys[i] = privKey
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: privKey.PublicKey().Marshal(),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
}
|
||||
}
|
||||
err = beaconState.SetValidators(validators)
|
||||
require.NoError(t, err)
|
||||
domain, err := signing.Domain(
|
||||
beaconState.Fork(),
|
||||
0,
|
||||
params.BeaconConfig().DomainBeaconAttester,
|
||||
beaconState.GenesisValidatorRoot(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create valid signatures for all input attestations in the test.
|
||||
for _, attestationWrapper := range tt.args.attestationQueue {
|
||||
signingRoot, err := signing.ComputeSigningRoot(attestationWrapper.IndexedAttestation.Data, domain)
|
||||
require.NoError(t, err)
|
||||
attestingIndices := attestationWrapper.IndexedAttestation.AttestingIndices
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, validatorIndex := range attestingIndices {
|
||||
privKey := privKeys[validatorIndex]
|
||||
sigs[i] = privKey.Sign(signingRoot[:])
|
||||
}
|
||||
attestationWrapper.IndexedAttestation.Signature = bls.AggregateSignatures(sigs).Marshal()
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
AttestationStateFetcher: mockChain,
|
||||
SlashingPoolInserter: &slashings.PoolMock{},
|
||||
},
|
||||
params: DefaultParams(),
|
||||
attsQueue: newAttestationsQueue(),
|
||||
genesisTime: genesisTime,
|
||||
}
|
||||
currentSlotChan := make(chan types.Slot)
|
||||
exitChan := make(chan struct{})
|
||||
go func() {
|
||||
s.processQueuedAttestations(ctx, currentSlotChan)
|
||||
exitChan <- struct{}{}
|
||||
}()
|
||||
s.attsQueue.extend(tt.args.attestationQueue)
|
||||
currentSlotChan <- slot
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
cancel()
|
||||
<-exitChan
|
||||
if tt.shouldNotBeSlashable {
|
||||
require.LogsDoNotContain(t, hook, "Attester slashing detected")
|
||||
} else {
|
||||
require.LogsContain(t, hook, "Attester slashing detected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_processQueuedAttestations_MultipleChunkIndices(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
defer hook.Reset()
|
||||
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
slasherParams := DefaultParams()
|
||||
|
||||
// We process submit attestations from chunk index 0 to chunk index 1.
|
||||
// What we want to test here is if we can proceed
|
||||
// with processing queued attestations once the chunk index changes.
|
||||
// For example, epochs 0 - 15 are chunk 0, epochs 16 - 31 are chunk 1, etc.
|
||||
startEpoch := types.Epoch(slasherParams.chunkSize)
|
||||
endEpoch := types.Epoch(slasherParams.chunkSize + 1)
|
||||
|
||||
currentTime := time.Now()
|
||||
totalSlots := uint64(startEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot)
|
||||
genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second)
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
AttestationStateFetcher: mockChain,
|
||||
SlashingPoolInserter: &slashings.PoolMock{},
|
||||
},
|
||||
params: slasherParams,
|
||||
attsQueue: newAttestationsQueue(),
|
||||
genesisTime: genesisTime,
|
||||
}
|
||||
currentSlotChan := make(chan types.Slot)
|
||||
exitChan := make(chan struct{})
|
||||
go func() {
|
||||
s.processQueuedAttestations(ctx, currentSlotChan)
|
||||
exitChan <- struct{}{}
|
||||
}()
|
||||
|
||||
for i := startEpoch; i <= endEpoch; i++ {
|
||||
source := types.Epoch(0)
|
||||
target := types.Epoch(0)
|
||||
if i != 0 {
|
||||
source = i - 1
|
||||
target = i
|
||||
}
|
||||
var sr [32]byte
|
||||
copy(sr[:], fmt.Sprintf("%d", i))
|
||||
att := createAttestationWrapper(t, source, target, []uint64{0}, sr[:])
|
||||
s.attsQueue = newAttestationsQueue()
|
||||
s.attsQueue.push(att)
|
||||
slot, err := core.StartSlot(i)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, mockChain.State.SetSlot(slot))
|
||||
s.serviceCfg.HeadStateFetcher = mockChain
|
||||
currentSlotChan <- slot
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
cancel()
|
||||
<-exitChan
|
||||
require.LogsDoNotContain(t, hook, "Slashable offenses found")
|
||||
require.LogsDoNotContain(t, hook, "Could not detect")
|
||||
}
|
||||
|
||||
func Test_processQueuedAttestations_OverlappingChunkIndices(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
defer hook.Reset()
|
||||
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
slasherParams := DefaultParams()
|
||||
|
||||
startEpoch := types.Epoch(slasherParams.chunkSize)
|
||||
|
||||
currentTime := time.Now()
|
||||
totalSlots := uint64(startEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot)
|
||||
genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second)
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
AttestationStateFetcher: mockChain,
|
||||
SlashingPoolInserter: &slashings.PoolMock{},
|
||||
},
|
||||
params: slasherParams,
|
||||
attsQueue: newAttestationsQueue(),
|
||||
genesisTime: genesisTime,
|
||||
}
|
||||
currentSlotChan := make(chan types.Slot)
|
||||
exitChan := make(chan struct{})
|
||||
go func() {
|
||||
s.processQueuedAttestations(ctx, currentSlotChan)
|
||||
exitChan <- struct{}{}
|
||||
}()
|
||||
|
||||
// We create two attestations fully spanning chunk indices 0 and chunk 1
|
||||
att1 := createAttestationWrapper(t, types.Epoch(slasherParams.chunkSize-2), types.Epoch(slasherParams.chunkSize), []uint64{0, 1}, nil)
|
||||
att2 := createAttestationWrapper(t, types.Epoch(slasherParams.chunkSize-1), types.Epoch(slasherParams.chunkSize+1), []uint64{0, 1}, nil)
|
||||
|
||||
// We attempt to process the batch.
|
||||
s.attsQueue = newAttestationsQueue()
|
||||
s.attsQueue.push(att1)
|
||||
s.attsQueue.push(att2)
|
||||
slot, err := core.StartSlot(att2.IndexedAttestation.Data.Target.Epoch)
|
||||
require.NoError(t, err)
|
||||
mockChain.Slot = &slot
|
||||
s.serviceCfg.HeadStateFetcher = mockChain
|
||||
currentSlotChan <- slot
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
cancel()
|
||||
<-exitChan
|
||||
require.LogsDoNotContain(t, hook, "Slashable offenses found")
|
||||
require.LogsDoNotContain(t, hook, "Could not detect")
|
||||
}
|
||||
|
||||
func Test_determineChunksToUpdateForValidators_FromLatestWrittenEpoch(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Check if the chunk at chunk index already exists in-memory.
|
||||
s := &Service{
|
||||
params: &Parameters{
|
||||
chunkSize: 2, // 2 epochs in a chunk.
|
||||
validatorChunkSize: 2, // 2 validators in a chunk.
|
||||
historyLength: 4,
|
||||
},
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
}
|
||||
validators := []types.ValidatorIndex{
|
||||
1, 2,
|
||||
}
|
||||
currentEpoch := types.Epoch(3)
|
||||
|
||||
// Set the latest written epoch for validators to current epoch - 1.
|
||||
latestWrittenEpoch := currentEpoch - 1
|
||||
err := slasherDB.SaveLastEpochWrittenForValidators(ctx, validators, latestWrittenEpoch)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Because the validators have no recorded latest epoch written in the database,
|
||||
// Because the latest written epoch for the input validators is == 2, we expect
|
||||
// that we will update all epochs from 2 up to 3 (the current epoch). This is all
|
||||
// safe contained in chunk index 1.
|
||||
chunkIndices, err := s.determineChunksToUpdateForValidators(
|
||||
ctx,
|
||||
&chunkUpdateArgs{
|
||||
currentEpoch: currentEpoch,
|
||||
},
|
||||
validators,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []uint64{1}, chunkIndices)
|
||||
}
|
||||
|
||||
func Test_determineChunksToUpdateForValidators_FromGenesis(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Check if the chunk at chunk index already exists in-memory.
|
||||
s := &Service{
|
||||
params: &Parameters{
|
||||
chunkSize: 2, // 2 epochs in a chunk.
|
||||
validatorChunkSize: 2, // 2 validators in a chunk.
|
||||
historyLength: 4,
|
||||
},
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
}
|
||||
validators := []types.ValidatorIndex{
|
||||
1, 2,
|
||||
}
|
||||
// Because the validators have no recorded latest epoch written in the database,
|
||||
// we expect that we will update all epochs from genesis up to the current epoch.
|
||||
// Given the chunk size is 2 epochs per chunk, updating with current epoch == 3
|
||||
// will mean that we should be updating from epoch 0 to 3, meaning chunk indices 0 and 1.
|
||||
chunkIndices, err := s.determineChunksToUpdateForValidators(
|
||||
ctx,
|
||||
&chunkUpdateArgs{
|
||||
currentEpoch: 3,
|
||||
},
|
||||
validators,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
sort.Slice(chunkIndices, func(i, j int) bool {
|
||||
return chunkIndices[i] < chunkIndices[j]
|
||||
})
|
||||
require.DeepEqual(t, []uint64{0, 1}, chunkIndices)
|
||||
}
|
||||
|
||||
func Test_applyAttestationForValidator_MinSpanChunk(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
params := DefaultParams()
|
||||
srv := &Service{
|
||||
params: params,
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
}
|
||||
// We initialize an empty chunks slice.
|
||||
chunk := EmptyMinSpanChunksSlice(params)
|
||||
chunkIdx := uint64(0)
|
||||
currentEpoch := types.Epoch(3)
|
||||
validatorIdx := types.ValidatorIndex(0)
|
||||
args := &chunkUpdateArgs{
|
||||
chunkIndex: chunkIdx,
|
||||
currentEpoch: currentEpoch,
|
||||
}
|
||||
chunksByChunkIdx := map[uint64]Chunker{
|
||||
chunkIdx: chunk,
|
||||
}
|
||||
|
||||
// We apply attestation with (source 1, target 2) for our validator.
|
||||
source := types.Epoch(1)
|
||||
target := types.Epoch(2)
|
||||
att := createAttestationWrapper(t, source, target, nil, nil)
|
||||
slashing, err := srv.applyAttestationForValidator(
|
||||
ctx,
|
||||
args,
|
||||
validatorIdx,
|
||||
chunksByChunkIdx,
|
||||
att,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, slashing == nil)
|
||||
att.IndexedAttestation.AttestingIndices = []uint64{uint64(validatorIdx)}
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(
|
||||
ctx,
|
||||
[]*slashertypes.IndexedAttestationWrapper{att},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Next, we apply an attestation with (source 0, target 3) and
|
||||
// expect a slashable offense to be returned.
|
||||
source = types.Epoch(0)
|
||||
target = types.Epoch(3)
|
||||
slashableAtt := createAttestationWrapper(t, source, target, nil, nil)
|
||||
slashing, err = srv.applyAttestationForValidator(
|
||||
ctx,
|
||||
args,
|
||||
validatorIdx,
|
||||
chunksByChunkIdx,
|
||||
slashableAtt,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, slashing)
|
||||
}
|
||||
|
||||
func Test_applyAttestationForValidator_MaxSpanChunk(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
params := DefaultParams()
|
||||
srv := &Service{
|
||||
params: params,
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
}
|
||||
// We initialize an empty chunks slice.
|
||||
chunk := EmptyMaxSpanChunksSlice(params)
|
||||
chunkIdx := uint64(0)
|
||||
currentEpoch := types.Epoch(3)
|
||||
validatorIdx := types.ValidatorIndex(0)
|
||||
args := &chunkUpdateArgs{
|
||||
chunkIndex: chunkIdx,
|
||||
currentEpoch: currentEpoch,
|
||||
}
|
||||
chunksByChunkIdx := map[uint64]Chunker{
|
||||
chunkIdx: chunk,
|
||||
}
|
||||
|
||||
// We apply attestation with (source 0, target 3) for our validator.
|
||||
source := types.Epoch(0)
|
||||
target := types.Epoch(3)
|
||||
att := createAttestationWrapper(t, source, target, nil, nil)
|
||||
slashing, err := srv.applyAttestationForValidator(
|
||||
ctx,
|
||||
args,
|
||||
validatorIdx,
|
||||
chunksByChunkIdx,
|
||||
att,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, slashing == nil)
|
||||
att.IndexedAttestation.AttestingIndices = []uint64{uint64(validatorIdx)}
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(
|
||||
ctx,
|
||||
[]*slashertypes.IndexedAttestationWrapper{att},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Next, we apply an attestation with (source 1, target 2) and
|
||||
// expect a slashable offense to be returned.
|
||||
source = types.Epoch(1)
|
||||
target = types.Epoch(2)
|
||||
slashableAtt := createAttestationWrapper(t, source, target, nil, nil)
|
||||
slashing, err = srv.applyAttestationForValidator(
|
||||
ctx,
|
||||
args,
|
||||
validatorIdx,
|
||||
chunksByChunkIdx,
|
||||
slashableAtt,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, slashing)
|
||||
}
|
||||
|
||||
func Test_checkDoubleVotes_SlashableInputAttestations(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx := context.Background()
|
||||
// For a list of input attestations, check that we can
|
||||
// indeed check there could exist a double vote offense
|
||||
// within the list with respect to other entries in the list.
|
||||
atts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 1, []uint64{1, 2}, []byte{1}),
|
||||
createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}),
|
||||
createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}), // Different signing root.
|
||||
}
|
||||
srv := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
params: DefaultParams(),
|
||||
}
|
||||
prev1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1})
|
||||
cur1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2})
|
||||
prev2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1})
|
||||
cur2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2})
|
||||
wanted := []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: prev1.IndexedAttestation,
|
||||
Attestation_2: cur1.IndexedAttestation,
|
||||
},
|
||||
{
|
||||
Attestation_1: prev2.IndexedAttestation,
|
||||
Attestation_2: cur2.IndexedAttestation,
|
||||
},
|
||||
}
|
||||
slashings, err := srv.checkDoubleVotes(ctx, atts)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, wanted, slashings)
|
||||
}
|
||||
|
||||
func Test_checkDoubleVotes_SlashableAttestationsOnDisk(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx := context.Background()
|
||||
// For a list of input attestations, check that we can
|
||||
// indeed check there could exist a double vote offense
|
||||
// within the list with respect to previous entries in the db.
|
||||
prevAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 1, []uint64{1, 2}, []byte{1}),
|
||||
createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}),
|
||||
}
|
||||
srv := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
params: DefaultParams(),
|
||||
}
|
||||
err := slasherDB.SaveAttestationRecordsForValidators(ctx, prevAtts)
|
||||
require.NoError(t, err)
|
||||
|
||||
prev1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1})
|
||||
cur1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2})
|
||||
prev2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1})
|
||||
cur2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2})
|
||||
wanted := []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: prev1.IndexedAttestation,
|
||||
Attestation_2: cur1.IndexedAttestation,
|
||||
},
|
||||
{
|
||||
Attestation_1: prev2.IndexedAttestation,
|
||||
Attestation_2: cur2.IndexedAttestation,
|
||||
},
|
||||
}
|
||||
newAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}), // Different signing root.
|
||||
}
|
||||
slashings, err := srv.checkDoubleVotes(ctx, newAtts)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, wanted, slashings)
|
||||
}
|
||||
|
||||
func Test_loadChunks_MinSpans(t *testing.T) {
|
||||
testLoadChunks(t, slashertypes.MinSpan)
|
||||
}
|
||||
|
||||
func Test_loadChunks_MaxSpans(t *testing.T) {
|
||||
testLoadChunks(t, slashertypes.MaxSpan)
|
||||
}
|
||||
|
||||
func testLoadChunks(t *testing.T, kind slashertypes.ChunkKind) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Check if the chunk at chunk index already exists in-memory.
|
||||
params := DefaultParams()
|
||||
s := &Service{
|
||||
params: DefaultParams(),
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
},
|
||||
}
|
||||
// If a chunk at a chunk index does not exist, ensure it
|
||||
// is initialized as an empty chunk.
|
||||
var emptyChunk Chunker
|
||||
if kind == slashertypes.MinSpan {
|
||||
emptyChunk = EmptyMinSpanChunksSlice(params)
|
||||
} else {
|
||||
emptyChunk = EmptyMaxSpanChunksSlice(params)
|
||||
}
|
||||
chunkIdx := uint64(2)
|
||||
received, err := s.loadChunks(ctx, &chunkUpdateArgs{
|
||||
validatorChunkIndex: 0,
|
||||
kind: kind,
|
||||
}, []uint64{chunkIdx})
|
||||
require.NoError(t, err)
|
||||
wanted := map[uint64]Chunker{
|
||||
chunkIdx: emptyChunk,
|
||||
}
|
||||
require.DeepEqual(t, wanted, received)
|
||||
|
||||
// Save chunks to disk, then load them properly from disk.
|
||||
var existingChunk Chunker
|
||||
if kind == slashertypes.MinSpan {
|
||||
existingChunk = EmptyMinSpanChunksSlice(params)
|
||||
} else {
|
||||
existingChunk = EmptyMaxSpanChunksSlice(params)
|
||||
}
|
||||
validatorIdx := types.ValidatorIndex(0)
|
||||
epochInChunk := types.Epoch(0)
|
||||
targetEpoch := types.Epoch(2)
|
||||
err = setChunkDataAtEpoch(
|
||||
params,
|
||||
existingChunk.Chunk(),
|
||||
validatorIdx,
|
||||
epochInChunk,
|
||||
targetEpoch,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.DeepNotEqual(t, existingChunk, emptyChunk)
|
||||
|
||||
updatedChunks := map[uint64]Chunker{
|
||||
2: existingChunk,
|
||||
4: existingChunk,
|
||||
6: existingChunk,
|
||||
}
|
||||
err = s.saveUpdatedChunks(
|
||||
ctx,
|
||||
&chunkUpdateArgs{
|
||||
validatorChunkIndex: 0,
|
||||
kind: kind,
|
||||
},
|
||||
updatedChunks,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
// Check if the retrieved chunks match what we just saved to disk.
|
||||
received, err = s.loadChunks(ctx, &chunkUpdateArgs{
|
||||
validatorChunkIndex: 0,
|
||||
kind: kind,
|
||||
}, []uint64{2, 4, 6})
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, updatedChunks, received)
|
||||
}
|
||||
|
||||
func TestService_processQueuedAttestations(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
slot, err := core.StartSlot(1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetSlot(slot))
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
Slot: &slot,
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
params: DefaultParams(),
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
},
|
||||
attsQueue: newAttestationsQueue(),
|
||||
}
|
||||
|
||||
s.attsQueue.extend([]*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(t, 0, 1, []uint64{0, 1} /* indices */, nil /* signingRoot */),
|
||||
})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tickerChan := make(chan types.Slot)
|
||||
exitChan := make(chan struct{})
|
||||
go func() {
|
||||
s.processQueuedAttestations(ctx, tickerChan)
|
||||
exitChan <- struct{}{}
|
||||
}()
|
||||
|
||||
// Send a value over the ticker.
|
||||
tickerChan <- 1
|
||||
cancel()
|
||||
<-exitChan
|
||||
assert.LogsContain(t, hook, "New slot, processing queued")
|
||||
}
|
||||
|
||||
func BenchmarkCheckSlashableAttestations(b *testing.B) {
|
||||
slasherDB := dbtest.SetupSlasherDB(b)
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(b, err)
|
||||
slot := types.Slot(0)
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
Slot: &slot,
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
params: DefaultParams(),
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
},
|
||||
attsQueue: newAttestationsQueue(),
|
||||
}
|
||||
|
||||
b.Run("1 attestation 1 validator", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 1, 1 /* validator */)
|
||||
})
|
||||
b.Run("1 attestation 100 validators", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 1, 100 /* validator */)
|
||||
})
|
||||
b.Run("1 attestation 1000 validators", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 1, 1000 /* validator */)
|
||||
})
|
||||
|
||||
b.Run("100 attestations 1 validator", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 100, 1 /* validator */)
|
||||
})
|
||||
b.Run("100 attestations 100 validators", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 100, 100 /* validator */)
|
||||
})
|
||||
b.Run("100 attestations 1000 validators", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 100, 1000 /* validator */)
|
||||
})
|
||||
|
||||
b.Run("1000 attestations 1 validator", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 1000, 1 /* validator */)
|
||||
})
|
||||
b.Run("1000 attestations 100 validators", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 1000, 100 /* validator */)
|
||||
})
|
||||
b.Run("1000 attestations 1000 validators", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
runAttestationsBenchmark(b, s, 1000, 1000 /* validator */)
|
||||
})
|
||||
}
|
||||
|
||||
func runAttestationsBenchmark(b *testing.B, s *Service, numAtts, numValidators uint64) {
|
||||
indices := make([]uint64, numValidators)
|
||||
for i := uint64(0); i < numValidators; i++ {
|
||||
indices[i] = i
|
||||
}
|
||||
atts := make([]*slashertypes.IndexedAttestationWrapper, numAtts)
|
||||
for i := uint64(0); i < numAtts; i++ {
|
||||
source := types.Epoch(i)
|
||||
target := types.Epoch(i + 1)
|
||||
signingRoot := [32]byte{}
|
||||
copy(signingRoot[:], fmt.Sprintf("%d", i))
|
||||
atts[i] = createAttestationWrapper(
|
||||
b,
|
||||
source,
|
||||
target, /* target */
|
||||
indices, /* indices */
|
||||
signingRoot[:], /* signingRoot */
|
||||
)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
numEpochs := numAtts
|
||||
totalSeconds := numEpochs * uint64(params.BeaconConfig().SlotsPerEpoch) * params.BeaconConfig().SecondsPerSlot
|
||||
genesisTime := time.Now().Add(-time.Second * time.Duration(totalSeconds))
|
||||
s.genesisTime = genesisTime
|
||||
|
||||
_, err := s.checkSlashableAttestations(context.Background(), atts)
|
||||
require.NoError(b, err)
|
||||
}
|
||||
}
|
||||
|
||||
func createAttestationWrapper(t testing.TB, source, target types.Epoch, indices []uint64, signingRoot []byte) *slashertypes.IndexedAttestationWrapper {
|
||||
data := ðpb.AttestationData{
|
||||
BeaconBlockRoot: bytesutil.PadTo(signingRoot, 32),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: source,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: target,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
}
|
||||
signRoot, err := data.HashTreeRoot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &slashertypes.IndexedAttestationWrapper{
|
||||
IndexedAttestation: ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
Data: data,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
},
|
||||
SigningRoot: signRoot,
|
||||
}
|
||||
}
|
||||
92
beacon-chain/slasher/detect_blocks.go
Normal file
92
beacon-chain/slasher/detect_blocks.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// detectProposerSlashings takes in signed block header wrappers and returns a list of proposer slashings detected.
|
||||
func (s *Service) detectProposerSlashings(
|
||||
ctx context.Context,
|
||||
proposedBlocks []*slashertypes.SignedBlockHeaderWrapper,
|
||||
) ([]*ethpb.ProposerSlashing, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "slasher.detectProposerSlashings")
|
||||
defer span.End()
|
||||
// We check if there are any slashable double proposals in the input list
|
||||
// of proposals with respect to each other.
|
||||
slashings := make([]*ethpb.ProposerSlashing, 0)
|
||||
existingProposals := make(map[string]*slashertypes.SignedBlockHeaderWrapper)
|
||||
for i, proposal := range proposedBlocks {
|
||||
key := proposalKey(proposal)
|
||||
existingProposal, ok := existingProposals[key]
|
||||
if !ok {
|
||||
existingProposals[key] = proposal
|
||||
continue
|
||||
}
|
||||
if isDoubleProposal(proposedBlocks[i].SigningRoot, existingProposal.SigningRoot) {
|
||||
doubleProposalsTotal.Inc()
|
||||
slashing := ðpb.ProposerSlashing{
|
||||
Header_1: existingProposal.SignedBeaconBlockHeader,
|
||||
Header_2: proposedBlocks[i].SignedBeaconBlockHeader,
|
||||
}
|
||||
slashings = append(slashings, slashing)
|
||||
}
|
||||
}
|
||||
|
||||
proposerSlashings, err := s.serviceCfg.Database.CheckDoubleBlockProposals(ctx, proposedBlocks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check for double proposals on disk")
|
||||
}
|
||||
if err := s.saveSafeProposals(ctx, proposedBlocks, proposerSlashings); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save safe proposals")
|
||||
}
|
||||
slashings = append(slashings, proposerSlashings...)
|
||||
return slashings, nil
|
||||
}
|
||||
|
||||
// Check for double proposals in our database given a list of incoming block proposals.
|
||||
// For the proposals that were not slashable, we save them to the database.
|
||||
func (s *Service) saveSafeProposals(
|
||||
ctx context.Context,
|
||||
proposedBlocks []*slashertypes.SignedBlockHeaderWrapper,
|
||||
proposerSlashings []*ethpb.ProposerSlashing,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "slasher.saveSafeProposals")
|
||||
defer span.End()
|
||||
return s.serviceCfg.Database.SaveBlockProposals(
|
||||
ctx,
|
||||
filterSafeProposals(proposedBlocks, proposerSlashings),
|
||||
)
|
||||
}
|
||||
|
||||
func filterSafeProposals(
|
||||
proposedBlocks []*slashertypes.SignedBlockHeaderWrapper,
|
||||
proposerSlashings []*ethpb.ProposerSlashing,
|
||||
) []*slashertypes.SignedBlockHeaderWrapper {
|
||||
// We initialize a map of proposers that are safe from slashing.
|
||||
safeProposers := make(map[types.ValidatorIndex]*slashertypes.SignedBlockHeaderWrapper, len(proposedBlocks))
|
||||
for _, proposal := range proposedBlocks {
|
||||
safeProposers[proposal.SignedBeaconBlockHeader.Header.ProposerIndex] = proposal
|
||||
}
|
||||
for _, doubleProposal := range proposerSlashings {
|
||||
// If a proposer is found to have committed a slashable offense, we delete
|
||||
// them from the safe proposers map.
|
||||
delete(safeProposers, doubleProposal.Header_1.Header.ProposerIndex)
|
||||
}
|
||||
// We save all the proposals that are determined "safe" and not-slashable to our database.
|
||||
safeProposals := make([]*slashertypes.SignedBlockHeaderWrapper, 0, len(safeProposers))
|
||||
for _, proposal := range safeProposers {
|
||||
safeProposals = append(safeProposals, proposal)
|
||||
}
|
||||
return safeProposals
|
||||
}
|
||||
|
||||
func proposalKey(proposal *slashertypes.SignedBlockHeaderWrapper) string {
|
||||
header := proposal.SignedBeaconBlockHeader.Header
|
||||
return uintToString(uint64(header.Slot)) + ":" + uintToString(uint64(header.ProposerIndex))
|
||||
}
|
||||
170
beacon-chain/slasher/detect_blocks_test.go
Normal file
170
beacon-chain/slasher/detect_blocks_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
|
||||
dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func Test_processQueuedBlocks_DetectsDoubleProposals(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
beaconDB := dbtest.SetupDB(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Initialize validators in the state.
|
||||
numVals := params.BeaconConfig().MinGenesisActiveValidatorCount
|
||||
validators := make([]*ethpb.Validator, numVals)
|
||||
privKeys := make([]bls.SecretKey, numVals)
|
||||
for i := range validators {
|
||||
privKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
privKeys[i] = privKey
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: privKey.PublicKey().Marshal(),
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
}
|
||||
}
|
||||
err = beaconState.SetValidators(validators)
|
||||
require.NoError(t, err)
|
||||
domain, err := signing.Domain(
|
||||
beaconState.Fork(),
|
||||
0,
|
||||
params.BeaconConfig().DomainBeaconProposer,
|
||||
beaconState.GenesisValidatorRoot(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
mockChain := &mock.ChainService{
|
||||
State: beaconState,
|
||||
}
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
StateGen: stategen.New(beaconDB),
|
||||
SlashingPoolInserter: &slashings.PoolMock{},
|
||||
},
|
||||
params: DefaultParams(),
|
||||
blksQueue: newBlocksQueue(),
|
||||
}
|
||||
|
||||
parentRoot := bytesutil.ToBytes32([]byte("parent"))
|
||||
err = s.serviceCfg.StateGen.SaveState(ctx, parentRoot, beaconState)
|
||||
require.NoError(t, err)
|
||||
|
||||
currentSlotChan := make(chan types.Slot)
|
||||
exitChan := make(chan struct{})
|
||||
go func() {
|
||||
s.processQueuedBlocks(ctx, currentSlotChan)
|
||||
exitChan <- struct{}{}
|
||||
}()
|
||||
|
||||
signedBlkHeaders := []*slashertypes.SignedBlockHeaderWrapper{
|
||||
createProposalWrapper(t, 4, 1, []byte{1}),
|
||||
createProposalWrapper(t, 4, 1, []byte{1}),
|
||||
createProposalWrapper(t, 4, 1, []byte{1}),
|
||||
createProposalWrapper(t, 4, 1, []byte{2}),
|
||||
}
|
||||
|
||||
// Add valid signatures to the block headers we are testing.
|
||||
for _, proposalWrapper := range signedBlkHeaders {
|
||||
proposalWrapper.SignedBeaconBlockHeader.Header.ParentRoot = parentRoot[:]
|
||||
headerHtr, err := proposalWrapper.SignedBeaconBlockHeader.Header.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
container := ðpb.SigningData{
|
||||
ObjectRoot: headerHtr[:],
|
||||
Domain: domain,
|
||||
}
|
||||
signingRoot, err := container.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
privKey := privKeys[proposalWrapper.SignedBeaconBlockHeader.Header.ProposerIndex]
|
||||
proposalWrapper.SignedBeaconBlockHeader.Signature = privKey.Sign(signingRoot[:]).Marshal()
|
||||
}
|
||||
|
||||
s.blksQueue.extend(signedBlkHeaders)
|
||||
|
||||
currentSlot := types.Slot(4)
|
||||
currentSlotChan <- currentSlot
|
||||
cancel()
|
||||
<-exitChan
|
||||
require.LogsContain(t, hook, "Proposer slashing detected")
|
||||
}
|
||||
|
||||
func Test_processQueuedBlocks_NotSlashable(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
serviceCfg: &ServiceConfig{
|
||||
Database: slasherDB,
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
HeadStateFetcher: mockChain,
|
||||
},
|
||||
params: DefaultParams(),
|
||||
blksQueue: newBlocksQueue(),
|
||||
}
|
||||
currentSlotChan := make(chan types.Slot)
|
||||
exitChan := make(chan struct{})
|
||||
go func() {
|
||||
s.processQueuedBlocks(ctx, currentSlotChan)
|
||||
exitChan <- struct{}{}
|
||||
}()
|
||||
s.blksQueue.extend([]*slashertypes.SignedBlockHeaderWrapper{
|
||||
createProposalWrapper(t, 4, 1, []byte{1}),
|
||||
createProposalWrapper(t, 4, 1, []byte{1}),
|
||||
})
|
||||
currentSlotChan <- currentSlot
|
||||
cancel()
|
||||
<-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,
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -515,29 +514,3 @@ func Test_isDoubleProposal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createAttestationWrapper(t testing.TB, source, target types.Epoch, indices []uint64, signingRoot []byte) *slashertypes.IndexedAttestationWrapper {
|
||||
data := ðpb.AttestationData{
|
||||
BeaconBlockRoot: bytesutil.PadTo(signingRoot, 32),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: source,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: target,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
}
|
||||
signRoot, err := data.HashTreeRoot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &slashertypes.IndexedAttestationWrapper{
|
||||
IndexedAttestation: ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
Data: data,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
},
|
||||
SigningRoot: signRoot,
|
||||
}
|
||||
}
|
||||
|
||||
5
beacon-chain/slasher/log.go
Normal file
5
beacon-chain/slasher/log.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package slasher
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
var log = logrus.WithField("prefix", "slasher")
|
||||
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
|
||||
}
|
||||
@@ -110,8 +110,11 @@ func (s *Service) processQueuedAttestations(ctx context.Context, slotTicker <-ch
|
||||
}
|
||||
|
||||
// Check for slashings.
|
||||
// TODO(#8331): Detect slashings.
|
||||
slashings := make([]*ethpb.AttesterSlashing, 0)
|
||||
slashings, err := s.checkSlashableAttestations(ctx, validAtts)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not check slashable attestations")
|
||||
continue
|
||||
}
|
||||
|
||||
// Process attester slashings by verifying their signatures, submitting
|
||||
// to the beacon node's operations pool, and logging them.
|
||||
@@ -148,8 +151,11 @@ func (s *Service) processQueuedBlocks(ctx context.Context, slotTicker <-chan typ
|
||||
|
||||
start := time.Now()
|
||||
// Check for slashings.
|
||||
// TODO(#8331): Detect slashings.
|
||||
slashings := make([]*ethpb.ProposerSlashing, 0)
|
||||
slashings, err := s.detectProposerSlashings(ctx, blocks)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not detect proposer slashings")
|
||||
continue
|
||||
}
|
||||
|
||||
// Process proposer slashings by verifying their signatures, submitting
|
||||
// to the beacon node's operations pool, and logging them.
|
||||
|
||||
@@ -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)))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,157 @@
|
||||
// Package slasher implements slashing detection for eth2, able to catch slashable attestations
|
||||
// and proposals that it receives via two event feeds, respectively. Any found slashings
|
||||
// are then submitted to the beacon node's slashing operations pool. See the design document
|
||||
// here https://hackmd.io/@prysmaticlabs/slasher.
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "slasher")
|
||||
|
||||
// ServiceConfig contains service dependencies for slasher.
|
||||
// ServiceConfig for the slasher service in the beacon node.
|
||||
// This struct allows us to specify required dependencies and
|
||||
// parameters for slasher to function as needed.
|
||||
type ServiceConfig struct {
|
||||
Database db.SlasherDatabase
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
IndexedAttestationsFeed *event.Feed
|
||||
BeaconBlockHeadersFeed *event.Feed
|
||||
StateGen stategen.StateManager
|
||||
SlashingPoolInserter slashings.PoolManager
|
||||
Database db.SlasherDatabase
|
||||
StateNotifier statefeed.Notifier
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
StateGen stategen.StateManager
|
||||
SlashingPoolInserter slashings.PoolInserter
|
||||
HeadStateFetcher blockchain.HeadFetcher
|
||||
SyncChecker sync.Checker
|
||||
}
|
||||
|
||||
// Service for running slasher mode in a beacon node.
|
||||
type Service struct {
|
||||
params *Parameters
|
||||
serviceCfg *ServiceConfig
|
||||
blksQueue *blocksQueue
|
||||
attsQueue *attestationsQueue
|
||||
// SlashingChecker is an interface for defining services that the beacon node may interact with to provide slashing data.
|
||||
type SlashingChecker interface {
|
||||
IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error)
|
||||
IsSlashableAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error)
|
||||
}
|
||||
|
||||
// Service defining a slasher implementation as part of
|
||||
// the beacon node, able to detect eth2 slashable offenses.
|
||||
type Service struct {
|
||||
params *Parameters
|
||||
serviceCfg *ServiceConfig
|
||||
indexedAttsChan chan *ethpb.IndexedAttestation
|
||||
beaconBlockHeadersChan chan *ethpb.SignedBeaconBlockHeader
|
||||
attsQueue *attestationsQueue
|
||||
blksQueue *blocksQueue
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
slotTicker *slots.SlotTicker
|
||||
genesisTime time.Time
|
||||
}
|
||||
|
||||
// New instantiates a new slasher from configuration values.
|
||||
func New(ctx context.Context, srvCfg *ServiceConfig) (*Service, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &Service{
|
||||
params: DefaultParams(),
|
||||
serviceCfg: srvCfg,
|
||||
indexedAttsChan: make(chan *ethpb.IndexedAttestation, 1),
|
||||
beaconBlockHeadersChan: make(chan *ethpb.SignedBeaconBlockHeader, 1),
|
||||
attsQueue: newAttestationsQueue(),
|
||||
blksQueue: newBlocksQueue(),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start listening for received indexed attestations and blocks
|
||||
// and perform slashing detection on them.
|
||||
func (s *Service) Start() {
|
||||
go s.run()
|
||||
}
|
||||
|
||||
func (s *Service) run() {
|
||||
stateChannel := make(chan *feed.Event, 1)
|
||||
stateSub := s.serviceCfg.StateNotifier.StateFeed().Subscribe(stateChannel)
|
||||
stateEvent := <-stateChannel
|
||||
|
||||
// Wait for us to receive the genesis time via a chain started notification.
|
||||
if stateEvent.Type == statefeed.ChainStarted {
|
||||
data, ok := stateEvent.Data.(*statefeed.ChainStartedData)
|
||||
if !ok {
|
||||
log.Error("Could not receive chain start notification, want *statefeed.ChainStartedData")
|
||||
return
|
||||
}
|
||||
s.genesisTime = data.StartTime
|
||||
log.WithField("genesisTime", s.genesisTime).Info("Starting slasher, received chain start event")
|
||||
} else if stateEvent.Type == statefeed.Initialized {
|
||||
// Alternatively, if the chain has already started, we then read the genesis
|
||||
// time value from this data.
|
||||
data, ok := stateEvent.Data.(*statefeed.InitializedData)
|
||||
if !ok {
|
||||
log.Error("Could not receive chain start notification, want *statefeed.ChainStartedData")
|
||||
return
|
||||
}
|
||||
s.genesisTime = data.StartTime
|
||||
log.WithField("genesisTime", s.genesisTime).Info("Starting slasher, chain already initialized")
|
||||
} else {
|
||||
// This should not happen.
|
||||
log.Error("Could start slasher, could not receive chain start event")
|
||||
return
|
||||
}
|
||||
|
||||
stateSub.Unsubscribe()
|
||||
secondsPerSlot := params.BeaconConfig().SecondsPerSlot
|
||||
s.slotTicker = slots.NewSlotTicker(s.genesisTime, secondsPerSlot)
|
||||
|
||||
s.waitForSync(s.genesisTime)
|
||||
|
||||
indexedAttsChan := make(chan *ethpb.IndexedAttestation, 1)
|
||||
beaconBlockHeadersChan := make(chan *ethpb.SignedBeaconBlockHeader, 1)
|
||||
log.Info("Completed chain sync, starting slashing detection")
|
||||
go s.processQueuedAttestations(s.ctx, s.slotTicker.C())
|
||||
go s.processQueuedBlocks(s.ctx, s.slotTicker.C())
|
||||
go s.receiveAttestations(s.ctx, indexedAttsChan)
|
||||
go s.receiveBlocks(s.ctx, beaconBlockHeadersChan)
|
||||
go s.pruneSlasherData(s.ctx, s.slotTicker.C())
|
||||
}
|
||||
|
||||
// Stop the slasher service.
|
||||
func (s *Service) Stop() error {
|
||||
s.cancel()
|
||||
if s.slotTicker != nil {
|
||||
s.slotTicker.Done()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status of the slasher service.
|
||||
func (s *Service) Status() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) waitForSync(genesisTime time.Time) {
|
||||
if slots.SlotsSinceGenesis(genesisTime) == 0 || !s.serviceCfg.SyncChecker.Syncing() {
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-s.slotTicker.C():
|
||||
// If node is still syncing, do not operate slasher.
|
||||
if s.serviceCfg.SyncChecker.Syncing() {
|
||||
continue
|
||||
}
|
||||
return
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,11 +10,12 @@ 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__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools/benchmark-files-gen:__pkg__",
|
||||
|
||||
@@ -18,7 +18,9 @@ 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__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core:go_default_library",
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
transition "github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Package featureconfig defines which features are enabled for runtime
|
||||
Package features defines which features are enabled for runtime
|
||||
in order to selectively enable certain features to maintain a stable runtime.
|
||||
|
||||
The process for implementing new features using this package is as follows:
|
||||
@@ -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,
|
||||
|
||||
@@ -110,19 +110,23 @@ func TestLoadConfigFileMainnet(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("mainnet", func(t *testing.T) {
|
||||
mainnetPresetsFile := presetsFilePath(t, "mainnet")
|
||||
params.LoadChainConfigFile(mainnetPresetsFile)
|
||||
mainnetPresetsFiles := presetsFilePath(t, "mainnet")
|
||||
for _, fp := range mainnetPresetsFiles {
|
||||
params.LoadChainConfigFile(fp)
|
||||
}
|
||||
mainnetConfigFile := configFilePath(t, "mainnet")
|
||||
params.LoadChainConfigFile(mainnetConfigFile)
|
||||
fields := fieldsFromYamls(t, []string{mainnetPresetsFile, mainnetConfigFile})
|
||||
fields := fieldsFromYamls(t, append(mainnetPresetsFiles, mainnetConfigFile))
|
||||
assertVals("mainnet", fields, params.MainnetConfig(), params.BeaconConfig())
|
||||
})
|
||||
|
||||
t.Run("minimal", func(t *testing.T) {
|
||||
minimalPresetsFile := presetsFilePath(t, "minimal")
|
||||
params.LoadChainConfigFile(minimalPresetsFile)
|
||||
minimalPresetsFiles := presetsFilePath(t, "minimal")
|
||||
for _, fp := range minimalPresetsFiles {
|
||||
params.LoadChainConfigFile(fp)
|
||||
}
|
||||
minimalConfigFile := configFilePath(t, "minimal")
|
||||
fields := fieldsFromYamls(t, []string{minimalPresetsFile, minimalConfigFile})
|
||||
fields := fieldsFromYamls(t, append(minimalPresetsFiles, minimalConfigFile))
|
||||
assertVals("minimal", fields, params.MinimalSpecConfig(), params.BeaconConfig())
|
||||
})
|
||||
}
|
||||
@@ -225,13 +229,16 @@ func configFilePath(t *testing.T, config string) string {
|
||||
return configFilePath
|
||||
}
|
||||
|
||||
// presetsFilePath sets the proper preset and returns the relevant
|
||||
// preset file path from eth2-spec-tests directory.
|
||||
func presetsFilePath(t *testing.T, config string) string {
|
||||
// presetsFilePath returns the relevant preset file paths from eth2-spec-tests
|
||||
// directory. This method returns a preset file path for each hard fork or
|
||||
// major network upgrade, in order.
|
||||
func presetsFilePath(t *testing.T, config string) []string {
|
||||
filepath, err := bazel.Runfile("external/consensus_spec")
|
||||
require.NoError(t, err)
|
||||
configFilePath := path.Join(filepath, "presets", config, "phase0.yaml")
|
||||
return configFilePath
|
||||
return []string{
|
||||
path.Join(filepath, "presets", config, "phase0.yaml"),
|
||||
path.Join(filepath, "presets", config, "altair.yaml"),
|
||||
}
|
||||
}
|
||||
|
||||
func fieldsFromYamls(t *testing.T, fps []string) []string {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package params
|
||||
|
||||
const AltairE2EForkEpoch = 6
|
||||
const altairE2EForkEpoch = 6
|
||||
|
||||
// UseE2EConfig for beacon chain services.
|
||||
func UseE2EConfig() {
|
||||
@@ -35,7 +35,7 @@ func E2ETestConfig() *BeaconChainConfig {
|
||||
e2eConfig.DepositNetworkID = 1337 // Network ID of eth1 dev net.
|
||||
|
||||
// Altair Fork Parameters.
|
||||
e2eConfig.AltairForkEpoch = AltairE2EForkEpoch
|
||||
e2eConfig.AltairForkEpoch = altairE2EForkEpoch
|
||||
|
||||
// Prysm constants.
|
||||
e2eConfig.ConfigName = ConfigNames[EndToEnd]
|
||||
|
||||
@@ -7,10 +7,12 @@ import (
|
||||
)
|
||||
|
||||
func TestPraterConfigMatchesUpstreamYaml(t *testing.T) {
|
||||
presetFP := presetsFilePath(t, "mainnet")
|
||||
params.LoadChainConfigFile(presetFP)
|
||||
presetFPs := presetsFilePath(t, "mainnet")
|
||||
for _, fp := range presetFPs {
|
||||
params.LoadChainConfigFile(fp)
|
||||
}
|
||||
configFP := testnetConfigFilePath(t, "prater")
|
||||
params.LoadChainConfigFile(configFP)
|
||||
fields := fieldsFromYamls(t, []string{configFP, presetFP})
|
||||
fields := fieldsFromYamls(t, append(presetFPs, configFP))
|
||||
assertYamlFieldsMatch(t, "prater", fields, params.BeaconConfig(), params.PraterConfig())
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
},
|
||||
|
||||
371
proto/eth/service/beacon_chain_service.pb.go
generated
371
proto/eth/service/beacon_chain_service.pb.go
generated
@@ -55,7 +55,7 @@ var file_proto_eth_service_beacon_chain_service_proto_rawDesc = []byte{
|
||||
0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f,
|
||||
0x76, 0x32, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xe0, 0x22, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xe8, 0x22, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63,
|
||||
0x6f, 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x6f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x47, 0x65,
|
||||
0x6e, 0x65, 0x73, 0x69, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e,
|
||||
@@ -163,187 +163,188 @@ var file_proto_eth_service_beacon_chain_service_proto_rawDesc = []byte{
|
||||
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74,
|
||||
0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x68, 0x65, 0x61, 0x64,
|
||||
0x65, 0x72, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x77,
|
||||
0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x25, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3,
|
||||
0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
|
||||
0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x89, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52,
|
||||
0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x30, 0x12, 0x2e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65,
|
||||
0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72,
|
||||
0x6f, 0x6f, 0x74, 0x12, 0x7c, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12,
|
||||
0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31,
|
||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f,
|
||||
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64,
|
||||
0x7d, 0x12, 0x86, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53,
|
||||
0x5a, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x69, 0x6e,
|
||||
0x65, 0x72, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x7f,
|
||||
0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e,
|
||||
0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x56, 0x32, 0x1a, 0x16, 0x2e, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62,
|
||||
0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x01, 0x2a, 0x12,
|
||||
0x89, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74,
|
||||
0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x12, 0x2e, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65,
|
||||
0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x73, 0x7a, 0x12, 0x82, 0x01, 0x0a, 0x0a, 0x47,
|
||||
0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x32, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x32, 0x1a, 0x20, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x22, 0x31, 0x82, 0xd3,
|
||||
0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
|
||||
0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12,
|
||||
0x8c, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x56,
|
||||
0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x7c, 0x0a, 0x08, 0x47,
|
||||
0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29,
|
||||
0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b,
|
||||
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x86, 0x01, 0x0a, 0x0b, 0x47, 0x65,
|
||||
0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65,
|
||||
0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73,
|
||||
0x73, 0x7a, 0x12, 0x82, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56,
|
||||
0x32, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x56, 0x32, 0x1a, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12,
|
||||
0x2d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76,
|
||||
0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f,
|
||||
0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x73, 0x7a, 0x12, 0xa2,
|
||||
0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x65,
|
||||
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x38, 0x12, 0x36, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65,
|
||||
0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x12, 0x9e, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c,
|
||||
0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63,
|
||||
0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75,
|
||||
0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
|
||||
0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x22, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e,
|
||||
0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9c, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f,
|
||||
0x6f, 0x6c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2e, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74,
|
||||
0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x50,
|
||||
0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65,
|
||||
0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f,
|
||||
0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12,
|
||||
0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74,
|
||||
0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c,
|
||||
0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9b, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50,
|
||||
0x6f, 0x6f, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50,
|
||||
0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x50,
|
||||
0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65,
|
||||
0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f,
|
||||
0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50,
|
||||
0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12,
|
||||
0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74,
|
||||
0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x93, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50,
|
||||
0x6f, 0x6f, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74,
|
||||
0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75,
|
||||
0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, 0x2c,
|
||||
0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x76, 0x6f, 0x6c,
|
||||
0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x12, 0x8c, 0x01, 0x0a,
|
||||
0x13, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79,
|
||||
0x45, 0x78, 0x69, 0x74, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c,
|
||||
0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2c, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61,
|
||||
0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61,
|
||||
0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xa8, 0x01, 0x0a, 0x21,
|
||||
0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f,
|
||||
0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
|
||||
0x73, 0x12, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x79,
|
||||
0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61,
|
||||
0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x37, 0x82,
|
||||
0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
|
||||
0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70,
|
||||
0x6f, 0x6f, 0x6c, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74,
|
||||
0x65, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72,
|
||||
0x6b, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
||||
0x79, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27,
|
||||
0x12, 0x25, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f,
|
||||
0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x73,
|
||||
0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x66, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x53, 0x70,
|
||||
0x65, 0x63, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x65,
|
||||
0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02,
|
||||
0x1e, 0x12, 0x1c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68,
|
||||
0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x12,
|
||||
0x88, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x28,
|
||||
0x56, 0x32, 0x1a, 0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x56, 0x32, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x62,
|
||||
0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8c, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x56, 0x32, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x32, 0x1a, 0x23, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x22,
|
||||
0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e,
|
||||
0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69,
|
||||
0x64, 0x7d, 0x2f, 0x73, 0x73, 0x7a, 0x12, 0xa2, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x42,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x38, 0x12, 0x36, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65,
|
||||
0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9e, 0x01, 0x0a, 0x14,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a,
|
||||
0x12, 0x28, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f,
|
||||
0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x42, 0x95, 0x01, 0x0a, 0x18, 0x6f,
|
||||
0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x17, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43,
|
||||
0x68, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x50, 0x01, 0x5a, 0x30, 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, 0x65, 0x74, 0x68, 0x2f, 0x73, 0x65, 0x72,
|
||||
0x76, 0x69, 0x63, 0x65, 0xaa, 0x02, 0x14, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x45, 0x74, 0x68, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xca, 0x02, 0x14, 0x45, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f,
|
||||
0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02,
|
||||
0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68,
|
||||
0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f,
|
||||
0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8e, 0x01, 0x0a,
|
||||
0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65,
|
||||
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x22,
|
||||
0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74,
|
||||
0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9c, 0x01,
|
||||
0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||
0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x1a, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c,
|
||||
0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65,
|
||||
0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||
0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a,
|
||||
0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53,
|
||||
0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||
0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61,
|
||||
0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65,
|
||||
0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9b,
|
||||
0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f,
|
||||
0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53,
|
||||
0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65,
|
||||
0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73,
|
||||
0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a,
|
||||
0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53,
|
||||
0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73,
|
||||
0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61,
|
||||
0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65,
|
||||
0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x93,
|
||||
0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6e,
|
||||
0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
||||
0x79, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69,
|
||||
0x74, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34,
|
||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, 0x2c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f,
|
||||
0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65,
|
||||
0x78, 0x69, 0x74, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x56,
|
||||
0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x12, 0x24, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53,
|
||||
0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78,
|
||||
0x69, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x31, 0x22, 0x2c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74,
|
||||
0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c,
|
||||
0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73,
|
||||
0x3a, 0x01, 0x2a, 0x12, 0xa8, 0x01, 0x0a, 0x21, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x6f,
|
||||
0x6f, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53,
|
||||
0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x6d,
|
||||
0x69, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
|
||||
0x74, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x16, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2c, 0x2f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f,
|
||||
0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x73, 0x79, 0x6e, 0x63,
|
||||
0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f,
|
||||
0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
|
||||
0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x6b,
|
||||
0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x2f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12,
|
||||
0x66, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x12, 0x88, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44,
|
||||
0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x16,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x12, 0x28, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x2f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61,
|
||||
0x63, 0x74, 0x42, 0x95, 0x01, 0x0a, 0x18, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42,
|
||||
0x17, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 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, 0x65, 0x74, 0x68, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xaa, 0x02, 0x14, 0x45,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x53, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0xca, 0x02, 0x14, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45,
|
||||
0x74, 0x68, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_proto_eth_service_beacon_chain_service_proto_goTypes = []interface{}{
|
||||
@@ -356,7 +357,7 @@ var file_proto_eth_service_beacon_chain_service_proto_goTypes = []interface{}{
|
||||
(*v2.StateSyncCommitteesRequest)(nil), // 6: ethereum.eth.v2.StateSyncCommitteesRequest
|
||||
(*v1.BlockHeadersRequest)(nil), // 7: ethereum.eth.v1.BlockHeadersRequest
|
||||
(*v1.BlockRequest)(nil), // 8: ethereum.eth.v1.BlockRequest
|
||||
(*v1.BeaconBlockContainer)(nil), // 9: ethereum.eth.v1.BeaconBlockContainer
|
||||
(*v2.SignedBeaconBlockContainerV2)(nil), // 9: ethereum.eth.v2.SignedBeaconBlockContainerV2
|
||||
(*v2.BlockRequestV2)(nil), // 10: ethereum.eth.v2.BlockRequestV2
|
||||
(*v1.AttestationsPoolRequest)(nil), // 11: ethereum.eth.v1.AttestationsPoolRequest
|
||||
(*v1.SubmitAttestationsRequest)(nil), // 12: ethereum.eth.v1.SubmitAttestationsRequest
|
||||
@@ -401,7 +402,7 @@ var file_proto_eth_service_beacon_chain_service_proto_depIdxs = []int32{
|
||||
6, // 8: ethereum.eth.service.BeaconChain.ListSyncCommittees:input_type -> ethereum.eth.v2.StateSyncCommitteesRequest
|
||||
7, // 9: ethereum.eth.service.BeaconChain.ListBlockHeaders:input_type -> ethereum.eth.v1.BlockHeadersRequest
|
||||
8, // 10: ethereum.eth.service.BeaconChain.GetBlockHeader:input_type -> ethereum.eth.v1.BlockRequest
|
||||
9, // 11: ethereum.eth.service.BeaconChain.SubmitBlock:input_type -> ethereum.eth.v1.BeaconBlockContainer
|
||||
9, // 11: ethereum.eth.service.BeaconChain.SubmitBlock:input_type -> ethereum.eth.v2.SignedBeaconBlockContainerV2
|
||||
8, // 12: ethereum.eth.service.BeaconChain.GetBlockRoot:input_type -> ethereum.eth.v1.BlockRequest
|
||||
8, // 13: ethereum.eth.service.BeaconChain.GetBlock:input_type -> ethereum.eth.v1.BlockRequest
|
||||
8, // 14: ethereum.eth.service.BeaconChain.GetBlockSSZ:input_type -> ethereum.eth.v1.BlockRequest
|
||||
@@ -504,7 +505,7 @@ type BeaconChainClient interface {
|
||||
ListSyncCommittees(ctx context.Context, in *v2.StateSyncCommitteesRequest, opts ...grpc.CallOption) (*v2.StateSyncCommitteesResponse, error)
|
||||
ListBlockHeaders(ctx context.Context, in *v1.BlockHeadersRequest, opts ...grpc.CallOption) (*v1.BlockHeadersResponse, error)
|
||||
GetBlockHeader(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockHeaderResponse, error)
|
||||
SubmitBlock(ctx context.Context, in *v1.BeaconBlockContainer, opts ...grpc.CallOption) (*empty.Empty, error)
|
||||
SubmitBlock(ctx context.Context, in *v2.SignedBeaconBlockContainerV2, opts ...grpc.CallOption) (*empty.Empty, error)
|
||||
GetBlockRoot(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockRootResponse, error)
|
||||
GetBlock(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockResponse, error)
|
||||
GetBlockSSZ(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockSSZResponse, error)
|
||||
@@ -632,7 +633,7 @@ func (c *beaconChainClient) GetBlockHeader(ctx context.Context, in *v1.BlockRequ
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *beaconChainClient) SubmitBlock(ctx context.Context, in *v1.BeaconBlockContainer, opts ...grpc.CallOption) (*empty.Empty, error) {
|
||||
func (c *beaconChainClient) SubmitBlock(ctx context.Context, in *v2.SignedBeaconBlockContainerV2, opts ...grpc.CallOption) (*empty.Empty, error) {
|
||||
out := new(empty.Empty)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.eth.service.BeaconChain/SubmitBlock", in, out, opts...)
|
||||
if err != nil {
|
||||
@@ -816,7 +817,7 @@ type BeaconChainServer interface {
|
||||
ListSyncCommittees(context.Context, *v2.StateSyncCommitteesRequest) (*v2.StateSyncCommitteesResponse, error)
|
||||
ListBlockHeaders(context.Context, *v1.BlockHeadersRequest) (*v1.BlockHeadersResponse, error)
|
||||
GetBlockHeader(context.Context, *v1.BlockRequest) (*v1.BlockHeaderResponse, error)
|
||||
SubmitBlock(context.Context, *v1.BeaconBlockContainer) (*empty.Empty, error)
|
||||
SubmitBlock(context.Context, *v2.SignedBeaconBlockContainerV2) (*empty.Empty, error)
|
||||
GetBlockRoot(context.Context, *v1.BlockRequest) (*v1.BlockRootResponse, error)
|
||||
GetBlock(context.Context, *v1.BlockRequest) (*v1.BlockResponse, error)
|
||||
GetBlockSSZ(context.Context, *v1.BlockRequest) (*v1.BlockSSZResponse, error)
|
||||
@@ -874,7 +875,7 @@ func (*UnimplementedBeaconChainServer) ListBlockHeaders(context.Context, *v1.Blo
|
||||
func (*UnimplementedBeaconChainServer) GetBlockHeader(context.Context, *v1.BlockRequest) (*v1.BlockHeaderResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBlockHeader not implemented")
|
||||
}
|
||||
func (*UnimplementedBeaconChainServer) SubmitBlock(context.Context, *v1.BeaconBlockContainer) (*empty.Empty, error) {
|
||||
func (*UnimplementedBeaconChainServer) SubmitBlock(context.Context, *v2.SignedBeaconBlockContainerV2) (*empty.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SubmitBlock not implemented")
|
||||
}
|
||||
func (*UnimplementedBeaconChainServer) GetBlockRoot(context.Context, *v1.BlockRequest) (*v1.BlockRootResponse, error) {
|
||||
@@ -1135,7 +1136,7 @@ func _BeaconChain_GetBlockHeader_Handler(srv interface{}, ctx context.Context, d
|
||||
}
|
||||
|
||||
func _BeaconChain_SubmitBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(v1.BeaconBlockContainer)
|
||||
in := new(v2.SignedBeaconBlockContainerV2)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1147,7 +1148,7 @@ func _BeaconChain_SubmitBlock_Handler(srv interface{}, ctx context.Context, dec
|
||||
FullMethod: "/ethereum.eth.service.BeaconChain/SubmitBlock",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BeaconChainServer).SubmitBlock(ctx, req.(*v1.BeaconBlockContainer))
|
||||
return srv.(BeaconChainServer).SubmitBlock(ctx, req.(*v2.SignedBeaconBlockContainerV2))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
@@ -674,7 +674,7 @@ func local_request_BeaconChain_GetBlockHeader_0(ctx context.Context, marshaler r
|
||||
}
|
||||
|
||||
func request_BeaconChain_SubmitBlock_0(ctx context.Context, marshaler runtime.Marshaler, client BeaconChainClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq v1.BeaconBlockContainer
|
||||
var protoReq eth.SignedBeaconBlockContainerV2
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
@@ -691,7 +691,7 @@ func request_BeaconChain_SubmitBlock_0(ctx context.Context, marshaler runtime.Ma
|
||||
}
|
||||
|
||||
func local_request_BeaconChain_SubmitBlock_0(ctx context.Context, marshaler runtime.Marshaler, server BeaconChainServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq v1.BeaconBlockContainer
|
||||
var protoReq eth.SignedBeaconBlockContainerV2
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
|
||||
@@ -123,7 +123,7 @@ service BeaconChain {
|
||||
// response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the
|
||||
// new block into its state, and therefore validate the block internally, however blocks which fail the validation are
|
||||
// still broadcast but a different status code is returned (202).
|
||||
rpc SubmitBlock(v1.BeaconBlockContainer) returns (google.protobuf.Empty) {
|
||||
rpc SubmitBlock(v2.SignedBeaconBlockContainerV2) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
post: "/internal/eth/v1/beacon/blocks"
|
||||
body: "*"
|
||||
|
||||
178
proto/eth/v2/beacon_block.pb.go
generated
178
proto/eth/v2/beacon_block.pb.go
generated
@@ -271,11 +271,11 @@ type SignedBeaconBlockContainerV2 struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Block:
|
||||
// Types that are assignable to Message:
|
||||
// *SignedBeaconBlockContainerV2_Phase0Block
|
||||
// *SignedBeaconBlockContainerV2_AltairBlock
|
||||
Block isSignedBeaconBlockContainerV2_Block `protobuf_oneof:"block"`
|
||||
Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"`
|
||||
Message isSignedBeaconBlockContainerV2_Message `protobuf_oneof:"message"`
|
||||
Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"`
|
||||
}
|
||||
|
||||
func (x *SignedBeaconBlockContainerV2) Reset() {
|
||||
@@ -310,22 +310,22 @@ func (*SignedBeaconBlockContainerV2) Descriptor() ([]byte, []int) {
|
||||
return file_proto_eth_v2_beacon_block_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (m *SignedBeaconBlockContainerV2) GetBlock() isSignedBeaconBlockContainerV2_Block {
|
||||
func (m *SignedBeaconBlockContainerV2) GetMessage() isSignedBeaconBlockContainerV2_Message {
|
||||
if m != nil {
|
||||
return m.Block
|
||||
return m.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SignedBeaconBlockContainerV2) GetPhase0Block() *v1.BeaconBlock {
|
||||
if x, ok := x.GetBlock().(*SignedBeaconBlockContainerV2_Phase0Block); ok {
|
||||
if x, ok := x.GetMessage().(*SignedBeaconBlockContainerV2_Phase0Block); ok {
|
||||
return x.Phase0Block
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SignedBeaconBlockContainerV2) GetAltairBlock() *BeaconBlockAltair {
|
||||
if x, ok := x.GetBlock().(*SignedBeaconBlockContainerV2_AltairBlock); ok {
|
||||
if x, ok := x.GetMessage().(*SignedBeaconBlockContainerV2_AltairBlock); ok {
|
||||
return x.AltairBlock
|
||||
}
|
||||
return nil
|
||||
@@ -338,8 +338,8 @@ func (x *SignedBeaconBlockContainerV2) GetSignature() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type isSignedBeaconBlockContainerV2_Block interface {
|
||||
isSignedBeaconBlockContainerV2_Block()
|
||||
type isSignedBeaconBlockContainerV2_Message interface {
|
||||
isSignedBeaconBlockContainerV2_Message()
|
||||
}
|
||||
|
||||
type SignedBeaconBlockContainerV2_Phase0Block struct {
|
||||
@@ -350,9 +350,9 @@ type SignedBeaconBlockContainerV2_AltairBlock struct {
|
||||
AltairBlock *BeaconBlockAltair `protobuf:"bytes,2,opt,name=altair_block,json=altairBlock,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*SignedBeaconBlockContainerV2_Phase0Block) isSignedBeaconBlockContainerV2_Block() {}
|
||||
func (*SignedBeaconBlockContainerV2_Phase0Block) isSignedBeaconBlockContainerV2_Message() {}
|
||||
|
||||
func (*SignedBeaconBlockContainerV2_AltairBlock) isSignedBeaconBlockContainerV2_Block() {}
|
||||
func (*SignedBeaconBlockContainerV2_AltairBlock) isSignedBeaconBlockContainerV2_Message() {}
|
||||
|
||||
type SignedBeaconBlockAltair struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -641,7 +641,7 @@ var file_proto_eth_v2_beacon_block_proto_rawDesc = []byte{
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x6c,
|
||||
0x74, 0x61, 0x69, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x22, 0xd9, 0x01, 0x0a, 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61,
|
||||
0x63, 0x6b, 0x22, 0xdb, 0x01, 0x0a, 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61,
|
||||
0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
|
||||
0x72, 0x56, 0x32, 0x12, 0x41, 0x0a, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x5f, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
@@ -654,84 +654,84 @@ var file_proto_eth_v2_beacon_block_proto_rawDesc = []byte{
|
||||
0x48, 0x00, 0x52, 0x0b, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12,
|
||||
0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x7d,
|
||||
0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61,
|
||||
0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x07,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61,
|
||||
0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02,
|
||||
0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xc0, 0x02,
|
||||
0x0a, 0x11, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74,
|
||||
0x61, 0x69, 0x72, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 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,
|
||||
0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52,
|
||||
0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x5d, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65,
|
||||
0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x36, 0x82,
|
||||
0xb5, 0x18, 0x32, 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, 0x65, 0x74, 0x68, 0x32,
|
||||
0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72,
|
||||
0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33,
|
||||
0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a,
|
||||
0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79,
|
||||
0x22, 0xfa, 0x04, 0x0a, 0x15, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61,
|
||||
0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61,
|
||||
0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f,
|
||||
0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x74, 0x68,
|
||||
0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12,
|
||||
0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66,
|
||||
0x69, 0x74, 0x69, 0x12, 0x58, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f,
|
||||
0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70,
|
||||
0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x57, 0x0a,
|
||||
0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65,
|
||||
0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5,
|
||||
0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61,
|
||||
0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41,
|
||||
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03,
|
||||
0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x12, 0x3c, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92,
|
||||
0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12,
|
||||
0x55, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69,
|
||||
0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65,
|
||||
0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06,
|
||||
0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72,
|
||||
0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61,
|
||||
0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e,
|
||||
0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x22, 0x7d, 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e,
|
||||
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42,
|
||||
0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72,
|
||||
0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67,
|
||||
0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5,
|
||||
0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22,
|
||||
0xc0, 0x02, 0x0a, 0x11, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41,
|
||||
0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 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, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f,
|
||||
0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x5d, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f,
|
||||
0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42,
|
||||
0x36, 0x82, 0xb5, 0x18, 0x32, 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, 0x65, 0x74,
|
||||
0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
|
||||
0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65,
|
||||
0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
|
||||
0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18,
|
||||
0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12,
|
||||
0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x04, 0x62, 0x6f,
|
||||
0x64, 0x79, 0x22, 0xfa, 0x04, 0x0a, 0x15, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x0d,
|
||||
0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e,
|
||||
0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x09, 0x65, 0x74, 0x68,
|
||||
0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x45,
|
||||
0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74,
|
||||
0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61,
|
||||
0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x58, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65,
|
||||
0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73,
|
||||
0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72,
|
||||
0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12,
|
||||
0x57, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73,
|
||||
0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74,
|
||||
0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05,
|
||||
0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53,
|
||||
0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65,
|
||||
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d,
|
||||
0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x42, 0x80, 0x01,
|
||||
0x0a, 0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69,
|
||||
0x74, 0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 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, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32, 0xca, 0x02,
|
||||
0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x32,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5,
|
||||
0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18,
|
||||
0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42,
|
||||
0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x73, 0x12, 0x55, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65,
|
||||
0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67,
|
||||
0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74,
|
||||
0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74,
|
||||
0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63,
|
||||
0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65,
|
||||
0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x42,
|
||||
0x80, 0x01, 0x0a, 0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d,
|
||||
0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 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, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02,
|
||||
0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32,
|
||||
0xca, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c,
|
||||
0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -51,7 +51,7 @@ message BeaconBlockContainerV2 {
|
||||
}
|
||||
|
||||
message SignedBeaconBlockContainerV2 {
|
||||
oneof block {
|
||||
oneof message {
|
||||
v1.BeaconBlock phase0_block = 1;
|
||||
BeaconBlockAltair altair_block = 2;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ go_test(
|
||||
deps = [
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
"//proto/eth/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
@@ -44,8 +44,8 @@ func V1Alpha1ToV1SignedBlock(alphaBlk *ethpbalpha.SignedBeaconBlock) (*ethpbv1.S
|
||||
}
|
||||
|
||||
// V1ToV1Alpha1SignedBlock converts a v1 SignedBeaconBlock proto to a v1alpha1 proto.
|
||||
func V1ToV1Alpha1SignedBlock(alphaBlk *ethpbv1.SignedBeaconBlock) (*ethpbalpha.SignedBeaconBlock, error) {
|
||||
marshaledBlk, err := proto.Marshal(alphaBlk)
|
||||
func V1ToV1Alpha1SignedBlock(v1Blk *ethpbv1.SignedBeaconBlock) (*ethpbalpha.SignedBeaconBlock, error) {
|
||||
marshaledBlk, err := proto.Marshal(v1Blk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not marshal block")
|
||||
}
|
||||
@@ -56,6 +56,19 @@ func V1ToV1Alpha1SignedBlock(alphaBlk *ethpbv1.SignedBeaconBlock) (*ethpbalpha.S
|
||||
return v1alpha1Block, nil
|
||||
}
|
||||
|
||||
// AltairToV1Alpha1SignedBlock converts a v2 SignedBeaconBlockAltair proto to a v1alpha1 proto.
|
||||
func AltairToV1Alpha1SignedBlock(altairBlk *ethpbv2.SignedBeaconBlockAltair) (*ethpbalpha.SignedBeaconBlockAltair, error) {
|
||||
marshaledBlk, err := proto.Marshal(altairBlk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not marshal block")
|
||||
}
|
||||
v1alpha1Block := ðpbalpha.SignedBeaconBlockAltair{}
|
||||
if err := proto.Unmarshal(marshaledBlk, v1alpha1Block); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal block")
|
||||
}
|
||||
return v1alpha1Block, nil
|
||||
}
|
||||
|
||||
// V1Alpha1ToV1Block converts a v1alpha1 BeaconBlock proto to a v1 proto.
|
||||
func V1Alpha1ToV1Block(alphaBlk *ethpbalpha.BeaconBlock) (*ethpbv1.BeaconBlock, error) {
|
||||
marshaledBlk, err := proto.Marshal(alphaBlk)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user