Compare commits

...

10 Commits

Author SHA1 Message Date
james-prysm
a4860ff055 Merge branch 'develop' into el-offline-api-change 2024-10-21 09:11:40 -05:00
Potuz
073cf19b69 Rollback on errors from forkchoice insertion (#14556) 2024-10-18 16:49:55 +00:00
Potuz
6ac8090599 rollback on SaveState error (#14555)
* rollback on SaveState error

* add test
2024-10-18 15:51:58 +00:00
james-prysm
cad38e2692 Merge branch 'develop' into el-offline-api-change 2024-10-18 09:26:31 -05:00
james-prysm
c4be13ea9c Update beacon-chain/rpc/eth/node/handlers.go
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
2024-10-18 09:26:24 -05:00
james-prysm
6222ebae43 Update beacon-chain/execution/service.go
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
2024-10-18 09:23:48 -05:00
james-prysm
ce2c914567 Update service.go
Co-authored-by: Md Amaan <114795592+Redidacove@users.noreply.github.com>
2024-10-17 14:59:03 -05:00
james-prysm
a1581c04f1 gaz 2024-10-17 14:44:09 -05:00
james-prysm
a04877f2b3 changelog 2024-10-17 14:43:28 -05:00
james-prysm
34a4d6e3ea el_offline should be false when it is syncing 2024-10-17 14:37:30 -05:00
14 changed files with 121 additions and 7 deletions

View File

@@ -23,6 +23,9 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- Fix `engine_exchangeCapabilities` implementation.
- Updated the default `scrape-interval` in `Client-stats` to 2 minutes to accommodate Beaconcha.in API rate limits.
- Switch to compounding when consolidating with source==target.
- Revert block db save when saving state fails.
- Return false from HasBlock if the block is being synced.
- Cleanup forkchoice on failed insertions.
### Deprecated
@@ -37,6 +40,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- Fixed mesh size by appending `gParams.Dhi = gossipSubDhi`
- Fix skipping partial withdrawals count.
- recover from panics when writing the event stream [pr](https://github.com/prysmaticlabs/prysm/pull/14545)
- `/eth/v1/node/syncing` returns `el_offline` false while the el is syncing.
### Security

View File

@@ -404,6 +404,10 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interface
return errors.Wrapf(err, "could not save block from slot %d", b.Block().Slot())
}
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
log.Warnf("Rolling back insertion of block with root %#x", r)
if err := s.cfg.BeaconDB.DeleteBlock(ctx, r); err != nil {
log.WithError(err).Errorf("Could not delete block with block root %#x", r)
}
return errors.Wrap(err, "could not save state")
}
return nil

View File

@@ -349,6 +349,9 @@ func (s *Service) ReceiveBlockBatch(ctx context.Context, blocks []blocks.ROBlock
// HasBlock returns true if the block of the input root exists in initial sync blocks cache or DB.
func (s *Service) HasBlock(ctx context.Context, root [32]byte) bool {
if s.BlockBeingSynced(root) {
return false
}
return s.hasBlockInInitSyncOrDB(ctx, root)
}

View File

@@ -278,6 +278,8 @@ func TestService_HasBlock(t *testing.T) {
r, err = b.Block.HashTreeRoot()
require.NoError(t, err)
require.Equal(t, true, s.HasBlock(context.Background(), r))
s.blockBeingSynced.set(r)
require.Equal(t, false, s.HasBlock(context.Background(), r))
}
func TestCheckSaveHotStateDB_Enabling(t *testing.T) {

View File

@@ -34,6 +34,7 @@ import (
contracts "github.com/prysmaticlabs/prysm/v5/contracts/deposit"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/monitoring/clientstats"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
"github.com/prysmaticlabs/prysm/v5/network"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
prysmTime "github.com/prysmaticlabs/prysm/v5/time"
@@ -41,6 +42,11 @@ import (
"github.com/sirupsen/logrus"
)
const (
// EthSyncing request string for JSON-RPC.
EthSyncing = "eth_syncing"
)
var (
validDepositsCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "powchain_valid_deposits_received",
@@ -79,6 +85,7 @@ type ChainInfoFetcher interface {
ExecutionClientConnected() bool
ExecutionClientEndpoint() string
ExecutionClientConnectionErr() error
IsExecutionClientSyncing(ctx context.Context) (bool, error)
}
// POWBlockFetcher defines a struct that can retrieve mainchain blocks.
@@ -303,6 +310,20 @@ func (s *Service) updateConnectedETH1(state bool) {
s.updateBeaconNodeStats()
}
// IsExecutionClientSyncing returns true if the execution client is syncing, otherwise false.
func (s *Service) IsExecutionClientSyncing(ctx context.Context) (bool, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.IsExecutionClientSyncing")
defer span.End()
var result bool
err := s.rpcClient.CallContext(ctx, &result, EthSyncing)
if err != nil {
return result, handleRPCError(err)
}
return result, nil
}
// refers to the latest eth1 block which follows the condition: eth1_timestamp +
// SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= current_unix_time
func (s *Service) followedBlockHeight(ctx context.Context) (uint64, error) {

View File

@@ -131,6 +131,10 @@ func (m *Chain) ExecutionClientConnectionErr() error {
return m.CurrError
}
func (m *Chain) IsExecutionClientSyncing(_ context.Context) (bool, error) {
return false, nil
}
func (m *Chain) ETH1Endpoints() []string {
return m.Endpoints
}

View File

@@ -141,7 +141,14 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro
}
jc, fc = f.store.pullTips(state, node, jc, fc)
return f.updateCheckpoints(ctx, jc, fc)
if err := f.updateCheckpoints(ctx, jc, fc); err != nil {
_, remErr := f.store.removeNode(ctx, node)
if remErr != nil {
log.WithError(remErr).Error("could not remove node")
}
return errors.Wrap(err, "could not update checkpoints")
}
return nil
}
// updateCheckpoints update the checkpoints when inserting a new node.

View File

@@ -3,6 +3,7 @@ package doublylinkedtree
import (
"context"
"encoding/binary"
"errors"
"testing"
"time"
@@ -887,3 +888,16 @@ func TestForkchoiceParentRoot(t *testing.T) {
require.NoError(t, err)
require.Equal(t, zeroHash, root)
}
func TestForkChoice_CleanupInserting(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 2)
f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) {
return f.justifiedBalances, errors.New("mock err")
})
require.NoError(t, err)
require.NotNil(t, f.InsertNode(ctx, st, blkRoot))
require.Equal(t, false, f.HasNode(blkRoot))
}

View File

@@ -107,7 +107,9 @@ func (s *Store) insert(ctx context.Context,
s.headNode = n
s.highestReceivedNode = n
} else {
return n, errInvalidParentRoot
delete(s.nodeByRoot, root)
delete(s.nodeByPayload, payloadHash)
return nil, errInvalidParentRoot
}
} else {
parent.children = append(parent.children, n)
@@ -128,7 +130,11 @@ func (s *Store) insert(ctx context.Context,
jEpoch := s.justifiedCheckpoint.Epoch
fEpoch := s.finalizedCheckpoint.Epoch
if err := s.treeRootNode.updateBestDescendant(ctx, jEpoch, fEpoch, slots.ToEpoch(currentSlot)); err != nil {
return n, err
_, remErr := s.removeNode(ctx, n)
if remErr != nil {
log.WithError(remErr).Error("could not remove node")
}
return nil, errors.Wrap(err, "could not update best descendants")
}
}
// Update metrics.

View File

@@ -525,3 +525,12 @@ func TestStore_TargetRootForEpoch(t *testing.T) {
require.NoError(t, err)
require.Equal(t, root4, target)
}
func TestStore_CleanupInserting(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
st, blkRoot, err := prepareForkchoiceState(ctx, 1, indexToHash(1), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
require.NoError(t, err)
require.NotNil(t, f.InsertNode(ctx, st, blkRoot))
require.Equal(t, false, f.HasNode(blkRoot))
}

View File

@@ -28,6 +28,7 @@ go_library(
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_libp2p_go_libp2p//core/peer:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_grpc//:go_default_library",
],
)

View File

@@ -14,6 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/network/httputil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/eth/v1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
log "github.com/sirupsen/logrus"
)
var (
@@ -37,6 +38,18 @@ func (s *Server) GetSyncStatus(w http.ResponseWriter, r *http.Request) {
return
}
isElOnline := s.ExecutionChainInfoFetcher.ExecutionClientConnected()
if !isElOnline {
isSyncing, err := s.ExecutionChainInfoFetcher.IsExecutionClientSyncing(ctx)
if err != nil {
log.WithField("error", err.Error()).Debug("failed to get execution client sync status")
} else if isSyncing {
isElOnline = true
} else {
log.Debug("execution client is not syncing and not connected")
}
}
headSlot := s.HeadFetcher.HeadSlot()
response := &structs.SyncStatusResponse{
Data: &structs.SyncStatusResponseData{
@@ -44,7 +57,7 @@ func (s *Server) GetSyncStatus(w http.ResponseWriter, r *http.Request) {
SyncDistance: strconv.FormatUint(uint64(s.GenesisTimeFetcher.CurrentSlot()-headSlot), 10),
IsSyncing: s.SyncChecker.Syncing(),
IsOptimistic: isOptimistic,
ElOffline: !s.ExecutionChainInfoFetcher.ExecutionClientConnected(),
ElOffline: !isElOnline,
},
}
httputil.WriteJson(w, response)

View File

@@ -56,7 +56,6 @@ func TestSyncStatus(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetSyncStatus(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
@@ -67,6 +66,26 @@ func TestSyncStatus(t *testing.T) {
assert.Equal(t, "10", resp.Data.SyncDistance)
assert.Equal(t, true, resp.Data.IsSyncing)
assert.Equal(t, true, resp.Data.IsOptimistic)
assert.Equal(t, true, resp.Data.ElOffline)
// if execution client is syncing return offline false
s.ExecutionChainInfoFetcher = &testutil.MockExecutionChainInfoFetcher{Syncing: true}
writer = httptest.NewRecorder()
s.GetSyncStatus(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp = &structs.SyncStatusResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.NotNil(t, resp)
assert.Equal(t, false, resp.Data.ElOffline)
// if execution client is connected offline should be false
s.ExecutionChainInfoFetcher = &testutil.MockExecutionChainInfoFetcher{Connected: true}
writer = httptest.NewRecorder()
s.GetSyncStatus(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp = &structs.SyncStatusResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.NotNil(t, resp)
assert.Equal(t, false, resp.Data.ElOffline)
}

View File

@@ -1,6 +1,7 @@
package testutil
import (
"context"
"math/big"
)
@@ -8,14 +9,16 @@ import (
type MockExecutionChainInfoFetcher struct {
CurrEndpoint string
CurrError error
Syncing bool
Connected bool
}
func (*MockExecutionChainInfoFetcher) GenesisExecutionChainInfo() (uint64, *big.Int) {
return uint64(0), &big.Int{}
}
func (*MockExecutionChainInfoFetcher) ExecutionClientConnected() bool {
return true
func (m *MockExecutionChainInfoFetcher) ExecutionClientConnected() bool {
return m.Connected
}
func (m *MockExecutionChainInfoFetcher) ExecutionClientEndpoint() string {
@@ -25,3 +28,7 @@ func (m *MockExecutionChainInfoFetcher) ExecutionClientEndpoint() string {
func (m *MockExecutionChainInfoFetcher) ExecutionClientConnectionErr() error {
return m.CurrError
}
func (m *MockExecutionChainInfoFetcher) IsExecutionClientSyncing(_ context.Context) (bool, error) {
return m.Syncing, nil
}