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 != "" { if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL) utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
} }
// Configure full-sync tester service if requested // Configure full-sync tester service if requested
if ctx.IsSet(utils.SyncTargetFlag.Name) { if ctx.IsSet(utils.SyncTargetFlag.Name) {
hex := hexutil.MustDecode(ctx.String(utils.SyncTargetFlag.Name)) hex := hexutil.MustDecode(ctx.String(utils.SyncTargetFlag.Name))
if len(hex) != common.HashLength { if len(hex) != common.HashLength {
utils.Fatalf("invalid sync target length: have %d, want %d", 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) { 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"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth" "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/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
@ -1997,12 +1996,6 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
return filterSystem 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. // SetupMetrics configures the metrics system.
func SetupMetrics(cfg *metrics.Config) { func SetupMetrics(cfg *metrics.Config) {
if !cfg.Enabled { if !cfg.Enabled {

View file

@ -443,3 +443,8 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
} }
return api.eth.blockchain.GetTrieFlushInterval().String(), nil 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) 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
syncOverride SyncOverride // allows the sync target to be overridden
} }
// 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),
@ -202,6 +204,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
discmix: enode.NewFairMix(discmixTimeout), discmix: enode.NewFairMix(discmixTimeout),
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb), shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
} }
eth.syncOverride = SyncOverride{
stack: stack,
backend: eth,
closed: make(chan struct{}),
}
bcVersion := rawdb.ReadDatabaseVersion(chainDb) bcVersion := rawdb.ReadDatabaseVersion(chainDb)
var dbVer = "<nil>" var dbVer = "<nil>"
if bcVersion != nil { if bcVersion != nil {
@ -576,6 +583,8 @@ func (s *Ethereum) Stop() error {
s.chainDb.Close() s.chainDb.Close()
s.eventMux.Stop() s.eventMux.Stop()
s.syncOverride.Stop()
return nil return nil
} }
@ -604,3 +613,9 @@ func (s *Ethereum) SyncMode() ethconfig.SyncMode {
// Nope, we're really full syncing // Nope, we're really full syncing
return ethconfig.FullSync 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 // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package catalyst package eth
import ( import (
"sync"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "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 // alone without consensus-layer attached. Users must specify a valid block hash
// as the sync target. // as the sync target.
// //
// This tester can be applied to different networks, no matter it's pre-merge or // This tester can be applied to different networks, no matter it's pre-merge or
// post-merge, but only for full-sync. // post-merge, but only for full-sync.
type FullSyncTester struct { type SyncOverride struct {
stack *node.Node stack *node.Node
backend *eth.Ethereum backend *Ethereum
target common.Hash closed chan struct{}
closed chan struct{}
wg sync.WaitGroup
exitWhenSynced bool
} }
// RegisterFullSyncTester registers the full-sync tester service into the node // SyncTarget sets the target of the client sync to the given block hash
// stack for launching and stopping the service controlled by node. func (f *SyncOverride) SyncTarget(target common.Hash, exitWhenSynced bool) {
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)
go func() { go func() {
defer tester.wg.Done()
// Trigger beacon sync with the provided block hash as trusted // Trigger beacon sync with the provided block hash as trusted
// chain head. // 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 { if err != nil {
log.Info("Failed to trigger beacon sync", "err", err) log.Info("Failed to trigger beacon sync", "err", err)
} }
@ -76,28 +54,25 @@ func (tester *FullSyncTester) Start() error {
select { select {
case <-ticker.C: case <-ticker.C:
// Stop in case the target block is already stored locally. // 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()) log.Info("Full-sync target reached", "number", block.NumberU64(), "hash", block.Hash())
if exitWhenSynced {
if tester.exitWhenSynced { log.Info("Terminating node")
go tester.stack.Close() // async since we need to close ourselves f.stack.Close()
log.Info("Terminating the node")
} }
return return
} }
case <-tester.closed: case <-f.closed:
return return
} }
} }
}() }()
return nil
} }
// Stop stops the full-sync tester to stop all background activities. // Stop stops the full-sync tester to stop all background activities.
// This function can only be called for one time. // This function can only be called for one time.
func (tester *FullSyncTester) Stop() error { func (f *SyncOverride) Stop() error {
close(tester.closed) close(f.closed)
tester.wg.Wait()
return nil return nil
} }

View file

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