all: add debug_syncTarget as an API method

This commit is contained in:
Jared Wasinger 2025-07-08 17:14:15 +09:00
parent e71487b033
commit cc7bc75829
6 changed files with 43 additions and 49 deletions

View file

@ -262,13 +262,14 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
}
// Configure full-sync tester service if requested
if ctx.IsSet(utils.SyncTargetFlag.Name) {
hex := hexutil.MustDecode(ctx.String(utils.SyncTargetFlag.Name))
if len(hex) != common.HashLength {
utils.Fatalf("invalid sync target length: have %d, want %d", len(hex), common.HashLength)
}
utils.RegisterFullSyncTester(stack, eth, common.BytesToHash(hex), ctx.Bool(utils.ExitWhenSyncedFlag.Name))
eth.SyncOverride().SyncTarget(common.BytesToHash(hex), ctx.Bool(utils.ExitWhenSyncedFlag.Name))
}
if ctx.IsSet(utils.DeveloperFlag.Name) {

View file

@ -49,7 +49,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
@ -1997,12 +1996,6 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
return filterSystem
}
// RegisterFullSyncTester adds the full-sync tester service into node.
func RegisterFullSyncTester(stack *node.Node, eth *eth.Ethereum, target common.Hash, exitWhenSynced bool) {
catalyst.RegisterFullSyncTester(stack, eth, target, exitWhenSynced)
log.Info("Registered full-sync tester", "hash", target, "exitWhenSynced", exitWhenSynced)
}
// SetupMetrics configures the metrics system.
func SetupMetrics(cfg *metrics.Config) {
if !cfg.Enabled {

View file

@ -443,3 +443,8 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
}
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
}
// SetSyncTarget initiates a full-sync to the target block hash
func (api *DebugAPI) SyncTarget(target common.Hash) {
api.eth.SyncOverride().SyncTarget(target, false)
}

View file

@ -124,6 +124,8 @@ type Ethereum struct {
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
syncOverride SyncOverride // allows the sync target to be overridden
}
// New creates a new Ethereum object (including the initialisation of the common Ethereum object),
@ -202,6 +204,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
discmix: enode.NewFairMix(discmixTimeout),
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
}
eth.syncOverride = SyncOverride{
stack: stack,
backend: eth,
closed: make(chan struct{}),
}
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
var dbVer = "<nil>"
if bcVersion != nil {
@ -576,6 +583,8 @@ func (s *Ethereum) Stop() error {
s.chainDb.Close()
s.eventMux.Stop()
s.syncOverride.Stop()
return nil
}
@ -604,3 +613,9 @@ func (s *Ethereum) SyncMode() ethconfig.SyncMode {
// Nope, we're really full syncing
return ethconfig.FullSync
}
// SyncOverride returns a SyncOverride instance which is used to force the node
// to sync to a target block hash.
func (s *Ethereum) SyncOverride() *SyncOverride {
return &s.syncOverride
}

View file

@ -14,57 +14,35 @@
// 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 catalyst
package eth
import (
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
)
// FullSyncTester is an auxiliary service that allows Geth to perform full sync
// SyncOverride is an auxiliary service that allows Geth to perform full sync
// alone without consensus-layer attached. Users must specify a valid block hash
// as the sync target.
//
// This tester can be applied to different networks, no matter it's pre-merge or
// post-merge, but only for full-sync.
type FullSyncTester struct {
stack *node.Node
backend *eth.Ethereum
target common.Hash
closed chan struct{}
wg sync.WaitGroup
exitWhenSynced bool
type SyncOverride struct {
stack *node.Node
backend *Ethereum
closed chan struct{}
}
// RegisterFullSyncTester registers the full-sync tester service into the node
// stack for launching and stopping the service controlled by node.
func RegisterFullSyncTester(stack *node.Node, backend *eth.Ethereum, target common.Hash, exitWhenSynced bool) (*FullSyncTester, error) {
cl := &FullSyncTester{
stack: stack,
backend: backend,
target: target,
closed: make(chan struct{}),
exitWhenSynced: exitWhenSynced,
}
stack.RegisterLifecycle(cl)
return cl, nil
}
// Start launches the beacon sync with provided sync target.
func (tester *FullSyncTester) Start() error {
tester.wg.Add(1)
// SyncTarget sets the target of the client sync to the given block hash
func (f *SyncOverride) SyncTarget(target common.Hash, exitWhenSynced bool) {
go func() {
defer tester.wg.Done()
// Trigger beacon sync with the provided block hash as trusted
// chain head.
err := tester.backend.Downloader().BeaconDevSync(ethconfig.FullSync, tester.target, tester.closed)
err := f.backend.Downloader().BeaconDevSync(ethconfig.FullSync, target, f.closed)
if err != nil {
log.Info("Failed to trigger beacon sync", "err", err)
}
@ -76,28 +54,25 @@ func (tester *FullSyncTester) Start() error {
select {
case <-ticker.C:
// Stop in case the target block is already stored locally.
if block := tester.backend.BlockChain().GetBlockByHash(tester.target); block != nil {
if block := f.backend.BlockChain().GetBlockByHash(target); block != nil {
log.Info("Full-sync target reached", "number", block.NumberU64(), "hash", block.Hash())
if tester.exitWhenSynced {
go tester.stack.Close() // async since we need to close ourselves
log.Info("Terminating the node")
if exitWhenSynced {
log.Info("Terminating node")
f.stack.Close()
}
return
}
case <-tester.closed:
case <-f.closed:
return
}
}
}()
return nil
}
// Stop stops the full-sync tester to stop all background activities.
// This function can only be called for one time.
func (tester *FullSyncTester) Stop() error {
close(tester.closed)
tester.wg.Wait()
func (f *SyncOverride) Stop() error {
close(f.closed)
return nil
}

View file

@ -468,6 +468,11 @@ web3._extend({
call: 'debug_getTrieFlushInterval',
params: 0
}),
new web3._extend.Method({
name: 'syncTarget',
call: 'debug_syncTarget',
params: 1
}),
],
properties: []
});