diff --git a/eth/config.go b/eth/config.go
index 77d03e95697d7cc09770fa48d706940d465bc138..446467d3646271c9532f7d0687405e83a5ef8e37 100644
--- a/eth/config.go
+++ b/eth/config.go
@@ -127,11 +127,12 @@ type Config struct {
 	Whitelist map[uint64]common.Hash `toml:"-"`
 
 	// Light client options
-	LightServ    int  `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
-	LightIngress int  `toml:",omitempty"` // Incoming bandwidth limit for light servers
-	LightEgress  int  `toml:",omitempty"` // Outgoing bandwidth limit for light servers
-	LightPeers   int  `toml:",omitempty"` // Maximum number of LES client peers
-	LightNoPrune bool `toml:",omitempty"` // Whether to disable light chain pruning
+	LightServ          int  `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
+	LightIngress       int  `toml:",omitempty"` // Incoming bandwidth limit for light servers
+	LightEgress        int  `toml:",omitempty"` // Outgoing bandwidth limit for light servers
+	LightPeers         int  `toml:",omitempty"` // Maximum number of LES client peers
+	LightNoPrune       bool `toml:",omitempty"` // Whether to disable light chain pruning
+	SyncFromCheckpoint bool `toml:",omitempty"` // Whether to sync the header chain from the configured checkpoint
 
 	// Ultra Light client options
 	UltraLightServers      []string `toml:",omitempty"` // List of trusted ultra light servers
diff --git a/eth/gen_config.go b/eth/gen_config.go
index dd04635eeed281a01dce4a32ecd56bbe5b221d23..e68b29ce5e39c80411490277996f55130d754f15 100644
--- a/eth/gen_config.go
+++ b/eth/gen_config.go
@@ -21,6 +21,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
 		NetworkId               uint64
 		SyncMode                downloader.SyncMode
 		EthDiscoveryURLs        []string
+		SnapDiscoveryURLs       []string
 		NoPruning               bool
 		NoPrefetch              bool
 		TxLookupLimit           uint64                 `toml:",omitempty"`
@@ -30,6 +31,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
 		LightEgress             int                    `toml:",omitempty"`
 		LightPeers              int                    `toml:",omitempty"`
 		LightNoPrune            bool                   `toml:",omitempty"`
+		SyncFromCheckpoint      bool                   `toml:",omitempty"`
 		UltraLightServers       []string               `toml:",omitempty"`
 		UltraLightFraction      int                    `toml:",omitempty"`
 		UltraLightOnlyAnnounce  bool                   `toml:",omitempty"`
@@ -62,6 +64,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
 	enc.NetworkId = c.NetworkId
 	enc.SyncMode = c.SyncMode
 	enc.EthDiscoveryURLs = c.EthDiscoveryURLs
+	enc.SnapDiscoveryURLs = c.SnapDiscoveryURLs
 	enc.NoPruning = c.NoPruning
 	enc.NoPrefetch = c.NoPrefetch
 	enc.TxLookupLimit = c.TxLookupLimit
@@ -71,6 +74,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
 	enc.LightEgress = c.LightEgress
 	enc.LightPeers = c.LightPeers
 	enc.LightNoPrune = c.LightNoPrune
+	enc.SyncFromCheckpoint = c.SyncFromCheckpoint
 	enc.UltraLightServers = c.UltraLightServers
 	enc.UltraLightFraction = c.UltraLightFraction
 	enc.UltraLightOnlyAnnounce = c.UltraLightOnlyAnnounce
@@ -107,6 +111,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
 		NetworkId               *uint64
 		SyncMode                *downloader.SyncMode
 		EthDiscoveryURLs        []string
+		SnapDiscoveryURLs       []string
 		NoPruning               *bool
 		NoPrefetch              *bool
 		TxLookupLimit           *uint64                `toml:",omitempty"`
@@ -116,6 +121,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
 		LightEgress             *int                   `toml:",omitempty"`
 		LightPeers              *int                   `toml:",omitempty"`
 		LightNoPrune            *bool                  `toml:",omitempty"`
+		SyncFromCheckpoint      *bool                  `toml:",omitempty"`
 		UltraLightServers       []string               `toml:",omitempty"`
 		UltraLightFraction      *int                   `toml:",omitempty"`
 		UltraLightOnlyAnnounce  *bool                  `toml:",omitempty"`
@@ -159,6 +165,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
 	if dec.EthDiscoveryURLs != nil {
 		c.EthDiscoveryURLs = dec.EthDiscoveryURLs
 	}
+	if dec.SnapDiscoveryURLs != nil {
+		c.SnapDiscoveryURLs = dec.SnapDiscoveryURLs
+	}
 	if dec.NoPruning != nil {
 		c.NoPruning = *dec.NoPruning
 	}
@@ -186,6 +195,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
 	if dec.LightNoPrune != nil {
 		c.LightNoPrune = *dec.LightNoPrune
 	}
+	if dec.SyncFromCheckpoint != nil {
+		c.SyncFromCheckpoint = *dec.SyncFromCheckpoint
+	}
 	if dec.UltraLightServers != nil {
 		c.UltraLightServers = dec.UltraLightServers
 	}
diff --git a/les/client_handler.go b/les/client_handler.go
index 6de57669615dfa3d1dffbd95b1ed86e9bc660221..6cd786cda0a97c6143dfbf239f07666ed50eaff8 100644
--- a/les/client_handler.go
+++ b/les/client_handler.go
@@ -44,9 +44,12 @@ type clientHandler struct {
 	downloader *downloader.Downloader
 	backend    *LightEthereum
 
-	closeCh  chan struct{}
-	wg       sync.WaitGroup // WaitGroup used to track all connected peers.
-	syncDone func()         // Test hooks when syncing is done.
+	closeCh chan struct{}
+	wg      sync.WaitGroup // WaitGroup used to track all connected peers.
+
+	// Hooks used in the testing
+	syncStart func(header *types.Header) // Hook called when the syncing is started
+	syncEnd   func(header *types.Header) // Hook called when the syncing is done
 }
 
 func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.TrustedCheckpoint, backend *LightEthereum) *clientHandler {
diff --git a/les/commons.go b/les/commons.go
index 003e196d2b824021d747ccc6954c91b0efb176c5..8de1057d2664faa8e7a4cc8ba3c3ebb12478444f 100644
--- a/les/commons.go
+++ b/les/commons.go
@@ -157,17 +157,17 @@ func (c *lesCommons) setupOracle(node *node.Node, genesis common.Hash, ethconfig
 		config = params.CheckpointOracles[genesis]
 	}
 	if config == nil {
-		log.Info("Checkpoint registrar is not enabled")
+		log.Info("Checkpoint oracle is not enabled")
 		return nil
 	}
 	if config.Address == (common.Address{}) || uint64(len(config.Signers)) < config.Threshold {
-		log.Warn("Invalid checkpoint registrar config")
+		log.Warn("Invalid checkpoint oracle config")
 		return nil
 	}
 	oracle := checkpointoracle.New(config, c.localCheckpoint)
 	rpcClient, _ := node.Attach()
 	client := ethclient.NewClient(rpcClient)
 	oracle.Start(client)
-	log.Info("Configured checkpoint registrar", "address", config.Address, "signers", len(config.Signers), "threshold", config.Threshold)
+	log.Info("Configured checkpoint oracle", "address", config.Address, "signers", len(config.Signers), "threshold", config.Threshold)
 	return oracle
 }
diff --git a/les/sync.go b/les/sync.go
index ad3a0e0f3cdc27247389ce229a8fb62f1f4034d5..fa5ef4ff820098203cfcd3e8d37c385217bb6de4 100644
--- a/les/sync.go
+++ b/les/sync.go
@@ -26,6 +26,7 @@ import (
 	"github.com/ethereum/go-ethereum/eth/downloader"
 	"github.com/ethereum/go-ethereum/light"
 	"github.com/ethereum/go-ethereum/log"
+	"github.com/ethereum/go-ethereum/params"
 )
 
 var errInvalidCheckpoint = errors.New("invalid advertised checkpoint")
@@ -98,22 +99,33 @@ func (h *clientHandler) synchronise(peer *serverPeer) {
 	if currentTd != nil && peer.Td().Cmp(currentTd) < 0 {
 		return
 	}
-	// Recap the checkpoint.
-	//
-	// The light client may be connected to several different versions of the server.
-	// (1) Old version server which can not provide stable checkpoint in the handshake packet.
-	//     => Use hardcoded checkpoint or empty checkpoint
-	// (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network)
-	//     => Use hardcoded checkpoint or empty checkpoint
-	// (3) New version server but the provided stable checkpoint is even lower than the hardcoded one.
-	//     => Use hardcoded checkpoint
+	// Recap the checkpoint. The light client may be connected to several different
+	// versions of the server.
+	// (1) Old version server which can not provide stable checkpoint in the
+	//     handshake packet.
+	//     => Use local checkpoint or empty checkpoint
+	// (2) New version server but simple checkpoint syncing is not enabled
+	//     (e.g. mainnet, new testnet or private network)
+	//     => Use local checkpoint or empty checkpoint
+	// (3) New version server but the provided stable checkpoint is even lower
+	//     than the local one.
+	//     => Use local checkpoint
 	// (4) New version server with valid and higher stable checkpoint
 	//     => Use provided checkpoint
-	var checkpoint = &peer.checkpoint
-	var hardcoded bool
+	var (
+		local      bool
+		checkpoint = &peer.checkpoint
+	)
 	if h.checkpoint != nil && h.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex {
-		checkpoint = h.checkpoint // Use the hardcoded one.
-		hardcoded = true
+		local, checkpoint = true, h.checkpoint
+	}
+	// Replace the checkpoint with locally configured one If it's required by
+	// users. Nil checkpoint means synchronization from the scratch.
+	if h.backend.config.SyncFromCheckpoint {
+		local, checkpoint = true, h.backend.config.Checkpoint
+		if h.backend.config.Checkpoint == nil {
+			checkpoint = &params.TrustedCheckpoint{}
+		}
 	}
 	// Determine whether we should run checkpoint syncing or normal light syncing.
 	//
@@ -121,7 +133,7 @@ func (h *clientHandler) synchronise(peer *serverPeer) {
 	//
 	// 1. The checkpoint is empty
 	// 2. The latest head block of the local chain is above the checkpoint.
-	// 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint)
+	// 3. The checkpoint is local(replaced with local checkpoint)
 	// 4. For some networks the checkpoint syncing is not activated.
 	mode := checkpointSync
 	switch {
@@ -131,7 +143,7 @@ func (h *clientHandler) synchronise(peer *serverPeer) {
 	case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*h.backend.iConfig.ChtSize-1:
 		mode = lightSync
 		log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint")
-	case hardcoded:
+	case local:
 		mode = legacyCheckpointSync
 		log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded")
 	case h.backend.oracle == nil || !h.backend.oracle.IsRunning():
@@ -143,12 +155,14 @@ func (h *clientHandler) synchronise(peer *serverPeer) {
 		}
 		log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated")
 	}
+
 	// Notify testing framework if syncing has completed(for testing purpose).
 	defer func() {
-		if h.syncDone != nil {
-			h.syncDone()
+		if h.syncEnd != nil {
+			h.syncEnd(h.backend.blockchain.CurrentHeader())
 		}
 	}()
+
 	start := time.Now()
 	if mode == checkpointSync || mode == legacyCheckpointSync {
 		// Validate the advertised checkpoint
@@ -177,6 +191,10 @@ func (h *clientHandler) synchronise(peer *serverPeer) {
 			return
 		}
 	}
+
+	if h.syncStart != nil {
+		h.syncStart(h.backend.blockchain.CurrentHeader())
+	}
 	// Fetch the remaining block headers based on the current chain header.
 	if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil {
 		log.Debug("Synchronise failed", "reason", err)
diff --git a/les/sync_test.go b/les/sync_test.go
index 2eb0f88bf97af6b5341039c05991711b84fcf9d3..64e7283663c9cd830e2bee39994792d7abf2cabe 100644
--- a/les/sync_test.go
+++ b/les/sync_test.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/core"
+	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/light"
 	"github.com/ethereum/go-ethereum/params"
@@ -53,7 +54,7 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) {
 			time.Sleep(10 * time.Millisecond)
 		}
 	}
-	// Generate 128+1 blocks (totally 1 CHT sections)
+	// Generate 128+1 blocks (totally 1 CHT section)
 	server, client, tearDown := newClientServerEnv(t, int(config.ChtSize+config.ChtConfirms), protocol, waitIndexers, nil, 0, false, false, true)
 	defer tearDown()
 
@@ -100,8 +101,7 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) {
 	}
 
 	done := make(chan error)
-	client.handler.syncDone = func() {
-		header := client.handler.backend.blockchain.CurrentHeader()
+	client.handler.syncEnd = func(header *types.Header) {
 		if header.Number.Uint64() == expected {
 			done <- nil
 		} else {
@@ -144,7 +144,7 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) {
 			time.Sleep(10 * time.Millisecond)
 		}
 	}
-	// Generate 512+4 blocks (totally 1 CHT sections)
+	// Generate 128+1 blocks (totally 1 CHT section)
 	server, client, tearDown := newClientServerEnv(t, int(config.ChtSize+config.ChtConfirms), 3, waitIndexers, nil, 0, false, false, true)
 	defer tearDown()
 
@@ -198,8 +198,7 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) {
 	}
 
 	done := make(chan error)
-	client.handler.syncDone = func() {
-		header := client.handler.backend.blockchain.CurrentHeader()
+	client.handler.syncEnd = func(header *types.Header) {
 		if header.Number.Uint64() == expected {
 			done <- nil
 		} else {
@@ -220,3 +219,144 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool) {
 		t.Error("checkpoint syncing timeout")
 	}
 }
+
+func TestSyncFromConfiguredCheckpoint(t *testing.T) {
+	config := light.TestServerIndexerConfig
+
+	waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
+		for {
+			cs, _, _ := cIndexer.Sections()
+			bts, _, _ := btIndexer.Sections()
+			if cs >= 2 && bts >= 2 {
+				break
+			}
+			time.Sleep(10 * time.Millisecond)
+		}
+	}
+	// Generate 256+1 blocks (totally 2 CHT sections)
+	server, client, tearDown := newClientServerEnv(t, int(2*config.ChtSize+config.ChtConfirms), 3, waitIndexers, nil, 0, false, false, true)
+	defer tearDown()
+
+	// Configure the local checkpoint(the first section)
+	head := server.handler.blockchain.GetHeaderByNumber(config.ChtSize - 1).Hash()
+	cp := &params.TrustedCheckpoint{
+		SectionIndex: 0,
+		SectionHead:  head,
+		CHTRoot:      light.GetChtRoot(server.db, 0, head),
+		BloomRoot:    light.GetBloomTrieRoot(server.db, 0, head),
+	}
+	client.handler.backend.config.SyncFromCheckpoint = true
+	client.handler.backend.config.Checkpoint = cp
+	client.handler.checkpoint = cp
+	client.handler.backend.blockchain.AddTrustedCheckpoint(cp)
+
+	var (
+		start       = make(chan error, 1)
+		end         = make(chan error, 1)
+		expectStart = config.ChtSize - 1
+		expectEnd   = 2*config.ChtSize + config.ChtConfirms
+	)
+	client.handler.syncStart = func(header *types.Header) {
+		if header.Number.Uint64() == expectStart {
+			start <- nil
+		} else {
+			start <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectStart, header.Number)
+		}
+	}
+	client.handler.syncEnd = func(header *types.Header) {
+		if header.Number.Uint64() == expectEnd {
+			end <- nil
+		} else {
+			end <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectEnd, header.Number)
+		}
+	}
+	// Create connected peer pair.
+	if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil {
+		t.Fatalf("Failed to connect testing peers %v", err)
+	}
+
+	select {
+	case err := <-start:
+		if err != nil {
+			t.Error("sync failed", err)
+		}
+		return
+	case <-time.NewTimer(10 * time.Second).C:
+		t.Error("checkpoint syncing timeout")
+	}
+
+	select {
+	case err := <-end:
+		if err != nil {
+			t.Error("sync failed", err)
+		}
+		return
+	case <-time.NewTimer(10 * time.Second).C:
+		t.Error("checkpoint syncing timeout")
+	}
+}
+
+func TestSyncAll(t *testing.T) {
+	config := light.TestServerIndexerConfig
+
+	waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
+		for {
+			cs, _, _ := cIndexer.Sections()
+			bts, _, _ := btIndexer.Sections()
+			if cs >= 2 && bts >= 2 {
+				break
+			}
+			time.Sleep(10 * time.Millisecond)
+		}
+	}
+	// Generate 256+1 blocks (totally 2 CHT sections)
+	server, client, tearDown := newClientServerEnv(t, int(2*config.ChtSize+config.ChtConfirms), 3, waitIndexers, nil, 0, false, false, true)
+	defer tearDown()
+
+	client.handler.backend.config.SyncFromCheckpoint = true
+
+	var (
+		start       = make(chan error, 1)
+		end         = make(chan error, 1)
+		expectStart = uint64(0)
+		expectEnd   = 2*config.ChtSize + config.ChtConfirms
+	)
+	client.handler.syncStart = func(header *types.Header) {
+		if header.Number.Uint64() == expectStart {
+			start <- nil
+		} else {
+			start <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectStart, header.Number)
+		}
+	}
+	client.handler.syncEnd = func(header *types.Header) {
+		if header.Number.Uint64() == expectEnd {
+			end <- nil
+		} else {
+			end <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expectEnd, header.Number)
+		}
+	}
+	// Create connected peer pair.
+	if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil {
+		t.Fatalf("Failed to connect testing peers %v", err)
+	}
+
+	select {
+	case err := <-start:
+		if err != nil {
+			t.Error("sync failed", err)
+		}
+		return
+	case <-time.NewTimer(10 * time.Second).C:
+		t.Error("checkpoint syncing timeout")
+	}
+
+	select {
+	case err := <-end:
+		if err != nil {
+			t.Error("sync failed", err)
+		}
+		return
+	case <-time.NewTimer(10 * time.Second).C:
+		t.Error("checkpoint syncing timeout")
+	}
+}
diff --git a/les/test_helper.go b/les/test_helper.go
index d108a8dacefb0da7e7dc52e4c3445e49b4dadf01..04482ba68ec1d4ea5454b0bd5e3cfd04b15fab65 100644
--- a/les/test_helper.go
+++ b/les/test_helper.go
@@ -541,7 +541,7 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, callback indexer
 	)
 	if connect {
 		done := make(chan struct{})
-		client.syncDone = func() { close(done) }
+		client.syncEnd = func(_ *types.Header) { close(done) }
 		cpeer, speer, err = newTestPeerPair("peer", protocol, server, client)
 		if err != nil {
 			t.Fatalf("Failed to connect testing peers %v", err)