diff --git a/cmd/geth/config.go b/cmd/geth/config.go index d7c354ff9f2..39498fc1749 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -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) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5c592549c1e..d1394256db1 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -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 { diff --git a/eth/api_debug.go b/eth/api_debug.go index 188dee11aa5..0afaf59d522 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -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) +} diff --git a/eth/backend.go b/eth/backend.go index 80ffd301cee..8a1e9f31818 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -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 = "" 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 +} diff --git a/eth/catalyst/tester.go b/eth/sync_override.go similarity index 55% rename from eth/catalyst/tester.go rename to eth/sync_override.go index 10a480837e2..3cd99e15dd9 100644 --- a/eth/catalyst/tester.go +++ b/eth/sync_override.go @@ -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 . -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 } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a6d93fc1c53..cefbd90bd97 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -468,6 +468,11 @@ web3._extend({ call: 'debug_getTrieFlushInterval', params: 0 }), + new web3._extend.Method({ + name: 'syncTarget', + call: 'debug_syncTarget', + params: 1 + }), ], properties: [] });