mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
Compare commits
11 Commits
batch-retr
...
revert_134
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9039816ac9 | ||
|
|
ee9274a9bc | ||
|
|
ef21d3adf8 | ||
|
|
b6ce6c2eba | ||
|
|
b3caaa9acc | ||
|
|
d6fb8c29c9 | ||
|
|
3df7a1f067 | ||
|
|
4c3dbae3c0 | ||
|
|
68b78dd520 | ||
|
|
2e2ef4a179 | ||
|
|
b61d17731e |
@@ -163,7 +163,7 @@ func Test_getBlkParentHashAndTD(t *testing.T) {
|
||||
parentHash, totalDifficulty, err := service.getBlkParentHashAndTD(ctx, h[:])
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, p, bytesutil.ToBytes32(parentHash))
|
||||
require.Equal(t, td, totalDifficulty.String())
|
||||
require.Equal(t, td, totalDifficulty.Hex())
|
||||
|
||||
_, _, err = service.getBlkParentHashAndTD(ctx, []byte{'c'})
|
||||
require.ErrorContains(t, "could not get pow block: block not found", err)
|
||||
|
||||
@@ -307,16 +307,16 @@ func (s *Service) updateEpochBoundaryCaches(ctx context.Context, st state.Beacon
|
||||
if err := helpers.UpdateProposerIndicesInCache(ctx, st, e); err != nil {
|
||||
return errors.Wrap(err, "could not update proposer index cache")
|
||||
}
|
||||
go func() {
|
||||
go func(ep primitives.Epoch) {
|
||||
// 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 := helpers.UpdateCommitteeCache(slotCtx, st, e+1); err != nil {
|
||||
if err := helpers.UpdateCommitteeCache(slotCtx, st, ep+1); err != nil {
|
||||
log.WithError(err).Warn("Could not update committee cache")
|
||||
}
|
||||
}()
|
||||
}(e)
|
||||
// The latest block header is from the previous epoch
|
||||
r, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
|
||||
@@ -2047,9 +2047,7 @@ func TestFillMissingBlockPayloadId_PrepareAllPayloads(t *testing.T) {
|
||||
// boost. It alters the genesisTime tracked by the store.
|
||||
func driftGenesisTime(s *Service, slot, delay int64) {
|
||||
offset := slot*int64(params.BeaconConfig().SecondsPerSlot) + delay
|
||||
newTime := time.Unix(time.Now().Unix()-offset, 0)
|
||||
s.SetGenesisTime(newTime)
|
||||
s.cfg.ForkChoiceStore.SetGenesisTime(uint64(newTime.Unix()))
|
||||
s.SetGenesisTime(time.Unix(time.Now().Unix()-offset, 0))
|
||||
}
|
||||
|
||||
func TestMissingIndices(t *testing.T) {
|
||||
@@ -2115,7 +2113,7 @@ func TestMissingIndices(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
bm, bs := filesystem.NewEphemeralBlobStorageWithMocker(t)
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
require.NoError(t, bm.CreateFakeIndices(c.root, c.present))
|
||||
require.NoError(t, bm.CreateFakeIndices(c.root, c.present...))
|
||||
missing, err := missingIndices(bs, c.root, c.expected)
|
||||
if c.err != nil {
|
||||
require.ErrorIs(t, err, c.err)
|
||||
|
||||
@@ -37,7 +37,7 @@ type BlobMocker struct {
|
||||
|
||||
// CreateFakeIndices creates empty blob sidecar files at the expected path for the given
|
||||
// root and indices to influence the result of Indices().
|
||||
func (bm *BlobMocker) CreateFakeIndices(root [32]byte, indices []uint64) error {
|
||||
func (bm *BlobMocker) CreateFakeIndices(root [32]byte, indices ...uint64) error {
|
||||
for i := range indices {
|
||||
n := blobNamer{root: root, index: indices[i]}
|
||||
if err := bm.fs.MkdirAll(n.dir(), directoryPermissions); err != nil {
|
||||
|
||||
@@ -22,7 +22,14 @@ func Restore(cliCtx *cli.Context) error {
|
||||
targetDir := cliCtx.String(cmd.RestoreTargetDirFlag.Name)
|
||||
|
||||
restoreDir := path.Join(targetDir, kv.BeaconNodeDbDirName)
|
||||
if file.Exists(path.Join(restoreDir, kv.DatabaseFileName)) {
|
||||
restoreFile := path.Join(restoreDir, kv.DatabaseFileName)
|
||||
|
||||
dbExists, err := file.Exists(restoreFile, file.Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if database exists in %s", restoreFile)
|
||||
}
|
||||
|
||||
if dbExists {
|
||||
resp, err := prompt.ValidatePrompt(
|
||||
os.Stdin, dbExistsYesNoPrompt, prompt.ValidateYesOrNo,
|
||||
)
|
||||
|
||||
@@ -77,6 +77,5 @@ go_test(
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -93,13 +93,13 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
||||
// Any node with different finalized or justified epoch than
|
||||
// the ones in fork choice store should not be viable to head.
|
||||
func (n *Node) viableForHead(justifiedEpoch, currentEpoch primitives.Epoch) bool {
|
||||
if justifiedEpoch == 0 {
|
||||
return true
|
||||
justified := justifiedEpoch == n.justifiedEpoch || justifiedEpoch == 0
|
||||
if !justified && justifiedEpoch+1 == currentEpoch {
|
||||
if n.unrealizedJustifiedEpoch+1 >= currentEpoch && n.justifiedEpoch+2 >= currentEpoch {
|
||||
justified = true
|
||||
}
|
||||
}
|
||||
// We use n.justifiedEpoch as the voting source because:
|
||||
// 1. if this node is from current epoch, n.justifiedEpoch is the realized justification epoch.
|
||||
// 2. if this node is from a previous epoch, n.justifiedEpoch has already been updated to the unrealized justification epoch.
|
||||
return n.justifiedEpoch == justifiedEpoch || n.justifiedEpoch+2 >= currentEpoch
|
||||
return justified
|
||||
}
|
||||
|
||||
func (n *Node) leadsToViableHead(justifiedEpoch, currentEpoch primitives.Epoch) bool {
|
||||
|
||||
@@ -146,9 +146,7 @@ func TestNode_ViableForHead(t *testing.T) {
|
||||
{&Node{}, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 2}, 3, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 2}, 4, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 3}, 4, true},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.n.viableForHead(tc.justifiedEpoch, 5)
|
||||
|
||||
@@ -82,6 +82,15 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Return early if we are checking before 10 seconds into the slot
|
||||
secs, err := slots.SecondsSinceSlotStart(head.slot, f.store.genesisTime, uint64(time.Now().Unix()))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not check current slot")
|
||||
return true
|
||||
}
|
||||
if secs < ProcessAttestationsThreshold {
|
||||
return true
|
||||
}
|
||||
// Only orphan a block if the parent LMD vote is strong
|
||||
if parent.weight*100 < f.store.committeeWeight*params.BeaconConfig().ReorgParentWeightThreshold {
|
||||
return
|
||||
|
||||
@@ -85,11 +85,19 @@ func TestForkChoice_ShouldOverrideFCU(t *testing.T) {
|
||||
require.Equal(t, false, f.ShouldOverrideFCU())
|
||||
f.store.headNode.parent = saved
|
||||
})
|
||||
t.Run("parent is weak", func(t *testing.T) {
|
||||
t.Run("parent is weak early call", func(t *testing.T) {
|
||||
saved := f.store.headNode.parent.weight
|
||||
f.store.headNode.parent.weight = 0
|
||||
require.Equal(t, true, f.ShouldOverrideFCU())
|
||||
f.store.headNode.parent.weight = saved
|
||||
})
|
||||
t.Run("parent is weak late call", func(t *testing.T) {
|
||||
saved := f.store.headNode.parent.weight
|
||||
driftGenesisTime(f, 2, 11)
|
||||
f.store.headNode.parent.weight = 0
|
||||
require.Equal(t, false, f.ShouldOverrideFCU())
|
||||
f.store.headNode.parent.weight = saved
|
||||
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
|
||||
})
|
||||
t.Run("Head is strong", func(t *testing.T) {
|
||||
f.store.headNode.weight = f.store.committeeWeight
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
)
|
||||
|
||||
func TestStore_SetUnrealizedEpochs(t *testing.T) {
|
||||
@@ -64,14 +63,14 @@ func TestStore_UpdateUnrealizedCheckpoints(t *testing.T) {
|
||||
func TestStore_LongFork(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 75, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 80, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'b'}, 2))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 95, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'c'}, 2))
|
||||
@@ -79,28 +78,27 @@ func TestStore_LongFork(t *testing.T) {
|
||||
// Add an attestation to c, it is head
|
||||
f.ProcessAttestation(ctx, []uint64{0}, [32]byte{'c'}, 1)
|
||||
f.justifiedBalances = []uint64{100}
|
||||
c := f.store.nodeByRoot[[32]byte{'c'}]
|
||||
require.Equal(t, primitives.Epoch(2), slots.ToEpoch(c.slot))
|
||||
driftGenesisTime(f, c.slot, 0)
|
||||
headRoot, err := f.Head(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{'c'}, headRoot)
|
||||
|
||||
// c remains the head even if a block d with higher realized justification is seen
|
||||
// D is head even though its weight is lower.
|
||||
ha := [32]byte{'a'}
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'b'}, [32]byte{'D'}, 2, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.UpdateJustifiedCheckpoint(ctx, &forkchoicetypes.Checkpoint{Epoch: 2, Root: ha}))
|
||||
d := f.store.nodeByRoot[[32]byte{'d'}]
|
||||
require.Equal(t, primitives.Epoch(3), slots.ToEpoch(d.slot))
|
||||
driftGenesisTime(f, d.slot, 0)
|
||||
require.Equal(t, true, d.viableForHead(f.store.justifiedCheckpoint.Epoch, slots.ToEpoch(d.slot)))
|
||||
headRoot, err = f.Head(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{'d'}, headRoot)
|
||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight)
|
||||
require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'c'}].weight)
|
||||
|
||||
// Update unrealized justification, c becomes head
|
||||
require.NoError(t, f.updateUnrealizedCheckpoints(ctx))
|
||||
headRoot, err = f.Head(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{'c'}, headRoot)
|
||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight)
|
||||
require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'c'}].weight)
|
||||
}
|
||||
|
||||
// Epoch 1 Epoch 2 Epoch 3
|
||||
|
||||
@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"endpoints.go",
|
||||
"log.go",
|
||||
"service.go",
|
||||
],
|
||||
@@ -58,6 +59,9 @@ go_library(
|
||||
"@com_github_grpc_ecosystem_go_grpc_middleware//tracing/opentracing:go_default_library",
|
||||
"@com_github_grpc_ecosystem_go_grpc_prometheus//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promhttp:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//plugin/ocgrpc:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
@@ -70,23 +74,14 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = ["service_test.go"],
|
||||
srcs = [
|
||||
"endpoints_test.go",
|
||||
"service_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/execution/testing:go_default_library",
|
||||
"//beacon-chain/rpc/eth/beacon:go_default_library",
|
||||
"//beacon-chain/rpc/eth/blob:go_default_library",
|
||||
"//beacon-chain/rpc/eth/builder:go_default_library",
|
||||
"//beacon-chain/rpc/eth/debug:go_default_library",
|
||||
"//beacon-chain/rpc/eth/events:go_default_library",
|
||||
"//beacon-chain/rpc/eth/light-client:go_default_library",
|
||||
"//beacon-chain/rpc/eth/node:go_default_library",
|
||||
"//beacon-chain/rpc/eth/rewards:go_default_library",
|
||||
"//beacon-chain/rpc/eth/validator:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/beacon:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/node:go_default_library",
|
||||
"//beacon-chain/rpc/prysm/validator:go_default_library",
|
||||
"//beacon-chain/startup:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync/testing:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
780
beacon-chain/rpc/endpoints.go
Normal file
780
beacon-chain/rpc/endpoints.go
Normal file
@@ -0,0 +1,780 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/beacon"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/blob"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/builder"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/config"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/debug"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/events"
|
||||
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/light-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/node"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/rewards"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/lookup"
|
||||
beaconprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/beacon"
|
||||
nodeprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/node"
|
||||
validatorv1alpha1 "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/v1alpha1/validator"
|
||||
validatorprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
|
||||
)
|
||||
|
||||
type endpoint struct {
|
||||
template string
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
methods []string
|
||||
}
|
||||
|
||||
func (s *Service) endpoints(
|
||||
enableDebug bool,
|
||||
blocker lookup.Blocker,
|
||||
stater lookup.Stater,
|
||||
rewardFetcher rewards.BlockRewardsFetcher,
|
||||
validatorServer *validatorv1alpha1.Server,
|
||||
coreService *core.Service,
|
||||
ch *stategen.CanonicalHistory,
|
||||
) []endpoint {
|
||||
endpoints := make([]endpoint, 0)
|
||||
endpoints = append(endpoints, s.rewardsEndpoints(blocker, stater, rewardFetcher)...)
|
||||
endpoints = append(endpoints, s.builderEndpoints(stater)...)
|
||||
endpoints = append(endpoints, s.blobEndpoints(blocker)...)
|
||||
endpoints = append(endpoints, s.validatorEndpoints(validatorServer, stater, coreService, rewardFetcher)...)
|
||||
endpoints = append(endpoints, s.nodeEndpoints()...)
|
||||
endpoints = append(endpoints, s.beaconEndpoints(ch, stater, blocker, validatorServer, coreService)...)
|
||||
endpoints = append(endpoints, s.configEndpoints()...)
|
||||
endpoints = append(endpoints, s.lightClientEndpoints(blocker, stater)...)
|
||||
endpoints = append(endpoints, s.eventsEndpoints()...)
|
||||
endpoints = append(endpoints, s.prysmBeaconEndpoints(ch, stater)...)
|
||||
endpoints = append(endpoints, s.prysmNodeEndpoints()...)
|
||||
endpoints = append(endpoints, s.prysmValidatorEndpoints(coreService, stater)...)
|
||||
if enableDebug {
|
||||
endpoints = append(endpoints, s.debugEndpoints(stater)...)
|
||||
}
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func (s *Service) rewardsEndpoints(blocker lookup.Blocker, stater lookup.Stater, rewardFetcher rewards.BlockRewardsFetcher) []endpoint {
|
||||
server := &rewards.Server{
|
||||
Blocker: blocker,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
Stater: stater,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
}
|
||||
|
||||
const namespace = "rewards"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/beacon/rewards/blocks/{block_id}",
|
||||
name: namespace + ".BlockRewards",
|
||||
handler: server.BlockRewards,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/rewards/attestations/{epoch}",
|
||||
name: namespace + ".AttestationRewards",
|
||||
handler: server.AttestationRewards,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/rewards/sync_committee/{block_id}",
|
||||
name: namespace + ".SyncCommitteeRewards",
|
||||
handler: server.SyncCommitteeRewards,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) builderEndpoints(stater lookup.Stater) []endpoint {
|
||||
server := &builder.Server{
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
Stater: stater,
|
||||
}
|
||||
|
||||
const namespace = "builder"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/builder/states/{state_id}/expected_withdrawals",
|
||||
name: namespace + ".ExpectedWithdrawals",
|
||||
handler: server.ExpectedWithdrawals,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) blobEndpoints(blocker lookup.Blocker) []endpoint {
|
||||
server := &blob.Server{
|
||||
Blocker: blocker,
|
||||
}
|
||||
|
||||
const namespace = "blob"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/beacon/blob_sidecars/{block_id}",
|
||||
name: namespace + ".Blobs",
|
||||
handler: server.Blobs,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) validatorEndpoints(
|
||||
validatorServer *validatorv1alpha1.Server,
|
||||
stater lookup.Stater,
|
||||
coreService *core.Service,
|
||||
rewardFetcher rewards.BlockRewardsFetcher,
|
||||
) []endpoint {
|
||||
server := &validator.Server{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
V1Alpha1Server: validatorServer,
|
||||
Stater: stater,
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
BlockBuilder: s.cfg.BlockBuilder,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
}
|
||||
|
||||
const namespace = "validator"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/validator/aggregate_attestation",
|
||||
name: namespace + ".GetAggregateAttestation",
|
||||
handler: server.GetAggregateAttestation,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/contribution_and_proofs",
|
||||
name: namespace + ".SubmitContributionAndProofs",
|
||||
handler: server.SubmitContributionAndProofs,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/aggregate_and_proofs",
|
||||
name: namespace + ".SubmitAggregateAndProofs",
|
||||
handler: server.SubmitAggregateAndProofs,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/sync_committee_contribution",
|
||||
name: namespace + ".ProduceSyncCommitteeContribution",
|
||||
handler: server.ProduceSyncCommitteeContribution,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/sync_committee_subscriptions",
|
||||
name: namespace + ".SubmitSyncCommitteeSubscription",
|
||||
handler: server.SubmitSyncCommitteeSubscription,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/beacon_committee_subscriptions",
|
||||
name: namespace + ".SubmitBeaconCommitteeSubscription",
|
||||
handler: server.SubmitBeaconCommitteeSubscription,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/attestation_data",
|
||||
name: namespace + ".GetAttestationData",
|
||||
handler: server.GetAttestationData,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/register_validator",
|
||||
name: namespace + ".RegisterValidator",
|
||||
handler: server.RegisterValidator,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/duties/attester/{epoch}",
|
||||
name: namespace + ".GetAttesterDuties",
|
||||
handler: server.GetAttesterDuties,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/duties/proposer/{epoch}",
|
||||
name: namespace + ".GetProposerDuties",
|
||||
handler: server.GetProposerDuties,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/duties/sync/{epoch}",
|
||||
name: namespace + ".GetSyncCommitteeDuties",
|
||||
handler: server.GetSyncCommitteeDuties,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/prepare_beacon_proposer",
|
||||
name: namespace + ".PrepareBeaconProposer",
|
||||
handler: server.PrepareBeaconProposer,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/liveness/{epoch}",
|
||||
name: namespace + ".GetLiveness",
|
||||
handler: server.GetLiveness,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/validator/blocks/{slot}",
|
||||
name: namespace + ".ProduceBlockV2",
|
||||
handler: server.ProduceBlockV2,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/blinded_blocks/{slot}",
|
||||
name: namespace + ".ProduceBlindedBlock",
|
||||
handler: server.ProduceBlindedBlock,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v3/validator/blocks/{slot}",
|
||||
name: namespace + ".ProduceBlockV3",
|
||||
handler: server.ProduceBlockV3,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/beacon_committee_selections",
|
||||
name: namespace + ".BeaconCommitteeSelections",
|
||||
handler: server.BeaconCommitteeSelections,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/sync_committee_selections",
|
||||
name: namespace + ".SyncCommittee Selections",
|
||||
handler: server.SyncCommitteeSelections,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) nodeEndpoints() []endpoint {
|
||||
server := &node.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
Server: s.grpcServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
PeersFetcher: s.cfg.PeersFetcher,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
MetadataProvider: s.cfg.MetadataProvider,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ExecutionChainInfoFetcher: s.cfg.ExecutionChainInfoFetcher,
|
||||
}
|
||||
|
||||
const namespace = "node"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/node/syncing",
|
||||
name: namespace + ".GetSyncStatus",
|
||||
handler: server.GetSyncStatus,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/identity",
|
||||
name: namespace + ".GetIdentity",
|
||||
handler: server.GetIdentity,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/peers/{peer_id}",
|
||||
name: namespace + ".GetPeer",
|
||||
handler: server.GetPeer,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/peers",
|
||||
name: namespace + ".GetPeers",
|
||||
handler: server.GetPeers,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/peer_count",
|
||||
name: namespace + ".GetPeerCount",
|
||||
handler: server.GetPeerCount,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/version",
|
||||
name: namespace + ".GetVersion",
|
||||
handler: server.GetVersion,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/health",
|
||||
name: namespace + ".GetHealth",
|
||||
handler: server.GetHealth,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) beaconEndpoints(
|
||||
ch *stategen.CanonicalHistory,
|
||||
stater lookup.Stater,
|
||||
blocker lookup.Blocker,
|
||||
validatorServer *validatorv1alpha1.Server,
|
||||
coreService *core.Service,
|
||||
) []endpoint {
|
||||
server := &beacon.Server{
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
SlashingsPool: s.cfg.SlashingsPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
BlockNotifier: s.cfg.BlockNotifier,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
BlockReceiver: s.cfg.BlockReceiver,
|
||||
StateGenService: s.cfg.StateGen,
|
||||
Stater: stater,
|
||||
Blocker: blocker,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
VoluntaryExitsPool: s.cfg.ExitPool,
|
||||
V1Alpha1ValidatorServer: validatorServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
ExecutionPayloadReconstructor: s.cfg.ExecutionPayloadReconstructor,
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
CoreService: coreService,
|
||||
}
|
||||
|
||||
const namespace = "beacon"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/committees",
|
||||
name: namespace + ".GetCommittees",
|
||||
handler: server.GetCommittees,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/fork",
|
||||
name: namespace + ".GetStateFork",
|
||||
handler: server.GetStateFork,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/root",
|
||||
name: namespace + ".GetStateRoot",
|
||||
handler: server.GetStateRoot,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/sync_committees",
|
||||
name: namespace + ".GetSyncCommittees",
|
||||
handler: server.GetSyncCommittees,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/randao",
|
||||
name: namespace + ".GetRandao",
|
||||
handler: server.GetRandao,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/blocks",
|
||||
name: namespace + ".PublishBlock",
|
||||
handler: server.PublishBlock,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/blinded_blocks",
|
||||
name: namespace + ".PublishBlindedBlock",
|
||||
handler: server.PublishBlindedBlock,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/beacon/blocks",
|
||||
name: namespace + ".PublishBlockV2",
|
||||
handler: server.PublishBlockV2,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/beacon/blinded_blocks",
|
||||
name: namespace + ".PublishBlindedBlockV2",
|
||||
handler: server.PublishBlindedBlockV2,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/beacon/blocks/{block_id}",
|
||||
name: namespace + ".GetBlockV2",
|
||||
handler: server.GetBlockV2,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/blocks/{block_id}/attestations",
|
||||
name: namespace + ".GetBlockAttestations",
|
||||
handler: server.GetBlockAttestations,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/blinded_blocks/{block_id}",
|
||||
name: namespace + ".GetBlindedBlock",
|
||||
handler: server.GetBlindedBlock,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/blocks/{block_id}/root",
|
||||
name: namespace + ".GetBlockRoot",
|
||||
handler: server.GetBlockRoot,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/attestations",
|
||||
name: namespace + ".ListAttestations",
|
||||
handler: server.ListAttestations,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/attestations",
|
||||
name: namespace + ".SubmitAttestations",
|
||||
handler: server.SubmitAttestations,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/voluntary_exits",
|
||||
name: namespace + ".ListVoluntaryExits",
|
||||
handler: server.ListVoluntaryExits,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/voluntary_exits",
|
||||
name: namespace + ".SubmitVoluntaryExit",
|
||||
handler: server.SubmitVoluntaryExit,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/sync_committees",
|
||||
name: namespace + ".SubmitSyncCommitteeSignatures",
|
||||
handler: server.SubmitSyncCommitteeSignatures,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/bls_to_execution_changes",
|
||||
name: namespace + ".ListBLSToExecutionChanges",
|
||||
handler: server.ListBLSToExecutionChanges,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/bls_to_execution_changes",
|
||||
name: namespace + ".SubmitBLSToExecutionChanges",
|
||||
handler: server.SubmitBLSToExecutionChanges,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/attester_slashings",
|
||||
name: namespace + ".GetAttesterSlashings",
|
||||
handler: server.GetAttesterSlashings,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/attester_slashings",
|
||||
name: namespace + ".SubmitAttesterSlashing",
|
||||
handler: server.SubmitAttesterSlashing,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/proposer_slashings",
|
||||
name: namespace + ".GetProposerSlashings",
|
||||
handler: server.GetProposerSlashings,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/proposer_slashings",
|
||||
name: namespace + ".SubmitProposerSlashing",
|
||||
handler: server.SubmitProposerSlashing,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/headers",
|
||||
name: namespace + ".GetBlockHeaders",
|
||||
handler: server.GetBlockHeaders,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/headers/{block_id}",
|
||||
name: namespace + ".GetBlockHeader",
|
||||
handler: server.GetBlockHeader,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/genesis",
|
||||
name: namespace + ".GetGenesis",
|
||||
handler: server.GetGenesis,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/finality_checkpoints",
|
||||
name: namespace + ".GetFinalityCheckpoints",
|
||||
handler: server.GetFinalityCheckpoints,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/validators",
|
||||
name: namespace + ".GetValidators",
|
||||
handler: server.GetValidators,
|
||||
methods: []string{http.MethodGet, http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/validators/{validator_id}",
|
||||
name: namespace + ".GetValidator",
|
||||
handler: server.GetValidator,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/validator_balances",
|
||||
name: namespace + ".GetValidatorBalances",
|
||||
handler: server.GetValidatorBalances,
|
||||
methods: []string{http.MethodGet, http.MethodPost},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) configEndpoints() []endpoint {
|
||||
const namespace = "config"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/config/deposit_contract",
|
||||
name: namespace + ".GetDepositContract",
|
||||
handler: config.GetDepositContract,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/config/fork_schedule",
|
||||
name: namespace + ".GetForkSchedule",
|
||||
handler: config.GetForkSchedule,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/config/spec",
|
||||
name: namespace + ".GetSpec",
|
||||
handler: config.GetSpec,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) lightClientEndpoints(blocker lookup.Blocker, stater lookup.Stater) []endpoint {
|
||||
server := &lightclient.Server{
|
||||
Blocker: blocker,
|
||||
Stater: stater,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
}
|
||||
|
||||
const namespace = "lightclient"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/beacon/light_client/bootstrap/{block_root}",
|
||||
name: namespace + ".GetLightClientBootstrap",
|
||||
handler: server.GetLightClientBootstrap,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/light_client/updates",
|
||||
name: namespace + ".GetLightClientUpdatesByRange",
|
||||
handler: server.GetLightClientUpdatesByRange,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/light_client/finality_update",
|
||||
name: namespace + ".GetLightClientFinalityUpdate",
|
||||
handler: server.GetLightClientFinalityUpdate,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/light_client/optimistic_update",
|
||||
name: namespace + ".GetLightClientOptimisticUpdate",
|
||||
handler: server.GetLightClientOptimisticUpdate,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) debugEndpoints(stater lookup.Stater) []endpoint {
|
||||
server := &debug.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
Stater: stater,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
ForkFetcher: s.cfg.ForkFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
}
|
||||
|
||||
const namespace = "debug"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v2/debug/beacon/states/{state_id}",
|
||||
name: namespace + ".GetBeaconStateV2",
|
||||
handler: server.GetBeaconStateV2,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/debug/beacon/heads",
|
||||
name: namespace + ".GetForkChoiceHeadsV2",
|
||||
handler: server.GetForkChoiceHeadsV2,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/debug/fork_choice",
|
||||
name: namespace + ".GetForkChoice",
|
||||
handler: server.GetForkChoice,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) eventsEndpoints() []endpoint {
|
||||
server := &events.Server{
|
||||
StateNotifier: s.cfg.StateNotifier,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
}
|
||||
|
||||
const namespace = "events"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/eth/v1/events",
|
||||
name: namespace + ".StreamEvents",
|
||||
handler: server.StreamEvents,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Prysm custom endpoints
|
||||
|
||||
func (s *Service) prysmBeaconEndpoints(ch *stategen.CanonicalHistory, stater lookup.Stater) []endpoint {
|
||||
server := &beaconprysm.Server{
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
Stater: stater,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
}
|
||||
|
||||
const namespace = "prysm.beacon"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/prysm/v1/beacon/weak_subjectivity",
|
||||
name: namespace + ".GetWeakSubjectivity",
|
||||
handler: server.GetWeakSubjectivity,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/states/{state_id}/validator_count",
|
||||
name: namespace + ".GetValidatorCount",
|
||||
handler: server.GetValidatorCount,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/prysm/v1/beacon/states/{state_id}/validator_count",
|
||||
name: namespace + ".GetValidatorCount",
|
||||
handler: server.GetValidatorCount,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) prysmNodeEndpoints() []endpoint {
|
||||
server := &nodeprysm.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
PeersFetcher: s.cfg.PeersFetcher,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
MetadataProvider: s.cfg.MetadataProvider,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ExecutionChainInfoFetcher: s.cfg.ExecutionChainInfoFetcher,
|
||||
}
|
||||
|
||||
const namespace = "prysm.node"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/prysm/node/trusted_peers",
|
||||
name: namespace + ".ListTrustedPeer",
|
||||
handler: server.ListTrustedPeer,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/prysm/v1/node/trusted_peers",
|
||||
name: namespace + ".ListTrustedPeer",
|
||||
handler: server.ListTrustedPeer,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/prysm/node/trusted_peers",
|
||||
name: namespace + ".AddTrustedPeer",
|
||||
handler: server.AddTrustedPeer,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/prysm/v1/node/trusted_peers",
|
||||
name: namespace + ".AddTrustedPeer",
|
||||
handler: server.AddTrustedPeer,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/prysm/node/trusted_peers/{peer_id}",
|
||||
name: namespace + ".RemoveTrustedPeer",
|
||||
handler: server.RemoveTrustedPeer,
|
||||
methods: []string{http.MethodDelete},
|
||||
},
|
||||
{
|
||||
template: "/prysm/v1/node/trusted_peers/{peer_id}",
|
||||
name: namespace + ".RemoveTrustedPeer",
|
||||
handler: server.RemoveTrustedPeer,
|
||||
methods: []string{http.MethodDelete},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) prysmValidatorEndpoints(coreService *core.Service, stater lookup.Stater) []endpoint {
|
||||
server := &validatorprysm.Server{
|
||||
CoreService: coreService,
|
||||
}
|
||||
|
||||
const namespace = "prysm.validator"
|
||||
return []endpoint{
|
||||
{
|
||||
template: "/prysm/validators/performance",
|
||||
name: namespace + ".GetValidatorPerformance",
|
||||
handler: server.GetValidatorPerformance,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/prysm/v1/validators/performance",
|
||||
name: namespace + ".GetValidatorPerformance",
|
||||
handler: server.GetValidatorPerformance,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
}
|
||||
}
|
||||
150
beacon-chain/rpc/endpoints_test.go
Normal file
150
beacon-chain/rpc/endpoints_test.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
)
|
||||
|
||||
func Test_endpoints(t *testing.T) {
|
||||
rewardsRoutes := map[string][]string{
|
||||
"/eth/v1/beacon/rewards/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/rewards/attestations/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/beacon/rewards/sync_committee/{block_id}": {http.MethodPost},
|
||||
}
|
||||
|
||||
beaconRoutes := map[string][]string{
|
||||
"/eth/v1/beacon/genesis": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/fork": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/finality_checkpoints": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validators": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/validators/{validator_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validator_balances": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/sync_committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/randao": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v1/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v1/beacon/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v2/beacon/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
|
||||
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
|
||||
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
|
||||
}
|
||||
|
||||
lightClientRoutes := map[string][]string{
|
||||
"/eth/v1/beacon/light_client/bootstrap/{block_root}": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/updates": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/finality_update": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/optimistic_update": {http.MethodGet},
|
||||
}
|
||||
|
||||
builderRoutes := map[string][]string{
|
||||
"/eth/v1/builder/states/{state_id}/expected_withdrawals": {http.MethodGet},
|
||||
}
|
||||
|
||||
blobRoutes := map[string][]string{
|
||||
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
|
||||
}
|
||||
|
||||
configRoutes := map[string][]string{
|
||||
"/eth/v1/config/fork_schedule": {http.MethodGet},
|
||||
"/eth/v1/config/spec": {http.MethodGet},
|
||||
"/eth/v1/config/deposit_contract": {http.MethodGet},
|
||||
}
|
||||
|
||||
debugRoutes := map[string][]string{
|
||||
"/eth/v1/debug/beacon/states/{state_id}": {http.MethodGet},
|
||||
"/eth/v2/debug/beacon/states/{state_id}": {http.MethodGet},
|
||||
"/eth/v2/debug/beacon/heads": {http.MethodGet},
|
||||
"/eth/v1/debug/fork_choice": {http.MethodGet},
|
||||
}
|
||||
|
||||
eventsRoutes := map[string][]string{
|
||||
"/eth/v1/events": {http.MethodGet},
|
||||
}
|
||||
|
||||
nodeRoutes := map[string][]string{
|
||||
"/eth/v1/node/identity": {http.MethodGet},
|
||||
"/eth/v1/node/peers": {http.MethodGet},
|
||||
"/eth/v1/node/peers/{peer_id}": {http.MethodGet},
|
||||
"/eth/v1/node/peer_count": {http.MethodGet},
|
||||
"/eth/v1/node/version": {http.MethodGet},
|
||||
"/eth/v1/node/syncing": {http.MethodGet},
|
||||
"/eth/v1/node/health": {http.MethodGet},
|
||||
}
|
||||
|
||||
validatorRoutes := map[string][]string{
|
||||
"/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet},
|
||||
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
|
||||
"/eth/v2/validator/blocks/{slot}": {http.MethodGet},
|
||||
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
|
||||
"/eth/v1/validator/blinded_blocks/{slot}": {http.MethodGet},
|
||||
"/eth/v1/validator/attestation_data": {http.MethodGet},
|
||||
"/eth/v1/validator/aggregate_attestation": {http.MethodGet},
|
||||
"/eth/v1/validator/aggregate_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_contribution": {http.MethodGet},
|
||||
"/eth/v1/validator/contribution_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost},
|
||||
"/eth/v1/validator/register_validator": {http.MethodPost},
|
||||
"/eth/v1/validator/liveness/{epoch}": {http.MethodPost},
|
||||
}
|
||||
|
||||
prysmBeaconRoutes := map[string][]string{
|
||||
"/prysm/v1/beacon/weak_subjectivity": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validator_count": {http.MethodGet},
|
||||
"/prysm/v1/beacon/states/{state_id}/validator_count": {http.MethodGet},
|
||||
}
|
||||
|
||||
prysmNodeRoutes := map[string][]string{
|
||||
"/prysm/node/trusted_peers": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/v1/node/trusted_peers": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/node/trusted_peers/{peer_id}": {http.MethodDelete},
|
||||
"/prysm/v1/node/trusted_peers/{peer_id}": {http.MethodDelete},
|
||||
}
|
||||
|
||||
prysmValidatorRoutes := map[string][]string{
|
||||
"/prysm/validators/performance": {http.MethodPost},
|
||||
"/prysm/v1/validators/performance": {http.MethodPost},
|
||||
}
|
||||
|
||||
s := &Service{cfg: &Config{}}
|
||||
|
||||
routesMap := combineMaps(beaconRoutes, builderRoutes, configRoutes, debugRoutes, eventsRoutes, nodeRoutes, validatorRoutes, rewardsRoutes, lightClientRoutes, blobRoutes, prysmValidatorRoutes, prysmNodeRoutes, prysmBeaconRoutes)
|
||||
actual := s.endpoints(true, nil, nil, nil, nil, nil, nil)
|
||||
for _, e := range actual {
|
||||
methods, ok := routesMap[e.template]
|
||||
assert.Equal(t, true, ok, "endpoint "+e.template+" not found")
|
||||
if ok {
|
||||
for _, em := range e.methods {
|
||||
methodFound := false
|
||||
for _, m := range methods {
|
||||
if m == em {
|
||||
methodFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, methodFound, "method "+em+" for endpoint "+e.template+" not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,13 +115,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return vs.constructGenericBeaconBlock(sBlk, bundleCache.get(req.Slot))
|
||||
}
|
||||
|
||||
func (vs *Server) handleFailedReorgAttempt(ctx context.Context, slot primitives.Slot, parentRoot, headRoot [32]byte) (state.BeaconState, error) {
|
||||
blockchain.LateBlockAttemptedReorgCount.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": slot,
|
||||
"parentRoot": fmt.Sprintf("%#x", parentRoot),
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
}).Warn("late block attempted reorg failed")
|
||||
func (vs *Server) handleSuccesfulReorgAttempt(ctx context.Context, slot primitives.Slot, parentRoot, headRoot [32]byte) (state.BeaconState, error) {
|
||||
// Try to get the state from the NSC
|
||||
head := transition.NextSlotState(parentRoot[:], slot)
|
||||
if head != nil {
|
||||
@@ -135,7 +129,16 @@ func (vs *Server) handleFailedReorgAttempt(ctx context.Context, slot primitives.
|
||||
return head, nil
|
||||
}
|
||||
|
||||
func (vs *Server) getHeadNoFailedReorg(ctx context.Context, slot primitives.Slot, parentRoot [32]byte) (state.BeaconState, error) {
|
||||
func logFailedReorgAttempt(slot primitives.Slot, oldHeadRoot, headRoot [32]byte) {
|
||||
blockchain.LateBlockAttemptedReorgCount.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": slot,
|
||||
"oldHeadRoot": fmt.Sprintf("%#x", oldHeadRoot),
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
}).Warn("late block attempted reorg failed")
|
||||
}
|
||||
|
||||
func (vs *Server) getHeadNoReorg(ctx context.Context, slot primitives.Slot, parentRoot [32]byte) (state.BeaconState, error) {
|
||||
// Try to get the state from the NSC
|
||||
head := transition.NextSlotState(parentRoot[:], slot)
|
||||
if head != nil {
|
||||
@@ -148,11 +151,14 @@ func (vs *Server) getHeadNoFailedReorg(ctx context.Context, slot primitives.Slot
|
||||
return head, nil
|
||||
}
|
||||
|
||||
func (vs *Server) getParentStateFromReorgData(ctx context.Context, slot primitives.Slot, parentRoot, headRoot [32]byte) (head state.BeaconState, err error) {
|
||||
func (vs *Server) getParentStateFromReorgData(ctx context.Context, slot primitives.Slot, oldHeadRoot, parentRoot, headRoot [32]byte) (head state.BeaconState, err error) {
|
||||
if parentRoot != headRoot {
|
||||
head, err = vs.handleFailedReorgAttempt(ctx, slot, parentRoot, headRoot)
|
||||
head, err = vs.handleSuccesfulReorgAttempt(ctx, slot, parentRoot, headRoot)
|
||||
} else {
|
||||
head, err = vs.getHeadNoFailedReorg(ctx, slot, parentRoot)
|
||||
if oldHeadRoot != headRoot {
|
||||
logFailedReorgAttempt(slot, oldHeadRoot, headRoot)
|
||||
}
|
||||
head, err = vs.getHeadNoReorg(ctx, slot, parentRoot)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -169,10 +175,11 @@ func (vs *Server) getParentStateFromReorgData(ctx context.Context, slot primitiv
|
||||
|
||||
func (vs *Server) getParentState(ctx context.Context, slot primitives.Slot) (state.BeaconState, [32]byte, error) {
|
||||
// process attestations and update head in forkchoice
|
||||
oldHeadRoot := vs.ForkchoiceFetcher.CachedHeadRoot()
|
||||
vs.ForkchoiceFetcher.UpdateHead(ctx, vs.TimeFetcher.CurrentSlot())
|
||||
headRoot := vs.ForkchoiceFetcher.CachedHeadRoot()
|
||||
parentRoot := vs.ForkchoiceFetcher.GetProposerHead()
|
||||
head, err := vs.getParentStateFromReorgData(ctx, slot, parentRoot, headRoot)
|
||||
head, err := vs.getParentStateFromReorgData(ctx, slot, oldHeadRoot, parentRoot, headRoot)
|
||||
return head, parentRoot, err
|
||||
}
|
||||
|
||||
|
||||
@@ -2874,8 +2874,8 @@ func TestProposer_GetParentHeadState(t *testing.T) {
|
||||
Eth1BlockFetcher: &mockExecution.Chain{},
|
||||
StateGen: stategen.New(db, doublylinkedtree.New()),
|
||||
}
|
||||
t.Run("failed reorg", func(tt *testing.T) {
|
||||
head, err := proposerServer.getParentStateFromReorgData(ctx, 1, parentRoot, headRoot)
|
||||
t.Run("successful reorg", func(tt *testing.T) {
|
||||
head, err := proposerServer.getParentStateFromReorgData(ctx, 1, parentRoot, parentRoot, headRoot)
|
||||
require.NoError(t, err)
|
||||
st := parentState.Copy()
|
||||
st, err = transition.ProcessSlots(ctx, st, st.Slot()+1)
|
||||
@@ -2892,7 +2892,7 @@ func TestProposer_GetParentHeadState(t *testing.T) {
|
||||
|
||||
t.Run("no reorg", func(tt *testing.T) {
|
||||
require.NoError(t, transition.UpdateNextSlotCache(ctx, headRoot[:], headState))
|
||||
head, err := proposerServer.getParentStateFromReorgData(ctx, 1, headRoot, headRoot)
|
||||
head, err := proposerServer.getParentStateFromReorgData(ctx, 1, headRoot, headRoot, headRoot)
|
||||
require.NoError(t, err)
|
||||
st := headState.Copy()
|
||||
st, err = transition.ProcessSlots(ctx, st, st.Slot()+1)
|
||||
@@ -2906,4 +2906,23 @@ func TestProposer_GetParentHeadState(t *testing.T) {
|
||||
require.Equal(t, [32]byte(str), [32]byte(headStr))
|
||||
require.NotEqual(t, [32]byte(str), [32]byte(genesisStr))
|
||||
})
|
||||
|
||||
t.Run("failed reorg", func(tt *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
require.NoError(t, transition.UpdateNextSlotCache(ctx, headRoot[:], headState))
|
||||
head, err := proposerServer.getParentStateFromReorgData(ctx, 1, parentRoot, headRoot, headRoot)
|
||||
require.NoError(t, err)
|
||||
st := headState.Copy()
|
||||
st, err = transition.ProcessSlots(ctx, st, st.Slot()+1)
|
||||
require.NoError(t, err)
|
||||
str, err := st.StateRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
headStr, err := head.StateRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
genesisStr, err := parentState.StateRootAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte(str), [32]byte(headStr))
|
||||
require.NotEqual(t, [32]byte(str), [32]byte(genesisStr))
|
||||
require.LogsContain(t, hook, "late block attempted reorg failed")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
@@ -15,6 +14,9 @@ import (
|
||||
grpcopentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
|
||||
grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/builder"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
|
||||
@@ -32,24 +34,12 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/beacon"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/blob"
|
||||
rpcBuilder "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/builder"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/config"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/debug"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/events"
|
||||
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/light-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/node"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/rewards"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/lookup"
|
||||
beaconprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/beacon"
|
||||
nodeprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/node"
|
||||
beaconv1alpha1 "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/v1alpha1/beacon"
|
||||
debugv1alpha1 "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/v1alpha1/debug"
|
||||
nodev1alpha1 "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/v1alpha1/node"
|
||||
validatorv1alpha1 "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/v1alpha1/validator"
|
||||
validatorprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
|
||||
chainSync "github.com/prysmaticlabs/prysm/v5/beacon-chain/sync"
|
||||
@@ -68,6 +58,24 @@ import (
|
||||
|
||||
const attestationBufferSize = 100
|
||||
|
||||
var (
|
||||
httpRequestLatency = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "http_request_latency_seconds",
|
||||
Help: "Latency of HTTP requests in seconds",
|
||||
Buckets: []float64{0.001, 0.01, 0.025, 0.1, 0.25, 1, 2.5, 10},
|
||||
},
|
||||
[]string{"endpoint", "code", "method"},
|
||||
)
|
||||
httpRequestCount = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "http_request_count",
|
||||
Help: "Number of HTTP requests",
|
||||
},
|
||||
[]string{"endpoint", "code", "method"},
|
||||
)
|
||||
)
|
||||
|
||||
// Service defining an RPC server for a beacon node.
|
||||
type Service struct {
|
||||
cfg *Config
|
||||
@@ -208,25 +216,6 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
BlobStorage: s.cfg.BlobStorage,
|
||||
}
|
||||
rewardFetcher := &rewards.BlockRewardService{Replayer: ch}
|
||||
|
||||
s.initializeRewardServerRoutes(&rewards.Server{
|
||||
Blocker: blocker,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
Stater: stater,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
})
|
||||
s.initializeBuilderServerRoutes(&rpcBuilder.Server{
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
Stater: stater,
|
||||
})
|
||||
s.initializeBlobServerRoutes(&blob.Server{
|
||||
Blocker: blocker,
|
||||
})
|
||||
|
||||
coreService := &core.Service{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
@@ -240,7 +229,6 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
FinalizedFetcher: s.cfg.FinalizationFetcher,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
}
|
||||
|
||||
validatorServer := &validatorv1alpha1.Server{
|
||||
Ctx: s.ctx,
|
||||
AttPool: s.cfg.AttestationsPool,
|
||||
@@ -280,26 +268,6 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
}
|
||||
s.validatorServer = validatorServer
|
||||
s.initializeValidatorServerRoutes(&validator.Server{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
V1Alpha1Server: validatorServer,
|
||||
Stater: stater,
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
BlockBuilder: s.cfg.BlockBuilder,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
})
|
||||
nodeServer := &nodev1alpha1.Server{
|
||||
LogsStreamer: logs.NewStreamServer(),
|
||||
StreamLogsBufferSize: 1000, // Enough to handle bursts of beacon node logs for gRPC streaming.
|
||||
@@ -314,19 +282,6 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
BeaconMonitoringHost: s.cfg.BeaconMonitoringHost,
|
||||
BeaconMonitoringPort: s.cfg.BeaconMonitoringPort,
|
||||
}
|
||||
s.initializeNodeServerRoutes(&node.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
Server: s.grpcServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
PeersFetcher: s.cfg.PeersFetcher,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
MetadataProvider: s.cfg.MetadataProvider,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ExecutionChainInfoFetcher: s.cfg.ExecutionChainInfoFetcher,
|
||||
})
|
||||
|
||||
beaconChainServer := &beaconv1alpha1.Server{
|
||||
Ctx: s.ctx,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
@@ -352,52 +307,23 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
CoreService: coreService,
|
||||
}
|
||||
|
||||
s.initializeBeaconServerRoutes(&beacon.Server{
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
SlashingsPool: s.cfg.SlashingsPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
BlockNotifier: s.cfg.BlockNotifier,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
BlockReceiver: s.cfg.BlockReceiver,
|
||||
StateGenService: s.cfg.StateGen,
|
||||
Stater: stater,
|
||||
Blocker: blocker,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
VoluntaryExitsPool: s.cfg.ExitPool,
|
||||
V1Alpha1ValidatorServer: validatorServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
ExecutionPayloadReconstructor: s.cfg.ExecutionPayloadReconstructor,
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
CoreService: coreService,
|
||||
})
|
||||
|
||||
s.initializeConfigRoutes()
|
||||
|
||||
s.initializeEventsServerRoutes(&events.Server{
|
||||
StateNotifier: s.cfg.StateNotifier,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
})
|
||||
|
||||
s.initializeLightClientServerRoutes(&lightclient.Server{
|
||||
Blocker: blocker,
|
||||
Stater: stater,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
})
|
||||
endpoints := s.endpoints(s.cfg.EnableDebugRPCEndpoints, blocker, stater, rewardFetcher, validatorServer, coreService, ch)
|
||||
for _, e := range endpoints {
|
||||
s.cfg.Router.HandleFunc(
|
||||
e.template,
|
||||
promhttp.InstrumentHandlerDuration(
|
||||
httpRequestLatency.MustCurryWith(prometheus.Labels{"endpoint": e.name}),
|
||||
promhttp.InstrumentHandlerCounter(
|
||||
httpRequestCount.MustCurryWith(prometheus.Labels{"endpoint": e.name}),
|
||||
e.handler,
|
||||
),
|
||||
),
|
||||
).Methods(e.methods...)
|
||||
}
|
||||
|
||||
ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer)
|
||||
ethpbv1alpha1.RegisterHealthServer(s.grpcServer, nodeServer)
|
||||
ethpbv1alpha1.RegisterBeaconChainServer(s.grpcServer, beaconChainServer)
|
||||
|
||||
if s.cfg.EnableDebugRPCEndpoints {
|
||||
log.Info("Enabled debug gRPC endpoints")
|
||||
debugServer := &debugv1alpha1.Server{
|
||||
@@ -409,47 +335,12 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
PeersFetcher: s.cfg.PeersFetcher,
|
||||
ReplayerBuilder: ch,
|
||||
}
|
||||
s.initializeDebugServerRoutes(&debug.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
Stater: stater,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
ForkFetcher: s.cfg.ForkFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
})
|
||||
ethpbv1alpha1.RegisterDebugServer(s.grpcServer, debugServer)
|
||||
}
|
||||
ethpbv1alpha1.RegisterBeaconNodeValidatorServer(s.grpcServer, validatorServer)
|
||||
// Register reflection service on gRPC server.
|
||||
reflection.Register(s.grpcServer)
|
||||
|
||||
s.initializePrysmBeaconServerRoutes(&beaconprysm.Server{
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
Stater: stater,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
})
|
||||
|
||||
s.initializePrysmNodeServerRoutes(&nodeprysm.Server{
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
PeersFetcher: s.cfg.PeersFetcher,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
MetadataProvider: s.cfg.MetadataProvider,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ExecutionChainInfoFetcher: s.cfg.ExecutionChainInfoFetcher,
|
||||
})
|
||||
s.initializePrysmValidatorServerRoutes(&validatorprysm.Server{CoreService: coreService})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -457,130 +348,6 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
var _ stategen.CanonicalChecker = blockchain.ChainInfoFetcher(nil)
|
||||
var _ stategen.CurrentSlotter = blockchain.ChainInfoFetcher(nil)
|
||||
|
||||
func (s *Service) initializeRewardServerRoutes(rewardsServer *rewards.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/blocks/{block_id}", rewardsServer.BlockRewards).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/attestations/{epoch}", rewardsServer.AttestationRewards).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/sync_committee/{block_id}", rewardsServer.SyncCommitteeRewards).Methods(http.MethodPost)
|
||||
}
|
||||
|
||||
func (s *Service) initializeBuilderServerRoutes(builderServer *rpcBuilder.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/builder/states/{state_id}/expected_withdrawals", builderServer.ExpectedWithdrawals).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializeBlobServerRoutes(blobServer *blob.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/blob_sidecars/{block_id}", blobServer.Blobs).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializeValidatorServerRoutes(validatorServer *validator.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/aggregate_attestation", validatorServer.GetAggregateAttestation).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/contribution_and_proofs", validatorServer.SubmitContributionAndProofs).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/aggregate_and_proofs", validatorServer.SubmitAggregateAndProofs).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/sync_committee_contribution", validatorServer.ProduceSyncCommitteeContribution).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/sync_committee_subscriptions", validatorServer.SubmitSyncCommitteeSubscription).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/beacon_committee_subscriptions", validatorServer.SubmitBeaconCommitteeSubscription).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/attestation_data", validatorServer.GetAttestationData).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/register_validator", validatorServer.RegisterValidator).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/duties/attester/{epoch}", validatorServer.GetAttesterDuties).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/duties/proposer/{epoch}", validatorServer.GetProposerDuties).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/duties/sync/{epoch}", validatorServer.GetSyncCommitteeDuties).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/prepare_beacon_proposer", validatorServer.PrepareBeaconProposer).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/liveness/{epoch}", validatorServer.GetLiveness).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v2/validator/blocks/{slot}", validatorServer.ProduceBlockV2).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/blinded_blocks/{slot}", validatorServer.ProduceBlindedBlock).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v3/validator/blocks/{slot}", validatorServer.ProduceBlockV3).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/beacon_committee_selections", validatorServer.BeaconCommitteeSelections).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/validator/sync_committee_selections", validatorServer.SyncCommitteeSelections).Methods(http.MethodPost)
|
||||
}
|
||||
|
||||
func (s *Service) initializeNodeServerRoutes(nodeServer *node.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/syncing", nodeServer.GetSyncStatus).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/identity", nodeServer.GetIdentity).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/peers/{peer_id}", nodeServer.GetPeer).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/peers", nodeServer.GetPeers).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/peer_count", nodeServer.GetPeerCount).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/version", nodeServer.GetVersion).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/node/health", nodeServer.GetHealth).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializeBeaconServerRoutes(beaconServer *beacon.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/committees", beaconServer.GetCommittees).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/fork", beaconServer.GetStateFork).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/root", beaconServer.GetStateRoot).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/sync_committees", beaconServer.GetSyncCommittees).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/randao", beaconServer.GetRandao).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks", beaconServer.PublishBlock).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/blinded_blocks", beaconServer.PublishBlindedBlock).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v2/beacon/blocks", beaconServer.PublishBlockV2).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v2/beacon/blinded_blocks", beaconServer.PublishBlindedBlockV2).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v2/beacon/blocks/{block_id}", beaconServer.GetBlockV2).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks/{block_id}/attestations", beaconServer.GetBlockAttestations).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/blinded_blocks/{block_id}", beaconServer.GetBlindedBlock).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/blocks/{block_id}/root", beaconServer.GetBlockRoot).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/deposit_snapshot", beaconServer.GetDepositSnapshot).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/attestations", beaconServer.ListAttestations).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/attestations", beaconServer.SubmitAttestations).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/voluntary_exits", beaconServer.ListVoluntaryExits).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/voluntary_exits", beaconServer.SubmitVoluntaryExit).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/sync_committees", beaconServer.SubmitSyncCommitteeSignatures).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/bls_to_execution_changes", beaconServer.ListBLSToExecutionChanges).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/bls_to_execution_changes", beaconServer.SubmitBLSToExecutionChanges).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/attester_slashings", beaconServer.GetAttesterSlashings).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/attester_slashings", beaconServer.SubmitAttesterSlashing).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/proposer_slashings", beaconServer.GetProposerSlashings).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/pool/proposer_slashings", beaconServer.SubmitProposerSlashing).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/headers", beaconServer.GetBlockHeaders).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/headers/{block_id}", beaconServer.GetBlockHeader).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/genesis", beaconServer.GetGenesis).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/finality_checkpoints", beaconServer.GetFinalityCheckpoints).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/validators", beaconServer.GetValidators).Methods(http.MethodGet, http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/validators/{validator_id}", beaconServer.GetValidator).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/validator_balances", beaconServer.GetValidatorBalances).Methods(http.MethodGet, http.MethodPost)
|
||||
}
|
||||
|
||||
func (s *Service) initializeConfigRoutes() {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/config/deposit_contract", config.GetDepositContract).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/config/fork_schedule", config.GetForkSchedule).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/config/spec", config.GetSpec).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializeEventsServerRoutes(eventsServer *events.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/events", eventsServer.StreamEvents).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializeLightClientServerRoutes(lightClientServer *lightclient.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/light_client/bootstrap/{block_root}", lightClientServer.GetLightClientBootstrap).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/light_client/updates", lightClientServer.GetLightClientUpdatesByRange).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/light_client/finality_update", lightClientServer.GetLightClientFinalityUpdate).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/light_client/optimistic_update", lightClientServer.GetLightClientOptimisticUpdate).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializeDebugServerRoutes(debugServer *debug.Server) {
|
||||
s.cfg.Router.HandleFunc("/eth/v2/debug/beacon/states/{state_id}", debugServer.GetBeaconStateV2).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v2/debug/beacon/heads", debugServer.GetForkChoiceHeadsV2).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/debug/fork_choice", debugServer.GetForkChoice).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
// prysm internal routes
|
||||
func (s *Service) initializePrysmBeaconServerRoutes(beaconServerPrysm *beaconprysm.Server) {
|
||||
s.cfg.Router.HandleFunc("/prysm/v1/beacon/weak_subjectivity", beaconServerPrysm.GetWeakSubjectivity).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/eth/v1/beacon/states/{state_id}/validator_count", beaconServerPrysm.GetValidatorCount).Methods(http.MethodGet) // TODO: deprecate in Swagger, remove in v6
|
||||
s.cfg.Router.HandleFunc("/prysm/v1/beacon/states/{state_id}/validator_count", beaconServerPrysm.GetValidatorCount).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Service) initializePrysmNodeServerRoutes(nodeServerPrysm *nodeprysm.Server) {
|
||||
s.cfg.Router.HandleFunc("/prysm/node/trusted_peers", nodeServerPrysm.ListTrustedPeer).Methods(http.MethodGet) // TODO: deprecate in Swagger, remove in v6
|
||||
s.cfg.Router.HandleFunc("/prysm/v1/node/trusted_peers", nodeServerPrysm.ListTrustedPeer).Methods(http.MethodGet)
|
||||
s.cfg.Router.HandleFunc("/prysm/node/trusted_peers", nodeServerPrysm.AddTrustedPeer).Methods(http.MethodPost) // TODO: deprecate in Swagger, remove in v6
|
||||
s.cfg.Router.HandleFunc("/prysm/v1/node/trusted_peers", nodeServerPrysm.AddTrustedPeer).Methods(http.MethodPost)
|
||||
s.cfg.Router.HandleFunc("/prysm/node/trusted_peers/{peer_id}", nodeServerPrysm.RemoveTrustedPeer).Methods(http.MethodDelete) // TODO: deprecate in Swagger, remove in v6
|
||||
s.cfg.Router.HandleFunc("/prysm/v1/node/trusted_peers/{peer_id}", nodeServerPrysm.RemoveTrustedPeer).Methods(http.MethodDelete)
|
||||
}
|
||||
|
||||
func (s *Service) initializePrysmValidatorServerRoutes(validatorServerPrysm *validatorprysm.Server) {
|
||||
s.cfg.Router.HandleFunc("/prysm/validator/performance", validatorServerPrysm.GetValidatorPerformance).Methods(http.MethodPost) // TODO: deprecate in Swagger, remove in v6
|
||||
s.cfg.Router.HandleFunc("/prysm/v1/validator/performance", validatorServerPrysm.GetValidatorPerformance).Methods(http.MethodPost)
|
||||
}
|
||||
|
||||
// Start the gRPC server.
|
||||
func (s *Service) Start() {
|
||||
grpcprometheus.EnableHandlingTimeHistogram()
|
||||
|
||||
@@ -4,25 +4,12 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/beacon"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/blob"
|
||||
rpcBuilder "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/builder"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/debug"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/events"
|
||||
lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/light-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/node"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/rewards"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/validator"
|
||||
beaconprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/beacon"
|
||||
nodeprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/node"
|
||||
validatorprysm "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
|
||||
mockSync "github.com/prysmaticlabs/prysm/v5/beacon-chain/sync/initial-sync/testing"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
@@ -48,148 +35,6 @@ func combineMaps(maps ...map[string][]string) map[string][]string {
|
||||
return combinedMap
|
||||
}
|
||||
|
||||
func TestServer_InitializeRoutes(t *testing.T) {
|
||||
s := Service{
|
||||
cfg: &Config{
|
||||
Router: mux.NewRouter(),
|
||||
},
|
||||
}
|
||||
s.initializeRewardServerRoutes(&rewards.Server{})
|
||||
s.initializeBuilderServerRoutes(&rpcBuilder.Server{})
|
||||
s.initializeBlobServerRoutes(&blob.Server{})
|
||||
s.initializeValidatorServerRoutes(&validator.Server{})
|
||||
s.initializeNodeServerRoutes(&node.Server{})
|
||||
s.initializeBeaconServerRoutes(&beacon.Server{})
|
||||
s.initializeConfigRoutes()
|
||||
s.initializeEventsServerRoutes(&events.Server{})
|
||||
s.initializeLightClientServerRoutes(&lightclient.Server{})
|
||||
s.initializeDebugServerRoutes(&debug.Server{})
|
||||
|
||||
//prysm internal
|
||||
s.initializePrysmBeaconServerRoutes(&beaconprysm.Server{})
|
||||
s.initializePrysmNodeServerRoutes(&nodeprysm.Server{})
|
||||
s.initializePrysmValidatorServerRoutes(&validatorprysm.Server{})
|
||||
|
||||
beaconRoutes := map[string][]string{
|
||||
"/eth/v1/beacon/genesis": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/fork": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/finality_checkpoints": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validators": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/validators/{validator_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/validator_balances": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/sync_committees": {http.MethodGet},
|
||||
"/eth/v1/beacon/states/{state_id}/randao": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers": {http.MethodGet},
|
||||
"/eth/v1/beacon/headers/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blinded_blocks": {http.MethodPost},
|
||||
"/eth/v1/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks": {http.MethodPost},
|
||||
"/eth/v2/beacon/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/root": {http.MethodGet},
|
||||
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
|
||||
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/rewards/sync_committee/{block_id}": {http.MethodPost},
|
||||
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
|
||||
"/eth/v1/beacon/rewards/blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/rewards/attestations/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/bootstrap/{block_root}": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/updates": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/finality_update": {http.MethodGet},
|
||||
"/eth/v1/beacon/light_client/optimistic_update": {http.MethodGet},
|
||||
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
|
||||
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
|
||||
}
|
||||
|
||||
builderRoutes := map[string][]string{
|
||||
"/eth/v1/builder/states/{state_id}/expected_withdrawals": {http.MethodGet},
|
||||
}
|
||||
|
||||
configRoutes := map[string][]string{
|
||||
"/eth/v1/config/fork_schedule": {http.MethodGet},
|
||||
"/eth/v1/config/spec": {http.MethodGet},
|
||||
"/eth/v1/config/deposit_contract": {http.MethodGet},
|
||||
}
|
||||
|
||||
debugRoutes := map[string][]string{
|
||||
"/eth/v2/debug/beacon/states/{state_id}": {http.MethodGet},
|
||||
"/eth/v2/debug/beacon/heads": {http.MethodGet},
|
||||
"/eth/v1/debug/fork_choice": {http.MethodGet},
|
||||
}
|
||||
|
||||
eventsRoutes := map[string][]string{
|
||||
"/eth/v1/events": {http.MethodGet},
|
||||
}
|
||||
|
||||
nodeRoutes := map[string][]string{
|
||||
"/eth/v1/node/identity": {http.MethodGet},
|
||||
"/eth/v1/node/peers": {http.MethodGet},
|
||||
"/eth/v1/node/peers/{peer_id}": {http.MethodGet},
|
||||
"/eth/v1/node/peer_count": {http.MethodGet},
|
||||
"/eth/v1/node/version": {http.MethodGet},
|
||||
"/eth/v1/node/syncing": {http.MethodGet},
|
||||
"/eth/v1/node/health": {http.MethodGet},
|
||||
}
|
||||
|
||||
validatorRoutes := map[string][]string{
|
||||
"/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet},
|
||||
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
|
||||
"/eth/v2/validator/blocks/{slot}": {http.MethodGet}, //deprecated
|
||||
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
|
||||
"/eth/v1/validator/blinded_blocks/{slot}": {http.MethodGet}, //deprecated
|
||||
"/eth/v1/validator/attestation_data": {http.MethodGet},
|
||||
"/eth/v1/validator/aggregate_attestation": {http.MethodGet},
|
||||
"/eth/v1/validator/aggregate_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_contribution": {http.MethodGet},
|
||||
"/eth/v1/validator/sync_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/contribution_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost},
|
||||
"/eth/v1/validator/register_validator": {http.MethodPost},
|
||||
"/eth/v1/validator/liveness/{epoch}": {http.MethodPost},
|
||||
}
|
||||
|
||||
prysmCustomRoutes := map[string][]string{
|
||||
"/prysm/v1/beacon/weak_subjectivity": {http.MethodGet},
|
||||
"/prysm/node/trusted_peers": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/v1/node/trusted_peers": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/node/trusted_peers/{peer_id}": {http.MethodDelete},
|
||||
"/prysm/v1/node/trusted_peers/{peer_id}": {http.MethodDelete},
|
||||
"/prysm/validator/performance": {http.MethodPost},
|
||||
"/prysm/v1/validator/performance": {http.MethodPost},
|
||||
"/eth/v1/beacon/states/{state_id}/validator_count": {http.MethodGet},
|
||||
"/prysm/v1/beacon/states/{state_id}/validator_count": {http.MethodGet},
|
||||
}
|
||||
|
||||
wantRouteList := combineMaps(beaconRoutes, builderRoutes, configRoutes, debugRoutes, eventsRoutes, nodeRoutes, validatorRoutes, prysmCustomRoutes)
|
||||
gotRouteList := make(map[string][]string)
|
||||
err := s.cfg.Router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
|
||||
tpl, err1 := route.GetPathTemplate()
|
||||
require.NoError(t, err1)
|
||||
met, err2 := route.GetMethods()
|
||||
require.NoError(t, err2)
|
||||
methods, ok := gotRouteList[tpl]
|
||||
if !ok {
|
||||
gotRouteList[tpl] = met
|
||||
} else {
|
||||
gotRouteList[tpl] = append(methods, met...)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, wantRouteList, gotRouteList)
|
||||
}
|
||||
|
||||
func TestLifecycle_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
chainService := &mock.ChainService{
|
||||
|
||||
@@ -77,6 +77,7 @@ go_test(
|
||||
"//beacon-chain/das:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/filesystem:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/p2p/peers:go_default_library",
|
||||
|
||||
@@ -279,34 +279,22 @@ func (s *Service) markSynced() {
|
||||
close(s.cfg.InitialSyncComplete)
|
||||
}
|
||||
|
||||
func (s *Service) fetchOriginBlobs(pids []peer.ID) error {
|
||||
r, err := s.cfg.DB.OriginCheckpointBlockRoot(s.ctx)
|
||||
if errors.Is(err, db.ErrNotFoundOriginBlockRoot) {
|
||||
return nil
|
||||
}
|
||||
blk, err := s.cfg.DB.Block(s.ctx, r)
|
||||
if err != nil {
|
||||
log.WithField("root", r).Error("Block for checkpoint sync origin root not found in db")
|
||||
return err
|
||||
}
|
||||
func missingBlobRequest(blk blocks.ROBlock, store *filesystem.BlobStorage) (p2ptypes.BlobSidecarsByRootReq, error) {
|
||||
r := blk.Root()
|
||||
if blk.Version() < version.Deneb {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
cmts, err := blk.Block().Body().BlobKzgCommitments()
|
||||
if err != nil {
|
||||
log.WithField("root", r).Error("Error reading commitments from checkpoint sync origin block")
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if len(cmts) == 0 {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
rob, err := blocks.NewROBlockWithRoot(blk, r)
|
||||
onDisk, err := store.Indices(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
onDisk, err := s.cfg.BlobStorage.Indices(r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error checking existing blobs for checkpoint sync bloc root %#x", r)
|
||||
return nil, errors.Wrapf(err, "error checking existing blobs for checkpoint sync block root %#x", r)
|
||||
}
|
||||
req := make(p2ptypes.BlobSidecarsByRootReq, 0, len(cmts))
|
||||
for i := range cmts {
|
||||
@@ -315,8 +303,32 @@ func (s *Service) fetchOriginBlobs(pids []peer.ID) error {
|
||||
}
|
||||
req = append(req, ð.BlobIdentifier{BlockRoot: r[:], Index: uint64(i)})
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *Service) fetchOriginBlobs(pids []peer.ID) error {
|
||||
r, err := s.cfg.DB.OriginCheckpointBlockRoot(s.ctx)
|
||||
if errors.Is(err, db.ErrNotFoundOriginBlockRoot) {
|
||||
return nil
|
||||
}
|
||||
blk, err := s.cfg.DB.Block(s.ctx, r)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", r)).Error("Block for checkpoint sync origin root not found in db")
|
||||
return err
|
||||
}
|
||||
if !params.WithinDAPeriod(slots.ToEpoch(blk.Block().Slot()), slots.ToEpoch(s.clock.CurrentSlot())) {
|
||||
return nil
|
||||
}
|
||||
rob, err := blocks.NewROBlockWithRoot(blk, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := missingBlobRequest(rob, s.cfg.BlobStorage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(req) == 0 {
|
||||
log.WithField("nBlobs", len(cmts)).WithField("root", fmt.Sprintf("%#x", r)).Debug("All checkpoint block blobs are present")
|
||||
log.WithField("root", fmt.Sprintf("%#x", r)).Debug("All blobs for checkpoint block are present")
|
||||
return nil
|
||||
}
|
||||
shufflePeers(pids)
|
||||
|
||||
@@ -6,16 +6,19 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/paulbellamy/ratecounter"
|
||||
"github.com/prysmaticlabs/prysm/v5/async/abool"
|
||||
mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/kv"
|
||||
dbtest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
|
||||
p2pt "github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/testing"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/verification"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd/beacon-chain/flags"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
@@ -420,3 +423,91 @@ func TestService_Synced(t *testing.T) {
|
||||
s.synced.Set()
|
||||
assert.Equal(t, true, s.Synced())
|
||||
}
|
||||
|
||||
func TestMissingBlobRequest(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
setup func(t *testing.T) (blocks.ROBlock, *filesystem.BlobStorage)
|
||||
nReq int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "pre-deneb",
|
||||
setup: func(t *testing.T) (blocks.ROBlock, *filesystem.BlobStorage) {
|
||||
cb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
rob, err := blocks.NewROBlockWithRoot(cb, [32]byte{})
|
||||
require.NoError(t, err)
|
||||
return rob, nil
|
||||
},
|
||||
nReq: 0,
|
||||
},
|
||||
{
|
||||
name: "deneb zero commitments",
|
||||
setup: func(t *testing.T) (blocks.ROBlock, *filesystem.BlobStorage) {
|
||||
bk, _ := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, 0)
|
||||
return bk, nil
|
||||
},
|
||||
nReq: 0,
|
||||
},
|
||||
{
|
||||
name: "2 commitments, all missing",
|
||||
setup: func(t *testing.T) (blocks.ROBlock, *filesystem.BlobStorage) {
|
||||
bk, _ := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, 2)
|
||||
fs := filesystem.NewEphemeralBlobStorage(t)
|
||||
return bk, fs
|
||||
},
|
||||
nReq: 2,
|
||||
},
|
||||
{
|
||||
name: "2 commitments, 1 missing",
|
||||
setup: func(t *testing.T) (blocks.ROBlock, *filesystem.BlobStorage) {
|
||||
bk, _ := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, 2)
|
||||
bm, fs := filesystem.NewEphemeralBlobStorageWithMocker(t)
|
||||
require.NoError(t, bm.CreateFakeIndices(bk.Root(), 1))
|
||||
return bk, fs
|
||||
},
|
||||
nReq: 1,
|
||||
},
|
||||
{
|
||||
name: "2 commitments, 0 missing",
|
||||
setup: func(t *testing.T) (blocks.ROBlock, *filesystem.BlobStorage) {
|
||||
bk, _ := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, 2)
|
||||
bm, fs := filesystem.NewEphemeralBlobStorageWithMocker(t)
|
||||
require.NoError(t, bm.CreateFakeIndices(bk.Root(), 0, 1))
|
||||
return bk, fs
|
||||
},
|
||||
nReq: 0,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
blk, store := c.setup(t)
|
||||
req, err := missingBlobRequest(blk, store)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.nReq, len(req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOriginOutsideRetention(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
bdb := dbtest.SetupDB(t)
|
||||
genesis := time.Unix(0, 0)
|
||||
secsPerEpoch := params.BeaconConfig().SecondsPerSlot * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
retentionSeconds := time.Second * time.Duration(uint64(params.BeaconConfig().MinEpochsForBlobsSidecarsRequest+1)*secsPerEpoch)
|
||||
outsideRetention := genesis.Add(retentionSeconds)
|
||||
now := func() time.Time {
|
||||
return outsideRetention
|
||||
}
|
||||
clock := startup.NewClock(genesis, [32]byte{}, startup.WithNower(now))
|
||||
s := &Service{ctx: ctx, cfg: &Config{DB: bdb}, clock: clock}
|
||||
blk, _ := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, 1)
|
||||
require.NoError(t, bdb.SaveBlock(ctx, blk))
|
||||
concreteDB, ok := bdb.(*kv.Store)
|
||||
require.Equal(t, true, ok)
|
||||
require.NoError(t, concreteDB.SaveOriginCheckpointBlockRoot(ctx, blk.Root()))
|
||||
// This would break due to missing service dependencies, but will return nil fast due to being outside retention.
|
||||
require.Equal(t, false, params.WithinDAPeriod(slots.ToEpoch(blk.Block().Slot()), slots.ToEpoch(clock.CurrentSlot())))
|
||||
require.NoError(t, s.fetchOriginBlobs([]peer.ID{}))
|
||||
}
|
||||
|
||||
@@ -98,7 +98,9 @@ func TestBackupAccounts_Noninteractive_Derived(t *testing.T) {
|
||||
|
||||
// We check a backup.zip file was created at the output path.
|
||||
zipFilePath := filepath.Join(backupDir, accounts.ArchiveFilename)
|
||||
assert.DeepEqual(t, true, file.Exists(zipFilePath))
|
||||
fileExists, err := file.Exists(zipFilePath, file.Regular)
|
||||
require.NoError(t, err, "could not check if backup file exists")
|
||||
assert.Equal(t, true, fileExists, "backup file does not exist")
|
||||
|
||||
// We attempt to unzip the file and verify the keystores do match our accounts.
|
||||
f, err := os.Open(zipFilePath)
|
||||
@@ -189,7 +191,9 @@ func TestBackupAccounts_Noninteractive_Imported(t *testing.T) {
|
||||
|
||||
// We check a backup.zip file was created at the output path.
|
||||
zipFilePath := filepath.Join(backupDir, accounts.ArchiveFilename)
|
||||
assert.DeepEqual(t, true, file.Exists(zipFilePath))
|
||||
exists, err := file.Exists(zipFilePath, file.Regular)
|
||||
require.NoError(t, err, "could not check if backup file exists")
|
||||
assert.Equal(t, true, exists, "backup file does not exist")
|
||||
|
||||
// We attempt to unzip the file and verify the keystores do match our accounts.
|
||||
f, err := os.Open(zipFilePath)
|
||||
|
||||
@@ -395,5 +395,7 @@ func TestExitAccountsCli_WriteJSON_NoBroadcast(t *testing.T) {
|
||||
require.Equal(t, 1, len(formattedExitedKeys))
|
||||
assert.Equal(t, "0x"+keystore.Pubkey[:12], formattedExitedKeys[0])
|
||||
|
||||
require.Equal(t, true, file.Exists(path.Join(out, "validator-exit-1.json")), "Expected file to exist")
|
||||
exists, err := file.Exists(path.Join(out, "validator-exit-1.json"), file.Regular)
|
||||
require.NoError(t, err, "could not check if exit file exists")
|
||||
require.Equal(t, true, exists, "Expected file to exist")
|
||||
}
|
||||
|
||||
@@ -10,6 +10,22 @@ import (
|
||||
|
||||
var log = logrus.WithField("prefix", "db")
|
||||
|
||||
var (
|
||||
// SourceDataDirFlag defines a path on disk where source Prysm databases are stored. Used for conversion.
|
||||
SourceDataDirFlag = &cli.StringFlag{
|
||||
Name: "source-data-dir",
|
||||
Usage: "Source data directory",
|
||||
Required: true,
|
||||
}
|
||||
|
||||
// SourceDataDirFlag defines a path on disk where source Prysm databases are stored. Used for conversion.
|
||||
TargetDataDirFlag = &cli.StringFlag{
|
||||
Name: "target-data-dir",
|
||||
Usage: "Target data directory",
|
||||
Required: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Commands for interacting with the Prysm validator database.
|
||||
var Commands = &cli.Command{
|
||||
Name: "db",
|
||||
@@ -66,5 +82,29 @@ var Commands = &cli.Command{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "convert-complete-to-minimal",
|
||||
Category: "db",
|
||||
Usage: "Convert a complete EIP-3076 slashing protection to a minimal one",
|
||||
Flags: []cli.Flag{
|
||||
SourceDataDirFlag,
|
||||
TargetDataDirFlag,
|
||||
},
|
||||
Before: func(cliCtx *cli.Context) error {
|
||||
return cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags)
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
sourcedDatabasePath := cliCtx.String(SourceDataDirFlag.Name)
|
||||
targetDatabasePath := cliCtx.String(TargetDataDirFlag.Name)
|
||||
|
||||
// Convert the database
|
||||
err := validatordb.ConvertDatabase(cliCtx.Context, sourcedDatabasePath, targetDatabasePath, false)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Could not convert database")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ go_library(
|
||||
visibility = [
|
||||
"//cmd/prysmctl:__subpackages__",
|
||||
"//cmd/validator:__subpackages__",
|
||||
"//config:__subpackages__",
|
||||
"//testing/endtoend:__subpackages__",
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
|
||||
@@ -127,7 +127,8 @@ func main() {
|
||||
Version: version.Version(),
|
||||
Action: func(ctx *cli.Context) error {
|
||||
if err := startNode(ctx); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
log.Fatal(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -17,8 +17,11 @@ go_library(
|
||||
"//io/file:go_default_library",
|
||||
"//runtime/tos:go_default_library",
|
||||
"//validator/accounts/userprompt:go_default_library",
|
||||
"//validator/db/filesystem:go_default_library",
|
||||
"//validator/db/iface:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
"//validator/slashing-protection-history:go_default_library",
|
||||
"//validator/slashing-protection-history/format:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
@@ -35,7 +38,7 @@ go_test(
|
||||
"//io/file:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
"//validator/db/common:go_default_library",
|
||||
"//validator/db/testing:go_default_library",
|
||||
"//validator/slashing-protection-history/format:go_default_library",
|
||||
"//validator/testing:go_default_library",
|
||||
|
||||
@@ -8,10 +8,14 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/userprompt"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/filesystem"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
slashingprotection "github.com/prysmaticlabs/prysm/v5/validator/slashing-protection-history"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/slashing-protection-history/format"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -30,11 +34,21 @@ const (
|
||||
// the validator's db into an EIP standard slashing protection format
|
||||
// 4. Format and save the JSON file to a user's specified output directory.
|
||||
func exportSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
var (
|
||||
validatorDB iface.ValidatorDB
|
||||
found bool
|
||||
err error
|
||||
)
|
||||
|
||||
log.Info(
|
||||
"This command exports your validator's attestation and proposal history into " +
|
||||
"a file that can then be imported into any other Prysm setup across computers",
|
||||
)
|
||||
var err error
|
||||
|
||||
// Check if a minimal database is requested
|
||||
isDatabaseMinimal := cliCtx.Bool(features.EnableMinimalSlashingProtection.Name)
|
||||
|
||||
// Read the data directory from the CLI context.
|
||||
dataDir := cliCtx.String(cmd.DataDirFlag.Name)
|
||||
if !cliCtx.IsSet(cmd.DataDirFlag.Name) {
|
||||
dataDir, err = userprompt.InputDirectory(cliCtx, userprompt.DataDirDirPromptText, cmd.DataDirFlag)
|
||||
@@ -42,27 +56,45 @@ func exportSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
return errors.Wrapf(err, "could not read directory value from input")
|
||||
}
|
||||
}
|
||||
// ensure that the validator.db is found under the specified dir or its subdirectories
|
||||
found, _, err := file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
|
||||
// Ensure that the database is found under the specified dir or its subdirectories
|
||||
if isDatabaseMinimal {
|
||||
found, _, err = file.RecursiveDirFind(filesystem.DatabaseDirName, dataDir)
|
||||
} else {
|
||||
found, _, err = file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error finding validator database at path %s", dataDir)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return fmt.Errorf(
|
||||
"validator.db file (validator database) was not found at path %s, so nothing to export",
|
||||
dataDir,
|
||||
)
|
||||
databaseFileDir := kv.ProtectionDbFileName
|
||||
if isDatabaseMinimal {
|
||||
databaseFileDir = filesystem.DatabaseDirName
|
||||
}
|
||||
return fmt.Errorf("%s (validator database) was not found at path %s, so nothing to export", databaseFileDir, dataDir)
|
||||
}
|
||||
|
||||
// Open the validator database.
|
||||
if isDatabaseMinimal {
|
||||
validatorDB, err = filesystem.NewStore(dataDir, nil)
|
||||
} else {
|
||||
validatorDB, err = kv.NewKVStore(cliCtx.Context, dataDir, nil)
|
||||
}
|
||||
|
||||
validatorDB, err := kv.NewKVStore(cliCtx.Context, dataDir, &kv.Config{})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not access validator database at path %s", dataDir)
|
||||
}
|
||||
|
||||
// Close the database when we're done.
|
||||
defer func() {
|
||||
if err := validatorDB.Close(); err != nil {
|
||||
log.WithError(err).Errorf("Could not close validator DB")
|
||||
}
|
||||
}()
|
||||
|
||||
// Export the slashing protection history from the validator's database.
|
||||
eipJSON, err := slashingprotection.ExportStandardProtectionJSON(cliCtx.Context, validatorDB)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not export slashing protection history")
|
||||
@@ -79,39 +111,60 @@ func exportSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
)
|
||||
}
|
||||
|
||||
// Write the result to the output file
|
||||
if err := writeToOutput(cliCtx, eipJSON); err != nil {
|
||||
return errors.Wrap(err, "could not write slashing protection history to output file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeToOutput(cliCtx *cli.Context, eipJSON *format.EIPSlashingProtectionFormat) error {
|
||||
// Get the output directory where the slashing protection history file will be stored
|
||||
outputDir, err := userprompt.InputDirectory(
|
||||
cliCtx,
|
||||
"Enter your desired output directory for your slashing protection history file",
|
||||
flags.SlashingProtectionExportDirFlag,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get slashing protection json file")
|
||||
}
|
||||
|
||||
if outputDir == "" {
|
||||
return errors.New("output directory not specified")
|
||||
}
|
||||
|
||||
// Check is the output directory already exists, if not, create it
|
||||
exists, err := file.HasDir(outputDir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if output directory %s already exists", outputDir)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
if err := file.MkdirAll(outputDir); err != nil {
|
||||
return errors.Wrapf(err, "could not create output directory %s", outputDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Write into the output file
|
||||
outputFilePath := filepath.Join(outputDir, jsonExportFileName)
|
||||
log.Infof("Writing slashing protection export JSON file to %s", outputFilePath)
|
||||
|
||||
encoded, err := json.MarshalIndent(eipJSON, "", "\t")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not JSON marshal slashing protection history")
|
||||
}
|
||||
|
||||
if err := file.WriteFile(outputFilePath, encoded); err != nil {
|
||||
return errors.Wrapf(err, "could not write file to path %s", outputFilePath)
|
||||
}
|
||||
|
||||
log.Infof(
|
||||
"Successfully wrote %s. You can import this file using Prysm's "+
|
||||
"validator slashing-protection-history import command in another machine",
|
||||
outputFilePath,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/userprompt"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/filesystem"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
slashingprotection "github.com/prysmaticlabs/prysm/v5/validator/slashing-protection-history"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -24,7 +26,16 @@ import (
|
||||
// 4. Call the function which actually imports the data from
|
||||
// the standard slashing protection JSON file into our database.
|
||||
func importSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
var err error
|
||||
var (
|
||||
valDB iface.ValidatorDB
|
||||
found bool
|
||||
err error
|
||||
)
|
||||
|
||||
// Check if a minimal database is requested
|
||||
isDatabaseMimimal := cliCtx.Bool(features.EnableMinimalSlashingProtection.Name)
|
||||
|
||||
// Get the data directory from the CLI context.
|
||||
dataDir := cliCtx.String(cmd.DataDirFlag.Name)
|
||||
if !cliCtx.IsSet(cmd.DataDirFlag.Name) {
|
||||
dataDir, err = userprompt.InputDirectory(cliCtx, userprompt.DataDirDirPromptText, cmd.DataDirFlag)
|
||||
@@ -32,28 +43,44 @@ func importSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
return errors.Wrapf(err, "could not read directory value from input")
|
||||
}
|
||||
}
|
||||
// ensure that the validator.db is found under the specified dir or its subdirectories
|
||||
found, _, err := file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
|
||||
// Ensure that the database is found under the specified directory or its subdirectories
|
||||
if isDatabaseMimimal {
|
||||
found, _, err = file.RecursiveDirFind(filesystem.DatabaseDirName, dataDir)
|
||||
} else {
|
||||
found, _, err = file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error finding validator database at path %s", dataDir)
|
||||
}
|
||||
|
||||
message := "Found existing database inside of %s"
|
||||
if !found {
|
||||
log.Infof(
|
||||
"Did not find existing validator.db inside of %s, creating a new one",
|
||||
dataDir,
|
||||
)
|
||||
} else {
|
||||
log.Infof("Found existing validator.db inside of %s", dataDir)
|
||||
message = "Did not find existing database inside of %s, creating a new one"
|
||||
}
|
||||
valDB, err := kv.NewKVStore(cliCtx.Context, dataDir, &kv.Config{})
|
||||
|
||||
log.Infof(message, dataDir)
|
||||
|
||||
// Open the validator database.
|
||||
if isDatabaseMimimal {
|
||||
valDB, err = filesystem.NewStore(dataDir, nil)
|
||||
} else {
|
||||
valDB, err = kv.NewKVStore(cliCtx.Context, dataDir, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not access validator database at path: %s", dataDir)
|
||||
}
|
||||
|
||||
// Close the database when we're done.
|
||||
defer func() {
|
||||
if err := valDB.Close(); err != nil {
|
||||
log.WithError(err).Errorf("Could not close validator DB")
|
||||
}
|
||||
}()
|
||||
|
||||
// Get the path to the slashing protection JSON file from the CLI context.
|
||||
protectionFilePath, err := userprompt.InputDirectory(cliCtx, userprompt.SlashingProtectionJSONPromptText, flags.SlashingProtectionJSONFileFlag)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get slashing protection json file")
|
||||
@@ -65,17 +92,22 @@ func importSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
flags.SlashingProtectionJSONFileFlag.Name,
|
||||
)
|
||||
}
|
||||
|
||||
// Read the JSON file from user input.
|
||||
enc, err := file.ReadFileAsBytes(protectionFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Import the data from the standard slashing protection JSON file into our database.
|
||||
log.Infof("Starting import of slashing protection file %s", protectionFilePath)
|
||||
buf := bytes.NewBuffer(enc)
|
||||
if err := slashingprotection.ImportStandardProtectionJSON(
|
||||
cliCtx.Context, valDB, buf,
|
||||
); err != nil {
|
||||
return err
|
||||
|
||||
if err := valDB.ImportStandardProtectionJSON(cliCtx.Context, buf); err != nil {
|
||||
return errors.Wrapf(err, "could not import slashing protection JSON file %s", protectionFilePath)
|
||||
}
|
||||
|
||||
log.Infof("Slashing protection JSON successfully imported into %s", dataDir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/common"
|
||||
dbTest "github.com/prysmaticlabs/prysm/v5/validator/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/slashing-protection-history/format"
|
||||
mocks "github.com/prysmaticlabs/prysm/v5/validator/testing"
|
||||
@@ -35,6 +35,11 @@ func setupCliCtx(
|
||||
return cli.NewContext(&app, set, nil)
|
||||
}
|
||||
|
||||
// TestImportExportSlashingProtectionCli_RoundTrip imports a EIP-3076 interchange format JSON file,
|
||||
// and exports it back to disk. It then compare the exported file to the original file.
|
||||
// This test is only suitable for complete slashing protection history database, since minimal
|
||||
// slashing protection history database will keep only the latest signed block slot / attestations,
|
||||
// and thus will not be able to export the same data as the original file.
|
||||
func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) {
|
||||
numValidators := 10
|
||||
outputPath := filepath.Join(t.TempDir(), "slashing-exports")
|
||||
@@ -59,7 +64,8 @@ func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// We create a CLI context with the required values, such as the database datadir and output directory.
|
||||
validatorDB := dbTest.SetupDB(t, pubKeys)
|
||||
isSlashingProtectionMinimal := false
|
||||
validatorDB := dbTest.SetupDB(t, pubKeys, isSlashingProtectionMinimal)
|
||||
dbPath := validatorDB.DatabasePath()
|
||||
require.NoError(t, validatorDB.Close())
|
||||
cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath)
|
||||
@@ -108,6 +114,11 @@ func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestImportExportSlashingProtectionCli_EmptyData imports a EIP-3076 interchange format JSON file,
|
||||
// and exports it back to disk. It then compare the exported file to the original file.
|
||||
// This test is only suitable for complete slashing protection history database, since minimal
|
||||
// slashing protection history database will keep only the latest signed block slot / attestations,
|
||||
// and thus will not be able to export the same data as the original file.
|
||||
func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) {
|
||||
numValidators := 10
|
||||
outputPath := filepath.Join(t.TempDir(), "slashing-exports")
|
||||
@@ -118,10 +129,10 @@ func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) {
|
||||
// Create some mock slashing protection history. and JSON file
|
||||
pubKeys, err := mocks.CreateRandomPubKeys(numValidators)
|
||||
require.NoError(t, err)
|
||||
attestingHistory := make([][]*kv.AttestationRecord, 0)
|
||||
proposalHistory := make([]kv.ProposalHistoryForPubkey, len(pubKeys))
|
||||
attestingHistory := make([][]*common.AttestationRecord, 0)
|
||||
proposalHistory := make([]common.ProposalHistoryForPubkey, len(pubKeys))
|
||||
for i := 0; i < len(pubKeys); i++ {
|
||||
proposalHistory[i].Proposals = make([]kv.Proposal, 0)
|
||||
proposalHistory[i].Proposals = make([]common.Proposal, 0)
|
||||
}
|
||||
mockJSON, err := mocks.MockSlashingProtectionJSON(pubKeys, attestingHistory, proposalHistory)
|
||||
require.NoError(t, err)
|
||||
@@ -135,7 +146,8 @@ func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// We create a CLI context with the required values, such as the database datadir and output directory.
|
||||
validatorDB := dbTest.SetupDB(t, pubKeys)
|
||||
isSlashingProtectionMinimal := false
|
||||
validatorDB := dbTest.SetupDB(t, pubKeys, isSlashingProtectionMinimal)
|
||||
dbPath := validatorDB.DatabasePath()
|
||||
require.NoError(t, validatorDB.Close())
|
||||
cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath)
|
||||
|
||||
@@ -25,6 +25,7 @@ var Commands = &cli.Command{
|
||||
features.PraterTestnet,
|
||||
features.SepoliaTestnet,
|
||||
features.HoleskyTestnet,
|
||||
features.EnableMinimalSlashingProtection,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
Before: func(cliCtx *cli.Context) error {
|
||||
@@ -53,6 +54,7 @@ var Commands = &cli.Command{
|
||||
features.PraterTestnet,
|
||||
features.SepoliaTestnet,
|
||||
features.HoleskyTestnet,
|
||||
features.EnableMinimalSlashingProtection,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
Before: func(cliCtx *cli.Context) error {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
config_setting(
|
||||
name = "mainnet",
|
||||
flag_values = {
|
||||
@@ -11,3 +13,28 @@ config_setting(
|
||||
"//proto:network": "minimal",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/config",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/util/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["util_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//config/params:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -57,7 +57,8 @@ type Flags struct {
|
||||
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 bool // EnableSlashingProtectionPruning for the validator client.
|
||||
EnableSlashingProtectionPruning bool // Enable slashing protection pruning for the validator client.
|
||||
EnableMinimalSlashingProtection bool // Enable minimal slashing protection database for the validator client.
|
||||
|
||||
SaveFullExecutionPayloads bool // Save full beacon blocks with execution payloads in the database.
|
||||
EnableStartOptimistic bool // EnableStartOptimistic treats every block as optimistic at startup.
|
||||
@@ -276,6 +277,10 @@ func ConfigureValidator(ctx *cli.Context) error {
|
||||
logEnabled(enableSlashingProtectionPruning)
|
||||
cfg.EnableSlashingProtectionPruning = true
|
||||
}
|
||||
if ctx.Bool(EnableMinimalSlashingProtection.Name) {
|
||||
logEnabled(EnableMinimalSlashingProtection)
|
||||
cfg.EnableMinimalSlashingProtection = true
|
||||
}
|
||||
if ctx.Bool(enableDoppelGangerProtection.Name) {
|
||||
logEnabled(enableDoppelGangerProtection)
|
||||
cfg.EnableDoppelGanger = true
|
||||
|
||||
@@ -95,6 +95,10 @@ var (
|
||||
Name: "enable-slashing-protection-history-pruning",
|
||||
Usage: "Enables the pruning of the validator client's slashing protection database.",
|
||||
}
|
||||
EnableMinimalSlashingProtection = &cli.BoolFlag{
|
||||
Name: "enable-minimal-slashing-protection",
|
||||
Usage: "Enables the minimal slashing protection. See EIP-3076 for more details.",
|
||||
}
|
||||
enableDoppelGangerProtection = &cli.BoolFlag{
|
||||
Name: "enable-doppelganger",
|
||||
Usage: `Enables the validator to perform a doppelganger check.
|
||||
@@ -177,6 +181,7 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
|
||||
dynamicKeyReloadDebounceInterval,
|
||||
attestTimely,
|
||||
enableSlashingProtectionPruning,
|
||||
EnableMinimalSlashingProtection,
|
||||
enableDoppelGangerProtection,
|
||||
EnableBeaconRESTApi,
|
||||
}...)
|
||||
|
||||
@@ -27,8 +27,6 @@ var placeholderFields = []string{
|
||||
"EIP6110_FORK_VERSION",
|
||||
"EIP7002_FORK_EPOCH",
|
||||
"EIP7002_FORK_VERSION",
|
||||
"EIP7594_FORK_EPOCH",
|
||||
"EIP7594_FORK_VERSION",
|
||||
"MAX_BLOBS_PER_BLOCK",
|
||||
"REORG_HEAD_WEIGHT_THRESHOLD",
|
||||
"UPDATE_TIMEOUT",
|
||||
|
||||
@@ -2,10 +2,11 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["proposer_settings.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/config/validator/service",
|
||||
srcs = ["settings.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/config/proposer",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//consensus-types/validator:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
@@ -18,7 +19,7 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["proposer_settings_test.go"],
|
||||
srcs = ["settings_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
45
config/proposer/loader/BUILD.bazel
Normal file
45
config/proposer/loader/BUILD.bazel
Normal file
@@ -0,0 +1,45 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["loader_test.go"],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/validator:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//validator/db/iface:go_default_library",
|
||||
"//validator/db/testing:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["loader.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/config/proposer/loader",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/validator:go_default_library",
|
||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||
"//validator/db/iface:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
283
config/proposer/loader/loader.go
Normal file
283
config/proposer/loader/loader.go
Normal file
@@ -0,0 +1,283 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v5/config"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/validator"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/iface"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type settingsType int
|
||||
|
||||
const (
|
||||
none settingsType = iota
|
||||
defaultFlag
|
||||
fileFlag
|
||||
urlFlag
|
||||
onlyDB
|
||||
)
|
||||
|
||||
type settingsLoader struct {
|
||||
loadMethods []settingsType
|
||||
existsInDB bool
|
||||
db iface.ValidatorDB
|
||||
options *flagOptions
|
||||
}
|
||||
|
||||
type flagOptions struct {
|
||||
builderConfig *proposer.BuilderConfig
|
||||
gasLimit *validator.Uint64
|
||||
}
|
||||
|
||||
// SettingsLoaderOption sets additional options that affect the proposer settings
|
||||
type SettingsLoaderOption func(cliCtx *cli.Context, psl *settingsLoader) error
|
||||
|
||||
// WithBuilderConfig applies the --enable-builder flag to proposer settings
|
||||
func WithBuilderConfig() SettingsLoaderOption {
|
||||
return func(cliCtx *cli.Context, psl *settingsLoader) error {
|
||||
if cliCtx.Bool(flags.EnableBuilderFlag.Name) {
|
||||
psl.options.builderConfig = &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithGasLimit applies the --suggested-gas-limit flag to proposer settings
|
||||
func WithGasLimit() SettingsLoaderOption {
|
||||
return func(cliCtx *cli.Context, psl *settingsLoader) error {
|
||||
sgl := cliCtx.String(flags.BuilderGasLimitFlag.Name)
|
||||
if sgl != "" {
|
||||
gl, err := strconv.ParseUint(sgl, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("Value set by --%s is not a uint64", flags.BuilderGasLimitFlag.Name)
|
||||
}
|
||||
if gl == 0 {
|
||||
log.Warnf("Gas limit was intentionally set to 0, this will be replaced with the default gas limit of %d", params.BeaconConfig().DefaultBuilderGasLimit)
|
||||
}
|
||||
rgl := reviewGasLimit(validator.Uint64(gl))
|
||||
psl.options.gasLimit = &rgl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewProposerSettingsLoader returns a new proposer settings loader that can process the proposer settings based on flag options
|
||||
func NewProposerSettingsLoader(cliCtx *cli.Context, db iface.ValidatorDB, opts ...SettingsLoaderOption) (*settingsLoader, error) {
|
||||
if cliCtx.IsSet(flags.ProposerSettingsFlag.Name) && cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
|
||||
return nil, fmt.Errorf("cannot specify both --%s and --%s flags; choose one method for specifying proposer settings", flags.ProposerSettingsFlag.Name, flags.ProposerSettingsURLFlag.Name)
|
||||
}
|
||||
psExists, err := db.ProposerSettingsExists(cliCtx.Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
psl := &settingsLoader{db: db, existsInDB: psExists, options: &flagOptions{}}
|
||||
|
||||
if cliCtx.IsSet(flags.SuggestedFeeRecipientFlag.Name) {
|
||||
psl.loadMethods = append(psl.loadMethods, defaultFlag)
|
||||
}
|
||||
if cliCtx.IsSet(flags.ProposerSettingsFlag.Name) {
|
||||
psl.loadMethods = append(psl.loadMethods, fileFlag)
|
||||
}
|
||||
if cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
|
||||
psl.loadMethods = append(psl.loadMethods, urlFlag)
|
||||
}
|
||||
if len(psl.loadMethods) == 0 {
|
||||
method := none
|
||||
if psExists {
|
||||
// override with db
|
||||
method = onlyDB
|
||||
}
|
||||
psl.loadMethods = append(psl.loadMethods, method)
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
if err := o(cliCtx, psl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return psl, nil
|
||||
}
|
||||
|
||||
// Load saves the proposer settings to the database
|
||||
func (psl *settingsLoader) Load(cliCtx *cli.Context) (*proposer.Settings, error) {
|
||||
loadConfig := &validatorpb.ProposerSettingsPayload{}
|
||||
|
||||
// override settings based on other options
|
||||
if psl.options.builderConfig != nil && psl.options.gasLimit != nil {
|
||||
psl.options.builderConfig.GasLimit = *psl.options.gasLimit
|
||||
}
|
||||
|
||||
// check if database has settings already
|
||||
if psl.existsInDB {
|
||||
dbps, err := psl.db.ProposerSettings(cliCtx.Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loadConfig = dbps.ToConsensus()
|
||||
}
|
||||
|
||||
// start to process based on load method
|
||||
for _, method := range psl.loadMethods {
|
||||
switch method {
|
||||
case defaultFlag:
|
||||
suggestedFeeRecipient := cliCtx.String(flags.SuggestedFeeRecipientFlag.Name)
|
||||
if !common.IsHexAddress(suggestedFeeRecipient) {
|
||||
return nil, errors.Errorf("--%s is not a valid Ethereum address", flags.SuggestedFeeRecipientFlag.Name)
|
||||
}
|
||||
if err := config.WarnNonChecksummedAddress(suggestedFeeRecipient); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultConfig := &validatorpb.ProposerOptionPayload{
|
||||
FeeRecipient: suggestedFeeRecipient,
|
||||
}
|
||||
if psl.options.builderConfig != nil {
|
||||
defaultConfig.Builder = psl.options.builderConfig.ToConsensus()
|
||||
}
|
||||
loadConfig.DefaultConfig = defaultConfig
|
||||
case fileFlag:
|
||||
var settingFromFile *validatorpb.ProposerSettingsPayload
|
||||
if err := config.UnmarshalFromFile(cliCtx.String(flags.ProposerSettingsFlag.Name), &settingFromFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if settingFromFile == nil {
|
||||
return nil, errors.Errorf("proposer settings is empty after unmarshalling from file specified by %s flag", flags.ProposerSettingsFlag.Name)
|
||||
}
|
||||
loadConfig = psl.processProposerSettings(settingFromFile, loadConfig)
|
||||
case urlFlag:
|
||||
var settingFromURL *validatorpb.ProposerSettingsPayload
|
||||
if err := config.UnmarshalFromURL(cliCtx.Context, cliCtx.String(flags.ProposerSettingsURLFlag.Name), &settingFromURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if settingFromURL == nil {
|
||||
return nil, errors.New("proposer settings is empty after unmarshalling from url")
|
||||
}
|
||||
loadConfig = psl.processProposerSettings(settingFromURL, loadConfig)
|
||||
case onlyDB:
|
||||
loadConfig = psl.processProposerSettings(nil, loadConfig)
|
||||
case none:
|
||||
if psl.options.builderConfig != nil {
|
||||
// if there are no proposer settings provided, create a default where fee recipient is not populated, this will be skipped for validator registration on validators that don't have a fee recipient set.
|
||||
// skip saving to DB if only builder settings are provided until a trigger like keymanager API updates with fee recipient values
|
||||
option := &proposer.Option{
|
||||
BuilderConfig: psl.options.builderConfig.Clone(),
|
||||
}
|
||||
loadConfig.DefaultConfig = option.ToConsensus()
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("load method for proposer settings does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
// exit early if nothing is provided
|
||||
if loadConfig == nil || (loadConfig.ProposerConfig == nil && loadConfig.DefaultConfig == nil) {
|
||||
log.Warn("No proposer settings were provided")
|
||||
return nil, nil
|
||||
}
|
||||
ps, err := proposer.SettingFromConsensus(loadConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := psl.db.SaveProposerSettings(cliCtx.Context, ps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (psl *settingsLoader) processProposerSettings(loadedSettings, dbSettings *validatorpb.ProposerSettingsPayload) *validatorpb.ProposerSettingsPayload {
|
||||
if loadedSettings == nil && dbSettings == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// loaded settings have higher priority than db settings
|
||||
newSettings := &validatorpb.ProposerSettingsPayload{}
|
||||
|
||||
var builderConfig *validatorpb.BuilderConfig
|
||||
var gasLimitOnly *validator.Uint64
|
||||
|
||||
if psl.options != nil {
|
||||
if psl.options.builderConfig != nil {
|
||||
builderConfig = psl.options.builderConfig.ToConsensus()
|
||||
}
|
||||
if psl.options.gasLimit != nil {
|
||||
gasLimitOnly = psl.options.gasLimit
|
||||
}
|
||||
}
|
||||
|
||||
if dbSettings != nil && dbSettings.DefaultConfig != nil {
|
||||
if builderConfig == nil {
|
||||
dbSettings.DefaultConfig.Builder = nil
|
||||
}
|
||||
newSettings.DefaultConfig = dbSettings.DefaultConfig
|
||||
}
|
||||
if loadedSettings != nil && loadedSettings.DefaultConfig != nil {
|
||||
newSettings.DefaultConfig = loadedSettings.DefaultConfig
|
||||
}
|
||||
|
||||
// process any builder overrides on defaults
|
||||
if newSettings.DefaultConfig != nil {
|
||||
newSettings.DefaultConfig.Builder = processBuilderConfig(newSettings.DefaultConfig.Builder, builderConfig, gasLimitOnly)
|
||||
}
|
||||
|
||||
if dbSettings != nil && len(dbSettings.ProposerConfig) != 0 {
|
||||
for _, option := range dbSettings.ProposerConfig {
|
||||
if builderConfig == nil {
|
||||
option.Builder = nil
|
||||
}
|
||||
}
|
||||
newSettings.ProposerConfig = dbSettings.ProposerConfig
|
||||
}
|
||||
if loadedSettings != nil && len(loadedSettings.ProposerConfig) != 0 {
|
||||
newSettings.ProposerConfig = loadedSettings.ProposerConfig
|
||||
}
|
||||
|
||||
// process any overrides for proposer config
|
||||
for _, option := range newSettings.ProposerConfig {
|
||||
if option != nil {
|
||||
option.Builder = processBuilderConfig(option.Builder, builderConfig, gasLimitOnly)
|
||||
}
|
||||
}
|
||||
|
||||
// if default and proposer configs are both missing even after db setting
|
||||
if newSettings.DefaultConfig == nil && newSettings.ProposerConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return newSettings
|
||||
}
|
||||
|
||||
func processBuilderConfig(current *validatorpb.BuilderConfig, override *validatorpb.BuilderConfig, gasLimitOnly *validator.Uint64) *validatorpb.BuilderConfig {
|
||||
if current != nil {
|
||||
current.GasLimit = reviewGasLimit(current.GasLimit)
|
||||
if override != nil {
|
||||
current.Enabled = override.Enabled
|
||||
}
|
||||
if gasLimitOnly != nil {
|
||||
current.GasLimit = *gasLimitOnly
|
||||
}
|
||||
return current
|
||||
}
|
||||
return override
|
||||
}
|
||||
|
||||
func reviewGasLimit(gasLimit validator.Uint64) validator.Uint64 {
|
||||
// sets gas limit to default if not defined or set to 0
|
||||
if gasLimit == 0 {
|
||||
return validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit)
|
||||
}
|
||||
// TODO(10810): add in warning for ranges
|
||||
return gasLimit
|
||||
}
|
||||
927
config/proposer/loader/loader_test.go
Normal file
927
config/proposer/loader/loader_test.go
Normal file
@@ -0,0 +1,927 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd/validator/flags"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/iface"
|
||||
dbTest "github.com/prysmaticlabs/prysm/v5/validator/db/testing"
|
||||
logtest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func TestProposerSettingsLoader(t *testing.T) {
|
||||
hook := logtest.NewGlobal()
|
||||
type proposerSettingsFlag struct {
|
||||
dir string
|
||||
url string
|
||||
defaultfee string
|
||||
defaultgas string
|
||||
}
|
||||
|
||||
type args struct {
|
||||
proposerSettingsFlagValues *proposerSettingsFlag
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want func() *proposer.Settings
|
||||
urlResponse string
|
||||
wantInitErr string
|
||||
wantErr string
|
||||
wantLog string
|
||||
withdb func(db iface.ValidatorDB) error
|
||||
validatorRegistrationEnabled bool
|
||||
skipDBSavedCheck bool
|
||||
}{
|
||||
{
|
||||
name: "db settings override file settings if file default config is missing",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/proposer-config-only.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
withdb: func(db iface.ValidatorDB) error {
|
||||
settings := &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
return db.SaveProposerSettings(context.Background(), settings)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "db settings override file settings if file proposer config is missing and enable builder is true",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/default-only-proposer-config.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
withdb: func(db iface.ValidatorDB) error {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
settings := &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return db.SaveProposerSettings(context.Background(), settings)
|
||||
},
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Empty json file loaded throws a warning",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/empty.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return nil
|
||||
},
|
||||
wantLog: "No proposer settings were provided",
|
||||
skipDBSavedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "Happy Path default only proposer settings file with builder settings,",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/default-only-proposer-config.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Happy Path Config file File, bad checksum",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config-badchecksum.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
wantLog: "is not a checksum Ethereum address",
|
||||
},
|
||||
{
|
||||
name: "Happy Path Config file File multiple fee recipients",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config-multiple.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
key2, err := hexutil.Decode("0xb057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7b")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
bytesutil.ToBytes48(key2): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(35000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "Happy Path Config URL File",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "./testdata/good-prepare-beacon-proposer-config.json",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "Happy Path Config YAML file with custom Gas Limit",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.yaml",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: 40000000,
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: false,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "Happy Path Suggested Fee ",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89A",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "Happy Path Suggested Fee , validator registration enabled",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89A",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Happy Path Suggested Fee , validator registration enabled and default gas",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89A",
|
||||
defaultgas: "50000000",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: 50000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "File with default gas that overrides",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.yaml",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
defaultgas: "50000000",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: 50000000,
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: false,
|
||||
GasLimit: validator.Uint64(50000000),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "Suggested Fee does not Override Config",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.json",
|
||||
url: "",
|
||||
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89B",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
},
|
||||
{
|
||||
name: "Suggested Fee with validator registration does not Override Config",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.json",
|
||||
url: "",
|
||||
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89B",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: "",
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Enable Builder flag overrides empty config",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Enable Builder flag does override completed builder config",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.yaml",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "Only Enable Builder flag",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
validatorRegistrationEnabled: true,
|
||||
skipDBSavedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "No Flags but saved to DB with builder and override removed builder data",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
withdb: func(db iface.ValidatorDB) error {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
settings := &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
return db.SaveProposerSettings(context.Background(), settings)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Enable builder flag but saved to DB without builder data now includes builder data",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
withdb: func(db iface.ValidatorDB) error {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
settings := &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
return db.SaveProposerSettings(context.Background(), settings)
|
||||
},
|
||||
validatorRegistrationEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "No flags, but saved to database",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
return &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
withdb: func(db iface.ValidatorDB) error {
|
||||
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
|
||||
require.NoError(t, err)
|
||||
settings := &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
},
|
||||
}
|
||||
return db.SaveProposerSettings(context.Background(), settings)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "No flags set means empty config",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return nil
|
||||
},
|
||||
wantErr: "",
|
||||
skipDBSavedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "Bad File Path",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/bad-prepare-beacon-proposer-config.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return nil
|
||||
},
|
||||
wantErr: "failed to unmarshal yaml file",
|
||||
},
|
||||
{
|
||||
name: "Both URL and Dir flags used resulting in error",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/good-prepare-beacon-proposer-config.json",
|
||||
url: "./testdata/good-prepare-beacon-proposer-config.json",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return &proposer.Settings{}
|
||||
},
|
||||
wantInitErr: "cannot specify both",
|
||||
},
|
||||
{
|
||||
name: "Bad Gas value in JSON",
|
||||
args: args{
|
||||
proposerSettingsFlagValues: &proposerSettingsFlag{
|
||||
dir: "./testdata/bad-gas-value-proposer-settings.json",
|
||||
url: "",
|
||||
defaultfee: "",
|
||||
},
|
||||
},
|
||||
want: func() *proposer.Settings {
|
||||
return nil
|
||||
},
|
||||
wantErr: "failed to unmarshal yaml file",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("%v-minimal:%v", tt.name, isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
if tt.args.proposerSettingsFlagValues.dir != "" {
|
||||
set.String(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir, "")
|
||||
require.NoError(t, set.Set(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir))
|
||||
}
|
||||
if tt.args.proposerSettingsFlagValues.url != "" {
|
||||
content, err := os.ReadFile(tt.args.proposerSettingsFlagValues.url)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err := fmt.Fprintf(w, "%s", content)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
set.String(flags.ProposerSettingsURLFlag.Name, tt.args.proposerSettingsFlagValues.url, "")
|
||||
require.NoError(t, set.Set(flags.ProposerSettingsURLFlag.Name, srv.URL))
|
||||
}
|
||||
if tt.args.proposerSettingsFlagValues.defaultfee != "" {
|
||||
set.String(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee, "")
|
||||
require.NoError(t, set.Set(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee))
|
||||
}
|
||||
if tt.args.proposerSettingsFlagValues.defaultgas != "" {
|
||||
set.String(flags.BuilderGasLimitFlag.Name, tt.args.proposerSettingsFlagValues.defaultgas, "")
|
||||
require.NoError(t, set.Set(flags.BuilderGasLimitFlag.Name, tt.args.proposerSettingsFlagValues.defaultgas))
|
||||
}
|
||||
if tt.validatorRegistrationEnabled {
|
||||
set.Bool(flags.EnableBuilderFlag.Name, true, "")
|
||||
}
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
validatorDB := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal)
|
||||
if tt.withdb != nil {
|
||||
err := tt.withdb(validatorDB)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
loader, err := NewProposerSettingsLoader(
|
||||
cliCtx,
|
||||
validatorDB,
|
||||
WithBuilderConfig(),
|
||||
WithGasLimit(),
|
||||
)
|
||||
if tt.wantInitErr != "" {
|
||||
require.ErrorContains(t, tt.wantInitErr, err)
|
||||
return
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := loader.Load(cliCtx)
|
||||
if tt.wantErr != "" {
|
||||
require.ErrorContains(t, tt.wantErr, err)
|
||||
return
|
||||
}
|
||||
if tt.wantLog != "" {
|
||||
assert.LogsContain(t, hook,
|
||||
tt.wantLog,
|
||||
)
|
||||
}
|
||||
w := tt.want()
|
||||
require.DeepEqual(t, w, got)
|
||||
if !tt.skipDBSavedCheck {
|
||||
dbSettings, err := validatorDB.ProposerSettings(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, w, dbSettings)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ProposerSettingsLoaderWithOnlyBuilder_DoesNotSaveInDB(t *testing.T) {
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("minimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool(flags.EnableBuilderFlag.Name, true, "")
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
validatorDB := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}, isSlashingProtectionMinimal)
|
||||
loader, err := NewProposerSettingsLoader(
|
||||
cliCtx,
|
||||
validatorDB,
|
||||
WithBuilderConfig(),
|
||||
WithGasLimit(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
got, err := loader.Load(cliCtx)
|
||||
require.NoError(t, err)
|
||||
_, err = validatorDB.ProposerSettings(cliCtx.Context)
|
||||
require.ErrorContains(t, "no proposer settings found in bucket", err)
|
||||
want := &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
Relays: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.DeepEqual(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"proposer_config": {},
|
||||
"default_config": {
|
||||
"fee_recipient": "0xAe967917c465db8578ca9024c205720b1a3651A9",
|
||||
"builder": {"enabled": true}
|
||||
3
config/proposer/loader/testdata/empty.json
vendored
Normal file
3
config/proposer/loader/testdata/empty.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
7
config/proposer/loader/testdata/proposer-config-only.json
vendored
Normal file
7
config/proposer/loader/testdata/proposer-config-only.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"proposer_config": {
|
||||
"0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a": {
|
||||
"fee_recipient": "0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package validator_service_config
|
||||
package proposer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -6,51 +6,77 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/config"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
|
||||
)
|
||||
|
||||
// ToSettings converts struct to ProposerSettings
|
||||
func ToSettings(ps *validatorpb.ProposerSettingsPayload) (*ProposerSettings, error) {
|
||||
settings := &ProposerSettings{}
|
||||
if ps.ProposerConfig != nil {
|
||||
settings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption)
|
||||
// SettingFromConsensus converts struct to Settings while verifying the fields
|
||||
func SettingFromConsensus(ps *validatorpb.ProposerSettingsPayload) (*Settings, error) {
|
||||
settings := &Settings{}
|
||||
if ps.ProposerConfig != nil && len(ps.ProposerConfig) != 0 {
|
||||
settings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*Option)
|
||||
for key, optionPayload := range ps.ProposerConfig {
|
||||
if optionPayload.FeeRecipient == "" {
|
||||
continue
|
||||
}
|
||||
b, err := hexutil.Decode(key)
|
||||
decodedKey, err := hexutil.Decode(key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("cannot decode public key %s", key))
|
||||
}
|
||||
p := &ProposerOption{
|
||||
if len(decodedKey) != fieldparams.BLSPubkeyLength {
|
||||
return nil, fmt.Errorf("%v is not a bls public key", key)
|
||||
}
|
||||
if err := verifyOption(key, optionPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Option{
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress(optionPayload.FeeRecipient),
|
||||
},
|
||||
}
|
||||
if optionPayload.Builder != nil {
|
||||
p.BuilderConfig = ToBuilderConfig(optionPayload.Builder)
|
||||
p.BuilderConfig = BuilderConfigFromConsensus(optionPayload.Builder)
|
||||
}
|
||||
settings.ProposeConfig[bytesutil.ToBytes48(b)] = p
|
||||
settings.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = p
|
||||
}
|
||||
}
|
||||
if ps.DefaultConfig != nil {
|
||||
d := &ProposerOption{}
|
||||
d := &Option{}
|
||||
if ps.DefaultConfig.FeeRecipient != "" {
|
||||
if !common.IsHexAddress(ps.DefaultConfig.FeeRecipient) {
|
||||
return nil, errors.New("default fee recipient is not a valid Ethereum address")
|
||||
}
|
||||
if err := config.WarnNonChecksummedAddress(ps.DefaultConfig.FeeRecipient); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.FeeRecipientConfig = &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress(ps.DefaultConfig.FeeRecipient),
|
||||
}
|
||||
}
|
||||
if ps.DefaultConfig.Builder != nil {
|
||||
d.BuilderConfig = ToBuilderConfig(ps.DefaultConfig.Builder)
|
||||
d.BuilderConfig = BuilderConfigFromConsensus(ps.DefaultConfig.Builder)
|
||||
}
|
||||
settings.DefaultConfig = d
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func verifyOption(key string, option *validatorpb.ProposerOptionPayload) error {
|
||||
if option == nil {
|
||||
return fmt.Errorf("fee recipient is required for proposer %s", key)
|
||||
}
|
||||
if !common.IsHexAddress(option.FeeRecipient) {
|
||||
return errors.New("fee recipient is not a valid Ethereum address")
|
||||
}
|
||||
if err := config.WarnNonChecksummedAddress(option.FeeRecipient); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuilderConfig is the struct representation of the JSON config file set in the validator through the CLI.
|
||||
// GasLimit is a number set to help the network decide on the maximum gas in each block.
|
||||
type BuilderConfig struct {
|
||||
@@ -59,29 +85,28 @@ type BuilderConfig struct {
|
||||
Relays []string `json:"relays,omitempty" yaml:"relays,omitempty"`
|
||||
}
|
||||
|
||||
// ToBuilderConfig converts protobuf to a builder config used in inmemory storage
|
||||
func ToBuilderConfig(from *validatorpb.BuilderConfig) *BuilderConfig {
|
||||
// BuilderConfigFromConsensus converts protobuf to a builder config used in in-memory storage
|
||||
func BuilderConfigFromConsensus(from *validatorpb.BuilderConfig) *BuilderConfig {
|
||||
if from == nil {
|
||||
return nil
|
||||
}
|
||||
config := &BuilderConfig{
|
||||
c := &BuilderConfig{
|
||||
Enabled: from.Enabled,
|
||||
GasLimit: from.GasLimit,
|
||||
}
|
||||
if from.Relays != nil {
|
||||
relays := make([]string, len(from.Relays))
|
||||
copy(relays, from.Relays)
|
||||
config.Relays = relays
|
||||
c.Relays = relays
|
||||
}
|
||||
|
||||
return config
|
||||
return c
|
||||
}
|
||||
|
||||
// ProposerSettings is a Prysm internal representation of the fee recipient config on the validator client.
|
||||
// validatorpb.ProposerSettingsPayload maps to ProposerSettings on import through the CLI.
|
||||
type ProposerSettings struct {
|
||||
ProposeConfig map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption
|
||||
DefaultConfig *ProposerOption
|
||||
// Settings is a Prysm internal representation of the fee recipient config on the validator client.
|
||||
// validatorpb.ProposerSettingsPayload maps to Settings on import through the CLI.
|
||||
type Settings struct {
|
||||
ProposeConfig map[[fieldparams.BLSPubkeyLength]byte]*Option
|
||||
DefaultConfig *Option
|
||||
}
|
||||
|
||||
// ShouldBeSaved goes through checks to see if the value should be saveable
|
||||
@@ -89,12 +114,12 @@ type ProposerSettings struct {
|
||||
// 1. settings are not nil
|
||||
// 2. proposeconfig is not nil (this defines specific settings for each validator key), default config can be nil in this case and fall back to beacon node settings
|
||||
// 3. defaultconfig is not nil, meaning it has at least fee recipient settings (this defines general settings for all validator keys but keys will use settings from propose config if available), propose config can be nil in this case
|
||||
func (settings *ProposerSettings) ShouldBeSaved() bool {
|
||||
return settings != nil && (settings.ProposeConfig != nil || settings.DefaultConfig != nil && settings.DefaultConfig.FeeRecipientConfig != nil)
|
||||
func (ps *Settings) ShouldBeSaved() bool {
|
||||
return ps != nil && (ps.ProposeConfig != nil || ps.DefaultConfig != nil && ps.DefaultConfig.FeeRecipientConfig != nil)
|
||||
}
|
||||
|
||||
// ToPayload converts struct to ProposerSettingsPayload
|
||||
func (ps *ProposerSettings) ToPayload() *validatorpb.ProposerSettingsPayload {
|
||||
// ToConsensus converts struct to ProposerSettingsPayload
|
||||
func (ps *Settings) ToConsensus() *validatorpb.ProposerSettingsPayload {
|
||||
if ps == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -102,25 +127,11 @@ func (ps *ProposerSettings) ToPayload() *validatorpb.ProposerSettingsPayload {
|
||||
if ps.ProposeConfig != nil {
|
||||
payload.ProposerConfig = make(map[string]*validatorpb.ProposerOptionPayload)
|
||||
for key, option := range ps.ProposeConfig {
|
||||
p := &validatorpb.ProposerOptionPayload{}
|
||||
if option.FeeRecipientConfig != nil {
|
||||
p.FeeRecipient = option.FeeRecipientConfig.FeeRecipient.Hex()
|
||||
}
|
||||
if option.BuilderConfig != nil {
|
||||
p.Builder = option.BuilderConfig.ToPayload()
|
||||
}
|
||||
payload.ProposerConfig[hexutil.Encode(key[:])] = p
|
||||
payload.ProposerConfig[hexutil.Encode(key[:])] = option.ToConsensus()
|
||||
}
|
||||
}
|
||||
if ps.DefaultConfig != nil {
|
||||
p := &validatorpb.ProposerOptionPayload{}
|
||||
if ps.DefaultConfig.FeeRecipientConfig != nil {
|
||||
p.FeeRecipient = ps.DefaultConfig.FeeRecipientConfig.FeeRecipient.Hex()
|
||||
}
|
||||
if ps.DefaultConfig.BuilderConfig != nil {
|
||||
p.Builder = ps.DefaultConfig.BuilderConfig.ToPayload()
|
||||
}
|
||||
payload.DefaultConfig = p
|
||||
payload.DefaultConfig = ps.DefaultConfig.ToConsensus()
|
||||
}
|
||||
return payload
|
||||
}
|
||||
@@ -130,23 +141,52 @@ type FeeRecipientConfig struct {
|
||||
FeeRecipient common.Address
|
||||
}
|
||||
|
||||
// ProposerOption is a Prysm internal representation of the ProposerOptionPayload on the validator client in bytes format instead of hex.
|
||||
type ProposerOption struct {
|
||||
// Option is a Prysm internal representation of the ProposerOptionPayload on the validator client in bytes format instead of hex.
|
||||
type Option struct {
|
||||
FeeRecipientConfig *FeeRecipientConfig
|
||||
BuilderConfig *BuilderConfig
|
||||
}
|
||||
|
||||
// Clone creates a deep copy of proposer option
|
||||
func (po *Option) Clone() *Option {
|
||||
if po == nil {
|
||||
return nil
|
||||
}
|
||||
p := &Option{}
|
||||
if po.FeeRecipientConfig != nil {
|
||||
p.FeeRecipientConfig = po.FeeRecipientConfig.Clone()
|
||||
}
|
||||
if po.BuilderConfig != nil {
|
||||
p.BuilderConfig = po.BuilderConfig.Clone()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (po *Option) ToConsensus() *validatorpb.ProposerOptionPayload {
|
||||
if po == nil {
|
||||
return nil
|
||||
}
|
||||
p := &validatorpb.ProposerOptionPayload{}
|
||||
if po.FeeRecipientConfig != nil {
|
||||
p.FeeRecipient = po.FeeRecipientConfig.FeeRecipient.Hex()
|
||||
}
|
||||
if po.BuilderConfig != nil {
|
||||
p.Builder = po.BuilderConfig.ToConsensus()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Clone creates a deep copy of the proposer settings
|
||||
func (ps *ProposerSettings) Clone() *ProposerSettings {
|
||||
func (ps *Settings) Clone() *Settings {
|
||||
if ps == nil {
|
||||
return nil
|
||||
}
|
||||
clone := &ProposerSettings{}
|
||||
clone := &Settings{}
|
||||
if ps.DefaultConfig != nil {
|
||||
clone.DefaultConfig = ps.DefaultConfig.Clone()
|
||||
}
|
||||
if ps.ProposeConfig != nil {
|
||||
clone.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption)
|
||||
clone.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*Option)
|
||||
for k, v := range ps.ProposeConfig {
|
||||
keyCopy := k
|
||||
valCopy := v.Clone()
|
||||
@@ -170,46 +210,31 @@ func (bc *BuilderConfig) Clone() *BuilderConfig {
|
||||
if bc == nil {
|
||||
return nil
|
||||
}
|
||||
config := &BuilderConfig{}
|
||||
config.Enabled = bc.Enabled
|
||||
config.GasLimit = bc.GasLimit
|
||||
c := &BuilderConfig{}
|
||||
c.Enabled = bc.Enabled
|
||||
c.GasLimit = bc.GasLimit
|
||||
var relays []string
|
||||
if bc.Relays != nil {
|
||||
relays = make([]string, len(bc.Relays))
|
||||
copy(relays, bc.Relays)
|
||||
config.Relays = relays
|
||||
c.Relays = relays
|
||||
}
|
||||
return config
|
||||
return c
|
||||
}
|
||||
|
||||
// ToPayload converts Builder Config to the protobuf object
|
||||
func (bc *BuilderConfig) ToPayload() *validatorpb.BuilderConfig {
|
||||
// ToConsensus converts Builder Config to the protobuf object
|
||||
func (bc *BuilderConfig) ToConsensus() *validatorpb.BuilderConfig {
|
||||
if bc == nil {
|
||||
return nil
|
||||
}
|
||||
config := &validatorpb.BuilderConfig{}
|
||||
config.Enabled = bc.Enabled
|
||||
c := &validatorpb.BuilderConfig{}
|
||||
c.Enabled = bc.Enabled
|
||||
var relays []string
|
||||
if bc.Relays != nil {
|
||||
relays = make([]string, len(bc.Relays))
|
||||
copy(relays, bc.Relays)
|
||||
config.Relays = relays
|
||||
c.Relays = relays
|
||||
}
|
||||
config.GasLimit = bc.GasLimit
|
||||
return config
|
||||
}
|
||||
|
||||
// Clone creates a deep copy of proposer option
|
||||
func (po *ProposerOption) Clone() *ProposerOption {
|
||||
if po == nil {
|
||||
return nil
|
||||
}
|
||||
p := &ProposerOption{}
|
||||
if po.FeeRecipientConfig != nil {
|
||||
p.FeeRecipientConfig = po.FeeRecipientConfig.Clone()
|
||||
}
|
||||
if po.BuilderConfig != nil {
|
||||
p.BuilderConfig = po.BuilderConfig.Clone()
|
||||
}
|
||||
return p
|
||||
c.GasLimit = bc.GasLimit
|
||||
return c
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package validator_service_config
|
||||
package proposer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -16,8 +16,8 @@ func Test_Proposer_Setting_Cloning(t *testing.T) {
|
||||
key1hex := "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a"
|
||||
key1, err := hexutil.Decode(key1hex)
|
||||
require.NoError(t, err)
|
||||
settings := &ProposerSettings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption{
|
||||
settings := &Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
@@ -29,7 +29,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &ProposerOption{
|
||||
DefaultConfig: &Option{
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
|
||||
},
|
||||
@@ -59,15 +59,15 @@ func Test_Proposer_Setting_Cloning(t *testing.T) {
|
||||
require.NotEqual(t, settings.DefaultConfig.BuilderConfig.GasLimit, clone.GasLimit)
|
||||
})
|
||||
|
||||
t.Run("Happy Path ToBuilderConfig", func(t *testing.T) {
|
||||
t.Run("Happy Path BuilderConfigFromConsensus", func(t *testing.T) {
|
||||
clone := settings.DefaultConfig.BuilderConfig.Clone()
|
||||
config := ToBuilderConfig(clone.ToPayload())
|
||||
config := BuilderConfigFromConsensus(clone.ToConsensus())
|
||||
require.DeepEqual(t, config.Relays, clone.Relays)
|
||||
require.Equal(t, config.Enabled, clone.Enabled)
|
||||
require.Equal(t, config.GasLimit, clone.GasLimit)
|
||||
})
|
||||
t.Run("To Payload and ToSettings", func(t *testing.T) {
|
||||
payload := settings.ToPayload()
|
||||
t.Run("To Payload and SettingFromConsensus", func(t *testing.T) {
|
||||
payload := settings.ToConsensus()
|
||||
option, ok := settings.ProposeConfig[bytesutil.ToBytes48(key1)]
|
||||
require.Equal(t, true, ok)
|
||||
fee := option.FeeRecipientConfig.FeeRecipient.Hex()
|
||||
@@ -77,7 +77,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) {
|
||||
require.Equal(t, settings.DefaultConfig.FeeRecipientConfig.FeeRecipient.Hex(), payload.DefaultConfig.FeeRecipient)
|
||||
require.Equal(t, settings.DefaultConfig.BuilderConfig.Enabled, payload.DefaultConfig.Builder.Enabled)
|
||||
potion.FeeRecipient = ""
|
||||
newSettings, err := ToSettings(payload)
|
||||
newSettings, err := SettingFromConsensus(payload)
|
||||
require.NoError(t, err)
|
||||
|
||||
// when converting to settings if a fee recipient is empty string then it will be skipped
|
||||
@@ -88,7 +88,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) {
|
||||
|
||||
// if fee recipient is set it will not skip
|
||||
potion.FeeRecipient = fee
|
||||
newSettings, err = ToSettings(payload)
|
||||
newSettings, err = SettingFromConsensus(payload)
|
||||
require.NoError(t, err)
|
||||
noption, ok = newSettings.ProposeConfig[bytesutil.ToBytes48(key1)]
|
||||
require.Equal(t, true, ok)
|
||||
@@ -104,8 +104,8 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
key1, err := hexutil.Decode(key1hex)
|
||||
require.NoError(t, err)
|
||||
type fields struct {
|
||||
ProposeConfig map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption
|
||||
DefaultConfig *ProposerOption
|
||||
ProposeConfig map[[fieldparams.BLSPubkeyLength]byte]*Option
|
||||
DefaultConfig *Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -115,7 +115,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
{
|
||||
name: "Should be saved, proposeconfig populated and no default config",
|
||||
fields: fields{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
@@ -135,7 +135,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
name: "Should be saved, default populated and no proposeconfig ",
|
||||
fields: fields{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &ProposerOption{
|
||||
DefaultConfig: &Option{
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
@@ -151,7 +151,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
{
|
||||
name: "Should be saved, all populated",
|
||||
fields: fields{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*ProposerOption{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{
|
||||
bytesutil.ToBytes48(key1): {
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
@@ -163,7 +163,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &ProposerOption{
|
||||
DefaultConfig: &Option{
|
||||
FeeRecipientConfig: &FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
|
||||
},
|
||||
@@ -189,7 +189,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
name: "Should not be saved, builder data only",
|
||||
fields: fields{
|
||||
ProposeConfig: nil,
|
||||
DefaultConfig: &ProposerOption{
|
||||
DefaultConfig: &Option{
|
||||
BuilderConfig: &BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: validator.Uint64(40000000),
|
||||
@@ -202,7 +202,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
settings := &ProposerSettings{
|
||||
settings := &Settings{
|
||||
ProposeConfig: tt.fields.ProposeConfig,
|
||||
DefaultConfig: tt.fields.DefaultConfig,
|
||||
}
|
||||
76
config/util.go
Normal file
76
config/util.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
func UnmarshalFromURL(ctx context.Context, from string, to interface{}) error {
|
||||
u, err := url.ParseRequestURI(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Scheme == "" || u.Host == "" {
|
||||
return fmt.Errorf("invalid URL: %s", from)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, from, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create http request")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to send http request")
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err = Body.Close()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to close response body")
|
||||
}
|
||||
}(resp.Body)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.Errorf("http request to %v failed with status code %d", from, resp.StatusCode)
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&to); err != nil {
|
||||
return errors.Wrap(err, "failed to decode http response")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnmarshalFromFile(from string, to interface{}) error {
|
||||
cleanpath := filepath.Clean(from)
|
||||
b, err := os.ReadFile(cleanpath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open file")
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(b, to); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal yaml file")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func WarnNonChecksummedAddress(feeRecipient string) error {
|
||||
mixedcaseAddress, err := common.NewMixedcaseAddressFromString(feeRecipient)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not decode fee recipient %s", feeRecipient)
|
||||
}
|
||||
if !mixedcaseAddress.ValidChecksum() {
|
||||
log.Warnf("Fee recipient %s is not a checksum Ethereum address. "+
|
||||
"The checksummed address is %s and will be used as the fee recipient. "+
|
||||
"We recommend using a mixed-case address (checksum) "+
|
||||
"to prevent spelling mistakes in your fee recipient Ethereum address", feeRecipient, mixedcaseAddress.Address().Hex())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
61
config/util_test.go
Normal file
61
config/util_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func TestUnmarshalFromURL_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := w.Write([]byte(`{"key":"value"}`))
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
var result map[string]string
|
||||
err := UnmarshalFromURL(context.Background(), server.URL, &result)
|
||||
if err != nil {
|
||||
t.Errorf("UnmarshalFromURL failed: %v", err)
|
||||
}
|
||||
if result["key"] != "value" {
|
||||
t.Errorf("Expected value to be 'value', got '%s'", result["key"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalFromFile_Success(t *testing.T) {
|
||||
// Temporarily create a YAML file
|
||||
tmpFile, err := os.CreateTemp(t.TempDir(), "example.*.yaml")
|
||||
require.NoError(t, err)
|
||||
defer require.NoError(t, os.Remove(tmpFile.Name())) // Clean up
|
||||
|
||||
content := []byte("key: value")
|
||||
|
||||
require.NoError(t, os.WriteFile(tmpFile.Name(), content, params.BeaconIoConfig().ReadWritePermissions))
|
||||
require.NoError(t, tmpFile.Close())
|
||||
|
||||
var result map[string]string
|
||||
require.NoError(t, UnmarshalFromFile(tmpFile.Name(), &result))
|
||||
require.Equal(t, result["key"], "value")
|
||||
}
|
||||
|
||||
func TestWarnNonChecksummedAddress(t *testing.T) {
|
||||
logHook := test.NewGlobal()
|
||||
address := "0x967646dCD8d34F4E02204faeDcbAe0cC96fB9245"
|
||||
err := WarnNonChecksummedAddress(address)
|
||||
require.NoError(t, err)
|
||||
assert.LogsDoNotContain(t, logHook, "is not a checksum Ethereum address")
|
||||
address = strings.ToLower("0x967646dCD8d34F4E02204faeDcbAe0cC96fB9244")
|
||||
err = WarnNonChecksummedAddress(address)
|
||||
require.NoError(t, err)
|
||||
assert.LogsContain(t, logHook, "is not a checksum Ethereum address")
|
||||
}
|
||||
59
go.mod
59
go.mod
@@ -6,8 +6,8 @@ toolchain go1.21.5
|
||||
|
||||
require (
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.2.1
|
||||
github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20221202121132-bd37e8fb1d0d
|
||||
github.com/MariusVanDerWijden/tx-fuzz v1.0.2
|
||||
github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240209103030-ec53fa766bf8
|
||||
github.com/MariusVanDerWijden/tx-fuzz v1.3.3-0.20240227085032-f70dd7c85c97
|
||||
github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96
|
||||
github.com/bazelbuild/rules_go v0.23.2
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/emicklei/dot v0.11.0
|
||||
github.com/ethereum/go-ethereum v1.13.5-0.20231027145059-2d7dba024d76
|
||||
github.com/ethereum/go-ethereum v1.13.5
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
@@ -36,7 +36,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e
|
||||
github.com/holiman/uint256 v1.2.3
|
||||
github.com/holiman/uint256 v1.2.4
|
||||
github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279
|
||||
github.com/ipfs/go-log/v2 v2.5.1
|
||||
github.com/joonix/log v0.0.0-20200409080653-9c1d2ceb5f1d
|
||||
@@ -60,8 +60,8 @@ require (
|
||||
github.com/paulbellamy/ratecounter v0.2.0
|
||||
github.com/pborman/uuid v1.2.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/prometheus/client_model v0.4.0
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/prometheus/prom2json v1.3.0
|
||||
github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
|
||||
@@ -77,7 +77,7 @@ require (
|
||||
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e
|
||||
github.com/trailofbits/go-mutexasserts v0.0.0-20230328101604-8cdbc5f3d279
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
github.com/urfave/cli/v2 v2.26.0
|
||||
github.com/uudashr/gocognit v1.0.5
|
||||
github.com/wealdtech/go-bytesutil v1.1.1
|
||||
github.com/wealdtech/go-eth2-util v1.6.3
|
||||
@@ -93,7 +93,7 @@ require (
|
||||
golang.org/x/tools v0.16.0
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
|
||||
google.golang.org/grpc v1.56.3
|
||||
google.golang.org/protobuf v1.30.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -104,28 +104,28 @@ require (
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.12.1 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.7.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.11.0 // indirect
|
||||
github.com/cespare/cp v1.1.1 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cockroachdb/errors v1.9.1 // indirect
|
||||
github.com/cockroachdb/errors v1.11.1 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect
|
||||
github.com/cockroachdb/redact v1.1.3 // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/consensys/bavard v0.1.13 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
|
||||
github.com/deckarep/golang-set/v2 v2.5.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||
@@ -136,7 +136,7 @@ require (
|
||||
github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9 // indirect
|
||||
github.com/flynn/noise v1.0.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/getsentry/sentry-go v0.18.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.25.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
@@ -148,13 +148,13 @@ require (
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.3.0 // indirect
|
||||
github.com/hashicorp/go-bexpr v0.1.10 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
|
||||
github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/holiman/goevmlab v0.0.0-20230917164918-f3777d0b880b // indirect
|
||||
github.com/holiman/goevmlab v0.0.0-20231201084119-c73b3c97929c // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect
|
||||
@@ -164,7 +164,7 @@ require (
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
|
||||
github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c // indirect
|
||||
github.com/klauspost/compress v1.17.2 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
@@ -181,8 +181,9 @@ require (
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/miekg/dns v1.1.56 // indirect
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||
@@ -209,33 +210,33 @@ require (
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
github.com/quic-go/quic-go v0.39.4 // indirect
|
||||
github.com/quic-go/webtransport-go v0.6.0 // indirect
|
||||
github.com/raulk/go-watchdog v1.3.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.5.2 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.uber.org/dig v1.17.1 // indirect
|
||||
go.uber.org/fx v1.20.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.26.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/oauth2 v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
@@ -253,7 +254,7 @@ require (
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.13.0
|
||||
github.com/peterh/liner v1.2.0 // indirect
|
||||
github.com/prysmaticlabs/gohashtree v0.0.4-beta
|
||||
|
||||
@@ -8,7 +8,6 @@ go_library(
|
||||
deps = [
|
||||
"//config/params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -14,7 +14,13 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ObjType int
|
||||
|
||||
const (
|
||||
Regular ObjType = iota
|
||||
Directory
|
||||
)
|
||||
|
||||
// ExpandPath given a string which may be a relative path.
|
||||
@@ -85,7 +91,13 @@ func WriteFile(file string, data []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if Exists(expanded) {
|
||||
|
||||
exists, err := Exists(expanded, Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if file exists at path %s", expanded)
|
||||
}
|
||||
|
||||
if exists {
|
||||
info, err := os.Stat(expanded)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -136,19 +148,28 @@ func HasReadWritePermissions(itemPath string) (bool, error) {
|
||||
|
||||
// Exists returns true if a file is not a directory and exists
|
||||
// at the specified path.
|
||||
func Exists(filename string) bool {
|
||||
func Exists(filename string, objType ObjType) (bool, error) {
|
||||
filePath, err := ExpandPath(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
return false, errors.Wrapf(err, "could not expend path of file %s", filename)
|
||||
}
|
||||
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.WithError(err).Info("Checking for file existence returned an error")
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false
|
||||
|
||||
return false, errors.Wrapf(err, "could not get file info for file %s", filename)
|
||||
}
|
||||
return info != nil && !info.IsDir()
|
||||
|
||||
if info == nil {
|
||||
return false, errors.New("file info is nil")
|
||||
}
|
||||
|
||||
isDir := info.IsDir()
|
||||
|
||||
return objType == Directory && isDir || objType == Regular && !isDir, nil
|
||||
}
|
||||
|
||||
// RecursiveFileFind returns true, and the path, if a file is not a directory and exists
|
||||
@@ -183,6 +204,40 @@ func RecursiveFileFind(filename, dir string) (bool, string, error) {
|
||||
return found, fpath, nil
|
||||
}
|
||||
|
||||
// RecursiveDirFind searches for directory in a directory and its subdirectories.
|
||||
func RecursiveDirFind(dirname, dir string) (bool, string, error) {
|
||||
var (
|
||||
found bool
|
||||
fpath string
|
||||
)
|
||||
|
||||
dir = filepath.Clean(dir)
|
||||
found = false
|
||||
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error walking directory %s", dir)
|
||||
}
|
||||
|
||||
// Checks if its a file and has the exact name as the dirname
|
||||
// need to break the walk function by using a non-fatal error
|
||||
if info.IsDir() && dirname == info.Name() {
|
||||
found = true
|
||||
fpath = path
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
// No errors or file found
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil && err != errStopWalk {
|
||||
return false, "", errors.Wrapf(err, "error walking directory %s", dir)
|
||||
}
|
||||
|
||||
return found, fpath, nil
|
||||
}
|
||||
|
||||
// ReadFileAsBytes expands a file name's absolute path and reads it as bytes from disk.
|
||||
func ReadFileAsBytes(filename string) ([]byte, error) {
|
||||
filePath, err := ExpandPath(filename)
|
||||
@@ -194,7 +249,12 @@ func ReadFileAsBytes(filename string) ([]byte, error) {
|
||||
|
||||
// CopyFile copy a file from source to destination path.
|
||||
func CopyFile(src, dst string) error {
|
||||
if !Exists(src) {
|
||||
exists, err := Exists(src, Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if file exists at path %s", src)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return errors.New("source file does not exist at provided path")
|
||||
}
|
||||
f, err := os.Open(src) // #nosec G304
|
||||
|
||||
@@ -125,8 +125,9 @@ func TestWriteFile_OK(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
someFileName := filepath.Join(dirName, "somefile.txt")
|
||||
require.NoError(t, file.WriteFile(someFileName, []byte("hi")))
|
||||
exists := file.Exists(someFileName)
|
||||
assert.Equal(t, true, exists)
|
||||
exists, err := file.Exists(someFileName, file.Regular)
|
||||
require.NoError(t, err, "could not check if file exists")
|
||||
assert.Equal(t, true, exists, "file does not exist")
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
@@ -176,8 +177,14 @@ func TestCopyDir(t *testing.T) {
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir1, "subfolder2"), 0777))
|
||||
for _, fd := range fds {
|
||||
require.NoError(t, file.WriteFile(filepath.Join(tmpDir1, fd.path), fd.content))
|
||||
assert.Equal(t, true, file.Exists(filepath.Join(tmpDir1, fd.path)))
|
||||
assert.Equal(t, false, file.Exists(filepath.Join(tmpDir2, fd.path)))
|
||||
|
||||
exists, err := file.Exists(filepath.Join(tmpDir1, fd.path), file.Regular)
|
||||
require.NoError(t, err, "could not check if file exists")
|
||||
assert.Equal(t, true, exists, "file does not exist")
|
||||
|
||||
exists, err = file.Exists(filepath.Join(tmpDir2, fd.path), file.Regular)
|
||||
require.NoError(t, err, "could not check if file exists")
|
||||
assert.Equal(t, false, exists, "file does exist")
|
||||
}
|
||||
|
||||
// Make sure that files are copied into non-existent directory only. If directory exists function exits.
|
||||
@@ -186,7 +193,9 @@ func TestCopyDir(t *testing.T) {
|
||||
|
||||
// Now, all files should have been copied.
|
||||
for _, fd := range fds {
|
||||
assert.Equal(t, true, file.Exists(filepath.Join(tmpDir2, fd.path)))
|
||||
exists, err := file.Exists(filepath.Join(tmpDir2, fd.path), file.Regular)
|
||||
require.NoError(t, err, "could not check if file exists")
|
||||
assert.Equal(t, true, exists)
|
||||
assert.Equal(t, true, deepCompare(t, filepath.Join(tmpDir1, fd.path), filepath.Join(tmpDir2, fd.path)))
|
||||
}
|
||||
assert.Equal(t, true, file.DirsEqual(tmpDir1, tmpDir2))
|
||||
@@ -238,6 +247,66 @@ func TestHashDir(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
tmpFile := filepath.Join(tmpDir, "testfile")
|
||||
nonExistentTmpFile := filepath.Join(tmpDir, "nonexistent")
|
||||
_, err := os.Create(tmpFile)
|
||||
require.NoError(t, err, "could not create test file")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
itemPath string
|
||||
itemType file.ObjType
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "file exists",
|
||||
itemPath: tmpFile,
|
||||
itemType: file.Regular,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "dir exists",
|
||||
itemPath: tmpDir,
|
||||
itemType: file.Directory,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "non-existent file",
|
||||
itemPath: nonExistentTmpFile,
|
||||
itemType: file.Regular,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "non-existent dir",
|
||||
itemPath: nonExistentTmpFile,
|
||||
itemType: file.Directory,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "file is dir",
|
||||
itemPath: tmpDir,
|
||||
itemType: file.Regular,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "dir is file",
|
||||
itemPath: tmpFile,
|
||||
itemType: file.Directory,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
exists, err := file.Exists(tt.itemPath, tt.itemType)
|
||||
require.NoError(t, err, "could not check if file exists")
|
||||
assert.Equal(t, tt.want, exists)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashFile(t *testing.T) {
|
||||
originalData := []byte("test data")
|
||||
originalChecksum := sha256.Sum256(originalData)
|
||||
@@ -290,40 +359,43 @@ func TestDirFiles(t *testing.T) {
|
||||
|
||||
func TestRecursiveFileFind(t *testing.T) {
|
||||
tmpDir, _ := tmpDirWithContentsForRecursiveFind(t)
|
||||
/*
|
||||
tmpDir
|
||||
├── file3
|
||||
├── subfolder1
|
||||
│ └── subfolder11
|
||||
│ └── file1
|
||||
└── subfolder2
|
||||
└── file2
|
||||
*/
|
||||
tests := []struct {
|
||||
name string
|
||||
root string
|
||||
path string
|
||||
found bool
|
||||
}{
|
||||
{
|
||||
name: "file1",
|
||||
root: tmpDir,
|
||||
path: "subfolder1/subfolder11/file1",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "file2",
|
||||
root: tmpDir,
|
||||
path: "subfolder2/file2",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "file1",
|
||||
root: tmpDir + "/subfolder1",
|
||||
path: "subfolder11/file1",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "file3",
|
||||
root: tmpDir,
|
||||
path: "file3",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "file4",
|
||||
root: tmpDir,
|
||||
path: "",
|
||||
found: false,
|
||||
},
|
||||
}
|
||||
@@ -338,6 +410,61 @@ func TestRecursiveFileFind(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecursiveDirFind(t *testing.T) {
|
||||
tmpDir, _ := tmpDirWithContentsForRecursiveFind(t)
|
||||
|
||||
/*
|
||||
tmpDir
|
||||
├── file3
|
||||
├── subfolder1
|
||||
│ └── subfolder11
|
||||
│ └── file1
|
||||
└── subfolder2
|
||||
└── file2
|
||||
*/
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
root string
|
||||
found bool
|
||||
}{
|
||||
{
|
||||
name: "subfolder11",
|
||||
root: tmpDir,
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "subfolder2",
|
||||
root: tmpDir,
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "subfolder11",
|
||||
root: tmpDir + "/subfolder1",
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "file3",
|
||||
root: tmpDir,
|
||||
found: false,
|
||||
},
|
||||
{
|
||||
name: "file4",
|
||||
root: tmpDir,
|
||||
found: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
found, _, err := file.RecursiveDirFind(tt.name, tt.root)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.DeepEqual(t, tt.found, found)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func deepCompare(t *testing.T, file1, file2 string) bool {
|
||||
sf, err := os.Open(file1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -10,6 +10,7 @@ go_library(
|
||||
"//io/file:go_default_library",
|
||||
"//io/prompt:go_default_library",
|
||||
"@com_github_logrusorgru_aurora//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package tos
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/prysmaticlabs/prysm/v5/cmd"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/file"
|
||||
@@ -37,8 +38,13 @@ var (
|
||||
|
||||
// VerifyTosAcceptedOrPrompt checks if Tos was accepted before or asks to accept.
|
||||
func VerifyTosAcceptedOrPrompt(ctx *cli.Context) error {
|
||||
tosFilePath := filepath.Join(ctx.String(cmd.DataDirFlag.Name), acceptTosFilename)
|
||||
if file.Exists(tosFilePath) {
|
||||
acceptTosFilePath := filepath.Join(ctx.String(cmd.DataDirFlag.Name), acceptTosFilename)
|
||||
exists, err := file.Exists(acceptTosFilePath, file.Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if file exists: %s", acceptTosFilePath)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
147
third_party/com_github_datadog_zstd.patch
vendored
147
third_party/com_github_datadog_zstd.patch
vendored
@@ -1,147 +0,0 @@
|
||||
diff --git a/BUILD.bazel b/BUILD.bazel
|
||||
new file mode 100644
|
||||
index 0000000..f15d38e
|
||||
--- /dev/null
|
||||
+++ b/BUILD.bazel
|
||||
@@ -0,0 +1,131 @@
|
||||
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
+load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
+
|
||||
+cc_library(
|
||||
+ name = "zstd_cc",
|
||||
+ srcs = [
|
||||
+ "bitstream.h",
|
||||
+ "clevels.h",
|
||||
+ "compiler.h",
|
||||
+ "cover.c",
|
||||
+ "cover.h",
|
||||
+ "cpu.h",
|
||||
+ "debug.c",
|
||||
+ "debug.h",
|
||||
+ "divsufsort.c",
|
||||
+ "divsufsort.h",
|
||||
+ "entropy_common.c",
|
||||
+ "error_private.c",
|
||||
+ "error_private.h",
|
||||
+ "fastcover.c",
|
||||
+ "fse.h",
|
||||
+ "fse_compress.c",
|
||||
+ "fse_decompress.c",
|
||||
+ "hist.c",
|
||||
+ "hist.h",
|
||||
+ "huf.h",
|
||||
+ "huf_compress.c",
|
||||
+ "huf_decompress.c",
|
||||
+ "huf_decompress_amd64.S",
|
||||
+ "mem.h",
|
||||
+ "pool.c",
|
||||
+ "pool.h",
|
||||
+ "portability_macros.h",
|
||||
+ "threading.c",
|
||||
+ "threading.h",
|
||||
+ "xxhash.c",
|
||||
+ "xxhash.h",
|
||||
+ "zbuff.h",
|
||||
+ "zbuff_common.c",
|
||||
+ "zbuff_compress.c",
|
||||
+ "zbuff_decompress.c",
|
||||
+ "zdict.c",
|
||||
+ "zdict.h",
|
||||
+ "zstd.h",
|
||||
+ "zstd_common.c",
|
||||
+ "zstd_compress.c",
|
||||
+ "zstd_compress_internal.h",
|
||||
+ "zstd_compress_literals.c",
|
||||
+ "zstd_compress_literals.h",
|
||||
+ "zstd_compress_sequences.c",
|
||||
+ "zstd_compress_sequences.h",
|
||||
+ "zstd_compress_superblock.c",
|
||||
+ "zstd_compress_superblock.h",
|
||||
+ "zstd_cwksp.h",
|
||||
+ "zstd_ddict.c",
|
||||
+ "zstd_ddict.h",
|
||||
+ "zstd_decompress.c",
|
||||
+ "zstd_decompress_block.c",
|
||||
+ "zstd_decompress_block.h",
|
||||
+ "zstd_decompress_internal.h",
|
||||
+ "zstd_deps.h",
|
||||
+ "zstd_double_fast.c",
|
||||
+ "zstd_double_fast.h",
|
||||
+ "zstd_errors.h",
|
||||
+ "zstd_fast.c",
|
||||
+ "zstd_fast.h",
|
||||
+ "zstd_internal.h",
|
||||
+ "zstd_lazy.c",
|
||||
+ "zstd_lazy.h",
|
||||
+ "zstd_ldm.c",
|
||||
+ "zstd_ldm.h",
|
||||
+ "zstd_ldm_geartab.h",
|
||||
+ "zstd_legacy.h",
|
||||
+ "zstd_opt.c",
|
||||
+ "zstd_opt.h",
|
||||
+ "zstd_trace.h",
|
||||
+ "zstd_v01.c",
|
||||
+ "zstd_v01.h",
|
||||
+ "zstd_v02.c",
|
||||
+ "zstd_v02.h",
|
||||
+ "zstd_v03.c",
|
||||
+ "zstd_v03.h",
|
||||
+ "zstd_v04.c",
|
||||
+ "zstd_v04.h",
|
||||
+ "zstd_v05.c",
|
||||
+ "zstd_v05.h",
|
||||
+ "zstd_v06.c",
|
||||
+ "zstd_v06.h",
|
||||
+ "zstd_v07.c",
|
||||
+ "zstd_v07.h",
|
||||
+ "zstdmt_compress.c",
|
||||
+ "zstdmt_compress.h",
|
||||
+ ],
|
||||
+ copts = ["-DZSTD_LEGACY_SUPPORT=4"],
|
||||
+
|
||||
+)
|
||||
+go_library(
|
||||
+ name = "zstd",
|
||||
+ srcs = [
|
||||
+ "errors.go",
|
||||
+ "zstd.go",
|
||||
+ "zstd_bulk.go",
|
||||
+ "zstd_ctx.go",
|
||||
+ "zstd_stream.go",
|
||||
+ "zstd.h",
|
||||
+ ],
|
||||
+ cgo = True,
|
||||
+ cdeps = [":zstd_cc"],
|
||||
+ copts = ["-DZSTD_LEGACY_SUPPORT=4"],
|
||||
+ importpath = "github.com/DataDog/zstd",
|
||||
+ visibility = ["//visibility:public"],
|
||||
+)
|
||||
+
|
||||
+alias(
|
||||
+ name = "go_default_library",
|
||||
+ actual = ":zstd",
|
||||
+ visibility = ["//visibility:public"],
|
||||
+)
|
||||
+
|
||||
+go_test(
|
||||
+ name = "zstd_test",
|
||||
+ srcs = [
|
||||
+ "errors_test.go",
|
||||
+ "helpers_test.go",
|
||||
+ "zstd_bullk_test.go",
|
||||
+ "zstd_ctx_test.go",
|
||||
+ "zstd_stream_test.go",
|
||||
+ "zstd_test.go",
|
||||
+ ],
|
||||
+ embed = [":zstd"],
|
||||
+)
|
||||
diff --git a/WORKSPACE b/WORKSPACE
|
||||
new file mode 100644
|
||||
index 0000000..dea5b27
|
||||
--- /dev/null
|
||||
+++ b/WORKSPACE
|
||||
@@ -0,0 +1,2 @@
|
||||
+# DO NOT EDIT: automatically generated WORKSPACE file for go_repository rule
|
||||
+workspace(name = "com_github_datadog_zstd")
|
||||
--
|
||||
2.34.1
|
||||
@@ -156,7 +156,13 @@ func encrypt(cliCtx *cli.Context) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not expand path: %s", outputPath)
|
||||
}
|
||||
if file.Exists(fullPath) {
|
||||
|
||||
exists, err := file.Exists(fullPath, file.Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if file exists: %s", fullPath)
|
||||
}
|
||||
|
||||
if exists {
|
||||
response, err := prompt.ValidatePrompt(
|
||||
os.Stdin,
|
||||
fmt.Sprintf("file at path %s already exists, are you sure you want to overwrite it? [y/n]", fullPath),
|
||||
|
||||
@@ -47,7 +47,12 @@ func zipKeystoresToOutputDir(keystoresToBackup []*keymanager.Keystore, outputDir
|
||||
// Marshal and zip all keystore files together and write the zip file
|
||||
// to the specified output directory.
|
||||
archivePath := filepath.Join(outputDir, ArchiveFilename)
|
||||
if file.Exists(archivePath) {
|
||||
exists, err := file.Exists(archivePath, file.Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if file exists: %s", archivePath)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return errors.Errorf("Zip file already exists in directory: %s", archivePath)
|
||||
}
|
||||
// We create a new file to store our backup.zip.
|
||||
|
||||
@@ -226,7 +226,13 @@ func importPrivateKeyAsAccount(ctx context.Context, wallet *wallet.Wallet, impor
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not expand file path for %s", privKeyFile)
|
||||
}
|
||||
if !file.Exists(fullPath) {
|
||||
|
||||
exists, err := file.Exists(fullPath, file.Regular)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not check if file exists: %s", fullPath)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("file %s does not exist", fullPath)
|
||||
}
|
||||
privKeyHex, err := os.ReadFile(fullPath) // #nosec G304
|
||||
|
||||
@@ -10,7 +10,7 @@ go_library(
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//config/validator/service:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//validator/accounts/iface:go_default_library",
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v5/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/iface"
|
||||
@@ -89,7 +89,7 @@ func (_ *Wallet) InitializeKeymanager(_ context.Context, _ iface.InitKeymanagerC
|
||||
|
||||
type Validator struct {
|
||||
Km keymanager.IKeymanager
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
proposerSettings *proposer.Settings
|
||||
}
|
||||
|
||||
func (_ *Validator) LogSubmittedSyncCommitteeMessages() {}
|
||||
@@ -203,12 +203,12 @@ func (_ *Validator) SignValidatorRegistrationRequest(_ context.Context, _ iface2
|
||||
}
|
||||
|
||||
// ProposerSettings for mocking
|
||||
func (m *Validator) ProposerSettings() *validatorserviceconfig.ProposerSettings {
|
||||
func (m *Validator) ProposerSettings() *proposer.Settings {
|
||||
return m.proposerSettings
|
||||
}
|
||||
|
||||
// SetProposerSettings for mocking
|
||||
func (m *Validator) SetProposerSettings(_ context.Context, settings *validatorserviceconfig.ProposerSettings) error {
|
||||
func (m *Validator) SetProposerSettings(_ context.Context, settings *proposer.Settings) error {
|
||||
m.proposerSettings = settings
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -378,7 +378,10 @@ func (w *Wallet) WriteFileAtPath(_ context.Context, filePath, fileName string, d
|
||||
}
|
||||
}
|
||||
fullPath := filepath.Join(accountPath, fileName)
|
||||
existedPreviously := file.Exists(fullPath)
|
||||
existedPreviously, err := file.Exists(fullPath, file.Regular)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "could not check if file exists: %s", fullPath)
|
||||
}
|
||||
if err := file.WriteFile(fullPath, data); err != nil {
|
||||
return false, errors.Wrapf(err, "could not write %s", filePath)
|
||||
}
|
||||
@@ -439,7 +442,12 @@ func (w *Wallet) FileNameAtPath(_ context.Context, filePath, fileName string) (s
|
||||
// for reading if it exists at the wallet path.
|
||||
func (w *Wallet) ReadKeymanagerConfigFromDisk(_ context.Context) (io.ReadCloser, error) {
|
||||
configFilePath := filepath.Join(w.accountsPath, KeymanagerConfigFileName)
|
||||
if !file.Exists(configFilePath) {
|
||||
exists, err := file.Exists(configFilePath, file.Regular)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not check if file exists: %s", configFilePath)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("no keymanager config file found at path: %s", w.accountsPath)
|
||||
}
|
||||
w.configFilePath = configFilePath
|
||||
|
||||
@@ -5,13 +5,11 @@ go_library(
|
||||
srcs = [
|
||||
"aggregate.go",
|
||||
"attest.go",
|
||||
"attest_protect.go",
|
||||
"key_reload.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
"multiple_endpoints_grpc_resolver.go",
|
||||
"propose.go",
|
||||
"propose_protect.go",
|
||||
"registration.go",
|
||||
"runner.go",
|
||||
"service.go",
|
||||
@@ -36,7 +34,7 @@ go_library(
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/validator/service:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
@@ -49,7 +47,6 @@ go_library(
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//network/httputil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/slashings:go_default_library",
|
||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time:go_default_library",
|
||||
@@ -62,7 +59,7 @@ go_library(
|
||||
"//validator/client/node-client-factory:go_default_library",
|
||||
"//validator/client/validator-client-factory:go_default_library",
|
||||
"//validator/db:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
"//validator/db/common:go_default_library",
|
||||
"//validator/graffiti:go_default_library",
|
||||
"//validator/helpers:go_default_library",
|
||||
"//validator/keymanager:go_default_library",
|
||||
@@ -98,14 +95,12 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"aggregate_test.go",
|
||||
"attest_protect_test.go",
|
||||
"attest_test.go",
|
||||
"key_reload_test.go",
|
||||
"metrics_test.go",
|
||||
"propose_protect_test.go",
|
||||
"propose_test.go",
|
||||
"registration_test.go",
|
||||
"runner_test.go",
|
||||
@@ -126,7 +121,7 @@ go_test(
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/validator/service:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/blocks/testing:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
@@ -153,11 +148,11 @@ go_test(
|
||||
"//validator/client/testutil:go_default_library",
|
||||
"//validator/db/testing:go_default_library",
|
||||
"//validator/graffiti:go_default_library",
|
||||
"//validator/helpers:go_default_library",
|
||||
"//validator/keymanager:go_default_library",
|
||||
"//validator/keymanager/derived:go_default_library",
|
||||
"//validator/keymanager/local:go_default_library",
|
||||
"//validator/keymanager/remote-web3signer:go_default_library",
|
||||
"//validator/slashing-protection-history:go_default_library",
|
||||
"//validator/testing:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
|
||||
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/client/iface"
|
||||
@@ -23,208 +24,235 @@ import (
|
||||
)
|
||||
|
||||
func TestSubmitAggregateAndProof_GetDutiesRequestFailure(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, _, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitAggregateAndProof(context.Background(), 0, pubKey)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitAggregateAndProof(context.Background(), 0, pubKey)
|
||||
|
||||
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
||||
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitAggregateAndProof_SignFails(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.duties = ðpb.DutiesResponse{
|
||||
CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
},
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.duties = ðpb.DutiesResponse{
|
||||
CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}),
|
||||
).Return(ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: make([]byte, 1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: nil}, errors.New("bad domain root"))
|
||||
|
||||
validator.SubmitAggregateAndProof(context.Background(), 0, pubKey)
|
||||
})
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}),
|
||||
).Return(ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: make([]byte, 1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: nil}, errors.New("bad domain root"))
|
||||
|
||||
validator.SubmitAggregateAndProof(context.Background(), 0, pubKey)
|
||||
}
|
||||
|
||||
func TestSubmitAggregateAndProof_Ok(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.duties = ðpb.DutiesResponse{
|
||||
CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
},
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.duties = ðpb.DutiesResponse{
|
||||
CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}),
|
||||
).Return(ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: make([]byte, 1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedAggregateSubmitRequest{}),
|
||||
).Return(ðpb.SignedAggregateSubmitResponse{AttestationDataRoot: make([]byte, 32)}, nil)
|
||||
|
||||
validator.SubmitAggregateAndProof(context.Background(), 0, pubKey)
|
||||
})
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}),
|
||||
).Return(ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: make([]byte, 1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedAggregateSubmitRequest{}),
|
||||
).Return(ðpb.SignedAggregateSubmitResponse{AttestationDataRoot: make([]byte, 32)}, nil)
|
||||
|
||||
validator.SubmitAggregateAndProof(context.Background(), 0, pubKey)
|
||||
}
|
||||
|
||||
func TestSubmitAggregateAndProof_Distributed(t *testing.T) {
|
||||
validatorIdx := primitives.ValidatorIndex(123)
|
||||
slot := primitives.Slot(456)
|
||||
ctx := context.Background()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.duties = ðpb.DutiesResponse{
|
||||
CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
ValidatorIndex: validatorIdx,
|
||||
AttesterSlot: slot,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.duties = ðpb.DutiesResponse{
|
||||
CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
validator.distributed = true
|
||||
validator.attSelections = make(map[attSelectionKey]iface.BeaconCommitteeSelection)
|
||||
validator.attSelections[attSelectionKey{
|
||||
slot: slot,
|
||||
index: 123,
|
||||
}] = iface.BeaconCommitteeSelection{
|
||||
SelectionProof: make([]byte, 96),
|
||||
Slot: slot,
|
||||
ValidatorIndex: validatorIdx,
|
||||
AttesterSlot: slot,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().SubmitAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}),
|
||||
).Return(ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: make([]byte, 1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedAggregateSubmitRequest{}),
|
||||
).Return(ðpb.SignedAggregateSubmitResponse{AttestationDataRoot: make([]byte, 32)}, nil)
|
||||
|
||||
validator.SubmitAggregateAndProof(ctx, slot, pubKey)
|
||||
})
|
||||
}
|
||||
|
||||
validator.distributed = true
|
||||
validator.attSelections = make(map[attSelectionKey]iface.BeaconCommitteeSelection)
|
||||
validator.attSelections[attSelectionKey{
|
||||
slot: slot,
|
||||
index: 123,
|
||||
}] = iface.BeaconCommitteeSelection{
|
||||
SelectionProof: make([]byte, 96),
|
||||
Slot: slot,
|
||||
ValidatorIndex: validatorIdx,
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().SubmitAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}),
|
||||
).Return(ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: make([]byte, 1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedAggregateSelectionProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedAggregateSubmitRequest{}),
|
||||
).Return(ðpb.SignedAggregateSubmitResponse{AttestationDataRoot: make([]byte, 32)}, nil)
|
||||
|
||||
validator.SubmitAggregateAndProof(ctx, slot, pubKey)
|
||||
}
|
||||
|
||||
func TestWaitForSlotTwoThird_WaitCorrectly(t *testing.T) {
|
||||
validator, _, _, finish := setup(t)
|
||||
defer finish()
|
||||
currentTime := time.Now()
|
||||
numOfSlots := primitives.Slot(4)
|
||||
validator.genesisTime = uint64(currentTime.Unix()) - uint64(numOfSlots.Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
oneThird := slots.DivideSlotBy(3 /* one third of slot duration */)
|
||||
timeToSleep := oneThird + oneThird
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, _, _, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
currentTime := time.Now()
|
||||
numOfSlots := primitives.Slot(4)
|
||||
validator.genesisTime = uint64(currentTime.Unix()) - uint64(numOfSlots.Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
oneThird := slots.DivideSlotBy(3 /* one third of slot duration */)
|
||||
timeToSleep := oneThird + oneThird
|
||||
|
||||
twoThirdTime := currentTime.Add(timeToSleep)
|
||||
validator.waitToSlotTwoThirds(context.Background(), numOfSlots)
|
||||
currentTime = time.Now()
|
||||
assert.Equal(t, twoThirdTime.Unix(), currentTime.Unix())
|
||||
twoThirdTime := currentTime.Add(timeToSleep)
|
||||
validator.waitToSlotTwoThirds(context.Background(), numOfSlots)
|
||||
currentTime = time.Now()
|
||||
assert.Equal(t, twoThirdTime.Unix(), currentTime.Unix())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForSlotTwoThird_DoneContext_ReturnsImmediately(t *testing.T) {
|
||||
validator, _, _, finish := setup(t)
|
||||
defer finish()
|
||||
currentTime := time.Now()
|
||||
numOfSlots := primitives.Slot(4)
|
||||
validator.genesisTime = uint64(currentTime.Unix()) - uint64(numOfSlots.Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, _, _, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
currentTime := time.Now()
|
||||
numOfSlots := primitives.Slot(4)
|
||||
validator.genesisTime = uint64(currentTime.Unix()) - uint64(numOfSlots.Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
|
||||
expectedTime := time.Now()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
validator.waitToSlotTwoThirds(ctx, numOfSlots)
|
||||
currentTime = time.Now()
|
||||
assert.Equal(t, expectedTime.Unix(), currentTime.Unix())
|
||||
expectedTime := time.Now()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
validator.waitToSlotTwoThirds(ctx, numOfSlots)
|
||||
currentTime = time.Now()
|
||||
assert.Equal(t, expectedTime.Unix(), currentTime.Unix())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateAndProofSignature_CanSignValidSignature(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.DomainRequest{Epoch: 0, Domain: params.BeaconConfig().DomainAggregateAndProof[:]},
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.DomainRequest{Epoch: 0, Domain: params.BeaconConfig().DomainAggregateAndProof[:]},
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
|
||||
agg := ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: bitfield.NewBitlist(1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
agg := ðpb.AggregateAttestationAndProof{
|
||||
AggregatorIndex: 0,
|
||||
Aggregate: util.HydrateAttestation(ðpb.Attestation{
|
||||
AggregationBits: bitfield.NewBitlist(1),
|
||||
}),
|
||||
SelectionProof: make([]byte, 96),
|
||||
}
|
||||
sig, err := validator.aggregateAndProofSig(context.Background(), pubKey, agg, 0 /* slot */)
|
||||
require.NoError(t, err)
|
||||
_, err = bls.SignatureFromBytes(sig)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
sig, err := validator.aggregateAndProofSig(context.Background(), pubKey, agg, 0 /* slot */)
|
||||
require.NoError(t, err)
|
||||
_, err = bls.SignatureFromBytes(sig)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var failedAttLocalProtectionErr = "attempted to make slashable attestation, rejected by local slashing protection"
|
||||
|
||||
// SubmitAttestation completes the validator client's attester responsibility at a given slot.
|
||||
// It fetches the latest beacon block head along with the latest canonical beacon state
|
||||
// information in order to sign the block and include information about the validator's
|
||||
@@ -135,7 +137,7 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot primitives.Slot,
|
||||
|
||||
// Set the signature of the attestation and send it out to the beacon node.
|
||||
indexedAtt.Signature = sig
|
||||
if err := v.slashableAttestationCheck(ctx, indexedAtt, pubKey, signingRoot); err != nil {
|
||||
if err := v.db.SlashableAttestationCheck(ctx, indexedAtt, pubKey, signingRoot, v.emitAccountMetrics, ValidatorAttestFailVec); err != nil {
|
||||
log.WithError(err).Error("Failed attestation slashing protection check")
|
||||
log.WithFields(
|
||||
attestationLogFields(pubKey, indexedAtt),
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/slashings"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var failedAttLocalProtectionErr = "attempted to make slashable attestation, rejected by local slashing protection"
|
||||
|
||||
// Checks if an attestation is slashable by comparing it with the attesting
|
||||
// history for the given public key in our DB. If it is not, we then update the history
|
||||
// with new values and save it to the database.
|
||||
func (v *validator) slashableAttestationCheck(
|
||||
ctx context.Context,
|
||||
indexedAtt *ethpb.IndexedAttestation,
|
||||
pubKey [fieldparams.BLSPubkeyLength]byte,
|
||||
signingRoot32 [32]byte,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.postAttSignUpdate")
|
||||
defer span.End()
|
||||
|
||||
signingRoot := signingRoot32[:]
|
||||
|
||||
// Based on EIP3076, validator should refuse to sign any attestation with source epoch less
|
||||
// than the minimum source epoch present in that signer’s attestations.
|
||||
lowestSourceEpoch, exists, err := v.db.LowestSignedSourceEpoch(ctx, pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists && indexedAtt.Data.Source.Epoch < lowestSourceEpoch {
|
||||
return fmt.Errorf(
|
||||
"could not sign attestation lower than lowest source epoch in db, %d < %d",
|
||||
indexedAtt.Data.Source.Epoch,
|
||||
lowestSourceEpoch,
|
||||
)
|
||||
}
|
||||
existingSigningRoot, err := v.db.SigningRootAtTargetEpoch(ctx, pubKey, indexedAtt.Data.Target.Epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signingRootsDiffer := slashings.SigningRootsDiffer(existingSigningRoot, signingRoot)
|
||||
|
||||
// Based on EIP3076, validator should refuse to sign any attestation with target epoch less
|
||||
// than or equal to the minimum target epoch present in that signer’s attestations.
|
||||
lowestTargetEpoch, exists, err := v.db.LowestSignedTargetEpoch(ctx, pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if signingRootsDiffer && exists && indexedAtt.Data.Target.Epoch <= lowestTargetEpoch {
|
||||
return fmt.Errorf(
|
||||
"could not sign attestation lower than or equal to lowest target epoch in db, %d <= %d",
|
||||
indexedAtt.Data.Target.Epoch,
|
||||
lowestTargetEpoch,
|
||||
)
|
||||
}
|
||||
fmtKey := "0x" + hex.EncodeToString(pubKey[:])
|
||||
slashingKind, err := v.db.CheckSlashableAttestation(ctx, pubKey, signingRoot, indexedAtt)
|
||||
if err != nil {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
switch slashingKind {
|
||||
case kv.DoubleVote:
|
||||
log.Warn("Attestation is slashable as it is a double vote")
|
||||
case kv.SurroundingVote:
|
||||
log.Warn("Attestation is slashable as it is surrounding a previous attestation")
|
||||
case kv.SurroundedVote:
|
||||
log.Warn("Attestation is slashable as it is surrounded by a previous attestation")
|
||||
}
|
||||
return errors.Wrap(err, failedAttLocalProtectionErr)
|
||||
}
|
||||
|
||||
if err := v.db.SaveAttestationForPubKey(ctx, pubKey, signingRoot32, indexedAtt); err != nil {
|
||||
return errors.Wrap(err, "could not save attestation history for validator public key")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func Test_slashableAttestationCheck(t *testing.T) {
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 5,
|
||||
CommitteeIndex: 2,
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("great block"), 32),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 4,
|
||||
Root: bytesutil.PadTo([]byte("good source"), 32),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 10,
|
||||
Root: bytesutil.PadTo([]byte("good target"), 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := validator.slashableAttestationCheck(context.Background(), att, pubKey, [32]byte{1})
|
||||
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
||||
}
|
||||
|
||||
func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
ctx := context.Background()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 5,
|
||||
CommitteeIndex: 2,
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("great block"), 32),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 4,
|
||||
Root: bytesutil.PadTo([]byte("good source"), 32),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 10,
|
||||
Root: bytesutil.PadTo([]byte("good target"), 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.DomainRequest{Epoch: 10, Domain: []byte{1, 0, 0, 0}},
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
_, sr, err := validator.getDomainAndSigningRoot(ctx, att.Data)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = validator.slashableAttestationCheck(context.Background(), att, pubKey, sr)
|
||||
require.NoError(t, err)
|
||||
differentSigningRoot := [32]byte{2}
|
||||
|
||||
err = validator.slashableAttestationCheck(context.Background(), att, pubKey, differentSigningRoot)
|
||||
require.ErrorContains(t, "could not sign attestation", err)
|
||||
|
||||
e, exists, err := validator.db.LowestSignedSourceEpoch(context.Background(), pubKey)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, exists)
|
||||
require.Equal(t, primitives.Epoch(4), e)
|
||||
e, exists, err = validator.db.LowestSignedTargetEpoch(context.Background(), pubKey)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, exists)
|
||||
require.Equal(t, primitives.Epoch(10), e)
|
||||
}
|
||||
|
||||
func Test_slashableAttestationCheck_OK(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
validator, _, _, finish := setup(t)
|
||||
defer finish()
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 5,
|
||||
CommitteeIndex: 2,
|
||||
BeaconBlockRoot: []byte("great block"),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 4,
|
||||
Root: []byte("good source"),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 10,
|
||||
Root: []byte("good target"),
|
||||
},
|
||||
},
|
||||
}
|
||||
sr := [32]byte{1}
|
||||
fakePubkey := bytesutil.ToBytes48([]byte("test"))
|
||||
|
||||
err := validator.slashableAttestationCheck(ctx, att, fakePubkey, sr)
|
||||
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
||||
}
|
||||
|
||||
func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
validator, _, _, finish := setup(t)
|
||||
defer finish()
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 5,
|
||||
CommitteeIndex: 2,
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("great block root"), 32),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: bytesutil.PadTo([]byte("great root"), 32),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: bytesutil.PadTo([]byte("great root"), 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fakePubkey := bytesutil.ToBytes48([]byte("test"))
|
||||
err := validator.slashableAttestationCheck(ctx, att, fakePubkey, [32]byte{})
|
||||
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
||||
e, exists, err := validator.db.LowestSignedSourceEpoch(context.Background(), fakePubkey)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, exists)
|
||||
require.Equal(t, primitives.Epoch(0), e)
|
||||
e, exists, err = validator.db.LowestSignedTargetEpoch(context.Background(), fakePubkey)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, exists)
|
||||
require.Equal(t, primitives.Epoch(0), e)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ go_library(
|
||||
"index.go",
|
||||
"json_rest_handler.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
"prepare_beacon_proposer.go",
|
||||
"propose_attestation.go",
|
||||
"propose_beacon_block.go",
|
||||
@@ -60,6 +61,8 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_golang_protobuf//ptypes/empty",
|
||||
"@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_sirupsen_logrus//:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
|
||||
|
||||
@@ -50,32 +50,38 @@ func NewBeaconApiValidatorClient(jsonRestHandler JsonRestHandler, opts ...Valida
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetDuties(ctx context.Context, in *ethpb.DutiesRequest) (*ethpb.DutiesResponse, error) {
|
||||
return c.getDuties(ctx, in)
|
||||
return wrapInMetrics[*ethpb.DutiesResponse]("GetDuties", func() (*ethpb.DutiesResponse, error) {
|
||||
return c.getDuties(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) CheckDoppelGanger(ctx context.Context, in *ethpb.DoppelGangerRequest) (*ethpb.DoppelGangerResponse, error) {
|
||||
return c.checkDoppelGanger(ctx, in)
|
||||
return wrapInMetrics[*ethpb.DoppelGangerResponse]("CheckDoppelGanger", func() (*ethpb.DoppelGangerResponse, error) {
|
||||
return c.checkDoppelGanger(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) DomainData(ctx context.Context, in *ethpb.DomainRequest) (*ethpb.DomainResponse, error) {
|
||||
if len(in.Domain) != 4 {
|
||||
return nil, errors.Errorf("invalid domain type: %s", hexutil.Encode(in.Domain))
|
||||
}
|
||||
|
||||
domainType := bytesutil.ToBytes4(in.Domain)
|
||||
return c.getDomainData(ctx, in.Epoch, domainType)
|
||||
|
||||
return wrapInMetrics[*ethpb.DomainResponse]("DomainData", func() (*ethpb.DomainResponse, error) {
|
||||
return c.getDomainData(ctx, in.Epoch, domainType)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetAttestationData(ctx context.Context, in *ethpb.AttestationDataRequest) (*ethpb.AttestationData, error) {
|
||||
if in == nil {
|
||||
return nil, errors.New("GetAttestationData received nil argument `in`")
|
||||
}
|
||||
|
||||
return c.getAttestationData(ctx, in.Slot, in.CommitteeIndex)
|
||||
return wrapInMetrics[*ethpb.AttestationData]("GetAttestationData", func() (*ethpb.AttestationData, error) {
|
||||
return c.getAttestationData(ctx, in.Slot, in.CommitteeIndex)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetBeaconBlock(ctx context.Context, in *ethpb.BlockRequest) (*ethpb.GenericBeaconBlock, error) {
|
||||
return c.getBeaconBlock(ctx, in.Slot, in.RandaoReveal, in.Graffiti)
|
||||
return wrapInMetrics[*ethpb.GenericBeaconBlock]("GetBeaconBlock", func() (*ethpb.GenericBeaconBlock, error) {
|
||||
return c.getBeaconBlock(ctx, in.Slot, in.RandaoReveal, in.Graffiti)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetFeeRecipientByPubKey(_ context.Context, _ *ethpb.FeeRecipientByPubKeyRequest) (*ethpb.FeeRecipientByPubKeyResponse, error) {
|
||||
@@ -83,35 +89,51 @@ func (c *beaconApiValidatorClient) GetFeeRecipientByPubKey(_ context.Context, _
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetSyncCommitteeContribution(ctx context.Context, in *ethpb.SyncCommitteeContributionRequest) (*ethpb.SyncCommitteeContribution, error) {
|
||||
return c.getSyncCommitteeContribution(ctx, in)
|
||||
return wrapInMetrics[*ethpb.SyncCommitteeContribution]("GetSyncCommitteeContribution", func() (*ethpb.SyncCommitteeContribution, error) {
|
||||
return c.getSyncCommitteeContribution(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetSyncMessageBlockRoot(ctx context.Context, _ *empty.Empty) (*ethpb.SyncMessageBlockRootResponse, error) {
|
||||
return c.getSyncMessageBlockRoot(ctx)
|
||||
return wrapInMetrics[*ethpb.SyncMessageBlockRootResponse]("GetSyncMessageBlockRoot", func() (*ethpb.SyncMessageBlockRootResponse, error) {
|
||||
return c.getSyncMessageBlockRoot(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) GetSyncSubcommitteeIndex(ctx context.Context, in *ethpb.SyncSubcommitteeIndexRequest) (*ethpb.SyncSubcommitteeIndexResponse, error) {
|
||||
return c.getSyncSubcommitteeIndex(ctx, in)
|
||||
return wrapInMetrics[*ethpb.SyncSubcommitteeIndexResponse]("GetSyncSubcommitteeIndex", func() (*ethpb.SyncSubcommitteeIndexResponse, error) {
|
||||
return c.getSyncSubcommitteeIndex(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) MultipleValidatorStatus(ctx context.Context, in *ethpb.MultipleValidatorStatusRequest) (*ethpb.MultipleValidatorStatusResponse, error) {
|
||||
return c.multipleValidatorStatus(ctx, in)
|
||||
return wrapInMetrics[*ethpb.MultipleValidatorStatusResponse]("MultipleValidatorStatus", func() (*ethpb.MultipleValidatorStatusResponse, error) {
|
||||
return c.multipleValidatorStatus(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) PrepareBeaconProposer(ctx context.Context, in *ethpb.PrepareBeaconProposerRequest) (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.prepareBeaconProposer(ctx, in.Recipients)
|
||||
return wrapInMetrics[*empty.Empty]("PrepareBeaconProposer", func() (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.prepareBeaconProposer(ctx, in.Recipients)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ProposeAttestation(ctx context.Context, in *ethpb.Attestation) (*ethpb.AttestResponse, error) {
|
||||
return c.proposeAttestation(ctx, in)
|
||||
return wrapInMetrics[*ethpb.AttestResponse]("ProposeAttestation", func() (*ethpb.AttestResponse, error) {
|
||||
return c.proposeAttestation(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ProposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
return c.proposeBeaconBlock(ctx, in)
|
||||
return wrapInMetrics[*ethpb.ProposeResponse]("ProposeBeaconBlock", func() (*ethpb.ProposeResponse, error) {
|
||||
return c.proposeBeaconBlock(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ProposeExit(ctx context.Context, in *ethpb.SignedVoluntaryExit) (*ethpb.ProposeExitResponse, error) {
|
||||
return c.proposeExit(ctx, in)
|
||||
return wrapInMetrics[*ethpb.ProposeExitResponse]("ProposeExit", func() (*ethpb.ProposeExitResponse, error) {
|
||||
return c.proposeExit(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) StreamSlots(ctx context.Context, in *ethpb.StreamSlotsRequest) (ethpb.BeaconNodeValidator_StreamSlotsClient, error) {
|
||||
@@ -123,31 +145,45 @@ func (c *beaconApiValidatorClient) StreamBlocksAltair(ctx context.Context, in *e
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) {
|
||||
return c.submitAggregateSelectionProof(ctx, in)
|
||||
return wrapInMetrics[*ethpb.AggregateSelectionResponse]("SubmitAggregateSelectionProof", func() (*ethpb.AggregateSelectionResponse, error) {
|
||||
return c.submitAggregateSelectionProof(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) SubmitSignedAggregateSelectionProof(ctx context.Context, in *ethpb.SignedAggregateSubmitRequest) (*ethpb.SignedAggregateSubmitResponse, error) {
|
||||
return c.submitSignedAggregateSelectionProof(ctx, in)
|
||||
return wrapInMetrics[*ethpb.SignedAggregateSubmitResponse]("SubmitSignedAggregateSelectionProof", func() (*ethpb.SignedAggregateSubmitResponse, error) {
|
||||
return c.submitSignedAggregateSelectionProof(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) SubmitSignedContributionAndProof(ctx context.Context, in *ethpb.SignedContributionAndProof) (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.submitSignedContributionAndProof(ctx, in)
|
||||
return wrapInMetrics[*empty.Empty]("SubmitSignedContributionAndProof", func() (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.submitSignedContributionAndProof(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) SubmitSyncMessage(ctx context.Context, in *ethpb.SyncCommitteeMessage) (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.submitSyncMessage(ctx, in)
|
||||
return wrapInMetrics[*empty.Empty]("SubmitSyncMessage", func() (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.submitSyncMessage(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) SubmitValidatorRegistrations(ctx context.Context, in *ethpb.SignedValidatorRegistrationsV1) (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.submitValidatorRegistrations(ctx, in.Messages)
|
||||
return wrapInMetrics[*empty.Empty]("SubmitValidatorRegistrations", func() (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.submitValidatorRegistrations(ctx, in.Messages)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) SubscribeCommitteeSubnets(ctx context.Context, in *ethpb.CommitteeSubnetsSubscribeRequest, validatorIndices []primitives.ValidatorIndex) (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.subscribeCommitteeSubnets(ctx, in, validatorIndices)
|
||||
return wrapInMetrics[*empty.Empty]("SubscribeCommitteeSubnets", func() (*empty.Empty, error) {
|
||||
return new(empty.Empty), c.subscribeCommitteeSubnets(ctx, in, validatorIndices)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ValidatorIndex(ctx context.Context, in *ethpb.ValidatorIndexRequest) (*ethpb.ValidatorIndexResponse, error) {
|
||||
return c.validatorIndex(ctx, in)
|
||||
return wrapInMetrics[*ethpb.ValidatorIndexResponse]("ValidatorIndex", func() (*ethpb.ValidatorIndexResponse, error) {
|
||||
return c.validatorIndex(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ValidatorStatus(ctx context.Context, in *ethpb.ValidatorStatusRequest) (*ethpb.ValidatorStatusResponse, error) {
|
||||
@@ -183,3 +219,15 @@ func (c *beaconApiValidatorClient) GetAggregatedSelections(ctx context.Context,
|
||||
func (c *beaconApiValidatorClient) GetAggregatedSyncSelections(ctx context.Context, selections []iface.SyncCommitteeSelection) ([]iface.SyncCommitteeSelection, error) {
|
||||
return c.getAggregatedSyncSelections(ctx, selections)
|
||||
}
|
||||
|
||||
func wrapInMetrics[Resp any](action string, f func() (Resp, error)) (Resp, error) {
|
||||
now := time.Now()
|
||||
resp, err := f()
|
||||
httpActionCount.WithLabelValues(action).Inc()
|
||||
if err == nil {
|
||||
httpActionLatency.WithLabelValues(action).Observe(time.Since(now).Seconds())
|
||||
} else {
|
||||
failedHTTPActionCount.WithLabelValues(action).Inc()
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
@@ -54,12 +54,6 @@ func TestBeaconApiValidatorClient_GetAttestationDataValid(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedResp, resp)
|
||||
}
|
||||
|
||||
func TestBeaconApiValidatorClient_GetAttestationDataNilInput(t *testing.T) {
|
||||
validatorClient := beaconApiValidatorClient{}
|
||||
_, err := validatorClient.GetAttestationData(context.Background(), nil)
|
||||
assert.ErrorContains(t, "GetAttestationData received nil argument `in`", err)
|
||||
}
|
||||
|
||||
func TestBeaconApiValidatorClient_GetAttestationDataError(t *testing.T) {
|
||||
const slot = primitives.Slot(1)
|
||||
const committeeIndex = primitives.CommitteeIndex(2)
|
||||
|
||||
34
validator/client/beacon-api/metrics.go
Normal file
34
validator/client/beacon-api/metrics.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
httpActionLatency = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: "validator",
|
||||
Name: "http_action_latency_seconds",
|
||||
Help: "Latency of HTTP actions performed against the beacon node in seconds. This metric captures only actions that didn't result in an error.",
|
||||
Buckets: []float64{0.001, 0.01, 0.025, 0.1, 0.25, 1, 2.5, 10},
|
||||
},
|
||||
[]string{"action"},
|
||||
)
|
||||
httpActionCount = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: "validator",
|
||||
Name: "http_action_count",
|
||||
Help: "Number of all HTTP actions performed against the beacon node",
|
||||
},
|
||||
[]string{"action"},
|
||||
)
|
||||
failedHTTPActionCount = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: "validator",
|
||||
Name: "failed_http_action_count",
|
||||
Help: "Number of failed HTTP actions performed against the beacon node",
|
||||
},
|
||||
[]string{"action"},
|
||||
)
|
||||
)
|
||||
@@ -13,7 +13,7 @@ go_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/validator/service:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//consensus-types/validator:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v5/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
@@ -62,8 +62,8 @@ type Validator interface {
|
||||
CheckDoppelGanger(ctx context.Context) error
|
||||
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, deadline time.Time) error
|
||||
SignValidatorRegistrationRequest(ctx context.Context, signer SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error)
|
||||
ProposerSettings() *validatorserviceconfig.ProposerSettings
|
||||
SetProposerSettings(context.Context, *validatorserviceconfig.ProposerSettings) error
|
||||
ProposerSettings() *proposer.Settings
|
||||
SetProposerSettings(context.Context, *proposer.Settings) error
|
||||
StartEventStream(ctx context.Context) error
|
||||
EventStreamIsRunning() bool
|
||||
NodeIsHealthy(ctx context.Context) bool
|
||||
|
||||
@@ -28,9 +28,12 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
const domainDataErr = "could not get domain data"
|
||||
const signingRootErr = "could not get signing root"
|
||||
const signExitErr = "could not sign voluntary exit proposal"
|
||||
const (
|
||||
domainDataErr = "could not get domain data"
|
||||
signingRootErr = "could not get signing root"
|
||||
signExitErr = "could not sign voluntary exit proposal"
|
||||
failedBlockSignLocalErr = "block rejected by local protection"
|
||||
)
|
||||
|
||||
// ProposeBlock proposes a new beacon block for a given slot. This method collects the
|
||||
// previous beacon block, any pending deposits, and ETH1 data from the beacon
|
||||
@@ -111,7 +114,7 @@ func (v *validator) ProposeBlock(ctx context.Context, slot primitives.Slot, pubK
|
||||
return
|
||||
}
|
||||
|
||||
if err := v.slashableProposalCheck(ctx, pubKey, blk, signingRoot); err != nil {
|
||||
if err := v.db.SlashableProposalCheck(ctx, pubKey, blk, signingRoot, v.emitAccountMetrics, ValidatorProposeFailVec); err != nil {
|
||||
log.WithFields(
|
||||
blockLogFields(pubKey, wb, nil),
|
||||
).WithError(err).Error("Failed block slashing protection check")
|
||||
@@ -429,3 +432,15 @@ func (v *validator) getGraffiti(ctx context.Context, pubKey [fieldparams.BLSPubk
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func blockLogFields(pubKey [fieldparams.BLSPubkeyLength]byte, blk interfaces.ReadOnlyBeaconBlock, sig []byte) logrus.Fields {
|
||||
fields := logrus.Fields{
|
||||
"proposerPublicKey": fmt.Sprintf("%#x", pubKey),
|
||||
"proposerIndex": blk.ProposerIndex(),
|
||||
"blockSlot": blk.Slot(),
|
||||
}
|
||||
if sig != nil {
|
||||
fields["signature"] = fmt.Sprintf("%#x", sig)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var failedBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection"
|
||||
|
||||
// slashableProposalCheck checks if a block proposal is slashable by comparing it with the
|
||||
// block proposals history for the given public key in our DB. If it is not, we then update the history
|
||||
// with new values and save it to the database.
|
||||
func (v *validator) slashableProposalCheck(
|
||||
ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, signedBlock interfaces.ReadOnlySignedBeaconBlock, signingRoot [32]byte,
|
||||
) error {
|
||||
fmtKey := fmt.Sprintf("%#x", pubKey[:])
|
||||
|
||||
blk := signedBlock.Block()
|
||||
prevSigningRoot, proposalAtSlotExists, prevSigningRootExists, err := v.db.ProposalHistoryForSlot(ctx, pubKey, blk.Slot())
|
||||
if err != nil {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.Wrap(err, "failed to get proposal history")
|
||||
}
|
||||
|
||||
lowestSignedProposalSlot, lowestProposalExists, err := v.db.LowestSignedProposal(ctx, pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Based on EIP-3076 - Condition 2
|
||||
// -------------------------------
|
||||
if lowestProposalExists {
|
||||
// If the block slot is (strictly) less than the lowest signed proposal slot in the DB, we consider it slashable.
|
||||
if blk.Slot() < lowestSignedProposalSlot {
|
||||
return fmt.Errorf(
|
||||
"could not sign block with slot < lowest signed slot in db, block slot: %d < lowest signed slot: %d",
|
||||
blk.Slot(),
|
||||
lowestSignedProposalSlot,
|
||||
)
|
||||
}
|
||||
|
||||
// If the block slot is equal to the lowest signed proposal slot and
|
||||
// - condition1: there is no signed proposal in the DB for this slot, or
|
||||
// - condition2: there is a signed proposal in the DB for this slot, but with no associated signing root, or
|
||||
// - condition3: there is a signed proposal in the DB for this slot, but the signing root differs,
|
||||
// ==> we consider it slashable.
|
||||
condition1 := !proposalAtSlotExists
|
||||
condition2 := proposalAtSlotExists && !prevSigningRootExists
|
||||
condition3 := proposalAtSlotExists && prevSigningRootExists && prevSigningRoot != signingRoot
|
||||
if blk.Slot() == lowestSignedProposalSlot && (condition1 || condition2 || condition3) {
|
||||
return fmt.Errorf(
|
||||
"could not sign block with slot == lowest signed slot in db if it is not a repeat signing, block slot: %d == slowest signed slot: %d",
|
||||
blk.Slot(),
|
||||
lowestSignedProposalSlot,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Based on EIP-3076 - Condition 1
|
||||
// -------------------------------
|
||||
// If there is a signed proposal in the DB for this slot and
|
||||
// - there is no associated signing root, or
|
||||
// - the signing root differs,
|
||||
// ==> we consider it slashable.
|
||||
if proposalAtSlotExists && (!prevSigningRootExists || prevSigningRoot != signingRoot) {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.New(failedBlockSignLocalErr)
|
||||
}
|
||||
|
||||
// Save the proposal for this slot.
|
||||
if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, blk.Slot(), signingRoot[:]); err != nil {
|
||||
if v.emitAccountMetrics {
|
||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
||||
}
|
||||
return errors.Wrap(err, "failed to save updated proposal history")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func blockLogFields(pubKey [fieldparams.BLSPubkeyLength]byte, blk interfaces.ReadOnlyBeaconBlock, sig []byte) logrus.Fields {
|
||||
fields := logrus.Fields{
|
||||
"pubkey": fmt.Sprintf("%#x", pubKey),
|
||||
"proposerIndex": blk.ProposerIndex(),
|
||||
"slot": blk.Slot(),
|
||||
}
|
||||
if sig != nil {
|
||||
fields["signature"] = fmt.Sprintf("%#x", sig)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||
)
|
||||
|
||||
func Test_slashableProposalCheck_PreventsLowerThanMinProposal(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
lowestSignedSlot := primitives.Slot(10)
|
||||
var pubKeyBytes [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKeyBytes[:], validatorKey.PublicKey().Marshal())
|
||||
|
||||
// We save a proposal at the lowest signed slot in the DB.
|
||||
err := validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, lowestSignedSlot, []byte{1})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block with a slot lower than the lowest
|
||||
// signed slot to fail validation.
|
||||
blk := ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot - 1,
|
||||
ProposerIndex: 0,
|
||||
Body: ðpb.BeaconBlockBody{},
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
wsb, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wsb, [32]byte{4})
|
||||
require.ErrorContains(t, "could not sign block with slot < lowest signed", err)
|
||||
|
||||
// We expect the same block with a slot equal to the lowest
|
||||
// signed slot to pass validation if signing roots are equal.
|
||||
blk = ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot,
|
||||
ProposerIndex: 0,
|
||||
Body: ðpb.BeaconBlockBody{},
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
wsb, err = blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wsb, [32]byte{1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block with a slot equal to the lowest
|
||||
// signed slot to fail validation if signing roots are different.
|
||||
wsb, err = blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wsb, [32]byte{4})
|
||||
require.ErrorContains(t, "could not sign block with slot == lowest signed", err)
|
||||
|
||||
// We expect the same block with a slot > than the lowest
|
||||
// signed slot to pass validation.
|
||||
blk = ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: lowestSignedSlot + 1,
|
||||
ProposerIndex: 0,
|
||||
Body: ðpb.BeaconBlockBody{},
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
|
||||
wsb, err = blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wsb, [32]byte{3})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_slashableProposalCheck(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
|
||||
blk := util.HydrateSignedBeaconBlock(ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
Slot: 10,
|
||||
ProposerIndex: 0,
|
||||
Body: ðpb.BeaconBlockBody{},
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
})
|
||||
|
||||
var pubKeyBytes [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKeyBytes[:], validatorKey.PublicKey().Marshal())
|
||||
|
||||
// We save a proposal at slot 1 as our lowest proposal.
|
||||
err := validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, 1, []byte{1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// We save a proposal at slot 10 with a dummy signing root.
|
||||
dummySigningRoot := [32]byte{1}
|
||||
err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, 10, dummySigningRoot[:])
|
||||
require.NoError(t, err)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
sBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block sent out with the same root should not be slasahble.
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, dummySigningRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block sent out with a different signing root should be slasahble.
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2})
|
||||
require.ErrorContains(t, failedBlockSignLocalErr, err)
|
||||
|
||||
// We save a proposal at slot 11 with a nil signing root.
|
||||
blk.Block.Slot = 11
|
||||
sBlock, err = blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, blk.Block.Slot, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We expect the same block sent out should return slashable error even
|
||||
// if we had a nil signing root stored in the database.
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2})
|
||||
require.ErrorContains(t, failedBlockSignLocalErr, err)
|
||||
|
||||
// A block with a different slot for which we do not have a proposing history
|
||||
// should not be failing validation.
|
||||
blk.Block.Slot = 9
|
||||
sBlock, err = blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{3})
|
||||
require.NoError(t, err, "Expected allowed block not to throw error")
|
||||
}
|
||||
|
||||
func Test_slashableProposalCheck_RemoteProtection(t *testing.T) {
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.Slot = 10
|
||||
sBlock, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2})
|
||||
require.NoError(t, err, "Expected allowed block not to throw error")
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -17,38 +18,67 @@ import (
|
||||
)
|
||||
|
||||
func TestSubmitValidatorRegistrations(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
|
||||
ctx := context.Background()
|
||||
validatorRegsBatchSize := 2
|
||||
require.NoError(t, nil, SubmitValidatorRegistrations(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{}, validatorRegsBatchSize))
|
||||
ctx := context.Background()
|
||||
validatorRegsBatchSize := 2
|
||||
require.NoError(t, nil, SubmitValidatorRegistrations(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{}, validatorRegsBatchSize))
|
||||
|
||||
regs := [...]*ethpb.ValidatorRegistrationV1{
|
||||
{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 123,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 456,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 789,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
}
|
||||
regs := [...]*ethpb.ValidatorRegistrationV1{
|
||||
{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 123,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 456,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 789,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
},
|
||||
}
|
||||
|
||||
gomock.InOrder(
|
||||
m.validatorClient.EXPECT().
|
||||
SubmitValidatorRegistrations(gomock.Any(), ðpb.SignedValidatorRegistrationsV1{
|
||||
Messages: []*ethpb.SignedValidatorRegistrationV1{
|
||||
gomock.InOrder(
|
||||
m.validatorClient.EXPECT().
|
||||
SubmitValidatorRegistrations(gomock.Any(), ðpb.SignedValidatorRegistrationsV1{
|
||||
Messages: []*ethpb.SignedValidatorRegistrationV1{
|
||||
{
|
||||
Message: regs[0],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
{
|
||||
Message: regs[1],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
}).
|
||||
Return(nil, nil),
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
SubmitValidatorRegistrations(gomock.Any(), ðpb.SignedValidatorRegistrationsV1{
|
||||
Messages: []*ethpb.SignedValidatorRegistrationV1{
|
||||
{
|
||||
Message: regs[2],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
}).
|
||||
Return(nil, nil),
|
||||
)
|
||||
|
||||
require.NoError(t, nil, SubmitValidatorRegistrations(
|
||||
ctx, m.validatorClient,
|
||||
[]*ethpb.SignedValidatorRegistrationV1{
|
||||
{
|
||||
Message: regs[0],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
@@ -57,222 +87,206 @@ func TestSubmitValidatorRegistrations(t *testing.T) {
|
||||
Message: regs[1],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
}).
|
||||
Return(nil, nil),
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
SubmitValidatorRegistrations(gomock.Any(), ðpb.SignedValidatorRegistrationsV1{
|
||||
Messages: []*ethpb.SignedValidatorRegistrationV1{
|
||||
{
|
||||
Message: regs[2],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
}).
|
||||
Return(nil, nil),
|
||||
)
|
||||
|
||||
require.NoError(t, nil, SubmitValidatorRegistrations(
|
||||
ctx, m.validatorClient,
|
||||
[]*ethpb.SignedValidatorRegistrationV1{
|
||||
{
|
||||
Message: regs[0],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
{
|
||||
Message: regs[1],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
{
|
||||
Message: regs[2],
|
||||
Signature: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
validatorRegsBatchSize,
|
||||
))
|
||||
validatorRegsBatchSize,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitValidatorRegistration_CantSign(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
|
||||
ctx := context.Background()
|
||||
validatorRegsBatchSize := 500
|
||||
reg := ðpb.ValidatorRegistrationV1{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 123456,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
}
|
||||
ctx := context.Background()
|
||||
validatorRegsBatchSize := 500
|
||||
reg := ðpb.ValidatorRegistrationV1{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 123456,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
SubmitValidatorRegistrations(gomock.Any(), ðpb.SignedValidatorRegistrationsV1{
|
||||
Messages: []*ethpb.SignedValidatorRegistrationV1{
|
||||
m.validatorClient.EXPECT().
|
||||
SubmitValidatorRegistrations(gomock.Any(), ðpb.SignedValidatorRegistrationsV1{
|
||||
Messages: []*ethpb.SignedValidatorRegistrationV1{
|
||||
{Message: reg,
|
||||
Signature: params.BeaconConfig().ZeroHash[:]},
|
||||
},
|
||||
}).
|
||||
Return(nil, errors.New("could not sign"))
|
||||
require.ErrorContains(t, "could not sign", SubmitValidatorRegistrations(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{
|
||||
{Message: reg,
|
||||
Signature: params.BeaconConfig().ZeroHash[:]},
|
||||
},
|
||||
}).
|
||||
Return(nil, errors.New("could not sign"))
|
||||
require.ErrorContains(t, "could not sign", SubmitValidatorRegistrations(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{
|
||||
{Message: reg,
|
||||
Signature: params.BeaconConfig().ZeroHash[:]},
|
||||
}, validatorRegsBatchSize))
|
||||
}, validatorRegsBatchSize))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_signValidatorRegistration(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
|
||||
ctx := context.Background()
|
||||
reg := ðpb.ValidatorRegistrationV1{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 123456,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
ctx := context.Background()
|
||||
reg := ðpb.ValidatorRegistrationV1{
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
|
||||
GasLimit: 123456,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
}
|
||||
_, err := signValidatorRegistration(ctx, m.signfunc, reg)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
_, err := signValidatorRegistration(ctx, m.signfunc, reg)
|
||||
require.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
|
||||
_, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
ctx := context.Background()
|
||||
byteval, err := hexutil.Decode("0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766")
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
arg *ethpb.ValidatorRegistrationV1
|
||||
validatorSetter func(t *testing.T) *validator
|
||||
isCached bool
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: " Happy Path cached",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
_, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
ctx := context.Background()
|
||||
byteval, err := hexutil.Decode("0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766")
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
arg *ethpb.ValidatorRegistrationV1
|
||||
validatorSetter func(t *testing.T) *validator
|
||||
isCached bool
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: " Happy Path cached",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = ðpb.SignedValidatorRegistrationV1{
|
||||
Message: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
GasLimit: 30000000,
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
Signature: make([]byte, 0),
|
||||
}
|
||||
return &v
|
||||
},
|
||||
isCached: true,
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = ðpb.SignedValidatorRegistrationV1{
|
||||
Message: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
GasLimit: 30000000,
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
Signature: make([]byte, 0),
|
||||
}
|
||||
return &v
|
||||
{
|
||||
name: " Happy Path not cached gas updated",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = ðpb.SignedValidatorRegistrationV1{
|
||||
Message: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
GasLimit: 35000000,
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix() - 1),
|
||||
},
|
||||
Signature: make([]byte, 0),
|
||||
}
|
||||
return &v
|
||||
},
|
||||
isCached: false,
|
||||
},
|
||||
isCached: true,
|
||||
},
|
||||
{
|
||||
name: " Happy Path not cached gas updated",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
{
|
||||
name: " Happy Path not cached feerecipient updated",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: byteval,
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = ðpb.SignedValidatorRegistrationV1{
|
||||
Message: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
GasLimit: 30000000,
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix() - 1),
|
||||
},
|
||||
Signature: make([]byte, 0),
|
||||
}
|
||||
return &v
|
||||
},
|
||||
isCached: false,
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = ðpb.SignedValidatorRegistrationV1{
|
||||
Message: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
GasLimit: 35000000,
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix() - 1),
|
||||
},
|
||||
Signature: make([]byte, 0),
|
||||
}
|
||||
return &v
|
||||
{
|
||||
name: " Happy Path not cached first Entry",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: byteval,
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
return &v
|
||||
},
|
||||
isCached: false,
|
||||
},
|
||||
isCached: false,
|
||||
},
|
||||
{
|
||||
name: " Happy Path not cached feerecipient updated",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: byteval,
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = ðpb.SignedValidatorRegistrationV1{
|
||||
Message: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
GasLimit: 30000000,
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix() - 1),
|
||||
},
|
||||
Signature: make([]byte, 0),
|
||||
}
|
||||
return &v
|
||||
},
|
||||
isCached: false,
|
||||
},
|
||||
{
|
||||
name: " Happy Path not cached first Entry",
|
||||
arg: ðpb.ValidatorRegistrationV1{
|
||||
Pubkey: validatorKey.PublicKey().Marshal(),
|
||||
FeeRecipient: byteval,
|
||||
GasLimit: 30000000,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
}
|
||||
return &v
|
||||
},
|
||||
isCached: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := tt.validatorSetter(t)
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
v := tt.validatorSetter(t)
|
||||
|
||||
startingReq, ok := v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)]
|
||||
startingReq, ok := v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)]
|
||||
|
||||
got, err := v.SignValidatorRegistrationRequest(ctx, m.signfunc, tt.arg)
|
||||
require.NoError(t, err)
|
||||
if tt.isCached {
|
||||
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
|
||||
} else {
|
||||
if ok {
|
||||
require.NotEqual(t, got.Message.Timestamp, startingReq.Message.Timestamp)
|
||||
got, err := v.SignValidatorRegistrationRequest(ctx, m.signfunc, tt.arg)
|
||||
require.NoError(t, err)
|
||||
if tt.isCached {
|
||||
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
|
||||
} else {
|
||||
if ok {
|
||||
require.NotEqual(t, got.Message.Timestamp, startingReq.Message.Timestamp)
|
||||
}
|
||||
require.Equal(t, got.Message.Timestamp, tt.arg.Timestamp)
|
||||
require.Equal(t, got.Message.GasLimit, tt.arg.GasLimit)
|
||||
require.Equal(t, hexutil.Encode(got.Message.FeeRecipient), hexutil.Encode(tt.arg.FeeRecipient))
|
||||
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
|
||||
}
|
||||
require.Equal(t, got.Message.Timestamp, tt.arg.Timestamp)
|
||||
require.Equal(t, got.Message.GasLimit, tt.arg.GasLimit)
|
||||
require.Equal(t, hexutil.Encode(got.Message.FeeRecipient), hexutil.Encode(tt.arg.FeeRecipient))
|
||||
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/async/event"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v5/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
@@ -225,9 +225,9 @@ func notActive(t *testing.T) [fieldparams.BLSPubkeyLength]byte {
|
||||
|
||||
func TestUpdateProposerSettingsAt_EpochStart(t *testing.T) {
|
||||
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
|
||||
err := v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
err := v.SetProposerSettings(context.Background(), &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
@@ -250,9 +250,9 @@ func TestUpdateProposerSettingsAt_EpochStart(t *testing.T) {
|
||||
|
||||
func TestUpdateProposerSettingsAt_EpochEndOk(t *testing.T) {
|
||||
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}, ProposerSettingWait: time.Duration(params.BeaconConfig().SecondsPerSlot-1) * time.Second}
|
||||
err := v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
err := v.SetProposerSettings(context.Background(), &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
@@ -279,9 +279,9 @@ func TestUpdateProposerSettings_ContinuesAfterValidatorRegistrationFails(t *test
|
||||
ProposerSettingsErr: errors.Wrap(ErrBuilderValidatorRegistration, errSomeotherError.Error()),
|
||||
Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}},
|
||||
}
|
||||
err := v.SetProposerSettings(context.Background(), &validatorserviceconfig.ProposerSettings{
|
||||
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
||||
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
||||
err := v.SetProposerSettings(context.Background(), &proposer.Settings{
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
lruwrpr "github.com/prysmaticlabs/prysm/v5/cache/lru"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v5/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/wallet"
|
||||
@@ -75,7 +75,7 @@ type ValidatorService struct {
|
||||
grpcHeaders []string
|
||||
graffiti []byte
|
||||
Web3SignerConfig *remoteweb3signer.SetupConfig
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
proposerSettings *proposer.Settings
|
||||
validatorsRegBatchSize int
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ type Config struct {
|
||||
GraffitiFlag string
|
||||
Endpoint string
|
||||
Web3SignerConfig *remoteweb3signer.SetupConfig
|
||||
ProposerSettings *validatorserviceconfig.ProposerSettings
|
||||
ProposerSettings *proposer.Settings
|
||||
BeaconApiEndpoint string
|
||||
BeaconApiTimeout time.Duration
|
||||
ValidatorsRegBatchSize int
|
||||
@@ -271,7 +271,7 @@ func (v *ValidatorService) Keymanager() (keymanager.IKeymanager, error) {
|
||||
}
|
||||
|
||||
// ProposerSettings returns a deep copy of the underlying proposer settings in the validator
|
||||
func (v *ValidatorService) ProposerSettings() *validatorserviceconfig.ProposerSettings {
|
||||
func (v *ValidatorService) ProposerSettings() *proposer.Settings {
|
||||
settings := v.validator.ProposerSettings()
|
||||
if settings != nil {
|
||||
return settings.Clone()
|
||||
@@ -280,7 +280,7 @@ func (v *ValidatorService) ProposerSettings() *validatorserviceconfig.ProposerSe
|
||||
}
|
||||
|
||||
// SetProposerSettings sets the proposer settings on the validator service as well as the underlying validator
|
||||
func (v *ValidatorService) SetProposerSettings(ctx context.Context, settings *validatorserviceconfig.ProposerSettings) error {
|
||||
func (v *ValidatorService) SetProposerSettings(ctx context.Context, settings *proposer.Settings) error {
|
||||
// validator service proposer settings is only used for pass through from node -> validator service -> validator.
|
||||
// in memory use of proposer settings happens on validator.
|
||||
v.proposerSettings = settings
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||
history "github.com/prysmaticlabs/prysm/v5/validator/slashing-protection-history"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/helpers"
|
||||
)
|
||||
|
||||
type eip3076TestCase struct {
|
||||
@@ -46,6 +47,7 @@ type eip3076TestCase struct {
|
||||
Pubkey string `json:"pubkey"`
|
||||
Slot string `json:"slot"`
|
||||
SigningRoot string `json:"signing_root"`
|
||||
ShouldSucceedMinimal bool `json:"should_succeed"`
|
||||
ShouldSucceedComplete bool `json:"should_succeed_complete"`
|
||||
} `json:"blocks"`
|
||||
Attestations []struct {
|
||||
@@ -53,6 +55,7 @@ type eip3076TestCase struct {
|
||||
SourceEpoch string `json:"source_epoch"`
|
||||
TargetEpoch string `json:"target_epoch"`
|
||||
SigningRoot string `json:"signing_root"`
|
||||
ShouldSucceedMinimal bool `json:"should_succeed"`
|
||||
ShouldSucceedComplete bool `json:"should_succeed_complete"`
|
||||
} `json:"attestations"`
|
||||
} `json:"steps"`
|
||||
@@ -76,99 +79,115 @@ func setupEIP3076SpecTests(t *testing.T) []*eip3076TestCase {
|
||||
}
|
||||
|
||||
func TestEIP3076SpecTests(t *testing.T) {
|
||||
testCases := setupEIP3076SpecTests(t)
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
if tt.Name == "" {
|
||||
t.Skip("Skipping eip3076TestCase with empty name")
|
||||
}
|
||||
for _, isMinimal := range []bool{false, true} {
|
||||
slashingProtectionType := "complete"
|
||||
if isMinimal {
|
||||
slashingProtectionType = "minimal"
|
||||
}
|
||||
|
||||
// Set up validator client, one new validator client per eip3076TestCase.
|
||||
// This ensures we initialize a new (empty) slashing protection database.
|
||||
validator, _, _, _ := setup(t)
|
||||
|
||||
for _, step := range tt.Steps {
|
||||
if tt.GenesisValidatorsRoot != "" {
|
||||
r, err := history.RootFromHex(tt.GenesisValidatorsRoot)
|
||||
require.NoError(t, validator.db.SaveGenesisValidatorsRoot(context.Background(), r[:]))
|
||||
require.NoError(t, err)
|
||||
for _, tt := range setupEIP3076SpecTests(t) {
|
||||
t.Run(fmt.Sprintf("%s-%s", slashingProtectionType, tt.Name), func(t *testing.T) {
|
||||
if tt.Name == "" {
|
||||
t.Skip("Skipping eip3076TestCase with empty name")
|
||||
}
|
||||
|
||||
// The eip3076TestCase config contains the interchange config in json.
|
||||
// This loads the interchange data via ImportStandardProtectionJSON.
|
||||
interchangeBytes, err := json.Marshal(step.Interchange)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := bytes.NewBuffer(interchangeBytes)
|
||||
if err := history.ImportStandardProtectionJSON(context.Background(), validator.db, b); err != nil {
|
||||
if step.ShouldSucceed {
|
||||
// Set up validator client, one new validator client per eip3076TestCase.
|
||||
// This ensures we initialize a new (empty) slashing protection database.
|
||||
validator, _, _, _ := setup(t, isMinimal)
|
||||
|
||||
for _, step := range tt.Steps {
|
||||
if tt.GenesisValidatorsRoot != "" {
|
||||
r, err := helpers.RootFromHex(tt.GenesisValidatorsRoot)
|
||||
require.NoError(t, validator.db.SaveGenesisValidatorsRoot(context.Background(), r[:]))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// The eip3076TestCase config contains the interchange config in json.
|
||||
// This loads the interchange data via ImportStandardProtectionJSON.
|
||||
interchangeBytes, err := json.Marshal(step.Interchange)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else if !step.ShouldSucceed {
|
||||
require.NotNil(t, err, "import standard protection json should have failed")
|
||||
}
|
||||
|
||||
// This loops through a list of block signings to attempt after importing the interchange data above.
|
||||
for _, sb := range step.Blocks {
|
||||
bSlot, err := history.SlotFromString(sb.Slot)
|
||||
require.NoError(t, err)
|
||||
pk, err := history.PubKeyFromHex(sb.Pubkey)
|
||||
require.NoError(t, err)
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = bSlot
|
||||
|
||||
var signingRoot [32]byte
|
||||
if sb.SigningRoot != "" {
|
||||
signingRootBytes, err := hex.DecodeString(strings.TrimPrefix(sb.SigningRoot, "0x"))
|
||||
require.NoError(t, err)
|
||||
copy(signingRoot[:], signingRootBytes)
|
||||
b := bytes.NewBuffer(interchangeBytes)
|
||||
if err := validator.db.ImportStandardProtectionJSON(context.Background(), b); err != nil {
|
||||
if step.ShouldSucceed {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else if !step.ShouldSucceed {
|
||||
require.NotNil(t, err, "import standard protection json should have failed")
|
||||
}
|
||||
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
err = validator.slashableProposalCheck(context.Background(), pk, wsb, signingRoot)
|
||||
if sb.ShouldSucceedComplete {
|
||||
// This loops through a list of block signings to attempt after importing the interchange data above.
|
||||
for _, sb := range step.Blocks {
|
||||
shouldSucceed := sb.ShouldSucceedComplete
|
||||
if isMinimal {
|
||||
shouldSucceed = sb.ShouldSucceedMinimal
|
||||
}
|
||||
|
||||
bSlot, err := helpers.SlotFromString(sb.Slot)
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.NotEqual(t, nil, err, "pre validation should have failed for block")
|
||||
pk, err := helpers.PubKeyFromHex(sb.Pubkey)
|
||||
require.NoError(t, err)
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = bSlot
|
||||
|
||||
var signingRoot [32]byte
|
||||
if sb.SigningRoot != "" {
|
||||
signingRootBytes, err := hex.DecodeString(strings.TrimPrefix(sb.SigningRoot, "0x"))
|
||||
require.NoError(t, err)
|
||||
copy(signingRoot[:], signingRootBytes)
|
||||
}
|
||||
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
err = validator.db.SlashableProposalCheck(context.Background(), pk, wsb, signingRoot, validator.emitAccountMetrics, ValidatorProposeFailVec)
|
||||
if shouldSucceed {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.NotEqual(t, nil, err, "pre validation should have failed for block")
|
||||
}
|
||||
}
|
||||
|
||||
// This loops through a list of attestation signings to attempt after importing the interchange data above.
|
||||
for _, sa := range step.Attestations {
|
||||
shouldSucceed := sa.ShouldSucceedComplete
|
||||
if isMinimal {
|
||||
shouldSucceed = sa.ShouldSucceedMinimal
|
||||
}
|
||||
|
||||
target, err := helpers.EpochFromString(sa.TargetEpoch)
|
||||
require.NoError(t, err)
|
||||
source, err := helpers.EpochFromString(sa.SourceEpoch)
|
||||
require.NoError(t, err)
|
||||
pk, err := helpers.PubKeyFromHex(sa.Pubkey)
|
||||
require.NoError(t, err)
|
||||
ia := ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Target: ðpb.Checkpoint{Epoch: target, Root: make([]byte, 32)},
|
||||
Source: ðpb.Checkpoint{Epoch: source, Root: make([]byte, 32)},
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
}
|
||||
|
||||
var signingRoot [32]byte
|
||||
if sa.SigningRoot != "" {
|
||||
signingRootBytes, err := hex.DecodeString(strings.TrimPrefix(sa.SigningRoot, "0x"))
|
||||
require.NoError(t, err)
|
||||
copy(signingRoot[:], signingRootBytes)
|
||||
}
|
||||
|
||||
err = validator.db.SlashableAttestationCheck(context.Background(), ia, pk, signingRoot, false, nil)
|
||||
if shouldSucceed {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.NotNil(t, err, "pre validation should have failed for attestation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This loops through a list of attestation signings to attempt after importing the interchange data above.
|
||||
for _, sa := range step.Attestations {
|
||||
target, err := history.EpochFromString(sa.TargetEpoch)
|
||||
require.NoError(t, err)
|
||||
source, err := history.EpochFromString(sa.SourceEpoch)
|
||||
require.NoError(t, err)
|
||||
pk, err := history.PubKeyFromHex(sa.Pubkey)
|
||||
require.NoError(t, err)
|
||||
ia := ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Target: ðpb.Checkpoint{Epoch: target, Root: make([]byte, 32)},
|
||||
Source: ðpb.Checkpoint{Epoch: source, Root: make([]byte, 32)},
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
}
|
||||
|
||||
var signingRoot [32]byte
|
||||
if sa.SigningRoot != "" {
|
||||
signingRootBytes, err := hex.DecodeString(strings.TrimPrefix(sa.SigningRoot, "0x"))
|
||||
require.NoError(t, err)
|
||||
copy(signingRoot[:], signingRootBytes)
|
||||
}
|
||||
|
||||
err = validator.slashableAttestationCheck(context.Background(), ia, pk, signingRoot)
|
||||
if sa.ShouldSucceedComplete {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.NotNil(t, err, "pre validation should have failed for attestation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, validator.db.Close(), "failed to close slashing protection database")
|
||||
})
|
||||
require.NoError(t, validator.db.Close(), "failed to close slashing protection database")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -20,246 +21,278 @@ import (
|
||||
)
|
||||
|
||||
func TestSubmitSyncCommitteeMessage_ValidatorDutiesRequestFailure(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{}}
|
||||
defer finish()
|
||||
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo([]byte{}, 32),
|
||||
}, nil)
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo([]byte{}, 32),
|
||||
}, nil)
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSyncCommitteeMessage_BadDomainData(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
hook := logTest.NewGlobal()
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
hook := logTest.NewGlobal()
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
|
||||
r := []byte{'a'}
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo(r, 32),
|
||||
}, nil)
|
||||
r := []byte{'a'}
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo(r, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), gomock.Any()).
|
||||
Return(nil, errors.New("uh oh"))
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), gomock.Any()).
|
||||
Return(nil, errors.New("uh oh"))
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get sync committee domain data")
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get sync committee domain data")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSyncCommitteeMessage_CouldNotSubmit(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
hook := logTest.NewGlobal()
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
hook := logTest.NewGlobal()
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
|
||||
r := []byte{'a'}
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo(r, 32),
|
||||
}, nil)
|
||||
r := []byte{'a'}
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo(r, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSyncMessage(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SyncCommitteeMessage{}),
|
||||
).Return(&emptypb.Empty{}, errors.New("uh oh") /* error */)
|
||||
m.validatorClient.EXPECT().SubmitSyncMessage(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SyncCommitteeMessage{}),
|
||||
).Return(&emptypb.Empty{}, errors.New("uh oh") /* error */)
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
|
||||
require.LogsContain(t, hook, "Could not submit sync committee message")
|
||||
require.LogsContain(t, hook, "Could not submit sync committee message")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSyncCommitteeMessage_OK(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
defer finish()
|
||||
hook := logTest.NewGlobal()
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
defer finish()
|
||||
hook := logTest.NewGlobal()
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
|
||||
r := []byte{'a'}
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo(r, 32),
|
||||
}, nil)
|
||||
r := []byte{'a'}
|
||||
m.validatorClient.EXPECT().GetSyncMessageBlockRoot(
|
||||
gomock.Any(), // ctx
|
||||
&emptypb.Empty{},
|
||||
).Return(ðpb.SyncMessageBlockRootResponse{
|
||||
Root: bytesutil.PadTo(r, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
var generatedMsg *ethpb.SyncCommitteeMessage
|
||||
m.validatorClient.EXPECT().SubmitSyncMessage(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SyncCommitteeMessage{}),
|
||||
).Do(func(_ context.Context, msg *ethpb.SyncCommitteeMessage) {
|
||||
generatedMsg = msg
|
||||
}).Return(&emptypb.Empty{}, nil /* error */)
|
||||
var generatedMsg *ethpb.SyncCommitteeMessage
|
||||
m.validatorClient.EXPECT().SubmitSyncMessage(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SyncCommitteeMessage{}),
|
||||
).Do(func(_ context.Context, msg *ethpb.SyncCommitteeMessage) {
|
||||
generatedMsg = msg
|
||||
}).Return(&emptypb.Empty{}, nil /* error */)
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSyncCommitteeMessage(context.Background(), 1, pubKey)
|
||||
|
||||
require.LogsDoNotContain(t, hook, "Could not")
|
||||
require.Equal(t, primitives.Slot(1), generatedMsg.Slot)
|
||||
require.Equal(t, validatorIndex, generatedMsg.ValidatorIndex)
|
||||
require.DeepEqual(t, bytesutil.PadTo(r, 32), generatedMsg.BlockRoot)
|
||||
require.LogsDoNotContain(t, hook, "Could not")
|
||||
require.Equal(t, primitives.Slot(1), generatedMsg.Slot)
|
||||
require.Equal(t, validatorIndex, generatedMsg.ValidatorIndex)
|
||||
require.DeepEqual(t, bytesutil.PadTo(r, 32), generatedMsg.BlockRoot)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_ValidatorDutiesRequestFailure(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, _, validatorKey, finish := setup(t)
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, _, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_GetSyncSubcommitteeIndexFailure(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{}, errors.New("Bad index"))
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{}, errors.New("Bad index"))
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get sync subcommittee index")
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get sync subcommittee index")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_NothingToDo(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{}}, nil)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{}}, nil)
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Empty subcommittee index list, do nothing")
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Empty subcommittee index list, do nothing")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_BadDomain(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, errors.New("bad domain response"))
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, errors.New("bad domain response"))
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get selection proofs")
|
||||
require.LogsContain(t, hook, "bad domain response")
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get selection proofs")
|
||||
require.LogsContain(t, hook, "bad domain response")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_CouldNotGetContribution(t *testing.T) {
|
||||
@@ -270,46 +303,50 @@ func TestSubmitSignedContributionAndProof_CouldNotGetContribution(t *testing.T)
|
||||
validatorKey, err := bls.SecretKeyFromBytes(rawKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
validator, m, validatorKey, finish := setupWithKey(t, validatorKey)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setupWithKey(t, validatorKey, isSlashingProtectionMinimal)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().GetSyncCommitteeContribution(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncCommitteeContributionRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
SubnetId: 0,
|
||||
},
|
||||
).Return(nil, errors.New("Bad contribution"))
|
||||
m.validatorClient.EXPECT().GetSyncCommitteeContribution(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncCommitteeContributionRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
SubnetId: 0,
|
||||
},
|
||||
).Return(nil, errors.New("Bad contribution"))
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get sync committee contribution")
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not get sync committee contribution")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_CouldNotSubmitContribution(t *testing.T) {
|
||||
@@ -320,75 +357,79 @@ func TestSubmitSignedContributionAndProof_CouldNotSubmitContribution(t *testing.
|
||||
validatorKey, err := bls.SecretKeyFromBytes(rawKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
validator, m, validatorKey, finish := setupWithKey(t, validatorKey)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
aggBits := bitfield.NewBitvector128()
|
||||
aggBits.SetBitAt(0, true)
|
||||
m.validatorClient.EXPECT().GetSyncCommitteeContribution(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncCommitteeContributionRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
SubnetId: 0,
|
||||
},
|
||||
).Return(ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: aggBits,
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedContributionAndProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedContributionAndProof{
|
||||
Message: ðpb.ContributionAndProof{
|
||||
AggregatorIndex: 7,
|
||||
Contribution: ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: bitfield.NewBitvector128(),
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 1,
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setupWithKey(t, validatorKey, isSlashingProtectionMinimal)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).Return(&emptypb.Empty{}, errors.New("Could not submit contribution"))
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not submit signed contribution and proof")
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
aggBits := bitfield.NewBitvector128()
|
||||
aggBits.SetBitAt(0, true)
|
||||
m.validatorClient.EXPECT().GetSyncCommitteeContribution(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncCommitteeContributionRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
SubnetId: 0,
|
||||
},
|
||||
).Return(ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: aggBits,
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedContributionAndProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedContributionAndProof{
|
||||
Message: ðpb.ContributionAndProof{
|
||||
AggregatorIndex: 7,
|
||||
Contribution: ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: bitfield.NewBitvector128(),
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).Return(&emptypb.Empty{}, errors.New("Could not submit contribution"))
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
require.LogsContain(t, hook, "Could not submit signed contribution and proof")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSignedContributionAndProof_Ok(t *testing.T) {
|
||||
@@ -398,72 +439,76 @@ func TestSubmitSignedContributionAndProof_Ok(t *testing.T) {
|
||||
validatorKey, err := bls.SecretKeyFromBytes(rawKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
validator, m, validatorKey, finish := setupWithKey(t, validatorKey)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
aggBits := bitfield.NewBitvector128()
|
||||
aggBits.SetBitAt(0, true)
|
||||
m.validatorClient.EXPECT().GetSyncCommitteeContribution(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncCommitteeContributionRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
SubnetId: 0,
|
||||
},
|
||||
).Return(ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: aggBits,
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedContributionAndProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedContributionAndProof{
|
||||
Message: ðpb.ContributionAndProof{
|
||||
AggregatorIndex: 7,
|
||||
Contribution: ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: bitfield.NewBitvector128(),
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 1,
|
||||
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
||||
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
||||
validator, m, validatorKey, finish := setupWithKey(t, validatorKey, isSlashingProtectionMinimal)
|
||||
validatorIndex := primitives.ValidatorIndex(7)
|
||||
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
||||
validator.duties = ðpb.DutiesResponse{CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{
|
||||
{
|
||||
PublicKey: validatorKey.PublicKey().Marshal(),
|
||||
Committee: committee,
|
||||
ValidatorIndex: validatorIndex,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).Return(&emptypb.Empty{}, nil)
|
||||
}}
|
||||
defer finish()
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
var pubKey [fieldparams.BLSPubkeyLength]byte
|
||||
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
||||
m.validatorClient.EXPECT().GetSyncSubcommitteeIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncSubcommitteeIndexRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
},
|
||||
).Return(ðpb.SyncSubcommitteeIndexResponse{Indices: []primitives.CommitteeIndex{1}}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
aggBits := bitfield.NewBitvector128()
|
||||
aggBits.SetBitAt(0, true)
|
||||
m.validatorClient.EXPECT().GetSyncCommitteeContribution(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.SyncCommitteeContributionRequest{
|
||||
Slot: 1,
|
||||
PublicKey: pubKey[:],
|
||||
SubnetId: 0,
|
||||
},
|
||||
).Return(ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: aggBits,
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), // ctx
|
||||
gomock.Any()). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: make([]byte, 32),
|
||||
}, nil)
|
||||
|
||||
m.validatorClient.EXPECT().SubmitSignedContributionAndProof(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.SignedContributionAndProof{
|
||||
Message: ðpb.ContributionAndProof{
|
||||
AggregatorIndex: 7,
|
||||
Contribution: ðpb.SyncCommitteeContribution{
|
||||
BlockRoot: make([]byte, 32),
|
||||
Signature: make([]byte, 96),
|
||||
AggregationBits: bitfield.NewBitvector128(),
|
||||
Slot: 1,
|
||||
SubcommitteeIndex: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).Return(&emptypb.Empty{}, nil)
|
||||
|
||||
validator.SubmitSignedContributionAndProof(context.Background(), 1, pubKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ go_library(
|
||||
visibility = ["//validator:__subpackages__"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/validator/service:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v5/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
prysmTime "github.com/prysmaticlabs/prysm/v5/time"
|
||||
@@ -52,7 +52,7 @@ type FakeValidator struct {
|
||||
IndexToPubkeyMap map[uint64][fieldparams.BLSPubkeyLength]byte
|
||||
PubkeyToIndexMap map[[fieldparams.BLSPubkeyLength]byte]uint64
|
||||
PubkeysToStatusesMap map[[fieldparams.BLSPubkeyLength]byte]ethpb.ValidatorStatus
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
proposerSettings *proposer.Settings
|
||||
ProposerSettingWait time.Duration
|
||||
Km keymanager.IKeymanager
|
||||
}
|
||||
@@ -276,12 +276,12 @@ func (*FakeValidator) SignValidatorRegistrationRequest(_ context.Context, _ ifac
|
||||
}
|
||||
|
||||
// ProposerSettings for mocking
|
||||
func (fv *FakeValidator) ProposerSettings() *validatorserviceconfig.ProposerSettings {
|
||||
func (fv *FakeValidator) ProposerSettings() *proposer.Settings {
|
||||
return fv.proposerSettings
|
||||
}
|
||||
|
||||
// SetProposerSettings for mocking
|
||||
func (fv *FakeValidator) SetProposerSettings(_ context.Context, settings *validatorserviceconfig.ProposerSettings) error {
|
||||
func (fv *FakeValidator) SetProposerSettings(_ context.Context, settings *proposer.Settings) error {
|
||||
fv.proposerSettings = settings
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
validatorserviceconfig "github.com/prysmaticlabs/prysm/v5/config/validator/service"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
@@ -37,7 +37,7 @@ import (
|
||||
beacon_api "github.com/prysmaticlabs/prysm/v5/validator/client/beacon-api"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/client/iface"
|
||||
vdb "github.com/prysmaticlabs/prysm/v5/validator/db"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
dbCommon "github.com/prysmaticlabs/prysm/v5/validator/db/common"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/graffiti"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager/local"
|
||||
@@ -106,7 +106,7 @@ type validator struct {
|
||||
voteStats voteStats
|
||||
syncCommitteeStats syncCommitteeStats
|
||||
Web3SignerConfig *remoteweb3signer.SetupConfig
|
||||
proposerSettings *validatorserviceconfig.ProposerSettings
|
||||
proposerSettings *proposer.Settings
|
||||
walletInitializedChannel chan *wallet.Wallet
|
||||
validatorsRegBatchSize int
|
||||
}
|
||||
@@ -517,7 +517,7 @@ func buildDuplicateError(response []*ethpb.DoppelGangerResponse_ValidatorRespons
|
||||
}
|
||||
|
||||
// Ensures that the latest attestation history is retrieved.
|
||||
func retrieveLatestRecord(recs []*kv.AttestationRecord) *kv.AttestationRecord {
|
||||
func retrieveLatestRecord(recs []*dbCommon.AttestationRecord) *dbCommon.AttestationRecord {
|
||||
if len(recs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1028,12 +1028,12 @@ func (v *validator) logDuties(slot primitives.Slot, currentEpochDuties []*ethpb.
|
||||
}
|
||||
|
||||
// ProposerSettings gets the current proposer settings saved in memory validator
|
||||
func (v *validator) ProposerSettings() *validatorserviceconfig.ProposerSettings {
|
||||
func (v *validator) ProposerSettings() *proposer.Settings {
|
||||
return v.proposerSettings
|
||||
}
|
||||
|
||||
// SetProposerSettings sets and saves the passed in proposer settings overriding the in memory one
|
||||
func (v *validator) SetProposerSettings(ctx context.Context, settings *validatorserviceconfig.ProposerSettings) error {
|
||||
func (v *validator) SetProposerSettings(ctx context.Context, settings *proposer.Settings) error {
|
||||
if v.db == nil {
|
||||
return errors.New("db is not set")
|
||||
}
|
||||
@@ -1196,6 +1196,10 @@ func (v *validator) buildSignedRegReqs(ctx context.Context, pubkeys [][fieldpara
|
||||
gasLimit := params.BeaconConfig().DefaultBuilderGasLimit
|
||||
enabled := false
|
||||
|
||||
if v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig == nil && v.ProposerSettings().DefaultConfig.BuilderConfig != nil {
|
||||
log.Warn("Builder is `enabled` in default config but will be ignored because no fee recipient was provided!")
|
||||
}
|
||||
|
||||
if v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig != nil {
|
||||
defaultConfig := v.ProposerSettings().DefaultConfig
|
||||
feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient // Use cli defaultBuilderConfig for fee recipient.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"alias.go",
|
||||
"convert.go",
|
||||
"log.go",
|
||||
"migrate.go",
|
||||
"restore.go",
|
||||
@@ -15,8 +16,13 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//cmd:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//io/prompt:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//validator/db/common:go_default_library",
|
||||
"//validator/db/filesystem:go_default_library",
|
||||
"//validator/db/iface:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
@@ -28,17 +34,27 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"convert_test.go",
|
||||
"migrate_test.go",
|
||||
"restore_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//validator/db/common:go_default_library",
|
||||
"//validator/db/filesystem:go_default_library",
|
||||
"//validator/db/iface:go_default_library",
|
||||
"//validator/db/kv:go_default_library",
|
||||
"//validator/db/testing:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
|
||||
17
validator/db/common/BUILD.bazel
Normal file
17
validator/db/common/BUILD.bazel
Normal file
@@ -0,0 +1,17 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"progress.go",
|
||||
"structs.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/validator/db/common",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"@com_github_k0kubun_go_ansi//:go_default_library",
|
||||
"@com_github_schollz_progressbar_v3//:go_default_library",
|
||||
],
|
||||
)
|
||||
27
validator/db/common/progress.go
Normal file
27
validator/db/common/progress.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/k0kubun/go-ansi"
|
||||
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
func InitializeProgressBar(numItems int, msg string) *progressbar.ProgressBar {
|
||||
return progressbar.NewOptions(
|
||||
numItems,
|
||||
progressbar.OptionFullWidth(),
|
||||
progressbar.OptionSetWriter(ansi.NewAnsiStdout()),
|
||||
progressbar.OptionEnableColorCodes(true),
|
||||
progressbar.OptionSetTheme(progressbar.Theme{
|
||||
Saucer: "[green]=[reset]",
|
||||
SaucerHead: "[green]>[reset]",
|
||||
SaucerPadding: " ",
|
||||
BarStart: "[",
|
||||
BarEnd: "]",
|
||||
}),
|
||||
progressbar.OptionOnCompletion(func() { fmt.Println() }),
|
||||
progressbar.OptionSetDescription(msg),
|
||||
)
|
||||
}
|
||||
28
validator/db/common/structs.go
Normal file
28
validator/db/common/structs.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
)
|
||||
|
||||
const FailedBlockSignLocalErr = "block rejected by local protection"
|
||||
|
||||
// Proposal representation for a validator public key.
|
||||
type Proposal struct {
|
||||
Slot primitives.Slot `json:"slot"`
|
||||
SigningRoot []byte `json:"signing_root"`
|
||||
}
|
||||
|
||||
// ProposalHistoryForPubkey for a validator public key.
|
||||
type ProposalHistoryForPubkey struct {
|
||||
Proposals []Proposal
|
||||
}
|
||||
|
||||
// AttestationRecord which can be represented by these simple values
|
||||
// for manipulation by database methods.
|
||||
type AttestationRecord struct {
|
||||
PubKey [fieldparams.BLSPubkeyLength]byte
|
||||
Source primitives.Epoch
|
||||
Target primitives.Epoch
|
||||
SigningRoot []byte
|
||||
}
|
||||
257
validator/db/convert.go
Normal file
257
validator/db/convert.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/file"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/common"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/filesystem"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
)
|
||||
|
||||
// ConvertDatabase converts a minimal database to a complete database or a complete database to a minimal database.
|
||||
// Delete the source database after conversion.
|
||||
func ConvertDatabase(ctx context.Context, sourceDataDir string, targetDataDir string, minimalToComplete bool) error {
|
||||
// Check if the source database exists.
|
||||
var (
|
||||
sourceDatabaseExists bool
|
||||
err error
|
||||
)
|
||||
|
||||
if minimalToComplete {
|
||||
sourceDataBasePath := filepath.Join(sourceDataDir, filesystem.DatabaseDirName)
|
||||
sourceDatabaseExists, err = file.Exists(sourceDataBasePath, file.Directory)
|
||||
} else {
|
||||
sourceDataBasePath := filepath.Join(sourceDataDir, kv.ProtectionDbFileName)
|
||||
sourceDatabaseExists, err = file.Exists(sourceDataBasePath, file.Regular)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if source database exists")
|
||||
}
|
||||
|
||||
// If the source database does not exist, there is nothing to convert.
|
||||
if !sourceDatabaseExists {
|
||||
return errors.New("source database does not exist")
|
||||
}
|
||||
|
||||
// Get the source database.
|
||||
var sourceDatabase iface.ValidatorDB
|
||||
|
||||
if minimalToComplete {
|
||||
sourceDatabase, err = filesystem.NewStore(sourceDataDir, nil)
|
||||
} else {
|
||||
sourceDatabase, err = kv.NewKVStore(ctx, sourceDataDir, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get source database")
|
||||
}
|
||||
|
||||
// Close the source database.
|
||||
defer func() {
|
||||
if err := sourceDatabase.Close(); err != nil {
|
||||
log.WithError(err).Error("Failed to close source database")
|
||||
}
|
||||
}()
|
||||
|
||||
// Create the target database.
|
||||
var targetDatabase iface.ValidatorDB
|
||||
|
||||
if minimalToComplete {
|
||||
targetDatabase, err = kv.NewKVStore(ctx, targetDataDir, nil)
|
||||
} else {
|
||||
targetDatabase, err = filesystem.NewStore(targetDataDir, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create target database")
|
||||
}
|
||||
|
||||
// Close the target database.
|
||||
defer func() {
|
||||
if err := targetDatabase.Close(); err != nil {
|
||||
log.WithError(err).Error("Failed to close target database")
|
||||
}
|
||||
}()
|
||||
|
||||
// Genesis
|
||||
// -------
|
||||
// Get the genesis validators root.
|
||||
genesisValidatorRoot, err := sourceDatabase.GenesisValidatorsRoot(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get genesis validators root from source database")
|
||||
}
|
||||
|
||||
// Save the genesis validators root.
|
||||
if err := targetDatabase.SaveGenesisValidatorsRoot(ctx, genesisValidatorRoot); err != nil {
|
||||
return errors.Wrap(err, "could not save genesis validators root")
|
||||
}
|
||||
|
||||
// Graffiti
|
||||
// --------
|
||||
// Get the graffiti file hash.
|
||||
graffitiFileHash, exists, err := sourceDatabase.GraffitiFileHash()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get graffiti file hash from source database")
|
||||
}
|
||||
|
||||
if exists {
|
||||
// Calling GraffitiOrderedIndex will save the graffiti file hash.
|
||||
if _, err := targetDatabase.GraffitiOrderedIndex(ctx, graffitiFileHash); err != nil {
|
||||
return errors.Wrap(err, "could get graffiti ordered index")
|
||||
}
|
||||
}
|
||||
|
||||
// Get the graffiti ordered index.
|
||||
graffitiOrderedIndex, err := sourceDatabase.GraffitiOrderedIndex(ctx, graffitiFileHash)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get graffiti ordered index from source database")
|
||||
}
|
||||
|
||||
// Save the graffiti ordered index.
|
||||
if err := targetDatabase.SaveGraffitiOrderedIndex(ctx, graffitiOrderedIndex); err != nil {
|
||||
return errors.Wrap(err, "could not save graffiti ordered index")
|
||||
}
|
||||
|
||||
// Proposer settings
|
||||
// -----------------
|
||||
// Get the proposer settings.
|
||||
proposerSettings, err := sourceDatabase.ProposerSettings(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get proposer settings from source database")
|
||||
}
|
||||
|
||||
// Save the proposer settings.
|
||||
if err := targetDatabase.SaveProposerSettings(ctx, proposerSettings); err != nil {
|
||||
return errors.Wrap(err, "could not save proposer settings")
|
||||
}
|
||||
|
||||
// Attestations
|
||||
// ------------
|
||||
// Get all public keys that have attested.
|
||||
attestedPublicKeys, err := sourceDatabase.AttestedPublicKeys(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get attested public keys from source database")
|
||||
}
|
||||
|
||||
// Initialize the progress bar.
|
||||
bar := common.InitializeProgressBar(
|
||||
len(attestedPublicKeys),
|
||||
"Processing attestations:",
|
||||
)
|
||||
|
||||
for _, pubkey := range attestedPublicKeys {
|
||||
// Update the progress bar.
|
||||
if err := bar.Add(1); err != nil {
|
||||
log.WithError(err).Debug("Could not increase progress bar")
|
||||
}
|
||||
|
||||
// Get the attestation records.
|
||||
attestationRecords, err := sourceDatabase.AttestationHistoryForPubKey(ctx, pubkey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get attestation history for public key")
|
||||
}
|
||||
|
||||
// If there are no attestation records, skip this public key.
|
||||
if len(attestationRecords) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
highestSource, highestTarget := primitives.Epoch(0), primitives.Epoch(0)
|
||||
for _, record := range attestationRecords {
|
||||
// If the record is nil, skip it.
|
||||
if record == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the highest source and target epoch.
|
||||
if record.Source > highestSource {
|
||||
highestSource = record.Source
|
||||
}
|
||||
|
||||
if record.Target > highestTarget {
|
||||
highestTarget = record.Target
|
||||
}
|
||||
}
|
||||
|
||||
// Create the indexed attestation with the highest source and target epoch.
|
||||
indexedAttestation := ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: highestSource,
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: highestTarget,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := targetDatabase.SaveAttestationForPubKey(ctx, pubkey, [fieldparams.RootLength]byte{}, indexedAttestation); err != nil {
|
||||
return errors.Wrap(err, "could not save attestation for public key")
|
||||
}
|
||||
}
|
||||
|
||||
// Proposals
|
||||
// ---------
|
||||
// Get all pubkeys in database.
|
||||
proposedPublicKeys, err := sourceDatabase.ProposedPublicKeys(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get proposed public keys from source database")
|
||||
}
|
||||
|
||||
// Initialize the progress bar.
|
||||
bar = common.InitializeProgressBar(
|
||||
len(attestedPublicKeys),
|
||||
"Processing proposals:",
|
||||
)
|
||||
|
||||
for _, pubkey := range proposedPublicKeys {
|
||||
// Update the progress bar.
|
||||
if err := bar.Add(1); err != nil {
|
||||
log.WithError(err).Debug("Could not increase progress bar")
|
||||
}
|
||||
|
||||
// Get the proposal history.
|
||||
proposals, err := sourceDatabase.ProposalHistoryForPubKey(ctx, pubkey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get proposal history for public key")
|
||||
}
|
||||
|
||||
// If there are no proposals, skip this public key.
|
||||
if len(proposals) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
highestSlot := primitives.Slot(0)
|
||||
for _, proposal := range proposals {
|
||||
// If proposal is nil, skip it.
|
||||
if proposal == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the highest slot.
|
||||
if proposal.Slot > highestSlot {
|
||||
highestSlot = proposal.Slot
|
||||
}
|
||||
}
|
||||
|
||||
// Save the proposal history for the highest slot.
|
||||
if err := targetDatabase.SaveProposalHistoryForSlot(ctx, pubkey, highestSlot, nil); err != nil {
|
||||
return errors.Wrap(err, "could not save proposal history for public key")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the source database.
|
||||
if err := sourceDatabase.ClearDB(); err != nil {
|
||||
return errors.Wrap(err, "could not delete source database")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
265
validator/db/convert_test.go
Normal file
265
validator/db/convert_test.go
Normal file
@@ -0,0 +1,265 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/proposer"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/file"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/common"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/filesystem"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/iface"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db/kv"
|
||||
)
|
||||
|
||||
func getPubkeyFromString(t *testing.T, pubkeyString string) [fieldparams.BLSPubkeyLength]byte {
|
||||
var pubkey [fieldparams.BLSPubkeyLength]byte
|
||||
pubkeyBytes, err := hexutil.Decode(pubkeyString)
|
||||
require.NoError(t, err, "hexutil.Decode should not return an error")
|
||||
copy(pubkey[:], pubkeyBytes)
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func getFeeRecipientFromString(t *testing.T, feeRecipientString string) [fieldparams.FeeRecipientLength]byte {
|
||||
var feeRecipient [fieldparams.FeeRecipientLength]byte
|
||||
feeRecipientBytes, err := hexutil.Decode(feeRecipientString)
|
||||
require.NoError(t, err, "hexutil.Decode should not return an error")
|
||||
copy(feeRecipient[:], feeRecipientBytes)
|
||||
return feeRecipient
|
||||
}
|
||||
|
||||
func TestDB_ConvertDatabase(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
pubKeyString1 := "0x80000060606fa05c7339dd7bcd0d3e4d8b573fa30dea2fdb4997031a703e3300326e3c054be682f92d9c367cd647bbea"
|
||||
pubKeyString2 := "0x81000060606fa05c7339dd7bcd0d3e4d8b573fa30dea2fdb4997031a703e3300326e3c054be682f92d9c367cd647bbea"
|
||||
defaultFeeRecipientString := "0xe688b84b23f322a994A53dbF8E15FA82CDB71127"
|
||||
customFeeRecipientString := "0xeD33259a056F4fb449FFB7B7E2eCB43a9B5685Bf"
|
||||
|
||||
pubkey1 := getPubkeyFromString(t, pubKeyString1)
|
||||
pubkey2 := getPubkeyFromString(t, pubKeyString2)
|
||||
defaultFeeRecipient := getFeeRecipientFromString(t, defaultFeeRecipientString)
|
||||
customFeeRecipient := getFeeRecipientFromString(t, customFeeRecipientString)
|
||||
|
||||
for _, minimalToComplete := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("minimalToComplete=%v", minimalToComplete), func(t *testing.T) {
|
||||
// Create signing root
|
||||
signingRoot := [fieldparams.RootLength]byte{}
|
||||
var signingRootBytes []byte
|
||||
if minimalToComplete {
|
||||
signingRootBytes = signingRoot[:]
|
||||
}
|
||||
|
||||
// Create database directoriy path.
|
||||
datadir := t.TempDir()
|
||||
|
||||
// Run source DB preparation.
|
||||
// --------------------------
|
||||
// Create the source database.
|
||||
var (
|
||||
sourceDatabase, targetDatabase iface.ValidatorDB
|
||||
err error
|
||||
)
|
||||
|
||||
if minimalToComplete {
|
||||
sourceDatabase, err = filesystem.NewStore(datadir, &filesystem.Config{
|
||||
PubKeys: [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2},
|
||||
})
|
||||
} else {
|
||||
sourceDatabase, err = kv.NewKVStore(ctx, datadir, &kv.Config{
|
||||
PubKeys: [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2},
|
||||
})
|
||||
}
|
||||
|
||||
require.NoError(t, err, "could not create source database")
|
||||
|
||||
// Save the genesis validator root.
|
||||
expectedGenesisValidatorRoot := []byte("genesis-validator-root")
|
||||
err = sourceDatabase.SaveGenesisValidatorsRoot(ctx, expectedGenesisValidatorRoot)
|
||||
require.NoError(t, err, "could not save genesis validator root")
|
||||
|
||||
// Save the graffiti file hash.
|
||||
// (Getting the graffiti ordered index will set the graffiti file hash)
|
||||
expectedGraffitiFileHash := [32]byte{1}
|
||||
_, err = sourceDatabase.GraffitiOrderedIndex(ctx, expectedGraffitiFileHash)
|
||||
require.NoError(t, err, "could not get graffiti ordered index")
|
||||
|
||||
// Save the graffiti ordered index.
|
||||
expectedGraffitiOrderedIndex := uint64(1)
|
||||
err = sourceDatabase.SaveGraffitiOrderedIndex(ctx, expectedGraffitiOrderedIndex)
|
||||
require.NoError(t, err, "could not save graffiti ordered index")
|
||||
|
||||
// Save the proposer settings.
|
||||
var relays []string = nil
|
||||
|
||||
expectedProposerSettings := &proposer.Settings{
|
||||
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{
|
||||
pubkey1: {
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: customFeeRecipient,
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: true,
|
||||
GasLimit: 42,
|
||||
Relays: relays,
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultConfig: &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: defaultFeeRecipient,
|
||||
},
|
||||
BuilderConfig: &proposer.BuilderConfig{
|
||||
Enabled: false,
|
||||
GasLimit: 43,
|
||||
Relays: relays,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = sourceDatabase.SaveProposerSettings(ctx, expectedProposerSettings)
|
||||
require.NoError(t, err, "could not save proposer settings")
|
||||
|
||||
// Save some attestations.
|
||||
completeAttestations := []*ethpb.IndexedAttestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 1,
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 2,
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedAttestationRecords1 := []*common.AttestationRecord{
|
||||
{
|
||||
PubKey: pubkey1,
|
||||
Source: primitives.Epoch(2),
|
||||
Target: primitives.Epoch(3),
|
||||
SigningRoot: signingRootBytes,
|
||||
},
|
||||
}
|
||||
|
||||
expectedAttestationRecords2 := []*common.AttestationRecord{
|
||||
{
|
||||
PubKey: pubkey2,
|
||||
Source: primitives.Epoch(2),
|
||||
Target: primitives.Epoch(3),
|
||||
SigningRoot: signingRootBytes,
|
||||
},
|
||||
}
|
||||
|
||||
err = sourceDatabase.SaveAttestationsForPubKey(ctx, pubkey1, [][]byte{{1}, {2}}, completeAttestations)
|
||||
require.NoError(t, err, "could not save attestations")
|
||||
|
||||
err = sourceDatabase.SaveAttestationsForPubKey(ctx, pubkey2, [][]byte{{1}, {2}}, completeAttestations)
|
||||
require.NoError(t, err, "could not save attestations")
|
||||
|
||||
// Save some block proposals.
|
||||
err = sourceDatabase.SaveProposalHistoryForSlot(ctx, pubkey1, 42, []byte{})
|
||||
require.NoError(t, err, "could not save block proposal")
|
||||
|
||||
err = sourceDatabase.SaveProposalHistoryForSlot(ctx, pubkey1, 43, []byte{})
|
||||
require.NoError(t, err, "could not save block proposal")
|
||||
|
||||
expectedProposals := []*common.Proposal{
|
||||
{
|
||||
Slot: 43,
|
||||
SigningRoot: signingRootBytes,
|
||||
},
|
||||
}
|
||||
|
||||
// Close the source database.
|
||||
err = sourceDatabase.Close()
|
||||
require.NoError(t, err, "could not close source database")
|
||||
|
||||
// Source to target DB conversion.
|
||||
// ----------------------------------------
|
||||
err = ConvertDatabase(ctx, datadir, datadir, minimalToComplete)
|
||||
require.NoError(t, err, "could not convert source to target database")
|
||||
|
||||
// Check the target database.
|
||||
// --------------------------
|
||||
if minimalToComplete {
|
||||
targetDatabase, err = kv.NewKVStore(ctx, datadir, nil)
|
||||
} else {
|
||||
targetDatabase, err = filesystem.NewStore(datadir, nil)
|
||||
}
|
||||
require.NoError(t, err, "could not get minimal database")
|
||||
|
||||
// Check the genesis validator root.
|
||||
actualGenesisValidatoRoot, err := targetDatabase.GenesisValidatorsRoot(ctx)
|
||||
require.NoError(t, err, "could not get genesis validator root from target database")
|
||||
require.DeepSSZEqual(t, expectedGenesisValidatorRoot, actualGenesisValidatoRoot, "genesis validator root should match")
|
||||
|
||||
// Check the graffiti file hash.
|
||||
actualGraffitiFileHash, exists, err := targetDatabase.GraffitiFileHash()
|
||||
require.NoError(t, err, "could not get graffiti file hash from target database")
|
||||
require.Equal(t, true, exists, "graffiti file hash should exist")
|
||||
require.Equal(t, expectedGraffitiFileHash, actualGraffitiFileHash, "graffiti file hash should match")
|
||||
|
||||
// Check the graffiti ordered index.
|
||||
actualGraffitiOrderedIndex, err := targetDatabase.GraffitiOrderedIndex(ctx, expectedGraffitiFileHash)
|
||||
require.NoError(t, err, "could not get graffiti ordered index from target database")
|
||||
require.Equal(t, expectedGraffitiOrderedIndex, actualGraffitiOrderedIndex, "graffiti ordered index should match")
|
||||
|
||||
// Check the proposer settings.
|
||||
actualProposerSettings, err := targetDatabase.ProposerSettings(ctx)
|
||||
require.NoError(t, err, "could not get proposer settings from target database")
|
||||
require.DeepEqual(t, expectedProposerSettings, actualProposerSettings, "proposer settings should match")
|
||||
|
||||
// Check the attestations.
|
||||
actualAttestationRecords, err := targetDatabase.AttestationHistoryForPubKey(ctx, pubkey1)
|
||||
require.NoError(t, err, "could not get attestations from target database")
|
||||
require.DeepEqual(t, expectedAttestationRecords1, actualAttestationRecords, "attestations should match")
|
||||
|
||||
actualAttestationRecords, err = targetDatabase.AttestationHistoryForPubKey(ctx, pubkey2)
|
||||
require.NoError(t, err, "could not get attestations from target database")
|
||||
require.DeepEqual(t, expectedAttestationRecords2, actualAttestationRecords, "attestations should match")
|
||||
|
||||
// Check the block proposals.
|
||||
actualProposals, err := targetDatabase.ProposalHistoryForPubKey(ctx, pubkey1)
|
||||
require.NoError(t, err, "could not get block proposals from target database")
|
||||
require.DeepEqual(t, expectedProposals, actualProposals, "block proposals should match")
|
||||
|
||||
// Close the target database.
|
||||
err = targetDatabase.Close()
|
||||
require.NoError(t, err, "could not close target database")
|
||||
|
||||
// Check the source database does not exist anymore.
|
||||
var existing bool
|
||||
|
||||
if minimalToComplete {
|
||||
databasePath := filepath.Join(datadir, filesystem.DatabaseDirName)
|
||||
existing, err = file.Exists(databasePath, file.Directory)
|
||||
} else {
|
||||
databasePath := filepath.Join(datadir, kv.ProtectionDbFileName)
|
||||
existing, err = file.Exists(databasePath, file.Regular)
|
||||
}
|
||||
|
||||
require.NoError(t, err, "could not check if source database exists")
|
||||
require.Equal(t, false, existing, "source database should not exist")
|
||||
})
|
||||
}
|
||||
}
|
||||
70
validator/db/filesystem/BUILD.bazel
Normal file
70
validator/db/filesystem/BUILD.bazel
Normal file
@@ -0,0 +1,70 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attester_protection.go",
|
||||
"db.go",
|
||||
"genesis.go",
|
||||
"graffiti.go",
|
||||
"import.go",
|
||||
"migration.go",
|
||||
"proposer_protection.go",
|
||||
"proposer_settings.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/validator/db/filesystem",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||
"//validator/db/common:go_default_library",
|
||||
"//validator/db/iface:go_default_library",
|
||||
"//validator/helpers:go_default_library",
|
||||
"//validator/slashing-protection-history/format:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@in_gopkg_yaml_v3//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"attester_protection_test.go",
|
||||
"db_test.go",
|
||||
"genesis_test.go",
|
||||
"graffiti_test.go",
|
||||
"import_test.go",
|
||||
"migration_test.go",
|
||||
"proposer_protection_test.go",
|
||||
"proposer_settings_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//config/proposer:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//validator/db/common:go_default_library",
|
||||
"//validator/slashing-protection-history/format:go_default_library",
|
||||
"//validator/testing:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
],
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user