mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 08:49:29 +00:00
Merge 21b153df43 into 12eabbd76d
This commit is contained in:
commit
7a8ee0f607
8 changed files with 105 additions and 2 deletions
|
|
@ -423,6 +423,10 @@ func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||||
return prog
|
return prog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) ConsensusReady() bool {
|
||||||
|
return b.eth.ConsensusReady()
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return b.gpo.SuggestTipCap(ctx)
|
return b.gpo.SuggestTipCap(ctx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
|
@ -123,6 +124,21 @@ type Ethereum struct {
|
||||||
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
|
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
|
||||||
|
|
||||||
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
|
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
|
||||||
|
|
||||||
|
clExpected atomic.Bool // Set when catalyst.Register attaches the Engine API
|
||||||
|
clContacted atomic.Bool // Set on first Engine API call (newPayload / FCU)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkCLExpected and MarkCLContacted are setters for the two clXxx flags;
|
||||||
|
// catalyst calls them from its package and so cannot reach the fields directly.
|
||||||
|
func (s *Ethereum) MarkCLExpected() { s.clExpected.Store(true) }
|
||||||
|
func (s *Ethereum) MarkCLContacted() { s.clContacted.Store(true) }
|
||||||
|
|
||||||
|
// ConsensusReady reports whether eth_syncing should be allowed to return false.
|
||||||
|
// On nodes without an Engine API, always true. On nodes that expect a CL, true
|
||||||
|
// only after the CL has driven the node at least once.
|
||||||
|
func (s *Ethereum) ConsensusReady() bool {
|
||||||
|
return !s.clExpected.Load() || s.clContacted.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
|
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ import (
|
||||||
|
|
||||||
// Register adds the engine API and related APIs to the full node.
|
// Register adds the engine API and related APIs to the full node.
|
||||||
func Register(stack *node.Node, backend *eth.Ethereum) error {
|
func Register(stack *node.Node, backend *eth.Ethereum) error {
|
||||||
|
backend.MarkCLExpected()
|
||||||
stack.RegisterAPIs([]rpc.API{
|
stack.RegisterAPIs([]rpc.API{
|
||||||
newTestingAPI(backend),
|
newTestingAPI(backend),
|
||||||
{
|
{
|
||||||
|
|
@ -251,6 +252,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(ctx context.Context, update engine.Fo
|
||||||
}
|
}
|
||||||
// Stash away the last update to warn the user if the beacon client goes offline
|
// Stash away the last update to warn the user if the beacon client goes offline
|
||||||
api.lastForkchoiceUpdate.Store(time.Now().Unix())
|
api.lastForkchoiceUpdate.Store(time.Now().Unix())
|
||||||
|
api.eth.MarkCLContacted()
|
||||||
|
|
||||||
// Check whether we have the block yet in our database or not. If not, we'll
|
// Check whether we have the block yet in our database or not. If not, we'll
|
||||||
// need to either trigger a sync, or to reject this forkchoice update for a
|
// need to either trigger a sync, or to reject this forkchoice update for a
|
||||||
|
|
@ -875,6 +877,7 @@ func (api *ConsensusAPI) newPayload(ctx context.Context, params engine.Executabl
|
||||||
}
|
}
|
||||||
// Stash away the last update to warn the user if the beacon client goes offline
|
// Stash away the last update to warn the user if the beacon client goes offline
|
||||||
api.lastNewPayloadUpdate.Store(time.Now().Unix())
|
api.lastNewPayloadUpdate.Store(time.Now().Unix())
|
||||||
|
api.eth.MarkCLContacted()
|
||||||
|
|
||||||
// If we already have the block locally, ignore the entire execution and just
|
// If we already have the block locally, ignore the entire execution and just
|
||||||
// return a fake success.
|
// return a fake success.
|
||||||
|
|
|
||||||
|
|
@ -161,8 +161,10 @@ func (api *EthereumAPI) BaseFee(ctx context.Context) *hexutil.Big {
|
||||||
func (api *EthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
|
func (api *EthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
|
||||||
progress := api.b.SyncProgress(ctx)
|
progress := api.b.SyncProgress(ctx)
|
||||||
|
|
||||||
// Return not syncing if the synchronisation already completed
|
// Don't claim "synced" until the CL has driven us at least once (post-merge
|
||||||
if progress.Done() {
|
// nodes with Engine API attached). Backends without a CL report ready
|
||||||
|
// immediately via ConsensusReady. Refs #33687.
|
||||||
|
if progress.Done() && api.b.ConsensusReady() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
// Otherwise gather the block sync stats
|
// Otherwise gather the block sync stats
|
||||||
|
|
|
||||||
|
|
@ -493,6 +493,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E
|
||||||
func (b testBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
func (b testBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
return ethereum.SyncProgress{}
|
return ethereum.SyncProgress{}
|
||||||
}
|
}
|
||||||
|
func (b testBackend) ConsensusReady() bool { return true }
|
||||||
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return big.NewInt(0), nil
|
return big.NewInt(0), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import (
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
// General Ethereum API
|
// General Ethereum API
|
||||||
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||||
|
ConsensusReady() bool
|
||||||
|
|
||||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||||
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error)
|
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error)
|
||||||
|
|
|
||||||
75
internal/ethapi/syncing_test.go
Normal file
75
internal/ethapi/syncing_test.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2026 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package ethapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type syncingBackend struct {
|
||||||
|
Backend
|
||||||
|
progress ethereum.SyncProgress
|
||||||
|
ready bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *syncingBackend) SyncProgress(_ context.Context) ethereum.SyncProgress { return b.progress }
|
||||||
|
func (b *syncingBackend) ConsensusReady() bool { return b.ready }
|
||||||
|
|
||||||
|
// Issue #33687: a Done downloader but no CL handshake yet must report syncing.
|
||||||
|
func TestSyncingBeforeCLContact(t *testing.T) {
|
||||||
|
api := NewEthereumAPI(&syncingBackend{progress: ethereum.SyncProgress{}, ready: false})
|
||||||
|
res, err := api.Syncing(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Syncing returned error: %v", err)
|
||||||
|
}
|
||||||
|
if v, ok := res.(bool); ok && !v {
|
||||||
|
t.Fatal("expected truthy syncing payload before CL handshake, got false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncingAfterCLContact(t *testing.T) {
|
||||||
|
api := NewEthereumAPI(&syncingBackend{progress: ethereum.SyncProgress{}, ready: true})
|
||||||
|
res, err := api.Syncing(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Syncing returned error: %v", err)
|
||||||
|
}
|
||||||
|
if v, ok := res.(bool); !ok || v {
|
||||||
|
t.Fatalf("expected false after CL handshake when sync is done, got %v", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active sync stays truthy regardless of the CL gate.
|
||||||
|
func TestSyncingActiveSyncIgnoresCLGate(t *testing.T) {
|
||||||
|
api := NewEthereumAPI(&syncingBackend{
|
||||||
|
progress: ethereum.SyncProgress{
|
||||||
|
StartingBlock: 100,
|
||||||
|
CurrentBlock: 150,
|
||||||
|
HighestBlock: 200,
|
||||||
|
},
|
||||||
|
ready: false,
|
||||||
|
})
|
||||||
|
res, err := api.Syncing(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Syncing returned error: %v", err)
|
||||||
|
}
|
||||||
|
if _, ok := res.(map[string]interface{}); !ok {
|
||||||
|
t.Fatalf("expected progress map during active sync, got %T", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -327,6 +327,7 @@ func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
||||||
func (b *backendMock) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
func (b *backendMock) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
return ethereum.SyncProgress{}
|
return ethereum.SyncProgress{}
|
||||||
}
|
}
|
||||||
|
func (b *backendMock) ConsensusReady() bool { return true }
|
||||||
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
|
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
|
||||||
return nil, nil, nil, nil, nil, nil, nil
|
return nil, nil, nil, nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue