mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 08:49:29 +00:00
The previous version of this change unconditionally returned the progress
map until the consensus client had driven the node at least once. That
broke ethclient.TestEthClient/StatusFunctions and any other backend that
runs without a consensus client (in-process tests, --dev mode without
catalyst, light/legacy backends), where reporting "syncing" forever is
clearly wrong.
Split the gate into two flags:
- clExpected: set in eth/catalyst.Register, the only entry point that
attaches the Engine API to a node. If a backend never calls Register,
it is not paired with a consensus client.
- clContacted: set on every Engine API call (forkchoiceUpdated and
newPayload), unchanged from before.
Replace ConsensusContacted on the Backend interface with ConsensusReady,
which folds the two flags into the question eth_syncing actually wants
answered: "is the synced claim meaningful right now?" Backends that
never expect a CL answer yes immediately, preserving legacy behavior.
Backends that do expect one answer yes only after the first FCU/NewPayload.
- eth/backend.go: clExpected, clContacted, MarkConsensusExpected,
MarkConsensusContacted, ConsensusReady on (*Ethereum)
- eth/catalyst/api.go: backend.MarkConsensusExpected() in Register
- eth/api_backend.go: ConsensusReady delegates to (*Ethereum)
- internal/ethapi/backend.go: rename interface method to ConsensusReady
- internal/ethapi/api.go: Syncing checks ConsensusReady
- internal/ethapi/{api_test,transaction_args_test}.go: rename the test
mock methods (default to true so existing tests are unaffected)
- internal/ethapi/syncing_test.go: rename the helper field; tests now
cover (a) CL-paired node before handshake -> truthy, (b) ready node
-> false, (c) active sync -> progress map regardless of gate
Refs #33687.
97 lines
3.6 KiB
Go
97 lines
3.6 KiB
Go
// 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"
|
|
)
|
|
|
|
// syncingBackend is a minimal Backend embedding that only implements the two
|
|
// methods Syncing calls. Embedding the interface avoids pulling in the full
|
|
// testBackend setup just to flip a single bool.
|
|
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 }
|
|
|
|
// TestSyncingReportsBeforeConsensusContact verifies that on a CL-paired node
|
|
// (ConsensusReady false), eth_syncing returns a truthy progress object even
|
|
// when the local downloader believes itself to be done. This is the bug fix
|
|
// for issue #33687: a freshly started node must not advertise itself as
|
|
// "synced" before the consensus client has actually driven it.
|
|
func TestSyncingReportsBeforeConsensusContact(t *testing.T) {
|
|
api := NewEthereumAPI(&syncingBackend{
|
|
// progress.Done() returns true on a zero-valued struct because all
|
|
// remaining counters are zero and CurrentBlock >= HighestBlock.
|
|
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")
|
|
}
|
|
}
|
|
|
|
// TestSyncingReportsFalseAfterConsensusContact verifies that once the
|
|
// consensus layer has handshaken at least once (or the backend does not
|
|
// expect one) and progress.Done() is true, eth_syncing reports false.
|
|
func TestSyncingReportsFalseAfterConsensusContact(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)
|
|
}
|
|
v, ok := res.(bool)
|
|
if !ok || v {
|
|
t.Fatalf("expected false after CL handshake when sync is done, got %v", res)
|
|
}
|
|
}
|
|
|
|
// TestSyncingReportsActiveSyncEvenWithoutConsensusContact verifies that when
|
|
// the downloader is actively syncing, eth_syncing returns the progress map
|
|
// regardless of the CL gate. This preserves the legacy semantics for the case
|
|
// the issue thread did not affect.
|
|
func TestSyncingReportsActiveSyncEvenWithoutConsensusContact(t *testing.T) {
|
|
api := NewEthereumAPI(&syncingBackend{
|
|
progress: ethereum.SyncProgress{
|
|
StartingBlock: 100,
|
|
CurrentBlock: 150,
|
|
HighestBlock: 200, // CurrentBlock < HighestBlock => Done()=false
|
|
},
|
|
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)
|
|
}
|
|
}
|