mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
35 Commits
wrap-fetch
...
fix_forkch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d88745a8e3 | ||
|
|
50f9d2bab8 | ||
|
|
8743e6a688 | ||
|
|
08e6274c14 | ||
|
|
cca9ea6989 | ||
|
|
cbc2153664 | ||
|
|
8627fe72e8 | ||
|
|
65bf3d0fa8 | ||
|
|
a5da9aedd4 | ||
|
|
e1ab034d25 | ||
|
|
84bc8f3d64 | ||
|
|
c4deb84012 | ||
|
|
488e19e428 | ||
|
|
bcaae1c440 | ||
|
|
587ba83aca | ||
|
|
091f16b26c | ||
|
|
fb9626fdd7 | ||
|
|
c638e114db | ||
|
|
b1e08307ed | ||
|
|
cac5d0f234 | ||
|
|
52d48b328f | ||
|
|
9729b2ec77 | ||
|
|
7aa3776aa6 | ||
|
|
760c71ef77 | ||
|
|
6c209db3ca | ||
|
|
0725905797 | ||
|
|
166f8a1eb6 | ||
|
|
85896e994e | ||
|
|
4a00b295ed | ||
|
|
d2b39e9697 | ||
|
|
97dc86e742 | ||
|
|
cff3b99918 | ||
|
|
be9847f23c | ||
|
|
4796827d22 | ||
|
|
57b7e0b572 |
1
.github/workflows/go.yml
vendored
1
.github/workflows/go.yml
vendored
@@ -53,6 +53,7 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.47.2
|
||||
args: --config=.golangci.yml --out-${NO_FUTURE}format colored-line-number
|
||||
|
||||
build:
|
||||
name: Build
|
||||
|
||||
@@ -13,7 +13,7 @@ linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
- deadcode
|
||||
- unused
|
||||
- errcheck
|
||||
- gosimple
|
||||
- gocognit
|
||||
|
||||
@@ -21,14 +21,13 @@ import (
|
||||
// OriginData represents the BeaconState and SignedBeaconBlock necessary to start an empty Beacon Node
|
||||
// using Checkpoint Sync.
|
||||
type OriginData struct {
|
||||
wsd *WeakSubjectivityData
|
||||
sb []byte
|
||||
bb []byte
|
||||
st state.BeaconState
|
||||
b interfaces.SignedBeaconBlock
|
||||
vu *detect.VersionedUnmarshaler
|
||||
br [32]byte
|
||||
sr [32]byte
|
||||
sb []byte
|
||||
bb []byte
|
||||
st state.BeaconState
|
||||
b interfaces.SignedBeaconBlock
|
||||
vu *detect.VersionedUnmarshaler
|
||||
br [32]byte
|
||||
sr [32]byte
|
||||
}
|
||||
|
||||
// SaveBlock saves the downloaded block to a unique file in the given path.
|
||||
|
||||
@@ -95,8 +95,6 @@ func WithTimeout(timeout time.Duration) ClientOpt {
|
||||
// Client provides a collection of helper methods for calling the Eth Beacon Node API endpoints.
|
||||
type Client struct {
|
||||
hc *http.Client
|
||||
host string
|
||||
scheme string
|
||||
baseURL *url.URL
|
||||
}
|
||||
|
||||
|
||||
@@ -274,9 +274,6 @@ func non200Err(response *http.Response) error {
|
||||
if err != nil {
|
||||
body = "(Unable to read response body.)"
|
||||
} else {
|
||||
if jsonErr := json.Unmarshal(bodyBytes, &errMessage); jsonErr != nil {
|
||||
return errors.Wrap(jsonErr, "unable to read response body")
|
||||
}
|
||||
body = "response body:\n" + string(bodyBytes)
|
||||
}
|
||||
msg := fmt.Sprintf("code=%d, url=%s, body=%s", response.StatusCode, response.Request.URL, body)
|
||||
@@ -285,13 +282,25 @@ func non200Err(response *http.Response) error {
|
||||
log.WithError(ErrNoContent).Debug(msg)
|
||||
return ErrNoContent
|
||||
case 400:
|
||||
if jsonErr := json.Unmarshal(bodyBytes, &errMessage); jsonErr != nil {
|
||||
return errors.Wrap(jsonErr, "unable to read response body")
|
||||
}
|
||||
log.WithError(ErrBadRequest).Debug(msg)
|
||||
return errors.Wrap(ErrBadRequest, errMessage.Message)
|
||||
case 404:
|
||||
if jsonErr := json.Unmarshal(bodyBytes, &errMessage); jsonErr != nil {
|
||||
return errors.Wrap(jsonErr, "unable to read response body")
|
||||
}
|
||||
log.WithError(ErrNotFound).Debug(msg)
|
||||
return errors.Wrap(ErrNotFound, errMessage.Message)
|
||||
default:
|
||||
case 500:
|
||||
if jsonErr := json.Unmarshal(bodyBytes, &errMessage); jsonErr != nil {
|
||||
return errors.Wrap(jsonErr, "unable to read response body")
|
||||
}
|
||||
log.WithError(ErrNotOK).Debug(msg)
|
||||
return errors.Wrap(ErrNotOK, errMessage.Message)
|
||||
default:
|
||||
log.WithError(ErrNotOK).Debug(msg)
|
||||
return errors.Wrap(ErrNotOK, fmt.Sprintf("unsupported error code: %d", response.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,23 @@ func TestClient_GetHeader(t *testing.T) {
|
||||
_, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
|
||||
require.ErrorIs(t, err, ErrNotOK)
|
||||
|
||||
hc = &http.Client{
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, expectedPath, r.URL.Path)
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusNoContent,
|
||||
Body: io.NopCloser(bytes.NewBuffer([]byte("No header is available."))),
|
||||
Request: r.Clone(ctx),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
c = &Client{
|
||||
hc: hc,
|
||||
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
|
||||
}
|
||||
_, err = c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
|
||||
require.ErrorIs(t, err, ErrNoContent)
|
||||
|
||||
hc = &http.Client{
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, expectedPath, r.URL.Path)
|
||||
|
||||
@@ -89,7 +89,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
}
|
||||
return payloadID, nil
|
||||
case execution.ErrInvalidPayloadStatus:
|
||||
newPayloadInvalidNodeCount.Inc()
|
||||
forkchoiceUpdatedInvalidNodeCount.Inc()
|
||||
headRoot := arg.headRoot
|
||||
if len(lastValidHash) == 0 {
|
||||
lastValidHash = defaultLatestValidHash
|
||||
|
||||
@@ -1155,6 +1155,18 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
||||
require.Equal(t, false, optimistic)
|
||||
require.DeepEqual(t, validCheckpoint.Root, cp.Root)
|
||||
require.Equal(t, validCheckpoint.Epoch, cp.Epoch)
|
||||
|
||||
// Checkpoint with a lower epoch
|
||||
oldCp, err := service.cfg.BeaconDB.FinalizedCheckpoint(ctx)
|
||||
require.NoError(t, err)
|
||||
invalidCp := ðpb.Checkpoint{
|
||||
Epoch: oldCp.Epoch - 1,
|
||||
}
|
||||
// Nothing should happen as we no-op on an invalid checkpoint.
|
||||
require.NoError(t, service.updateFinalized(ctx, invalidCp))
|
||||
got, err := service.cfg.BeaconDB.FinalizedCheckpoint(ctx)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, oldCp, got)
|
||||
}
|
||||
|
||||
func TestService_removeInvalidBlockAndState(t *testing.T) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
consensusBlocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
@@ -51,10 +52,15 @@ func logStateTransitionData(b interfaces.BeaconBlock) error {
|
||||
}
|
||||
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash())))
|
||||
txs, err := p.Transactions()
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, consensusBlocks.ErrUnsupportedGetter):
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
log = log.WithField("txCount", len(txs))
|
||||
txsPerSlotCount.Set(float64(len(txs)))
|
||||
}
|
||||
log = log.WithField("txCount", len(txs))
|
||||
|
||||
}
|
||||
log.Info("Finished applying state transition")
|
||||
return nil
|
||||
|
||||
@@ -158,10 +158,47 @@ var (
|
||||
Name: "forkchoice_updated_optimistic_node_count",
|
||||
Help: "Count the number of optimistic nodes after forkchoiceUpdated EE call",
|
||||
})
|
||||
forkchoiceUpdatedInvalidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "forkchoice_updated_invalid_node_count",
|
||||
Help: "Count the number of invalid nodes after forkchoiceUpdated EE call",
|
||||
})
|
||||
txsPerSlotCount = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "txs_per_slot_count",
|
||||
Help: "Count the number of txs per slot",
|
||||
})
|
||||
missedPayloadIDFilledCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "missed_payload_id_filled_count",
|
||||
Help: "",
|
||||
})
|
||||
onBlockProcessingTime = promauto.NewSummary(prometheus.SummaryOpts{
|
||||
Name: "on_block_processing_milliseconds",
|
||||
Help: "Total time in milliseconds to complete a call to onBlock()",
|
||||
})
|
||||
stateTransitionProcessingTime = promauto.NewSummary(prometheus.SummaryOpts{
|
||||
Name: "state_transition_processing_milliseconds",
|
||||
Help: "Total time to call a state transition in onBlock()",
|
||||
})
|
||||
processAttsElapsedTime = promauto.NewHistogram(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "process_attestations_milliseconds",
|
||||
Help: "Captures latency for process attestations (forkchoice) in milliseconds",
|
||||
Buckets: []float64{1, 5, 20, 100, 500, 1000},
|
||||
},
|
||||
)
|
||||
newAttHeadElapsedTime = promauto.NewHistogram(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "new_att_head_milliseconds",
|
||||
Help: "Captures latency for new attestation head in milliseconds",
|
||||
Buckets: []float64{1, 5, 20, 100, 500, 1000},
|
||||
},
|
||||
)
|
||||
newBlockHeadElapsedTime = promauto.NewHistogram(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "new_block_head_milliseconds",
|
||||
Help: "Captures latency for new block head in milliseconds",
|
||||
Buckets: []float64{1, 5, 20, 100, 500, 1000},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
// reportSlotMetrics reports slot related metrics.
|
||||
|
||||
@@ -98,6 +98,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
if err := consensusblocks.BeaconBlockIsNil(signed); err != nil {
|
||||
return invalidBlock{error: err}
|
||||
}
|
||||
startTime := time.Now()
|
||||
b := signed.Block()
|
||||
|
||||
preState, err := s.getBlockPreState(ctx, b)
|
||||
@@ -115,10 +116,13 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stateTransitionStartTime := time.Now()
|
||||
postState, err := transition.ExecuteStateTransition(ctx, preState, signed)
|
||||
if err != nil {
|
||||
return invalidBlock{error: err}
|
||||
}
|
||||
stateTransitionProcessingTime.Observe(float64(time.Since(stateTransitionStartTime).Milliseconds()))
|
||||
|
||||
postStateVersion, postStateHeader, err := getStateVersionAndPayload(postState)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -182,10 +186,14 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
msg := fmt.Sprintf("could not read balances for state w/ justified checkpoint %#x", justified.Root)
|
||||
return errors.Wrap(err, msg)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
headRoot, err := s.cfg.ForkChoiceStore.Head(ctx, balances)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not update head")
|
||||
}
|
||||
newBlockHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
if err := s.notifyEngineIfChangedHead(ctx, headRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -262,7 +270,11 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
|
||||
|
||||
}
|
||||
defer reportAttestationInclusion(b)
|
||||
return s.handleEpochBoundary(ctx, postState)
|
||||
if err := s.handleEpochBoundary(ctx, postState); err != nil {
|
||||
return err
|
||||
}
|
||||
onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStateVersionAndPayload(st state.BeaconState) (int, *enginev1.ExecutionPayloadHeader, error) {
|
||||
|
||||
@@ -135,11 +135,21 @@ func (s *Service) verifyBlkFinalizedSlot(b interfaces.BeaconBlock) error {
|
||||
}
|
||||
|
||||
// updateFinalized saves the init sync blocks, finalized checkpoint, migrates
|
||||
// to cold old states and saves the last validated checkpoint to DB
|
||||
// to cold old states and saves the last validated checkpoint to DB. It returns
|
||||
// early if the new checkpoint is older than the one on db.
|
||||
func (s *Service) updateFinalized(ctx context.Context, cp *ethpb.Checkpoint) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.updateFinalized")
|
||||
defer span.End()
|
||||
|
||||
// return early if new checkpoint is not newer than the one in DB
|
||||
currentFinalized, err := s.cfg.BeaconDB.FinalizedCheckpoint(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cp.Epoch <= currentFinalized.Epoch {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Blocks need to be saved so that we can retrieve finalized block from
|
||||
// DB when migrating states.
|
||||
if err := s.cfg.BeaconDB.SaveBlocks(ctx, s.getInitSyncBlocks()); err != nil {
|
||||
@@ -267,7 +277,7 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfa
|
||||
if len(pendingNodes) == 1 {
|
||||
return nil
|
||||
}
|
||||
if root != s.ensureRootNotZeros(finalized.Root) {
|
||||
if root != s.ensureRootNotZeros(finalized.Root) && !s.ForkChoicer().HasNode(root) {
|
||||
return errNotDescendantOfFinalized
|
||||
}
|
||||
return s.cfg.ForkChoiceStore.InsertOptimisticChain(ctx, pendingNodes)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
testDB "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution"
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/protoarray"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/types"
|
||||
@@ -2866,12 +2867,35 @@ func TestStore_NoViableHead_Liveness_Protoarray(t *testing.T) {
|
||||
require.Equal(t, false, optimistic)
|
||||
}
|
||||
|
||||
type newForkChoicer func() forkchoice.ForkChoicer
|
||||
|
||||
func TestStore_NoViableHead_Reboot(t *testing.T) {
|
||||
cases := []struct {
|
||||
new newForkChoicer
|
||||
name string
|
||||
}{
|
||||
{
|
||||
new: func() forkchoice.ForkChoicer { return doublylinkedtree.New() },
|
||||
name: "doublylinkedtree",
|
||||
},
|
||||
{
|
||||
new: func() forkchoice.ForkChoicer { return protoarray.New() },
|
||||
name: "protoarray",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
noViableHead_Reboot(t, c.new)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// See the description in #10777 and #10782 for the full setup
|
||||
// We sync optimistically a chain of blocks. Block 12 is the first block in Epoch
|
||||
// 2 (and the merge block in this sequence). Block 18 justifies it and Block 19 returns
|
||||
// INVALID from NewPayload, with LVH block 12. No head is viable. We check that
|
||||
// the node can reboot from this state
|
||||
func TestStore_NoViableHead_Reboot_DoublyLinkedTree(t *testing.T) {
|
||||
func noViableHead_Reboot(t *testing.T, newfc newForkChoicer) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
config := params.BeaconConfig()
|
||||
config.SlotsPerEpoch = 6
|
||||
@@ -2890,7 +2914,7 @@ func TestStore_NoViableHead_Reboot_DoublyLinkedTree(t *testing.T) {
|
||||
WithDatabase(beaconDB),
|
||||
WithAttestationPool(attestations.NewPool()),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithForkChoiceStore(doublylinkedtree.New()),
|
||||
WithForkChoiceStore(newfc()),
|
||||
WithStateNotifier(&mock.MockStateNotifier{}),
|
||||
WithExecutionEngineCaller(mockEngine),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
@@ -2998,10 +3022,11 @@ func TestStore_NoViableHead_Reboot_DoublyLinkedTree(t *testing.T) {
|
||||
require.NoError(t, err) // HeadBlock returns no error when headroot == nil
|
||||
require.Equal(t, blk, nil)
|
||||
|
||||
service.cfg.ForkChoiceStore = newfc()
|
||||
require.NoError(t, service.StartFromSavedState(genesisState))
|
||||
|
||||
// Forkchoice has the genesisRoot loaded at startup
|
||||
require.Equal(t, genesisRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
// Service's store has the finalized state as headRoot
|
||||
headRoot, err := service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -3028,7 +3053,7 @@ func TestStore_NoViableHead_Reboot_DoublyLinkedTree(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.onBlock(ctx, wsb, root))
|
||||
// Check that the head is still INVALID and the node is optimistic
|
||||
require.Equal(t, genesisRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
|
||||
@@ -3056,7 +3081,7 @@ func TestStore_NoViableHead_Reboot_DoublyLinkedTree(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Head should still be INVALID and the node is optimistic
|
||||
require.Equal(t, genesisRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
|
||||
@@ -3222,9 +3247,10 @@ func TestStore_NoViableHead_Reboot_Protoarray(t *testing.T) {
|
||||
require.NoError(t, err) // HeadBlock returns no error when headroot == nil
|
||||
require.Equal(t, blk, nil)
|
||||
|
||||
service.cfg.ForkChoiceStore = protoarray.New()
|
||||
require.NoError(t, service.StartFromSavedState(genesisState))
|
||||
|
||||
require.Equal(t, genesisRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
// Service's store has the finalized state as headRoot
|
||||
headRoot, err := service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -3251,7 +3277,7 @@ func TestStore_NoViableHead_Reboot_Protoarray(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.onBlock(ctx, wsb, root))
|
||||
// Check that the head is still INVALID and the node is optimistic
|
||||
require.Equal(t, genesisRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
|
||||
@@ -3278,7 +3304,7 @@ func TestStore_NoViableHead_Reboot_Protoarray(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Head should still be INVALID and the node is optimistic
|
||||
require.Equal(t, genesisRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.ForkChoicer().CachedHeadRoot()))
|
||||
headRoot, err = service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
|
||||
|
||||
@@ -147,17 +147,22 @@ func (s *Service) UpdateHead(ctx context.Context) error {
|
||||
s.processAttestationsLock.Lock()
|
||||
defer s.processAttestationsLock.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
s.processAttestations(ctx)
|
||||
processAttsElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
justified := s.ForkChoicer().JustifiedCheckpoint()
|
||||
balances, err := s.justifiedBalances.get(ctx, justified.Root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
start = time.Now()
|
||||
newHeadRoot, err := s.cfg.ForkChoiceStore.Head(ctx, balances)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Resolving fork due to new attestation")
|
||||
}
|
||||
newAttHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
s.headLock.RLock()
|
||||
if s.headRoot() != newHeadRoot {
|
||||
log.WithFields(logrus.Fields{
|
||||
|
||||
@@ -21,8 +21,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution"
|
||||
f "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/protoarray"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/operations/slashings"
|
||||
@@ -206,29 +204,22 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
return errNilFinalizedCheckpoint
|
||||
}
|
||||
|
||||
var forkChoicer f.ForkChoicer
|
||||
fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root))
|
||||
if !features.Get().DisableForkchoiceDoublyLinkedTree {
|
||||
forkChoicer = doublylinkedtree.New()
|
||||
} else {
|
||||
forkChoicer = protoarray.New()
|
||||
}
|
||||
s.cfg.ForkChoiceStore = forkChoicer
|
||||
if err := forkChoicer.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: justified.Epoch,
|
||||
if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: justified.Epoch,
|
||||
Root: bytesutil.ToBytes32(justified.Root)}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's justified checkpoint")
|
||||
}
|
||||
if err := forkChoicer.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch,
|
||||
if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch,
|
||||
Root: bytesutil.ToBytes32(finalized.Root)}); err != nil {
|
||||
return errors.Wrap(err, "could not update forkchoice's finalized checkpoint")
|
||||
}
|
||||
forkChoicer.SetGenesisTime(uint64(s.genesisTime.Unix()))
|
||||
s.cfg.ForkChoiceStore.SetGenesisTime(uint64(s.genesisTime.Unix()))
|
||||
|
||||
st, err := s.cfg.StateGen.StateByRoot(s.ctx, fRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get finalized checkpoint state")
|
||||
}
|
||||
if err := forkChoicer.InsertNode(s.ctx, st, fRoot); err != nil {
|
||||
if err := s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, fRoot); err != nil {
|
||||
return errors.Wrap(err, "could not insert finalized block to forkchoice")
|
||||
}
|
||||
if !features.Get().EnableStartOptimistic {
|
||||
@@ -237,7 +228,7 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
|
||||
return errors.Wrap(err, "could not get last validated checkpoint")
|
||||
}
|
||||
if bytes.Equal(finalized.Root, lastValidatedCheckpoint.Root) {
|
||||
if err := forkChoicer.SetOptimisticToValid(s.ctx, fRoot); err != nil {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(s.ctx, fRoot); err != nil {
|
||||
return errors.Wrap(err, "could not set finalized block as validated")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,13 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
|
||||
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
|
||||
require.NoError(t, err)
|
||||
stateGen := stategen.New(beaconDB)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
c, err := NewService(ctx,
|
||||
WithForkChoiceStore(doublylinkedtree.New()),
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stateGen),
|
||||
WithAttestationService(attSrv),
|
||||
WithStateNotifier(&mock.MockStateNotifier{}),
|
||||
WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, stateGen.SaveState(ctx, headRoot, headState))
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
@@ -360,7 +366,13 @@ func TestChainService_InitializeChainInfo_SetHeadAtGenesis(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveStateSummary(ctx, ss))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: headRoot[:], Epoch: slots.ToEpoch(finalizedSlot)}))
|
||||
stateGen := stategen.New(beaconDB)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
c, err := NewService(ctx,
|
||||
WithForkChoiceStore(doublylinkedtree.New()),
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stateGen),
|
||||
WithAttestationService(attSrv),
|
||||
WithStateNotifier(&mock.MockStateNotifier{}),
|
||||
WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, c.StartFromSavedState(headState))
|
||||
@@ -561,7 +573,13 @@ func TestChainService_EverythingOptimistic(t *testing.T) {
|
||||
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
|
||||
require.NoError(t, err)
|
||||
stateGen := stategen.New(beaconDB)
|
||||
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
|
||||
c, err := NewService(ctx,
|
||||
WithForkChoiceStore(doublylinkedtree.New()),
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stateGen),
|
||||
WithAttestationService(attSrv),
|
||||
WithStateNotifier(&mock.MockStateNotifier{}),
|
||||
WithFinalizedStateAtStartUp(headState))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, stateGen.SaveState(ctx, headRoot, headState))
|
||||
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
|
||||
|
||||
@@ -30,8 +30,7 @@ type WeakSubjectivityVerifier struct {
|
||||
// NewWeakSubjectivityVerifier validates a checkpoint, and if valid, uses it to initialize a weak subjectivity verifier.
|
||||
func NewWeakSubjectivityVerifier(wsc *ethpb.Checkpoint, db weakSubjectivityDB) (*WeakSubjectivityVerifier, error) {
|
||||
if wsc == nil || len(wsc.Root) == 0 || wsc.Epoch == 0 {
|
||||
log.Info("--weak-subjectivity-checkpoint not provided. Prysm recommends providing a weak subjectivity checkpoint " +
|
||||
"for nodes synced from genesis, or manual verification of block and state roots for checkpoint sync nodes.")
|
||||
log.Debug("--weak-subjectivity-checkpoint not provided")
|
||||
return &WeakSubjectivityVerifier{
|
||||
enabled: false,
|
||||
}, nil
|
||||
|
||||
@@ -2,7 +2,6 @@ package builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -66,12 +65,12 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
|
||||
|
||||
// Is the builder up?
|
||||
if err := s.c.Status(ctx); err != nil {
|
||||
return nil, fmt.Errorf("could not connect to builder: %v", err)
|
||||
log.WithError(err).Error("Failed to check builder status")
|
||||
} else {
|
||||
log.WithField("endpoint", c.NodeURL()).Info("Builder has been configured")
|
||||
log.Warn("Outsourcing block construction to external builders adds non-trivial delay to block propagation time. " +
|
||||
"Builder-constructed blocks or fallback blocks may get orphaned. Use at your own risk!")
|
||||
}
|
||||
|
||||
log.WithField("endpoint", c.NodeURL()).Info("Builder has been configured")
|
||||
log.Warn("Outsourcing block construction to external builders adds non-trivial delay to block propagation time. " +
|
||||
"Builder-constructed blocks or fallback blocks may get orphaned. Use at your own risk!")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ go_library(
|
||||
"beacon_committee.go",
|
||||
"block.go",
|
||||
"genesis.go",
|
||||
"metrics.go",
|
||||
"randao.go",
|
||||
"rewards_penalties.go",
|
||||
"shuffle.go",
|
||||
|
||||
@@ -51,10 +51,11 @@ func ValidateSlotTargetEpoch(data *ethpb.AttestationData) error {
|
||||
// committee count as an argument allows cheaper computation at run time.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool:
|
||||
// committee = get_beacon_committee(state, slot, index)
|
||||
// modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
|
||||
// return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0
|
||||
//
|
||||
// def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool:
|
||||
// committee = get_beacon_committee(state, slot, index)
|
||||
// modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
|
||||
// return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0
|
||||
func IsAggregator(committeeCount uint64, slotSig []byte) (bool, error) {
|
||||
modulo := uint64(1)
|
||||
if committeeCount/params.BeaconConfig().TargetAggregatorsPerCommittee > 1 {
|
||||
@@ -68,9 +69,10 @@ func IsAggregator(committeeCount uint64, slotSig []byte) (bool, error) {
|
||||
// AggregateSignature returns the aggregated signature of the input attestations.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
|
||||
// signatures = [attestation.signature for attestation in attestations]
|
||||
// return bls.Aggregate(signatures)
|
||||
//
|
||||
// def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
|
||||
// signatures = [attestation.signature for attestation in attestations]
|
||||
// return bls.Aggregate(signatures)
|
||||
func AggregateSignature(attestations []*ethpb.Attestation) (bls.Signature, error) {
|
||||
sigs := make([]bls.Signature, len(attestations))
|
||||
var err error
|
||||
@@ -95,14 +97,15 @@ func IsAggregated(attestation *ethpb.Attestation) bool {
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64:
|
||||
// """
|
||||
// Compute the correct subnet for an attestation for Phase 0.
|
||||
// Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
|
||||
// """
|
||||
// slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
|
||||
// committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
||||
//
|
||||
// return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
||||
// """
|
||||
// Compute the correct subnet for an attestation for Phase 0.
|
||||
// Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
|
||||
// """
|
||||
// slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
|
||||
// committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
||||
//
|
||||
// return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
||||
func ComputeSubnetForAttestation(activeValCount uint64, att *ethpb.Attestation) uint64 {
|
||||
return ComputeSubnetFromCommitteeAndSlot(activeValCount, att.Data.CommitteeIndex, att.Data.Slot)
|
||||
}
|
||||
@@ -112,14 +115,15 @@ func ComputeSubnetForAttestation(activeValCount uint64, att *ethpb.Attestation)
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64:
|
||||
// """
|
||||
// Compute the correct subnet for an attestation for Phase 0.
|
||||
// Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
|
||||
// """
|
||||
// slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
|
||||
// committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
||||
//
|
||||
// return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
||||
// """
|
||||
// Compute the correct subnet for an attestation for Phase 0.
|
||||
// Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
|
||||
// """
|
||||
// slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
|
||||
// committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
||||
//
|
||||
// return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
||||
func ComputeSubnetFromCommitteeAndSlot(activeValCount uint64, comIdx types.CommitteeIndex, attSlot types.Slot) uint64 {
|
||||
slotSinceStart := slots.SinceEpochStarts(attSlot)
|
||||
comCount := SlotCommitteeCount(activeValCount)
|
||||
@@ -133,13 +137,15 @@ func ComputeSubnetFromCommitteeAndSlot(activeValCount uint64, comIdx types.Commi
|
||||
// slots.
|
||||
//
|
||||
// Example:
|
||||
// ATTESTATION_PROPAGATION_SLOT_RANGE = 5
|
||||
// clockDisparity = 24 seconds
|
||||
// current_slot = 100
|
||||
// invalid_attestation_slot = 92
|
||||
// invalid_attestation_slot = 103
|
||||
// valid_attestation_slot = 98
|
||||
// valid_attestation_slot = 101
|
||||
//
|
||||
// ATTESTATION_PROPAGATION_SLOT_RANGE = 5
|
||||
// clockDisparity = 24 seconds
|
||||
// current_slot = 100
|
||||
// invalid_attestation_slot = 92
|
||||
// invalid_attestation_slot = 103
|
||||
// valid_attestation_slot = 98
|
||||
// valid_attestation_slot = 101
|
||||
//
|
||||
// In the attestation must be within the range of 95 to 102 in the example above.
|
||||
func ValidateAttestationTime(attSlot types.Slot, genesisTime time.Time, clockDisparity time.Duration) error {
|
||||
if err := slots.ValidateClock(attSlot, uint64(genesisTime.Unix())); err != nil {
|
||||
@@ -170,13 +176,19 @@ func ValidateAttestationTime(attSlot types.Slot, genesisTime time.Time, clockDis
|
||||
lowerBounds := lowerTime.Add(-clockDisparity)
|
||||
|
||||
// Verify attestation slot within the time range.
|
||||
if attTime.Before(lowerBounds) || attTime.After(upperBounds) {
|
||||
return fmt.Errorf(
|
||||
"attestation slot %d not within attestation propagation range of %d to %d (current slot)",
|
||||
attSlot,
|
||||
lowerBoundsSlot,
|
||||
currentSlot,
|
||||
)
|
||||
attError := fmt.Errorf(
|
||||
"attestation slot %d not within attestation propagation range of %d to %d (current slot)",
|
||||
attSlot,
|
||||
lowerBoundsSlot,
|
||||
currentSlot,
|
||||
)
|
||||
if attTime.Before(lowerBounds) {
|
||||
attReceivedTooEarlyCount.Inc()
|
||||
return attError
|
||||
}
|
||||
if attTime.After(upperBounds) {
|
||||
attReceivedTooLateCount.Inc()
|
||||
return attError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
17
beacon-chain/core/helpers/metrics.go
Normal file
17
beacon-chain/core/helpers/metrics.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
attReceivedTooEarlyCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "attestation_too_early_total",
|
||||
Help: "Increased when an attestation is considered too early",
|
||||
})
|
||||
attReceivedTooLateCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "attestation_too_late_total",
|
||||
Help: "Increased when an attestation is considered too late",
|
||||
})
|
||||
)
|
||||
@@ -57,6 +57,7 @@ go_library(
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_dgraph_io_ristretto//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
|
||||
@@ -55,6 +55,14 @@ var (
|
||||
Name: "validator_entry_cache_delete_total",
|
||||
Help: "The total number of cache deletes on the validator entry cache.",
|
||||
})
|
||||
stateReadingTime = promauto.NewSummary(prometheus.SummaryOpts{
|
||||
Name: "db_beacon_state_reading_milliseconds",
|
||||
Help: "Milliseconds it takes to read a beacon state from the DB",
|
||||
})
|
||||
stateSavingTime = promauto.NewSummary(prometheus.SummaryOpts{
|
||||
Name: "db_beacon_state_saving_milliseconds",
|
||||
Help: "Milliseconds it takes to save a beacon state to the DB",
|
||||
})
|
||||
)
|
||||
|
||||
// BlockCacheSize specifies 1000 slots worth of blocks cached, which
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
func (s *Store) State(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.State")
|
||||
defer span.End()
|
||||
startTime := time.Now()
|
||||
enc, err := s.stateBytes(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -44,7 +46,12 @@ func (s *Store) State(ctx context.Context, blockRoot [32]byte) (state.BeaconStat
|
||||
return nil, valErr
|
||||
}
|
||||
|
||||
return s.unmarshalState(ctx, enc, valEntries)
|
||||
st, err := s.unmarshalState(ctx, enc, valEntries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateReadingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
return st, err
|
||||
}
|
||||
|
||||
// StateOrError is just like State(), except it only returns a non-error response
|
||||
@@ -127,6 +134,7 @@ func (s *Store) SaveStates(ctx context.Context, states []state.ReadOnlyBeaconSta
|
||||
if states == nil {
|
||||
return errors.New("nil state")
|
||||
}
|
||||
startTime := time.Now()
|
||||
multipleEncs := make([][]byte, len(states))
|
||||
for i, st := range states {
|
||||
stateBytes, err := marshalState(ctx, st)
|
||||
@@ -136,7 +144,7 @@ func (s *Store) SaveStates(ctx context.Context, states []state.ReadOnlyBeaconSta
|
||||
multipleEncs[i] = stateBytes
|
||||
}
|
||||
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
if err := s.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(stateBucket)
|
||||
for i, rt := range blockRoots {
|
||||
indicesByBucket := createStateIndicesFromStateSlot(ctx, states[i].Slot())
|
||||
@@ -148,7 +156,11 @@ func (s *Store) SaveStates(ctx context.Context, states []state.ReadOnlyBeaconSta
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
stateSavingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
return nil
|
||||
}
|
||||
|
||||
type withValidators interface {
|
||||
@@ -760,8 +772,10 @@ func createStateIndicesFromStateSlot(ctx context.Context, slot types.Slot) map[s
|
||||
// Only following states would be kept:
|
||||
// 1.) state_slot % archived_interval == 0. (e.g. archived_interval=2048, states with slot 2048, 4096... etc)
|
||||
// 2.) archived_interval - archived_interval/3 < state_slot % archived_interval
|
||||
// (e.g. archived_interval=2048, states with slots after 1365).
|
||||
// This is to tolerate skip slots. Not every state lays on the boundary.
|
||||
//
|
||||
// (e.g. archived_interval=2048, states with slots after 1365).
|
||||
// This is to tolerate skip slots. Not every state lays on the boundary.
|
||||
//
|
||||
// 3.) state with current finalized root
|
||||
// 4.) unfinalized States
|
||||
func (s *Store) CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint types.Slot) error {
|
||||
|
||||
@@ -24,12 +24,18 @@ var (
|
||||
configMismatchLog = "Configuration mismatch between your execution client and Prysm. " +
|
||||
"Please check your execution client and restart it with the proper configuration. If this is not done, " +
|
||||
"your node will not be able to complete the proof-of-stake transition"
|
||||
needsEnginePortLog = "Could not check execution client configuration. " +
|
||||
"You are probably connecting to your execution client on the wrong port. For the Ethereum " +
|
||||
"merge, you will need to connect to your " +
|
||||
"execution client on port 8551 rather than 8545. This is known as the 'engine API' port and needs to be " +
|
||||
"authenticated if connecting via HTTP. See our documentation on how to set up this up here " +
|
||||
"https://docs.prylabs.network/docs/execution-node/authentication"
|
||||
)
|
||||
|
||||
// Checks the transition configuration between Prysm and the connected execution node to ensure
|
||||
// there are no differences in terminal block difficulty and block hash.
|
||||
// If there are any discrepancies, we must log errors to ensure users can resolve
|
||||
//the problem and be ready for the merge transition.
|
||||
// the problem and be ready for the merge transition.
|
||||
func (s *Service) checkTransitionConfiguration(
|
||||
ctx context.Context, blockNotifications chan *feed.Event,
|
||||
) {
|
||||
@@ -48,10 +54,14 @@ func (s *Service) checkTransitionConfiguration(
|
||||
}
|
||||
err := s.ExchangeTransitionConfiguration(ctx, cfg)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrConfigMismatch) {
|
||||
switch {
|
||||
case errors.Is(err, ErrConfigMismatch):
|
||||
log.WithError(err).Fatal(configMismatchLog)
|
||||
case errors.Is(err, ErrMethodNotFound):
|
||||
log.WithError(err).Error(needsEnginePortLog)
|
||||
default:
|
||||
log.WithError(err).Error("Could not check configuration values between execution and consensus client")
|
||||
}
|
||||
log.WithError(err).Error("Could not check configuration values between execution and consensus client")
|
||||
}
|
||||
|
||||
// We poll the execution client to see if the transition configuration has changed.
|
||||
@@ -115,6 +125,9 @@ func (s *Service) handleExchangeConfigurationError(err error) {
|
||||
s.runError = err
|
||||
log.WithError(err).Error(configMismatchLog)
|
||||
return
|
||||
} else if errors.Is(err, ErrMethodNotFound) {
|
||||
log.WithError(err).Error(needsEnginePortLog)
|
||||
return
|
||||
}
|
||||
log.WithError(err).Error("Could not check configuration values between execution and consensus client")
|
||||
}
|
||||
|
||||
@@ -537,22 +537,31 @@ func handleRPCError(err error) error {
|
||||
}
|
||||
switch e.ErrorCode() {
|
||||
case -32700:
|
||||
errParseCount.Inc()
|
||||
return ErrParse
|
||||
case -32600:
|
||||
errInvalidRequestCount.Inc()
|
||||
return ErrInvalidRequest
|
||||
case -32601:
|
||||
errMethodNotFoundCount.Inc()
|
||||
return ErrMethodNotFound
|
||||
case -32602:
|
||||
errInvalidParamsCount.Inc()
|
||||
return ErrInvalidParams
|
||||
case -32603:
|
||||
errInternalCount.Inc()
|
||||
return ErrInternal
|
||||
case -38001:
|
||||
errUnknownPayloadCount.Inc()
|
||||
return ErrUnknownPayload
|
||||
case -38002:
|
||||
errInvalidForkchoiceStateCount.Inc()
|
||||
return ErrInvalidForkchoiceState
|
||||
case -38003:
|
||||
errInvalidPayloadAttributesCount.Inc()
|
||||
return ErrInvalidPayloadAttributes
|
||||
case -32000:
|
||||
errServerErrorCount.Inc()
|
||||
// Only -32000 status codes are data errors in the RPC specification.
|
||||
errWithData, ok := err.(rpc.DataError)
|
||||
if !ok {
|
||||
|
||||
@@ -31,6 +31,42 @@ var (
|
||||
Buckets: []float64{25, 50, 100, 200, 500, 1000, 2000, 4000},
|
||||
},
|
||||
)
|
||||
errParseCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_parse_error_count",
|
||||
Help: "The number of errors that occurred while parsing execution payload",
|
||||
})
|
||||
errInvalidRequestCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_invalid_request_count",
|
||||
Help: "The number of errors that occurred due to invalid request",
|
||||
})
|
||||
errMethodNotFoundCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_method_not_found_count",
|
||||
Help: "The number of errors that occurred due to method not found",
|
||||
})
|
||||
errInvalidParamsCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_invalid_params_count",
|
||||
Help: "The number of errors that occurred due to invalid params",
|
||||
})
|
||||
errInternalCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_internal_error_count",
|
||||
Help: "The number of errors that occurred due to internal error",
|
||||
})
|
||||
errUnknownPayloadCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_unknown_payload_count",
|
||||
Help: "The number of errors that occurred due to unknown payload",
|
||||
})
|
||||
errInvalidForkchoiceStateCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_invalid_forkchoice_state_count",
|
||||
Help: "The number of errors that occurred due to invalid forkchoice state",
|
||||
})
|
||||
errInvalidPayloadAttributesCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_invalid_payload_attributes_count",
|
||||
Help: "The number of errors that occurred due to invalid payload attributes",
|
||||
})
|
||||
errServerErrorCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "execution_server_error_count",
|
||||
Help: "The number of errors that occurred due to server error",
|
||||
})
|
||||
reconstructedExecutionPayloadCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "reconstructed_execution_payload_count",
|
||||
Help: "Count the number of execution payloads that are reconstructed using JSON-RPC from payload headers",
|
||||
|
||||
@@ -36,6 +36,14 @@ func WithHttpEndpointAndJWTSecret(endpointString string, secret []byte) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaders adds headers to the execution node JSON-RPC requests.
|
||||
func WithHeaders(headers []string) Option {
|
||||
return func(s *Service) error {
|
||||
s.cfg.headers = headers
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDepositContractAddress for the deposit contract.
|
||||
func WithDepositContractAddress(addr common.Address) Option {
|
||||
return func(s *Service) error {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
@@ -37,7 +38,13 @@ func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpo
|
||||
// Ensure we have the correct chain and deposit IDs.
|
||||
if err := ensureCorrectExecutionChain(ctx, fetcher); err != nil {
|
||||
client.Close()
|
||||
return errors.Wrap(err, "could not make initial request to verify execution chain ID")
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "401 Unauthorized") {
|
||||
errStr = "could not verify execution chain ID as your connection is not authenticated. " +
|
||||
"If connecting to your execution client via HTTP, you will need to set up JWT authentication. " +
|
||||
"See our documentation here https://docs.prylabs.network/docs/execution-node/authentication"
|
||||
}
|
||||
return errors.Wrap(err, errStr)
|
||||
}
|
||||
s.updateConnectedETH1(true)
|
||||
s.runError = nil
|
||||
@@ -128,6 +135,16 @@ func (s *Service) newRPCClientWithAuth(ctx context.Context, endpoint network.End
|
||||
}
|
||||
client.SetHeader("Authorization", header)
|
||||
}
|
||||
for _, h := range s.cfg.headers {
|
||||
if h != "" {
|
||||
keyValue := strings.Split(h, "=")
|
||||
if len(keyValue) < 2 {
|
||||
log.Warnf("Incorrect HTTP header flag format. Skipping %v", keyValue[0])
|
||||
continue
|
||||
}
|
||||
client.SetHeader(keyValue[0], strings.Join(keyValue[1:], "="))
|
||||
}
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +66,6 @@ var (
|
||||
logThreshold = 8
|
||||
// period to log chainstart related information
|
||||
logPeriod = 1 * time.Minute
|
||||
// threshold of how old we will accept an eth1 node's head to be.
|
||||
eth1Threshold = 20 * time.Minute
|
||||
// error when eth1 node is too far behind.
|
||||
errFarBehind = errors.Errorf("eth1 head is more than %s behind from current wall clock time", eth1Threshold.String())
|
||||
)
|
||||
|
||||
// ChainStartFetcher retrieves information pertaining to the chain start event
|
||||
@@ -128,6 +124,7 @@ type config struct {
|
||||
eth1HeaderReqLimit uint64
|
||||
beaconNodeStatsUpdater BeaconNodeStatsUpdater
|
||||
currHttpEndpoint network.Endpoint
|
||||
headers []string
|
||||
finalizedStateAtStartup state.BeaconState
|
||||
}
|
||||
|
||||
@@ -316,11 +313,6 @@ func (s *Service) updateBeaconNodeStats() {
|
||||
s.cfg.beaconNodeStatsUpdater.Update(bs)
|
||||
}
|
||||
|
||||
func (s *Service) updateCurrHttpEndpoint(endpoint network.Endpoint) {
|
||||
s.cfg.currHttpEndpoint = endpoint
|
||||
s.updateBeaconNodeStats()
|
||||
}
|
||||
|
||||
func (s *Service) updateConnectedETH1(state bool) {
|
||||
s.connectedETH1 = state
|
||||
s.updateBeaconNodeStats()
|
||||
@@ -608,11 +600,6 @@ func (s *Service) run(done <-chan struct{}) {
|
||||
log.WithError(err).Debug("Could not fetch latest eth1 header")
|
||||
continue
|
||||
}
|
||||
if eth1HeadIsBehind(head.Time) {
|
||||
s.pollConnectionStatus(s.ctx)
|
||||
log.WithError(errFarBehind).Debug("Could not get an up to date eth1 header")
|
||||
continue
|
||||
}
|
||||
s.processBlockHeader(head)
|
||||
s.handleETH1FollowDistance()
|
||||
case <-chainstartTicker.C:
|
||||
@@ -838,11 +825,3 @@ func dedupEndpoints(endpoints []string) []string {
|
||||
}
|
||||
return newEndpoints
|
||||
}
|
||||
|
||||
// Checks if the provided timestamp is beyond the prescribed bound from
|
||||
// the current wall clock time.
|
||||
func eth1HeadIsBehind(timestamp uint64) bool {
|
||||
timeout := prysmTime.Now().Add(-eth1Threshold)
|
||||
// check that web3 client is syncing
|
||||
return time.Unix(int64(timestamp), 0).Before(timeout) // lint:ignore uintcast -- timestamp will not exceed int64 in your lifetime.
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func TestStart_OK(t *testing.T) {
|
||||
WithDepositContractAddress(testAcc.ContractAddr),
|
||||
WithDatabase(beaconDB),
|
||||
)
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
require.NoError(t, err, "unable to setup execution service")
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
||||
@@ -156,7 +156,7 @@ func TestStart_OK(t *testing.T) {
|
||||
web3Service.Start()
|
||||
if len(hook.Entries) > 0 {
|
||||
msg := hook.LastEntry().Message
|
||||
want := "Could not connect to ETH1.0 chain RPC client"
|
||||
want := "Could not connect to execution endpoint"
|
||||
if strings.Contains(want, msg) {
|
||||
t.Errorf("incorrect log, expected %s, got %s", want, msg)
|
||||
}
|
||||
@@ -752,15 +752,6 @@ func TestService_ValidateDepositContainers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestampIsChecked(t *testing.T) {
|
||||
timestamp := uint64(time.Now().Unix())
|
||||
assert.Equal(t, false, eth1HeadIsBehind(timestamp))
|
||||
|
||||
// Give an older timestmap beyond threshold.
|
||||
timestamp = uint64(time.Now().Add(-eth1Threshold).Add(-1 * time.Minute).Unix())
|
||||
assert.Equal(t, true, eth1HeadIsBehind(timestamp))
|
||||
}
|
||||
|
||||
func TestETH1Endpoints(t *testing.T) {
|
||||
server, firstEndpoint, err := mockExecution.SetupRPCServer()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -63,6 +63,7 @@ go_test(
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v3:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
|
||||
@@ -3,6 +3,7 @@ package doublylinkedtree
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
@@ -83,7 +84,8 @@ func (f *ForkChoice) Head(
|
||||
|
||||
jc := f.JustifiedCheckpoint()
|
||||
fc := f.FinalizedCheckpoint()
|
||||
if err := f.store.treeRootNode.updateBestDescendant(ctx, jc.Epoch, fc.Epoch); err != nil {
|
||||
currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(f.store.genesisTime), 0))
|
||||
if err := f.store.treeRootNode.updateBestDescendant(ctx, jc.Epoch, fc.Epoch, currentEpoch); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not update best descendant")
|
||||
}
|
||||
return f.store.head(ctx)
|
||||
|
||||
@@ -208,7 +208,7 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||
require.Equal(t, uint64(10), f.store.nodeByRoot[[32]byte{'1'}].weight)
|
||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'2'}].weight)
|
||||
|
||||
require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1))
|
||||
require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||
require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.root)
|
||||
f.store.nodesLock.Unlock()
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
v1 "github.com/prysmaticlabs/prysm/v3/proto/eth/v1"
|
||||
@@ -43,7 +44,7 @@ func (n *Node) applyWeightChanges(ctx context.Context) error {
|
||||
|
||||
// updateBestDescendant updates the best descendant of this node and its
|
||||
// children. This function assumes the caller has a lock on Store.nodesLock
|
||||
func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finalizedEpoch, currentEpoch types.Epoch) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
@@ -59,10 +60,10 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
||||
if child == nil {
|
||||
return errors.Wrap(ErrNilNode, "could not update best descendant")
|
||||
}
|
||||
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch); err != nil {
|
||||
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch, currentEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
childLeadsToViableHead := child.leadsToViableHead(justifiedEpoch, finalizedEpoch)
|
||||
childLeadsToViableHead := child.leadsToViableHead(justifiedEpoch, finalizedEpoch, currentEpoch)
|
||||
if childLeadsToViableHead && !hasViableDescendant {
|
||||
// The child leads to a viable head, but the current
|
||||
// parent's best child doesn't.
|
||||
@@ -97,18 +98,25 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
||||
// viableForHead returns true if the node is viable to head.
|
||||
// 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, finalizedEpoch types.Epoch) bool {
|
||||
func (n *Node) viableForHead(justifiedEpoch, finalizedEpoch, currentEpoch types.Epoch) bool {
|
||||
justified := justifiedEpoch == n.justifiedEpoch || justifiedEpoch == 0
|
||||
finalized := finalizedEpoch == n.finalizedEpoch || finalizedEpoch == 0
|
||||
|
||||
if features.Get().EnableDefensivePull && !justified && justifiedEpoch+1 == currentEpoch {
|
||||
if n.unrealizedJustifiedEpoch+1 >= currentEpoch {
|
||||
justified = true
|
||||
}
|
||||
if n.unrealizedFinalizedEpoch >= finalizedEpoch {
|
||||
finalized = true
|
||||
}
|
||||
}
|
||||
return justified && finalized
|
||||
}
|
||||
|
||||
func (n *Node) leadsToViableHead(justifiedEpoch, finalizedEpoch types.Epoch) bool {
|
||||
func (n *Node) leadsToViableHead(justifiedEpoch, finalizedEpoch, currentEpoch types.Epoch) bool {
|
||||
if n.bestDescendant == nil {
|
||||
return n.viableForHead(justifiedEpoch, finalizedEpoch)
|
||||
return n.viableForHead(justifiedEpoch, finalizedEpoch, currentEpoch)
|
||||
}
|
||||
return n.bestDescendant.viableForHead(justifiedEpoch, finalizedEpoch)
|
||||
return n.bestDescendant.viableForHead(justifiedEpoch, finalizedEpoch, currentEpoch)
|
||||
}
|
||||
|
||||
// setNodeAndParentValidated sets the current node and all the ancestors as validated (i.e. non-optimistic).
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) {
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].weight = 100
|
||||
s.nodeByRoot[indexToHash(2)].weight = 200
|
||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1))
|
||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||
|
||||
assert.Equal(t, 2, len(s.treeRootNode.children))
|
||||
assert.Equal(t, s.treeRootNode.children[1], s.treeRootNode.bestDescendant)
|
||||
@@ -134,7 +134,7 @@ func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) {
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].weight = 200
|
||||
s.nodeByRoot[indexToHash(2)].weight = 100
|
||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1))
|
||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||
|
||||
assert.Equal(t, 2, len(s.treeRootNode.children))
|
||||
assert.Equal(t, s.treeRootNode.children[0], s.treeRootNode.bestDescendant)
|
||||
@@ -174,7 +174,7 @@ func TestNode_ViableForHead(t *testing.T) {
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.n.viableForHead(tc.justifiedEpoch, tc.finalizedEpoch)
|
||||
got := tc.n.viableForHead(tc.justifiedEpoch, tc.finalizedEpoch, 5)
|
||||
assert.Equal(t, tc.want, got)
|
||||
}
|
||||
}
|
||||
@@ -198,10 +198,10 @@ func TestNode_LeadsToViableHead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 3))
|
||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 3))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(2)].leadsToViableHead(4, 3))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(4)].leadsToViableHead(4, 3))
|
||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(2)].leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(4)].leadsToViableHead(4, 3, 5))
|
||||
}
|
||||
|
||||
func TestNode_SetFullyValidated(t *testing.T) {
|
||||
|
||||
@@ -95,8 +95,8 @@ func (s *Store) head(ctx context.Context) ([32]byte, error) {
|
||||
if bestDescendant == nil {
|
||||
bestDescendant = justifiedNode
|
||||
}
|
||||
|
||||
if !bestDescendant.viableForHead(s.justifiedCheckpoint.Epoch, s.finalizedCheckpoint.Epoch) {
|
||||
currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(s.genesisTime), 0))
|
||||
if !bestDescendant.viableForHead(s.justifiedCheckpoint.Epoch, s.finalizedCheckpoint.Epoch, currentEpoch) {
|
||||
s.allTipsAreInvalid = true
|
||||
return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch, justified Epoch %d, %d != %d, %d",
|
||||
bestDescendant.slot, bestDescendant.weight/10e9, bestDescendant.finalizedEpoch, bestDescendant.justifiedEpoch, s.finalizedCheckpoint.Epoch, s.justifiedCheckpoint.Epoch)
|
||||
@@ -175,7 +175,7 @@ func (s *Store) insert(ctx context.Context,
|
||||
jEpoch := s.justifiedCheckpoint.Epoch
|
||||
fEpoch := s.finalizedCheckpoint.Epoch
|
||||
s.checkpointsLock.RUnlock()
|
||||
if err := s.treeRootNode.updateBestDescendant(ctx, jEpoch, fEpoch); err != nil {
|
||||
if err := s.treeRootNode.updateBestDescendant(ctx, jEpoch, fEpoch, slots.ToEpoch(currentSlot)); err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
@@ -198,31 +199,36 @@ func TestStore_NoDeadLock(t *testing.T) {
|
||||
// D justifies and comes late.
|
||||
//
|
||||
func TestStore_ForkNextEpoch(t *testing.T) {
|
||||
resetCfg := features.InitWithReset(&features.Flags{
|
||||
EnableDefensivePull: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
|
||||
// Epoch 1 blocks (D does not arrive)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 92, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 93, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 94, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
// Epoch 2 blocks
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'c'}, [32]byte{'E'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 96, [32]byte{'e'}, [32]byte{'c'}, [32]byte{'E'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{'F'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 97, [32]byte{'f'}, [32]byte{'e'}, [32]byte{'F'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 106, [32]byte{'g'}, [32]byte{'f'}, [32]byte{'G'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 98, [32]byte{'g'}, [32]byte{'f'}, [32]byte{'G'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 107, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 99, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
@@ -234,16 +240,25 @@ func TestStore_ForkNextEpoch(t *testing.T) {
|
||||
require.Equal(t, types.Epoch(0), f.JustifiedCheckpoint().Epoch)
|
||||
|
||||
// D arrives late, D is head
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 95, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 1))
|
||||
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1}
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 2))
|
||||
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 2}
|
||||
f.updateUnrealizedCheckpoints()
|
||||
headRoot, err = f.Head(ctx, []uint64{100})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{'d'}, headRoot)
|
||||
require.Equal(t, types.Epoch(1), f.JustifiedCheckpoint().Epoch)
|
||||
require.Equal(t, types.Epoch(2), f.JustifiedCheckpoint().Epoch)
|
||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight)
|
||||
require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight)
|
||||
// Set current epoch to 3, and H's unrealized checkpoint. Check it's head
|
||||
driftGenesisTime(f, 99, 0)
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'h'}, 2))
|
||||
headRoot, err = f.Head(ctx, []uint64{100})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{'h'}, headRoot)
|
||||
require.Equal(t, types.Epoch(2), f.JustifiedCheckpoint().Epoch)
|
||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight)
|
||||
require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight)
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ go_test(
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v3:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
|
||||
@@ -885,7 +885,17 @@ func (s *Store) viableForHead(node *Node) bool {
|
||||
// It's also viable if we are in genesis epoch.
|
||||
justified := s.justifiedCheckpoint.Epoch == node.justifiedEpoch || s.justifiedCheckpoint.Epoch == 0
|
||||
finalized := s.finalizedCheckpoint.Epoch == node.finalizedEpoch || s.finalizedCheckpoint.Epoch == 0
|
||||
|
||||
if features.Get().EnableDefensivePull {
|
||||
currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(s.genesisTime), 0))
|
||||
if !justified && s.justifiedCheckpoint.Epoch+1 == currentEpoch {
|
||||
if node.unrealizedJustifiedEpoch+1 >= currentEpoch {
|
||||
justified = true
|
||||
}
|
||||
if node.unrealizedFinalizedEpoch >= s.finalizedCheckpoint.Epoch {
|
||||
finalized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return justified && finalized
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
@@ -887,6 +888,43 @@ func TestStore_ViableForHead(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_ViableForHead_DefensivePull(t *testing.T) {
|
||||
resetCfg := features.InitWithReset(&features.Flags{
|
||||
EnableDefensivePull: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
tests := []struct {
|
||||
n *Node
|
||||
justifiedEpoch types.Epoch
|
||||
finalizedEpoch types.Epoch
|
||||
currentEpoch types.Epoch
|
||||
want bool
|
||||
}{
|
||||
{&Node{}, 0, 0, 0, true},
|
||||
{&Node{}, 1, 0, 1, false},
|
||||
{&Node{}, 0, 1, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, 2, false},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, 3, true},
|
||||
{&Node{unrealizedFinalizedEpoch: 3, unrealizedJustifiedEpoch: 4}, 3, 2, 4, true},
|
||||
{&Node{unrealizedFinalizedEpoch: 2, unrealizedJustifiedEpoch: 3}, 3, 2, 4, true},
|
||||
{&Node{unrealizedFinalizedEpoch: 1, unrealizedJustifiedEpoch: 2}, 3, 2, 4, false},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
jc := &forkchoicetypes.Checkpoint{Epoch: tc.justifiedEpoch}
|
||||
fc := &forkchoicetypes.Checkpoint{Epoch: tc.finalizedEpoch}
|
||||
currentTime := uint64(time.Now().Unix())
|
||||
driftSeconds := uint64(params.BeaconConfig().SlotsPerEpoch) * params.BeaconConfig().SecondsPerSlot
|
||||
s := &Store{
|
||||
justifiedCheckpoint: jc,
|
||||
finalizedCheckpoint: fc,
|
||||
genesisTime: currentTime - driftSeconds*uint64(tc.currentEpoch),
|
||||
}
|
||||
assert.Equal(t, tc.want, s.viableForHead(tc.n))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_HasParent(t *testing.T) {
|
||||
tests := []struct {
|
||||
m map[[32]byte]uint64
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
"//beacon-chain/db/slasherkv:go_default_library",
|
||||
"//beacon-chain/deterministic-genesis:go_default_library",
|
||||
"//beacon-chain/execution:go_default_library",
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
|
||||
"//beacon-chain/forkchoice/protoarray:go_default_library",
|
||||
"//beacon-chain/gateway:go_default_library",
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db/slasherkv"
|
||||
interopcoldstart "github.com/prysmaticlabs/prysm/v3/beacon-chain/deterministic-genesis"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/gateway"
|
||||
@@ -105,9 +106,9 @@ type BeaconNode struct {
|
||||
slasherAttestationsFeed *event.Feed
|
||||
finalizedStateAtStartUp state.BeaconState
|
||||
serviceFlagOpts *serviceFlagOpts
|
||||
blockchainFlagOpts []blockchain.Option
|
||||
GenesisInitializer genesis.Initializer
|
||||
CheckpointInitializer checkpoint.Initializer
|
||||
forkChoicer forkchoice.ForkChoicer
|
||||
}
|
||||
|
||||
// New creates a new node instance, sets up configuration options, and registers
|
||||
@@ -183,6 +184,12 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if features.Get().DisableForkchoiceDoublyLinkedTree {
|
||||
beacon.forkChoicer = protoarray.New()
|
||||
} else {
|
||||
beacon.forkChoicer = doublylinkedtree.New()
|
||||
}
|
||||
|
||||
depositAddress, err := execution.DepositContractAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -228,7 +235,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
|
||||
}
|
||||
|
||||
log.Debugln("Registering Blockchain Service")
|
||||
if err := beacon.registerBlockchainService(); err != nil {
|
||||
if err := beacon.registerBlockchainService(beacon.forkChoicer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -573,7 +580,7 @@ func (b *BeaconNode) registerAttestationPool() error {
|
||||
return b.services.RegisterService(s)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerBlockchainService() error {
|
||||
func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer) error {
|
||||
var web3Service *execution.Service
|
||||
if err := b.services.FetchService(&web3Service); err != nil {
|
||||
return err
|
||||
@@ -587,6 +594,7 @@ func (b *BeaconNode) registerBlockchainService() error {
|
||||
// skipcq: CRT-D0001
|
||||
opts := append(
|
||||
b.serviceFlagOpts.blockchainFlagOpts,
|
||||
blockchain.WithForkChoiceStore(fc),
|
||||
blockchain.WithDatabase(b.db),
|
||||
blockchain.WithDepositCache(b.depositCache),
|
||||
blockchain.WithChainStartFetcher(web3Service),
|
||||
@@ -603,12 +611,6 @@ func (b *BeaconNode) registerBlockchainService() error {
|
||||
blockchain.WithProposerIdsCache(b.proposerIdsCache),
|
||||
)
|
||||
|
||||
if features.Get().DisableForkchoiceDoublyLinkedTree {
|
||||
opts = append(opts, blockchain.WithForkChoiceStore(protoarray.New()))
|
||||
} else {
|
||||
opts = append(opts, blockchain.WithForkChoiceStore(doublylinkedtree.New()))
|
||||
}
|
||||
|
||||
blockchainService, err := blockchain.NewService(b.ctx, opts...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not register blockchain service")
|
||||
|
||||
@@ -1,20 +1,39 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
knownAgentVersions = []string{
|
||||
"lighthouse",
|
||||
"nimbus",
|
||||
"prysm",
|
||||
"teku",
|
||||
"js-libp2p",
|
||||
"rust-libp2p",
|
||||
}
|
||||
p2pPeerCount = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "p2p_peer_count",
|
||||
Help: "The number of peers in a given state.",
|
||||
},
|
||||
[]string{"state"})
|
||||
totalPeerCount = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "libp2p_peers",
|
||||
Help: "Tracks the total number of libp2p peers",
|
||||
})
|
||||
connectedPeersCount = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "connected_libp2p_peers",
|
||||
Help: "Tracks the total number of connected libp2p peers by agent string",
|
||||
},
|
||||
[]string{"agent"},
|
||||
)
|
||||
avgScoreConnectedClients = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "connected_libp2p_peers_average_scores",
|
||||
Help: "Tracks the overall p2p scores of connected libp2p peers by agent string",
|
||||
},
|
||||
[]string{"agent"},
|
||||
)
|
||||
repeatPeerConnections = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "p2p_repeat_attempts",
|
||||
Help: "The number of repeat attempts the connection handler is triggered for a peer.",
|
||||
@@ -46,10 +65,60 @@ var (
|
||||
)
|
||||
|
||||
func (s *Service) updateMetrics() {
|
||||
totalPeerCount.Set(float64(len(s.peers.Connected())))
|
||||
p2pPeerCount.WithLabelValues("Connected").Set(float64(len(s.peers.Connected())))
|
||||
connectedPeers := s.peers.Connected()
|
||||
p2pPeerCount.WithLabelValues("Connected").Set(float64(len(connectedPeers)))
|
||||
p2pPeerCount.WithLabelValues("Disconnected").Set(float64(len(s.peers.Disconnected())))
|
||||
p2pPeerCount.WithLabelValues("Connecting").Set(float64(len(s.peers.Connecting())))
|
||||
p2pPeerCount.WithLabelValues("Disconnecting").Set(float64(len(s.peers.Disconnecting())))
|
||||
p2pPeerCount.WithLabelValues("Bad").Set(float64(len(s.peers.Bad())))
|
||||
|
||||
store := s.Host().Peerstore()
|
||||
numConnectedPeersByClient := make(map[string]float64)
|
||||
peerScoresByClient := make(map[string][]float64)
|
||||
for i := 0; i < len(connectedPeers); i++ {
|
||||
p := connectedPeers[i]
|
||||
pid, err := peer.Decode(p.String())
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Could not decode peer string")
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the agent data.
|
||||
rawAgent, err := store.Get(pid, "AgentVersion")
|
||||
agent, ok := rawAgent.(string)
|
||||
if err != nil || !ok {
|
||||
agent = "unknown"
|
||||
}
|
||||
foundName := "unknown"
|
||||
for _, knownAgent := range knownAgentVersions {
|
||||
// If the agent string matches one of our known agents, we set
|
||||
// the value to our own, sanitized string.
|
||||
if strings.Contains(strings.ToLower(agent), knownAgent) {
|
||||
foundName = knownAgent
|
||||
}
|
||||
}
|
||||
numConnectedPeersByClient[foundName] += 1
|
||||
|
||||
// Get peer scoring data.
|
||||
overallScore := s.peers.Scorers().Score(pid)
|
||||
peerScoresByClient[foundName] = append(peerScoresByClient[foundName], overallScore)
|
||||
}
|
||||
for agent, total := range numConnectedPeersByClient {
|
||||
connectedPeersCount.WithLabelValues(agent).Set(total)
|
||||
}
|
||||
for agent, scoringData := range peerScoresByClient {
|
||||
avgScore := average(scoringData)
|
||||
avgScoreConnectedClients.WithLabelValues(agent).Set(avgScore)
|
||||
}
|
||||
}
|
||||
|
||||
func average(xs []float64) float64 {
|
||||
if len(xs) == 0 {
|
||||
return 0
|
||||
}
|
||||
total := 0.0
|
||||
for _, v := range xs {
|
||||
total += v
|
||||
}
|
||||
return total / float64(len(xs))
|
||||
}
|
||||
|
||||
@@ -245,9 +245,7 @@ func (s *Service) Start() {
|
||||
})
|
||||
async.RunEvery(s.ctx, 30*time.Minute, s.Peers().Prune)
|
||||
async.RunEvery(s.ctx, params.BeaconNetworkConfig().RespTimeout, s.updateMetrics)
|
||||
async.RunEvery(s.ctx, refreshRate, func() {
|
||||
s.RefreshENR()
|
||||
})
|
||||
async.RunEvery(s.ctx, refreshRate, s.RefreshENR)
|
||||
async.RunEvery(s.ctx, 1*time.Minute, func() {
|
||||
log.WithFields(logrus.Fields{
|
||||
"inbound": len(s.peers.InboundConnected()),
|
||||
|
||||
@@ -813,6 +813,7 @@ type forkChoiceNodeJson struct {
|
||||
Weight string `json:"weight"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
ExecutionPayload string `json:"execution_payload" hex:"true"`
|
||||
TimeStamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
//----------------
|
||||
|
||||
@@ -195,11 +195,11 @@ func (vs *Server) GetProposerDuties(ctx context.Context, req *ethpbv1.ProposerDu
|
||||
// where `epoch` is described as `epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD <= current_epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD + 1`.
|
||||
//
|
||||
// Algorithm:
|
||||
// - Get the last valid epoch. This is the last epoch of the next sync committee period.
|
||||
// - Get the state for the requested epoch. If it's a future epoch from the current sync committee period
|
||||
// or an epoch from the next sync committee period, then get the current state.
|
||||
// - Get the state's current sync committee. If it's an epoch from the next sync committee period, then get the next sync committee.
|
||||
// - Get duties.
|
||||
// - Get the last valid epoch. This is the last epoch of the next sync committee period.
|
||||
// - Get the state for the requested epoch. If it's a future epoch from the current sync committee period
|
||||
// or an epoch from the next sync committee period, then get the current state.
|
||||
// - Get the state's current sync committee. If it's an epoch from the next sync committee period, then get the next sync committee.
|
||||
// - Get duties.
|
||||
func (vs *Server) GetSyncCommitteeDuties(ctx context.Context, req *ethpbv2.SyncCommitteeDutiesRequest) (*ethpbv2.SyncCommitteeDutiesResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.GetSyncCommitteeDuties")
|
||||
defer span.End()
|
||||
@@ -1035,19 +1035,6 @@ func v1ValidatorStatusToV1Alpha1(valStatus ethpbv1.ValidatorStatus) ethpbalpha.V
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *Server) v1BeaconBlock(ctx context.Context, req *ethpbv1.ProduceBlockRequest) (*ethpbv1.BeaconBlock, error) {
|
||||
v1alpha1req := ðpbalpha.BlockRequest{
|
||||
Slot: req.Slot,
|
||||
RandaoReveal: req.RandaoReveal,
|
||||
Graffiti: req.Graffiti,
|
||||
}
|
||||
v1alpha1resp, err := vs.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return migration.V1Alpha1ToV1Block(v1alpha1resp.GetPhase0())
|
||||
}
|
||||
|
||||
func syncCommitteeDutiesLastValidEpoch(currentEpoch types.Epoch) types.Epoch {
|
||||
currentSyncPeriodIndex := currentEpoch / params.BeaconConfig().EpochsPerSyncCommitteePeriod
|
||||
// Return the last epoch of the next sync committee.
|
||||
|
||||
@@ -998,12 +998,12 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 1, privKeys)
|
||||
require.NoError(t, err)
|
||||
graffiti := bytesutil.ToBytes32([]byte("eth2"))
|
||||
|
||||
req := ðpbv1.ProduceBlockRequest{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
RandaoReveal: randaoReveal,
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
v1Server.V1Alpha1Server.BeaconDB = db
|
||||
resp, err := v1Server.ProduceBlockV2(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
@@ -1489,6 +1489,7 @@ func TestProduceBlockV2SSZ(t *testing.T) {
|
||||
RandaoReveal: randaoReveal,
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
v1Server.V1Alpha1Server.BeaconDB = db
|
||||
resp, err := v1Server.ProduceBlockV2SSZ(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
@@ -2504,6 +2505,7 @@ func TestProduceBlindedBlockSSZ(t *testing.T) {
|
||||
RandaoReveal: randaoReveal,
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
v1Server.V1Alpha1Server.BeaconDB = db
|
||||
resp, err := v1Server.ProduceBlindedBlockSSZ(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
|
||||
@@ -53,6 +53,7 @@ go_library(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
|
||||
@@ -127,9 +127,8 @@ func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot types.Sl
|
||||
return nil, errors.New("builder returned nil bid")
|
||||
}
|
||||
|
||||
v := bid.Message.Value
|
||||
|
||||
if new(big.Int).SetBytes(bytesutil.ReverseByteOrder(v)).String() == "0" {
|
||||
v := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(bid.Message.Value))
|
||||
if v.String() == "0" {
|
||||
return nil, errors.New("builder returned header with 0 bid amount")
|
||||
}
|
||||
|
||||
@@ -159,7 +158,7 @@ func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot types.Sl
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"bid": bytesutil.BytesToUint64BigEndian(bid.Message.Value),
|
||||
"value": v.String(),
|
||||
"builderPubKey": fmt.Sprintf("%#x", bid.Message.Pubkey),
|
||||
"blockHash": fmt.Sprintf("%#x", bid.Message.Header.BlockHash),
|
||||
}).Info("Received header with bid")
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
fastssz "github.com/prysmaticlabs/fastssz"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
|
||||
@@ -106,6 +107,9 @@ func (vs *Server) canonicalEth1Data(
|
||||
canonicalEth1Data = beaconState.Eth1Data()
|
||||
eth1BlockHash = bytesutil.ToBytes32(beaconState.Eth1Data().BlockHash)
|
||||
}
|
||||
if features.Get().DisableStakinContractCheck && eth1BlockHash == [32]byte{} {
|
||||
return canonicalEth1Data, new(big.Int).SetInt64(0), nil
|
||||
}
|
||||
_, canonicalEth1DataHeight, err := vs.Eth1BlockFetcher.BlockExists(ctx, eth1BlockHash)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not fetch eth1data height")
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
@@ -41,11 +42,37 @@ var (
|
||||
// The payload is computed given the respected time of merge.
|
||||
func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx types.ValidatorIndex, headRoot [32]byte) (*enginev1.ExecutionPayload, error) {
|
||||
proposerID, payloadId, ok := vs.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, headRoot)
|
||||
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
|
||||
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
|
||||
switch err == nil {
|
||||
case true:
|
||||
feeRecipient = recipient
|
||||
case errors.As(err, kv.ErrNotFoundFeeRecipient):
|
||||
// If fee recipient is not found in DB and not set from beacon node CLI,
|
||||
// use the burn address.
|
||||
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"validatorIndex": vIdx,
|
||||
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
|
||||
}).Warn("Fee recipient is currently using the burn address, " +
|
||||
"you will not be rewarded transaction fees on this setting. " +
|
||||
"Please set a different eth address as the fee recipient. " +
|
||||
"Please refer to our documentation for instructions")
|
||||
}
|
||||
default:
|
||||
return nil, errors.Wrap(err, "could not get fee recipient in db")
|
||||
}
|
||||
|
||||
if ok && proposerID == vIdx && payloadId != [8]byte{} { // Payload ID is cache hit. Return the cached payload ID.
|
||||
var pid [8]byte
|
||||
copy(pid[:], payloadId[:])
|
||||
payloadIDCacheHit.Inc()
|
||||
return vs.ExecutionEngineCaller.GetPayload(ctx, pid)
|
||||
payload, err := vs.ExecutionEngineCaller.GetPayload(ctx, pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
warnIfFeeRecipientDiffers(payload, feeRecipient)
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
st, err := vs.HeadFetcher.HeadState(ctx)
|
||||
@@ -119,26 +146,6 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
|
||||
FinalizedBlockHash: finalizedBlockHash,
|
||||
}
|
||||
|
||||
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
|
||||
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
|
||||
switch err == nil {
|
||||
case true:
|
||||
feeRecipient = recipient
|
||||
case errors.As(err, kv.ErrNotFoundFeeRecipient):
|
||||
// If fee recipient is not found in DB and not set from beacon node CLI,
|
||||
// use the burn address.
|
||||
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"validatorIndex": vIdx,
|
||||
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
|
||||
}).Warn("Fee recipient is currently using the burn address, " +
|
||||
"you will not be rewarded transaction fees on this setting. " +
|
||||
"Please set a different eth address as the fee recipient. " +
|
||||
"Please refer to our documentation for instructions")
|
||||
}
|
||||
default:
|
||||
return nil, errors.Wrap(err, "could not get fee recipient in db")
|
||||
}
|
||||
p := &enginev1.PayloadAttributes{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
@@ -155,6 +162,13 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
warnIfFeeRecipientDiffers(payload, feeRecipient)
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// warnIfFeeRecipientDiffers logs a warning if the fee recipient in the included payload does not
|
||||
// match the requested one.
|
||||
func warnIfFeeRecipientDiffers(payload *enginev1.ExecutionPayload, feeRecipient common.Address) {
|
||||
// Warn if the fee recipient is not the value we expect.
|
||||
if payload != nil && !bytes.Equal(payload.FeeRecipient, feeRecipient[:]) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
@@ -163,7 +177,6 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
|
||||
}).Warn("Fee recipient address from execution client is not what was expected. " +
|
||||
"It is possible someone has compromised your client to try and take your transaction fees")
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// This returns the valid terminal block hash with an existence bool value.
|
||||
|
||||
@@ -123,6 +123,9 @@ func (vs *Server) ValidatorIndex(ctx context.Context, req *ethpb.ValidatorIndexR
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not determine head state: %v", err)
|
||||
}
|
||||
if st == nil || st.IsNil() {
|
||||
return nil, status.Errorf(codes.Internal, "head state is empty")
|
||||
}
|
||||
index, ok := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(req.PublicKey))
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.NotFound, "Could not find validator index for public key %#x", req.PublicKey)
|
||||
|
||||
@@ -48,6 +48,18 @@ func TestValidatorIndex_OK(t *testing.T) {
|
||||
assert.NoError(t, err, "Could not get validator index")
|
||||
}
|
||||
|
||||
func TestValidatorIndex_StateEmpty(t *testing.T) {
|
||||
Server := &Server{
|
||||
HeadFetcher: &mockChain.ChainService{},
|
||||
}
|
||||
pubKey := pubKey(1)
|
||||
req := ðpb.ValidatorIndexRequest{
|
||||
PublicKey: pubKey,
|
||||
}
|
||||
_, err := Server.ValidatorIndex(context.Background(), req)
|
||||
assert.ErrorContains(t, "head state is empty", err)
|
||||
}
|
||||
|
||||
func TestWaitForActivation_ContextClosed(t *testing.T) {
|
||||
beaconState, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: 0,
|
||||
|
||||
@@ -30,13 +30,14 @@ var errParticipation = status.Errorf(codes.Internal, "Failed to obtain epoch par
|
||||
|
||||
// ValidatorStatus returns the validator status of the current epoch.
|
||||
// The status response can be one of the following:
|
||||
// DEPOSITED - validator's deposit has been recognized by Ethereum 1, not yet recognized by Ethereum.
|
||||
// PENDING - validator is in Ethereum's activation queue.
|
||||
// ACTIVE - validator is active.
|
||||
// EXITING - validator has initiated an an exit request, or has dropped below the ejection balance and is being kicked out.
|
||||
// EXITED - validator is no longer validating.
|
||||
// SLASHING - validator has been kicked out due to meeting a slashing condition.
|
||||
// UNKNOWN_STATUS - validator does not have a known status in the network.
|
||||
//
|
||||
// DEPOSITED - validator's deposit has been recognized by Ethereum 1, not yet recognized by Ethereum.
|
||||
// PENDING - validator is in Ethereum's activation queue.
|
||||
// ACTIVE - validator is active.
|
||||
// EXITING - validator has initiated an an exit request, or has dropped below the ejection balance and is being kicked out.
|
||||
// EXITED - validator is no longer validating.
|
||||
// SLASHING - validator has been kicked out due to meeting a slashing condition.
|
||||
// UNKNOWN_STATUS - validator does not have a known status in the network.
|
||||
func (vs *Server) ValidatorStatus(
|
||||
ctx context.Context,
|
||||
req *ethpb.ValidatorStatusRequest,
|
||||
@@ -363,15 +364,6 @@ func (vs *Server) validatorStatus(
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *Server) retrieveAfterEpochTransition(ctx context.Context, epoch types.Epoch) (state.BeaconState, error) {
|
||||
endSlot, err := slots.EpochEnd(epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// replay to first slot of following epoch
|
||||
return vs.ReplayerBuilder.ReplayerForSlot(endSlot).ReplayToSlot(ctx, endSlot+1)
|
||||
}
|
||||
|
||||
func checkValidatorsAreRecent(headEpoch types.Epoch, req *ethpb.DoppelGangerRequest) (bool, *ethpb.DoppelGangerResponse) {
|
||||
validatorsAreRecent := true
|
||||
resp := ðpb.DoppelGangerResponse{
|
||||
|
||||
@@ -239,7 +239,8 @@ func (s *State) latestAncestor(ctx context.Context, blockRoot [32]byte) (state.B
|
||||
// Is the state the genesis state.
|
||||
parentRoot := bytesutil.ToBytes32(b.Block().ParentRoot())
|
||||
if parentRoot == params.BeaconConfig().ZeroHash {
|
||||
return s.beaconDB.GenesisState(ctx)
|
||||
s, err := s.beaconDB.GenesisState(ctx)
|
||||
return s, errors.Wrap(err, "could not get genesis state")
|
||||
}
|
||||
|
||||
// Return an error if slot hasn't been covered by checkpoint sync.
|
||||
@@ -268,12 +269,13 @@ func (s *State) latestAncestor(ctx context.Context, blockRoot [32]byte) (state.B
|
||||
|
||||
// Does the state exists in DB.
|
||||
if s.beaconDB.HasState(ctx, parentRoot) {
|
||||
return s.beaconDB.State(ctx, parentRoot)
|
||||
s, err := s.beaconDB.State(ctx, parentRoot)
|
||||
return s, errors.Wrap(err, "failed to retrieve state from db")
|
||||
}
|
||||
|
||||
b, err = s.beaconDB.Block(ctx, parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to retrieve block from db")
|
||||
}
|
||||
if b == nil || b.IsNil() {
|
||||
return nil, errUnknownBlock
|
||||
|
||||
@@ -13,4 +13,16 @@ var (
|
||||
Buckets: []float64{64, 256, 1024, 2048, 4096},
|
||||
},
|
||||
)
|
||||
replayBlocksSummary = promauto.NewSummary(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "replay_blocks_milliseconds",
|
||||
Help: "Time it took to replay blocks",
|
||||
},
|
||||
)
|
||||
replayToSlotSummary = promauto.NewSummary(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "replay_to_slot_milliseconds",
|
||||
Help: "Time it took to replay to slot",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -63,7 +63,6 @@ type chainer interface {
|
||||
}
|
||||
|
||||
type stateReplayer struct {
|
||||
s state.BeaconState
|
||||
target types.Slot
|
||||
method retrievalMethod
|
||||
chainer chainer
|
||||
@@ -120,6 +119,7 @@ func (rs *stateReplayer) ReplayBlocks(ctx context.Context) (state.BeaconState, e
|
||||
log.WithFields(logrus.Fields{
|
||||
"duration": duration,
|
||||
}).Debug("Finished calling process_blocks on all blocks in ReplayBlocks")
|
||||
replayBlocksSummary.Observe(float64(duration.Milliseconds()))
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -151,14 +151,14 @@ func (rs *stateReplayer) ReplayToSlot(ctx context.Context, replayTo types.Slot)
|
||||
|
||||
// err will be handled after the bookend log
|
||||
s, err = ReplayProcessSlots(ctx, s, replayTo)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("ReplayToSlot failed to seek to slot %d after applying blocks", replayTo))
|
||||
}
|
||||
duration := time.Since(start)
|
||||
log.WithFields(logrus.Fields{
|
||||
"duration": duration,
|
||||
}).Debug("time spent in process_slots")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("ReplayToSlot failed to seek to slot %d after applying blocks", replayTo))
|
||||
}
|
||||
replayToSlotSummary.Observe(float64(duration.Milliseconds()))
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ func (s *Service) processFetchedData(
|
||||
|
||||
// Use Batch Block Verify to process and verify batches directly.
|
||||
if err := s.processBatchedBlocks(ctx, genesis, data.blocks, s.cfg.Chain.ReceiveBlockBatch); err != nil {
|
||||
log.WithError(err).Warn("Batch is not processed")
|
||||
log.WithError(err).Warn("Skip processing batched blocks")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ func (s *Service) processBatchedBlocks(ctx context.Context, genesis time.Time,
|
||||
headSlot := s.cfg.Chain.HeadSlot()
|
||||
for headSlot >= firstBlock.Block().Slot() && s.isProcessedBlock(ctx, firstBlock, blkRoot) {
|
||||
if len(blks) == 1 {
|
||||
return errors.New("no good blocks in batch")
|
||||
return fmt.Errorf("headSlot:%d, blockSlot:%d , root %#x:%w", headSlot, firstBlock.Block().Slot(), blkRoot, errBlockAlreadyProcessed)
|
||||
}
|
||||
blks = blks[1:]
|
||||
firstBlock = blks[0]
|
||||
|
||||
@@ -457,7 +457,7 @@ func TestService_processBlockBatch(t *testing.T) {
|
||||
ctx context.Context, blocks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
|
||||
return nil
|
||||
})
|
||||
assert.ErrorContains(t, "no good blocks in batch", err)
|
||||
assert.ErrorContains(t, "block is already processed", err)
|
||||
|
||||
var badBatch2 []interfaces.SignedBeaconBlock
|
||||
for i, b := range batch2 {
|
||||
|
||||
@@ -89,6 +89,38 @@ var (
|
||||
Buckets: []float64{250, 500, 1000, 1500, 2000, 4000, 8000, 16000},
|
||||
},
|
||||
)
|
||||
|
||||
// Attestation processing granular error tracking.
|
||||
attBadBlockCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "gossip_attestation_bad_block_total",
|
||||
Help: "Increased when a gossip attestation references a bad block",
|
||||
})
|
||||
attBadLmdConsistencyCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "gossip_attestation_bad_lmd_consistency_total",
|
||||
Help: "Increased when a gossip attestation has bad LMD GHOST consistency",
|
||||
})
|
||||
attBadSelectionProofCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "gossip_attestation_bad_selection_proof_total",
|
||||
Help: "Increased when a gossip attestation has a bad selection proof",
|
||||
})
|
||||
attBadSignatureBatchCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "gossip_attestation_bad_signature_batch_total",
|
||||
Help: "Increased when a gossip attestation has a bad signature batch",
|
||||
})
|
||||
|
||||
// Attestation and block gossip verification performance.
|
||||
aggregateAttestationVerificationGossipSummary = promauto.NewSummary(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "gossip_aggregate_attestation_verification_milliseconds",
|
||||
Help: "Time to verify gossiped attestations",
|
||||
},
|
||||
)
|
||||
blockVerificationGossipSummary = promauto.NewSummary(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "gossip_block_verification_milliseconds",
|
||||
Help: "Time to verify gossiped blocks",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func (s *Service) updateMetrics() {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
prysmTime "github.com/prysmaticlabs/prysm/v3/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
// validateAggregateAndProof verifies the aggregated signature and the selection proof is valid before forwarding to the
|
||||
// network and downstream services.
|
||||
func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, msg *pubsub.Message) (pubsub.ValidationResult, error) {
|
||||
receivedTime := prysmTime.Now()
|
||||
if pid == s.cfg.p2p.PeerID() {
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
@@ -75,8 +77,11 @@ func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, ms
|
||||
|
||||
// Attestation's slot is within ATTESTATION_PROPAGATION_SLOT_RANGE and early attestation
|
||||
// processing tolerance.
|
||||
if err := helpers.ValidateAttestationTime(m.Message.Aggregate.Data.Slot, s.cfg.chain.GenesisTime(),
|
||||
earlyAttestationProcessingTolerance); err != nil {
|
||||
if err := helpers.ValidateAttestationTime(
|
||||
m.Message.Aggregate.Data.Slot,
|
||||
s.cfg.chain.GenesisTime(),
|
||||
earlyAttestationProcessingTolerance,
|
||||
); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
@@ -89,6 +94,7 @@ func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, ms
|
||||
if s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.BeaconBlockRoot)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.Target.Root)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.Source.Root)) {
|
||||
attBadBlockCount.Inc()
|
||||
return pubsub.ValidationReject, errors.New("bad block referenced in attestation data")
|
||||
}
|
||||
|
||||
@@ -114,6 +120,8 @@ func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, ms
|
||||
|
||||
msg.ValidatorData = m
|
||||
|
||||
aggregateAttestationVerificationGossipSummary.Observe(float64(prysmTime.Since(receivedTime).Milliseconds()))
|
||||
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
@@ -127,6 +135,7 @@ func (s *Service) validateAggregatedAtt(ctx context.Context, signed *ethpb.Signe
|
||||
// but it's invalid in the spirit of the protocol. Here we choose safety over profit.
|
||||
if err := s.cfg.chain.VerifyLmdFfgConsistency(ctx, signed.Message.Aggregate); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
attBadLmdConsistencyCount.Inc()
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
@@ -168,6 +177,7 @@ func (s *Service) validateAggregatedAtt(ctx context.Context, signed *ethpb.Signe
|
||||
if err != nil {
|
||||
wrappedErr := errors.Wrapf(err, "Could not validate selection for validator %d", signed.Message.AggregatorIndex)
|
||||
tracing.AnnotateError(span, wrappedErr)
|
||||
attBadSelectionProofCount.Inc()
|
||||
return pubsub.ValidationReject, wrappedErr
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
if s.hasBadBlock(bytesutil.ToBytes32(att.Data.BeaconBlockRoot)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(att.Data.Target.Root)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(att.Data.Source.Root)) {
|
||||
attBadBlockCount.Inc()
|
||||
return pubsub.ValidationReject, errors.New("attestation data references bad block root")
|
||||
}
|
||||
|
||||
@@ -141,6 +142,7 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
}
|
||||
if err := s.cfg.chain.VerifyLmdFfgConsistency(ctx, att); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
attBadLmdConsistencyCount.Inc()
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
@@ -222,6 +224,7 @@ func (s *Service) validateUnaggregatedAttWithState(ctx context.Context, a *eth.A
|
||||
set, err := blocks.AttestationSignatureBatch(ctx, bs, []*eth.Attestation{a})
|
||||
if err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
attBadSignatureBatchCount.Inc()
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
return s.validateWithBatchVerifier(ctx, "attestation", set)
|
||||
|
||||
@@ -204,6 +204,8 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
|
||||
"proposerIndex": blk.Block().ProposerIndex(),
|
||||
"graffiti": string(blk.Block().Body().Graffiti()),
|
||||
}).Debug("Received block")
|
||||
|
||||
blockVerificationGossipSummary.Observe(float64(prysmTime.Since(receivedTime).Milliseconds()))
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
@@ -253,16 +255,17 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.Signed
|
||||
|
||||
// validateBellatrixBeaconBlock validates the block for the Bellatrix fork.
|
||||
// spec code:
|
||||
// If the execution is enabled for the block -- i.e. is_execution_enabled(state, block.body) then validate the following:
|
||||
// [REJECT] The block's execution payload timestamp is correct with respect to the slot --
|
||||
// i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
|
||||
//
|
||||
// If exection_payload verification of block's parent by an execution node is not complete:
|
||||
// [REJECT] The block's parent (defined by block.parent_root) passes all validation (excluding execution
|
||||
// node verification of the block.body.execution_payload).
|
||||
// otherwise:
|
||||
// [IGNORE] The block's parent (defined by block.parent_root) passes all validation (including execution
|
||||
// node verification of the block.body.execution_payload).
|
||||
// If the execution is enabled for the block -- i.e. is_execution_enabled(state, block.body) then validate the following:
|
||||
// [REJECT] The block's execution payload timestamp is correct with respect to the slot --
|
||||
// i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
|
||||
//
|
||||
// If exection_payload verification of block's parent by an execution node is not complete:
|
||||
// [REJECT] The block's parent (defined by block.parent_root) passes all validation (excluding execution
|
||||
// node verification of the block.body.execution_payload).
|
||||
// otherwise:
|
||||
// [IGNORE] The block's parent (defined by block.parent_root) passes all validation (including execution
|
||||
// node verification of the block.body.execution_payload).
|
||||
func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, parentState state.BeaconState, blk interfaces.BeaconBlock) error {
|
||||
// Error if block and state are not the same version
|
||||
if parentState.Version() != blk.Version() {
|
||||
|
||||
@@ -23,9 +23,11 @@ func FlagOptions(c *cli.Context) ([]execution.Option, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not read JWT secret file for authenticating execution API")
|
||||
}
|
||||
headers := strings.Split(c.String(flags.ExecutionEngineHeaders.Name), ",")
|
||||
opts := []execution.Option{
|
||||
execution.WithHttpEndpoint(endpoint),
|
||||
execution.WithEth1HeaderRequestLimit(c.Uint64(flags.Eth1HeaderReqLimit.Name)),
|
||||
execution.WithHeaders(headers),
|
||||
}
|
||||
if len(jwtSecret) > 0 {
|
||||
opts = append(opts, execution.WithHttpEndpointAndJWTSecret(endpoint, jwtSecret))
|
||||
|
||||
@@ -32,6 +32,12 @@ var (
|
||||
Usage: "An execution client http endpoint. Can contain auth header as well in the format",
|
||||
Value: "http://localhost:8551",
|
||||
}
|
||||
// ExecutionEngineHeaders defines a list of HTTP headers to send with all execution client requests.
|
||||
ExecutionEngineHeaders = &cli.StringFlag{
|
||||
Name: "execution-headers",
|
||||
Usage: "A comma separated list of key value pairs to pass as HTTP headers for all execution " +
|
||||
"client calls. Example: --execution-headers=key1=value1,key2=value2",
|
||||
}
|
||||
// Deprecated: HTTPWeb3ProviderFlag is a deprecated flag and is an alias for the ExecutionEngineEndpoint flag.
|
||||
HTTPWeb3ProviderFlag = &cli.StringFlag{
|
||||
Name: "http-web3provider",
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
var appFlags = []cli.Flag{
|
||||
flags.DepositContractFlag,
|
||||
flags.ExecutionEngineEndpoint,
|
||||
flags.ExecutionEngineHeaders,
|
||||
flags.HTTPWeb3ProviderFlag,
|
||||
flags.ExecutionJWTSecretFlag,
|
||||
flags.RPCHost,
|
||||
|
||||
@@ -107,6 +107,7 @@ var appHelpFlagGroups = []flagGroup{
|
||||
flags.GRPCGatewayPort,
|
||||
flags.GPRCGatewayCorsDomain,
|
||||
flags.ExecutionEngineEndpoint,
|
||||
flags.ExecutionEngineHeaders,
|
||||
flags.HTTPWeb3ProviderFlag,
|
||||
flags.ExecutionJWTSecretFlag,
|
||||
flags.SetGCPercent,
|
||||
|
||||
@@ -11,9 +11,11 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//cmd/prysmctl/checkpoint:go_default_library",
|
||||
"//cmd/prysmctl/checkpointsync:go_default_library",
|
||||
"//cmd/prysmctl/deprecated:go_default_library",
|
||||
"//cmd/prysmctl/p2p:go_default_library",
|
||||
"//cmd/prysmctl/testnet:go_default_library",
|
||||
"//cmd/prysmctl/weaksubjectivity:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
|
||||
@@ -3,11 +3,10 @@ load("@prysm//tools/go:def.bzl", "go_library")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"checkpoint.go",
|
||||
"latest.go",
|
||||
"save.go",
|
||||
"cmd.go",
|
||||
"download.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/checkpoint",
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/checkpointsync",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//api/client/beacon:go_default_library",
|
||||
14
cmd/prysmctl/checkpointsync/cmd.go
Normal file
14
cmd/prysmctl/checkpointsync/cmd.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package checkpointsync
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "checkpoint-sync",
|
||||
Aliases: []string{"cpt-sync"},
|
||||
Usage: "commands for managing checkpoint sync",
|
||||
Subcommands: []*cli.Command{
|
||||
downloadCmd,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package checkpoint
|
||||
package checkpointsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,37 +10,38 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var saveFlags = struct {
|
||||
var downloadFlags = struct {
|
||||
BeaconNodeHost string
|
||||
Timeout time.Duration
|
||||
}{}
|
||||
|
||||
var saveCmd = &cli.Command{
|
||||
Name: "save",
|
||||
Usage: "Save the latest finalized header and the most recent block it integrates. To be used for checkpoint sync.",
|
||||
Action: cliActionSave,
|
||||
var downloadCmd = &cli.Command{
|
||||
Name: "download",
|
||||
Aliases: []string{"dl"},
|
||||
Usage: "Download the latest finalized state and the most recent block it integrates. To be used for checkpoint sync.",
|
||||
Action: cliActionDownload,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "beacon-node-host",
|
||||
Usage: "host:port for beacon node connection",
|
||||
Destination: &saveFlags.BeaconNodeHost,
|
||||
Destination: &downloadFlags.BeaconNodeHost,
|
||||
Value: "localhost:3500",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: "http-timeout",
|
||||
Usage: "timeout for http requests made to beacon-node-url (uses duration format, ex: 2m31s). default: 4m",
|
||||
Destination: &saveFlags.Timeout,
|
||||
Destination: &downloadFlags.Timeout,
|
||||
Value: time.Minute * 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func cliActionSave(_ *cli.Context) error {
|
||||
func cliActionDownload(_ *cli.Context) error {
|
||||
ctx := context.Background()
|
||||
f := saveFlags
|
||||
f := downloadFlags
|
||||
|
||||
opts := []beacon.ClientOpt{beacon.WithTimeout(f.Timeout)}
|
||||
client, err := beacon.NewClient(saveFlags.BeaconNodeHost, opts...)
|
||||
client, err := beacon.NewClient(downloadFlags.BeaconNodeHost, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
12
cmd/prysmctl/deprecated/BUILD.bazel
Normal file
12
cmd/prysmctl/deprecated/BUILD.bazel
Normal file
@@ -0,0 +1,12 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["cmd.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/deprecated",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/prysmctl/deprecated/checkpoint:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
13
cmd/prysmctl/deprecated/checkpoint/BUILD.bazel
Normal file
13
cmd/prysmctl/deprecated/checkpoint/BUILD.bazel
Normal file
@@ -0,0 +1,13 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"checkpoint.go",
|
||||
"latest.go",
|
||||
"save.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/deprecated/checkpoint",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["@com_github_urfave_cli_v2//:go_default_library"],
|
||||
)
|
||||
@@ -6,9 +6,9 @@ var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "checkpoint",
|
||||
Aliases: []string{"cpt"},
|
||||
Usage: "commands for managing checkpoint syncing",
|
||||
Usage: "deprecated",
|
||||
Subcommands: []*cli.Command{
|
||||
latestCmd,
|
||||
checkpointCmd,
|
||||
saveCmd,
|
||||
},
|
||||
},
|
||||
17
cmd/prysmctl/deprecated/checkpoint/latest.go
Normal file
17
cmd/prysmctl/deprecated/checkpoint/latest.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var checkpointCmd = &cli.Command{
|
||||
Name: "latest",
|
||||
Usage: "deprecated - please use 'prysmctl weak-subjectivity checkpoint' instead!",
|
||||
Action: cliDeprecatedLatest,
|
||||
}
|
||||
|
||||
func cliDeprecatedLatest(_ *cli.Context) error {
|
||||
return fmt.Errorf("This command has moved. Please use 'prysmctl weak-subjectivity checkpoint' instead!")
|
||||
}
|
||||
17
cmd/prysmctl/deprecated/checkpoint/save.go
Normal file
17
cmd/prysmctl/deprecated/checkpoint/save.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var saveCmd = &cli.Command{
|
||||
Name: "save",
|
||||
Usage: "deprecated - please use 'prysmctl checkpoint-sync download' instead!",
|
||||
Action: cliActionDeprecatedSave,
|
||||
}
|
||||
|
||||
func cliActionDeprecatedSave(_ *cli.Context) error {
|
||||
return fmt.Errorf("This command has moved. Please use 'prysmctl checkpoint-sync download' instead!")
|
||||
}
|
||||
12
cmd/prysmctl/deprecated/cmd.go
Normal file
12
cmd/prysmctl/deprecated/cmd.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package deprecated
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/deprecated/checkpoint"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var Commands = []*cli.Command{}
|
||||
|
||||
func init() {
|
||||
Commands = append(Commands, checkpoint.Commands...)
|
||||
}
|
||||
@@ -3,9 +3,11 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/checkpoint"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/checkpointsync"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/deprecated"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/p2p"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/testnet"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/weaksubjectivity"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -23,7 +25,12 @@ func main() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
prysmctlCommands = append(prysmctlCommands, checkpoint.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, testnet.Commands...)
|
||||
// contains the old checkpoint sync subcommands. these commands should display help/warn messages
|
||||
// pointing to their new locations
|
||||
prysmctlCommands = append(prysmctlCommands, deprecated.Commands...)
|
||||
|
||||
prysmctlCommands = append(prysmctlCommands, checkpointsync.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, p2p.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, testnet.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, weaksubjectivity.Commands...)
|
||||
}
|
||||
|
||||
15
cmd/prysmctl/weaksubjectivity/BUILD.bazel
Normal file
15
cmd/prysmctl/weaksubjectivity/BUILD.bazel
Normal file
@@ -0,0 +1,15 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"checkpoint.go",
|
||||
"cmd.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/weaksubjectivity",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//api/client/beacon:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package checkpoint
|
||||
package weaksubjectivity
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -9,37 +9,38 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var latestFlags = struct {
|
||||
var checkpointFlags = struct {
|
||||
BeaconNodeHost string
|
||||
Timeout time.Duration
|
||||
}{}
|
||||
|
||||
var latestCmd = &cli.Command{
|
||||
Name: "latest",
|
||||
Usage: "Compute the latest weak subjectivity checkpoint (block_root:epoch) using trusted server data.",
|
||||
Action: cliActionLatest,
|
||||
var checkpointCmd = &cli.Command{
|
||||
Name: "checkpoint",
|
||||
Aliases: []string{"cpt"},
|
||||
Usage: "Compute the latest weak subjectivity checkpoint (block_root:epoch) using trusted server data.",
|
||||
Action: cliActionCheckpoint,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "beacon-node-host",
|
||||
Usage: "host:port for beacon node to query",
|
||||
Destination: &latestFlags.BeaconNodeHost,
|
||||
Destination: &checkpointFlags.BeaconNodeHost,
|
||||
Value: "http://localhost:3500",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: "http-timeout",
|
||||
Usage: "timeout for http requests made to beacon-node-url (uses duration format, ex: 2m31s). default: 2m",
|
||||
Destination: &latestFlags.Timeout,
|
||||
Destination: &checkpointFlags.Timeout,
|
||||
Value: time.Minute * 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func cliActionLatest(_ *cli.Context) error {
|
||||
func cliActionCheckpoint(_ *cli.Context) error {
|
||||
ctx := context.Background()
|
||||
f := latestFlags
|
||||
f := checkpointFlags
|
||||
|
||||
opts := []beacon.ClientOpt{beacon.WithTimeout(f.Timeout)}
|
||||
client, err := beacon.NewClient(latestFlags.BeaconNodeHost, opts...)
|
||||
client, err := beacon.NewClient(checkpointFlags.BeaconNodeHost, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
14
cmd/prysmctl/weaksubjectivity/cmd.go
Normal file
14
cmd/prysmctl/weaksubjectivity/cmd.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package weaksubjectivity
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "weak-subjectivity",
|
||||
Aliases: []string{"ws"},
|
||||
Usage: "commands dealing with weak subjectivity",
|
||||
Subcommands: []*cli.Command{
|
||||
checkpointCmd,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/derived"
|
||||
constant "github.com/prysmaticlabs/prysm/v3/validator/testing"
|
||||
@@ -53,13 +52,14 @@ func TestBackupAccounts_Noninteractive_Derived(t *testing.T) {
|
||||
backupPasswordFile: backupPasswordFile,
|
||||
backupDir: backupDir,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Derived,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Derived),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
|
||||
km, err := w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false})
|
||||
@@ -170,13 +170,14 @@ func TestBackupAccounts_Noninteractive_Imported(t *testing.T) {
|
||||
backupPasswordFile: backupPasswordFile,
|
||||
backupDir: backupDir,
|
||||
})
|
||||
_, err = accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
_, err = acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We attempt to import accounts we wrote to the keys directory
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
prysmTime "github.com/prysmaticlabs/prysm/v3/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -160,13 +159,14 @@ func TestDeleteAccounts_Noninteractive(t *testing.T) {
|
||||
// Flags required for DeleteAccounts to work.
|
||||
deletePublicKeys: deletePublicKeys,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We attempt to import accounts.
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
mock2 "github.com/prysmaticlabs/prysm/v3/testing/mock"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
@@ -67,13 +66,14 @@ func TestExitAccountsCli_OK(t *testing.T) {
|
||||
// Flag required for ExitAccounts to work.
|
||||
voluntaryExitPublicKeys: keystore.Pubkey,
|
||||
})
|
||||
_, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
_, err = acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, accountsImport(cliCtx))
|
||||
|
||||
@@ -167,13 +167,14 @@ func TestExitAccountsCli_OK_AllPublicKeys(t *testing.T) {
|
||||
// Exit all public keys.
|
||||
exitAll: true,
|
||||
})
|
||||
_, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
_, err = acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, accountsImport(cliCtx))
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/io/prompt"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/userprompt"
|
||||
@@ -93,21 +94,51 @@ func walletImport(c *cli.Context) (*wallet.Wallet, error) {
|
||||
})
|
||||
}
|
||||
|
||||
cfg, err := accounts.ExtractWalletCreationConfigFromCli(cliCtx, keymanager.Local)
|
||||
wCfg, err := ExtractWalletDirPassword(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := wallet.New(&wallet.Config{
|
||||
KeymanagerKind: cfg.WalletCfg.KeymanagerKind,
|
||||
WalletDir: cfg.WalletCfg.WalletDir,
|
||||
WalletPassword: cfg.WalletCfg.WalletPassword,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletDir: wCfg.Dir,
|
||||
WalletPassword: wCfg.Password,
|
||||
})
|
||||
if err = accounts.CreateLocalKeymanagerWallet(cliCtx.Context, w); err != nil {
|
||||
return nil, errors.Wrap(err, "could not create keymanager")
|
||||
}
|
||||
log.WithField("wallet-path", cfg.WalletCfg.WalletDir).Info(
|
||||
log.WithField("wallet-path", wCfg.Dir).Info(
|
||||
"Successfully created new wallet",
|
||||
)
|
||||
return w, nil
|
||||
})
|
||||
}
|
||||
|
||||
// WalletDirPassword holds the directory and password of a wallet.
|
||||
type WalletDirPassword struct {
|
||||
Dir string
|
||||
Password string
|
||||
}
|
||||
|
||||
// ExtractWalletDirPassword prompts the user for wallet directory and password.
|
||||
func ExtractWalletDirPassword(cliCtx *cli.Context) (WalletDirPassword, error) {
|
||||
// Get wallet dir and check that no wallet exists at the location.
|
||||
walletDir, err := userprompt.InputDirectory(cliCtx, userprompt.WalletDirPromptText, flags.WalletDirFlag)
|
||||
if err != nil {
|
||||
return WalletDirPassword{}, err
|
||||
}
|
||||
walletPassword, err := prompt.InputPassword(
|
||||
cliCtx,
|
||||
flags.WalletPasswordFileFlag,
|
||||
wallet.NewWalletPasswordPromptText,
|
||||
wallet.ConfirmPasswordPromptText,
|
||||
true, /* Should confirm password */
|
||||
prompt.ValidatePasswordInput,
|
||||
)
|
||||
if err != nil {
|
||||
return WalletDirPassword{}, err
|
||||
}
|
||||
return WalletDirPassword{
|
||||
Dir: walletDir,
|
||||
Password: walletPassword,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -36,13 +36,14 @@ func TestImport_Noninteractive(t *testing.T) {
|
||||
walletPasswordFile: passwordFilePath,
|
||||
accountPasswordFile: passwordFilePath,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
newKm, err := local.NewKeymanager(
|
||||
cliCtx.Context,
|
||||
@@ -93,13 +94,14 @@ func TestImport_DuplicateKeys(t *testing.T) {
|
||||
walletPasswordFile: passwordFilePath,
|
||||
accountPasswordFile: passwordFilePath,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a key and then copy it to create a duplicate
|
||||
@@ -141,13 +143,14 @@ func TestImport_Noninteractive_RandomName(t *testing.T) {
|
||||
walletPasswordFile: passwordFilePath,
|
||||
accountPasswordFile: passwordFilePath,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
newKm, err := local.NewKeymanager(
|
||||
cliCtx.Context,
|
||||
@@ -224,13 +227,14 @@ func TestImport_Noninteractive_Filepath(t *testing.T) {
|
||||
walletPasswordFile: passwordFilePath,
|
||||
accountPasswordFile: passwordFilePath,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletPassword: password,
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
newKm, err := local.NewKeymanager(
|
||||
cliCtx.Context,
|
||||
|
||||
@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"create.go",
|
||||
"edit.go",
|
||||
"recover.go",
|
||||
"wallet.go",
|
||||
@@ -20,6 +21,7 @@ go_library(
|
||||
"//validator/accounts/wallet:go_default_library",
|
||||
"//validator/keymanager:go_default_library",
|
||||
"//validator/keymanager/remote:go_default_library",
|
||||
"@com_github_manifoldco_promptui//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_tyler_smith_go_bip39//:go_default_library",
|
||||
@@ -31,11 +33,13 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"create_test.go",
|
||||
"edit_test.go",
|
||||
"recover_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/validator/accounts:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
@@ -45,7 +49,11 @@ go_test(
|
||||
"//validator/accounts/wallet:go_default_library",
|
||||
"//validator/keymanager:go_default_library",
|
||||
"//validator/keymanager/derived:go_default_library",
|
||||
"//validator/keymanager/local:go_default_library",
|
||||
"//validator/keymanager/remote:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
171
cmd/validator/wallet/create.go
Normal file
171
cmd/validator/wallet/create.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package wallet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/io/prompt"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/userprompt"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// #nosec G101 -- Not sensitive data
|
||||
newMnemonicPassphraseYesNoText = "(Advanced) Do you want to setup a '25th word' passphrase for your mnemonic? [y/n]"
|
||||
// #nosec G101 -- Not sensitive data
|
||||
newMnemonicPassphrasePromptText = "(Advanced) Setup a passphrase '25th word' for your mnemonic " +
|
||||
"(WARNING: You cannot recover your keys from your mnemonic if you forget this passphrase!)"
|
||||
)
|
||||
|
||||
func walletCreate(c *cli.Context) error {
|
||||
keymanagerKind, err := inputKeymanagerKind(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts, err := ConstructCLIManagerOpts(c, keymanagerKind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := acc.WalletCreate(c.Context); err != nil {
|
||||
return errors.Wrap(err, "could not create wallet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConstructCLIManagerOpts prompts the user for wallet creation input.
|
||||
func ConstructCLIManagerOpts(cliCtx *cli.Context, keymanagerKind keymanager.Kind) ([]accounts.Option, error) {
|
||||
cliOpts := []accounts.Option{}
|
||||
// Get wallet dir and check that no wallet exists at the location.
|
||||
walletDir, err := userprompt.InputDirectory(cliCtx, userprompt.WalletDirPromptText, flags.WalletDirFlag)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, err
|
||||
}
|
||||
dirExists, err := wallet.Exists(walletDir)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, err
|
||||
}
|
||||
if dirExists {
|
||||
return []accounts.Option{}, errors.New("a wallet already exists at this location. Please input an" +
|
||||
" alternative location for the new wallet or remove the current wallet")
|
||||
}
|
||||
|
||||
walletPassword, err := prompt.InputPassword(
|
||||
cliCtx,
|
||||
flags.WalletPasswordFileFlag,
|
||||
wallet.NewWalletPasswordPromptText,
|
||||
wallet.ConfirmPasswordPromptText,
|
||||
true, /* Should confirm password */
|
||||
prompt.ValidatePasswordInput,
|
||||
)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, err
|
||||
}
|
||||
cliOpts = append(cliOpts, accounts.WithWalletDir(walletDir))
|
||||
cliOpts = append(cliOpts, accounts.WithWalletPassword(walletPassword))
|
||||
cliOpts = append(cliOpts, accounts.WithKeymanagerType(keymanagerKind))
|
||||
cliOpts = append(cliOpts, accounts.WithSkipMnemonicConfirm(cliCtx.Bool(flags.SkipDepositConfirmationFlag.Name)))
|
||||
|
||||
skipMnemonic25thWord := cliCtx.IsSet(flags.SkipMnemonic25thWordCheckFlag.Name)
|
||||
has25thWordFile := cliCtx.IsSet(flags.Mnemonic25thWordFileFlag.Name)
|
||||
if keymanagerKind == keymanager.Derived {
|
||||
numAccounts, err := inputNumAccounts(cliCtx)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, errors.Wrap(err, "could not get number of accounts to generate")
|
||||
}
|
||||
cliOpts = append(cliOpts, accounts.WithNumAccounts(int(numAccounts)))
|
||||
}
|
||||
if keymanagerKind == keymanager.Derived && !skipMnemonic25thWord && !has25thWordFile {
|
||||
resp, err := prompt.ValidatePrompt(
|
||||
os.Stdin, newMnemonicPassphraseYesNoText, prompt.ValidateYesOrNo,
|
||||
)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, errors.Wrap(err, "could not validate choice")
|
||||
}
|
||||
if strings.EqualFold(resp, "y") {
|
||||
mnemonicPassphrase, err := prompt.InputPassword(
|
||||
cliCtx,
|
||||
flags.Mnemonic25thWordFileFlag,
|
||||
newMnemonicPassphrasePromptText,
|
||||
"Confirm mnemonic passphrase",
|
||||
true, /* Should confirm password */
|
||||
func(input string) error {
|
||||
if strings.TrimSpace(input) == "" {
|
||||
return errors.New("input cannot be empty")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, err
|
||||
}
|
||||
cliOpts = append(cliOpts, accounts.WithMnemonic25thWord(mnemonicPassphrase))
|
||||
}
|
||||
}
|
||||
if keymanagerKind == keymanager.Remote {
|
||||
opts, err := userprompt.InputRemoteKeymanagerConfig(cliCtx)
|
||||
if err != nil {
|
||||
return []accounts.Option{}, errors.Wrap(err, "could not input remote keymanager config")
|
||||
}
|
||||
cliOpts = append(cliOpts, accounts.WithKeymanagerOpts(opts))
|
||||
}
|
||||
if keymanagerKind == keymanager.Web3Signer {
|
||||
return []accounts.Option{}, errors.New("web3signer keymanager does not require persistent wallets.")
|
||||
}
|
||||
return cliOpts, nil
|
||||
}
|
||||
|
||||
func inputKeymanagerKind(cliCtx *cli.Context) (keymanager.Kind, error) {
|
||||
if cliCtx.IsSet(flags.KeymanagerKindFlag.Name) {
|
||||
return keymanager.ParseKind(cliCtx.String(flags.KeymanagerKindFlag.Name))
|
||||
}
|
||||
promptSelect := promptui.Select{
|
||||
Label: "Select a type of wallet",
|
||||
Items: []string{
|
||||
wallet.KeymanagerKindSelections[keymanager.Local],
|
||||
wallet.KeymanagerKindSelections[keymanager.Derived],
|
||||
wallet.KeymanagerKindSelections[keymanager.Remote],
|
||||
wallet.KeymanagerKindSelections[keymanager.Web3Signer],
|
||||
},
|
||||
}
|
||||
selection, _, err := promptSelect.Run()
|
||||
if err != nil {
|
||||
return keymanager.Local, fmt.Errorf("could not select wallet type: %w", userprompt.FormatPromptError(err))
|
||||
}
|
||||
return keymanager.Kind(selection), nil
|
||||
}
|
||||
|
||||
// CreateAndSaveWalletCli from user input with a desired keymanager. If a
|
||||
// wallet already exists in the path, it suggests the user alternatives
|
||||
// such as how to edit their existing wallet configuration.
|
||||
func CreateAndSaveWalletCli(cliCtx *cli.Context) (*wallet.Wallet, error) {
|
||||
keymanagerKind, err := inputKeymanagerKind(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts, err := ConstructCLIManagerOpts(cliCtx, keymanagerKind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create wallet")
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
package accounts
|
||||
package wallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
cmdacc "github.com/prysmaticlabs/prysm/v3/cmd/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
|
||||
@@ -23,89 +21,11 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
passwordFileName = "password.txt"
|
||||
password = "OhWOWthisisatest42!$"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(io.Discard)
|
||||
}
|
||||
|
||||
type testWalletConfig struct {
|
||||
exitAll bool
|
||||
skipDepositConfirm bool
|
||||
keymanagerKind keymanager.Kind
|
||||
numAccounts int64
|
||||
grpcHeaders string
|
||||
privateKeyFile string
|
||||
accountPasswordFile string
|
||||
walletPasswordFile string
|
||||
backupPasswordFile string
|
||||
backupPublicKeys string
|
||||
voluntaryExitPublicKeys string
|
||||
deletePublicKeys string
|
||||
keysDir string
|
||||
backupDir string
|
||||
passwordsDir string
|
||||
walletDir string
|
||||
}
|
||||
|
||||
func setupWalletCtx(
|
||||
tb testing.TB,
|
||||
cfg *testWalletConfig,
|
||||
) *cli.Context {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.WalletDirFlag.Name, cfg.walletDir, "")
|
||||
set.String(flags.KeysDirFlag.Name, cfg.keysDir, "")
|
||||
set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "")
|
||||
set.String(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys, "")
|
||||
set.String(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys, "")
|
||||
set.String(flags.BackupDirFlag.Name, cfg.backupDir, "")
|
||||
set.String(flags.BackupPasswordFile.Name, cfg.backupPasswordFile, "")
|
||||
set.String(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys, "")
|
||||
set.String(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile, "")
|
||||
set.String(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile, "")
|
||||
set.Int64(flags.NumAccountsFlag.Name, cfg.numAccounts, "")
|
||||
set.Bool(flags.SkipDepositConfirmationFlag.Name, cfg.skipDepositConfirm, "")
|
||||
set.Bool(flags.SkipMnemonic25thWordCheckFlag.Name, true, "")
|
||||
set.Bool(flags.ExitAllFlag.Name, cfg.exitAll, "")
|
||||
set.String(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders, "")
|
||||
|
||||
if cfg.privateKeyFile != "" {
|
||||
set.String(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile, "")
|
||||
assert.NoError(tb, set.Set(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile))
|
||||
}
|
||||
assert.NoError(tb, set.Set(flags.WalletDirFlag.Name, cfg.walletDir))
|
||||
assert.NoError(tb, set.Set(flags.SkipMnemonic25thWordCheckFlag.Name, "true"))
|
||||
assert.NoError(tb, set.Set(flags.KeysDirFlag.Name, cfg.keysDir))
|
||||
assert.NoError(tb, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String()))
|
||||
assert.NoError(tb, set.Set(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys))
|
||||
assert.NoError(tb, set.Set(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys))
|
||||
assert.NoError(tb, set.Set(flags.BackupDirFlag.Name, cfg.backupDir))
|
||||
assert.NoError(tb, set.Set(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys))
|
||||
assert.NoError(tb, set.Set(flags.BackupPasswordFile.Name, cfg.backupPasswordFile))
|
||||
assert.NoError(tb, set.Set(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile))
|
||||
assert.NoError(tb, set.Set(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile))
|
||||
assert.NoError(tb, set.Set(flags.NumAccountsFlag.Name, strconv.Itoa(int(cfg.numAccounts))))
|
||||
assert.NoError(tb, set.Set(flags.SkipDepositConfirmationFlag.Name, strconv.FormatBool(cfg.skipDepositConfirm)))
|
||||
assert.NoError(tb, set.Set(flags.ExitAllFlag.Name, strconv.FormatBool(cfg.exitAll)))
|
||||
assert.NoError(tb, set.Set(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders))
|
||||
return cli.NewContext(&app, set, nil)
|
||||
}
|
||||
|
||||
func setupWalletAndPasswordsDir(t testing.TB) (string, string, string) {
|
||||
walletDir := filepath.Join(t.TempDir(), "wallet")
|
||||
passwordsDir := filepath.Join(t.TempDir(), "passwords")
|
||||
passwordFileDir := filepath.Join(t.TempDir(), "passwordFile")
|
||||
require.NoError(t, os.MkdirAll(passwordFileDir, params.BeaconIoConfig().ReadWriteExecutePermissions))
|
||||
passwordFilePath := filepath.Join(passwordFileDir, passwordFileName)
|
||||
require.NoError(t, os.WriteFile(passwordFilePath, []byte(password), os.ModePerm))
|
||||
return walletDir, passwordsDir, passwordFilePath
|
||||
}
|
||||
|
||||
func TestCreateOrOpenWallet(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
walletDir, passwordsDir, walletPasswordFile := setupWalletAndPasswordsDir(t)
|
||||
@@ -116,19 +36,19 @@ func TestCreateOrOpenWallet(t *testing.T) {
|
||||
walletPasswordFile: walletPasswordFile,
|
||||
})
|
||||
createLocalWallet := func(cliCtx *cli.Context) (*wallet.Wallet, error) {
|
||||
cfg, err := ExtractWalletCreationConfigFromCli(cliCtx, keymanager.Local)
|
||||
cfg, err := cmdacc.ExtractWalletDirPassword(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := wallet.New(&wallet.Config{
|
||||
KeymanagerKind: cfg.WalletCfg.KeymanagerKind,
|
||||
WalletDir: cfg.WalletCfg.WalletDir,
|
||||
WalletPassword: cfg.WalletCfg.WalletPassword,
|
||||
KeymanagerKind: keymanager.Local,
|
||||
WalletDir: cfg.Dir,
|
||||
WalletPassword: cfg.Password,
|
||||
})
|
||||
if err = CreateLocalKeymanagerWallet(cliCtx.Context, w); err != nil {
|
||||
if err = accounts.CreateLocalKeymanagerWallet(cliCtx.Context, w); err != nil {
|
||||
return nil, errors.Wrap(err, "could not create keymanager")
|
||||
}
|
||||
log.WithField("wallet-path", cfg.WalletCfg.WalletDir).Info(
|
||||
log.WithField("wallet-path", cfg.Dir).Info(
|
||||
"Successfully created new wallet",
|
||||
)
|
||||
return w, nil
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -52,6 +51,7 @@ type testWalletConfig struct {
|
||||
keysDir string
|
||||
backupDir string
|
||||
walletDir string
|
||||
passwordsDir string
|
||||
}
|
||||
|
||||
func setupWalletCtx(
|
||||
@@ -104,13 +104,14 @@ func TestEditWalletConfiguration(t *testing.T) {
|
||||
walletDir: walletDir,
|
||||
keymanagerKind: keymanager.Remote,
|
||||
})
|
||||
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanager.Remote,
|
||||
WalletPassword: "Passwordz0320$",
|
||||
},
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Remote),
|
||||
accounts.WithWalletPassword("Passwordz0320$"),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
w, err := acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
|
||||
originalCfg := &remote.KeymanagerOpts{
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/runtime/tos"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -43,13 +42,13 @@ var Commands = &cli.Command{
|
||||
if err := cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags); err != nil {
|
||||
return err
|
||||
}
|
||||
return tos.VerifyTosAcceptedOrPrompt(cliCtx)
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
if err := features.ConfigureValidator(cliCtx); err != nil {
|
||||
if err := tos.VerifyTosAcceptedOrPrompt(cliCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := accounts.CreateAndSaveWalletCli(cliCtx); err != nil {
|
||||
return features.ConfigureValidator(cliCtx)
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
if err := walletCreate(cliCtx); err != nil {
|
||||
log.WithError(err).Fatal("Could not create a wallet")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -61,13 +61,16 @@ type Flags struct {
|
||||
EnableSlashingProtectionPruning bool
|
||||
|
||||
EnableNativeState bool // EnableNativeState defines whether the beacon state will be represented as a pure Go struct or a Go struct that wraps a proto struct.
|
||||
DisablePullTips bool // DisablePullTips enables experimental disabling of boundary checks.
|
||||
DisablePullTips bool // DisablePullTips disables experimental disabling of boundary checks.
|
||||
EnableDefensivePull bool // EnableDefensivePull enables exerimental back boundary checks.
|
||||
EnableVectorizedHTR bool // EnableVectorizedHTR specifies whether the beacon state will use the optimized sha256 routines.
|
||||
DisableForkchoiceDoublyLinkedTree bool // DisableForkChoiceDoublyLinkedTree specifies whether fork choice store will use a doubly linked tree.
|
||||
EnableBatchGossipAggregation bool // EnableBatchGossipAggregation specifies whether to further aggregate our gossip batches before verifying them.
|
||||
EnableOnlyBlindedBeaconBlocks bool // EnableOnlyBlindedBeaconBlocks enables only storing blinded beacon blocks in the DB post-Bellatrix fork.
|
||||
EnableStartOptimistic bool // EnableStartOptimistic treats every block as optimistic at startup.
|
||||
|
||||
DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks
|
||||
|
||||
// KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have
|
||||
// changed on disk. This feature is for advanced use cases only.
|
||||
KeystoreImportDebounceInterval time.Duration
|
||||
@@ -209,6 +212,14 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logEnabled(disablePullTips)
|
||||
cfg.DisablePullTips = true
|
||||
}
|
||||
if ctx.Bool(enableDefensivePull.Name) {
|
||||
logEnabled(enableDefensivePull)
|
||||
cfg.EnableDefensivePull = true
|
||||
}
|
||||
if ctx.Bool(disableStakinContractCheck.Name) {
|
||||
logEnabled(disableStakinContractCheck)
|
||||
cfg.DisableStakinContractCheck = true
|
||||
}
|
||||
if ctx.Bool(disableVecHTR.Name) {
|
||||
logEnabled(disableVecHTR)
|
||||
} else {
|
||||
|
||||
@@ -90,6 +90,8 @@ var deprecatedFlags = []cli.Flag{
|
||||
deprecatedFallbackProvider,
|
||||
}
|
||||
|
||||
// deprecatedBeaconFlags contains flags that are still used by other components
|
||||
// and therefore cannot be added to deprecatedFlags
|
||||
var deprecatedBeaconFlags = []cli.Flag{
|
||||
deprecatedBackupWebHookFlag,
|
||||
}
|
||||
|
||||
@@ -83,6 +83,10 @@ var (
|
||||
"a foolproof method to find duplicate instances in the network. Your validator will still be" +
|
||||
" vulnerable if it is being run in unsafe configurations.",
|
||||
}
|
||||
disableStakinContractCheck = &cli.BoolFlag{
|
||||
Name: "disable-staking-contract-check",
|
||||
Usage: "Disables checking of staking contract deposits when proposing blocks, useful for devnets",
|
||||
}
|
||||
enableHistoricalSpaceRepresentation = &cli.BoolFlag{
|
||||
Name: "enable-historical-state-representation",
|
||||
Usage: "Enables the beacon chain to save historical states in a space efficient manner." +
|
||||
@@ -97,6 +101,11 @@ var (
|
||||
Name: "experimental-enable-boundary-checks",
|
||||
Usage: "Experimental enable of boundary checks, useful for debugging, may cause bad votes.",
|
||||
}
|
||||
enableDefensivePull = &cli.BoolFlag{
|
||||
Name: "enable-back-pull",
|
||||
Usage: "Experimental enable of past boundary checks, useful for debugging, may cause bad votes.",
|
||||
Hidden: true,
|
||||
}
|
||||
disableVecHTR = &cli.BoolFlag{
|
||||
Name: "disable-vectorized-htr",
|
||||
Usage: "Disables the new go sha256 library which utilizes optimized routines for merkle trees",
|
||||
@@ -163,6 +172,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
|
||||
disableGossipBatchAggregation,
|
||||
EnableOnlyBlindedBeaconBlocks,
|
||||
enableStartupOptimistic,
|
||||
enableDefensivePull,
|
||||
}...)...)
|
||||
|
||||
// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.
|
||||
|
||||
@@ -2,7 +2,7 @@ package params
|
||||
|
||||
const (
|
||||
altairE2EForkEpoch = 6
|
||||
bellatrixE2EForkEpoch = 8 //nolint:deadcode
|
||||
bellatrixE2EForkEpoch = 8
|
||||
)
|
||||
|
||||
// E2ETestConfig retrieves the configurations made specifically for E2E testing.
|
||||
|
||||
@@ -24,8 +24,6 @@ var (
|
||||
ErrNilObject = errors.New("received nil object")
|
||||
// ErrNilSignedBeaconBlock is returned when a nil signed beacon block is received.
|
||||
ErrNilSignedBeaconBlock = errors.New("signed beacon block can't be nil")
|
||||
errNilBeaconBlock = errors.New("beacon block can't be nil")
|
||||
errNilBeaconBlockBody = errors.New("beacon block body can't be nil")
|
||||
)
|
||||
|
||||
// NewSignedBeaconBlock creates a signed beacon block from a protobuf signed beacon block.
|
||||
|
||||
@@ -17,12 +17,6 @@ func BeaconBlockIsNil(b interfaces.SignedBeaconBlock) error {
|
||||
if b == nil || b.IsNil() {
|
||||
return ErrNilSignedBeaconBlock
|
||||
}
|
||||
if b.Block().IsNil() {
|
||||
return errNilBeaconBlock
|
||||
}
|
||||
if b.Block().Body().IsNil() {
|
||||
return errNilBeaconBlockBody
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
base:
|
||||
language: go
|
||||
version: 1.18
|
||||
build_tags:
|
||||
- fuzz
|
||||
- develop
|
||||
|
||||
@@ -33,7 +33,7 @@ func SinceGenesis(genesis time.Time) types.Slot {
|
||||
return types.Slot(uint64(prysmTime.Since(genesis).Seconds()) / params.BeaconConfig().SecondsPerSlot)
|
||||
}
|
||||
|
||||
// EpochsSinceGenesis returns the number of slots since
|
||||
// EpochsSinceGenesis returns the number of epochs since
|
||||
// the provided genesis time.
|
||||
func EpochsSinceGenesis(genesis time.Time) types.Epoch {
|
||||
return types.Epoch(SinceGenesis(genesis) / params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user