diff --git a/eth/backend.go b/eth/backend.go
index 6b9c98bf2af528b19c1fafdd17dacfe695a1b5c5..fbf4dd7bbcbdfc09c8a36c7157c21a1721a82097 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -480,6 +480,7 @@ func (s *Ethereum) IsListening() bool                  { return true } // Always
 func (s *Ethereum) EthVersion() int                    { return int(s.protocolManager.SubProtocols[0].Version) }
 func (s *Ethereum) NetVersion() uint64                 { return s.networkID }
 func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
+func (s *Ethereum) Synced() bool                       { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 }
 
 // Protocols implements node.Service, returning all the currently configured
 // network protocols to start.
diff --git a/les/backend.go b/les/backend.go
index 887f882108e1005dbc22c70a48d96c05027e6ad2..ed0f45057ff466ad31b22f38072cdf66a393acad 100644
--- a/les/backend.go
+++ b/les/backend.go
@@ -158,7 +158,8 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
 		leth.serverPool,
 		quitSync,
 		&leth.wg,
-		config.ULC); err != nil {
+		config.ULC,
+		nil); err != nil {
 		return nil, err
 	}
 
diff --git a/les/handler.go b/les/handler.go
index 0d235e7a5220877a2fc11372d4c148d6764477d1..f53a4722f1090c2d4c57280df735395b03b34f14 100644
--- a/les/handler.go
+++ b/les/handler.go
@@ -87,12 +87,16 @@ type txPool interface {
 }
 
 type ProtocolManager struct {
-	lightSync    bool
+	// Configs
+	chainConfig *params.ChainConfig
+	iConfig     *light.IndexerConfig
+
+	client    bool   // The indicator whether the node is light client
+	maxPeers  int    // The maximum number peers allowed to connect.
+	networkId uint64 // The identity of network.
+
 	txpool       txPool
 	txrelay      *LesTxRelay
-	networkId    uint64
-	chainConfig  *params.ChainConfig
-	iConfig      *light.IndexerConfig
 	blockchain   BlockChain
 	chainDb      ethdb.Database
 	odr          *LesOdr
@@ -102,23 +106,21 @@ type ProtocolManager struct {
 	reqDist      *requestDistributor
 	retriever    *retrieveManager
 	servingQueue *servingQueue
-
-	downloader *downloader.Downloader
-	fetcher    *lightFetcher
-	peers      *peerSet
-	maxPeers   int
-
-	eventMux *event.TypeMux
+	downloader   *downloader.Downloader
+	fetcher      *lightFetcher
+	ulc          *ulc
+	peers        *peerSet
 
 	// channels for fetcher, syncer, txsyncLoop
 	newPeerCh   chan *peer
 	quitSync    chan struct{}
 	noMorePeers chan struct{}
 
-	// wait group is used for graceful shutdowns during downloading
-	// and processing
-	wg  *sync.WaitGroup
-	ulc *ulc
+	wg       *sync.WaitGroup
+	eventMux *event.TypeMux
+
+	// Callbacks
+	synced func() bool
 }
 
 // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
@@ -126,7 +128,7 @@ type ProtocolManager struct {
 func NewProtocolManager(
 	chainConfig *params.ChainConfig,
 	indexerConfig *light.IndexerConfig,
-	lightSync bool,
+	client bool,
 	networkId uint64,
 	mux *event.TypeMux,
 	engine consensus.Engine,
@@ -139,10 +141,10 @@ func NewProtocolManager(
 	serverPool *serverPool,
 	quitSync chan struct{},
 	wg *sync.WaitGroup,
-	ulcConfig *eth.ULCConfig) (*ProtocolManager, error) {
+	ulcConfig *eth.ULCConfig, synced func() bool) (*ProtocolManager, error) {
 	// Create the protocol manager with the base fields
 	manager := &ProtocolManager{
-		lightSync:   lightSync,
+		client:      client,
 		eventMux:    mux,
 		blockchain:  blockchain,
 		chainConfig: chainConfig,
@@ -158,6 +160,7 @@ func NewProtocolManager(
 		quitSync:    quitSync,
 		wg:          wg,
 		noMorePeers: make(chan struct{}),
+		synced:      synced,
 	}
 	if odr != nil {
 		manager.retriever = odr.retriever
@@ -174,7 +177,7 @@ func NewProtocolManager(
 	if disableClientRemovePeer {
 		removePeer = func(id string) {}
 	}
-	if lightSync {
+	if client {
 		var checkpoint uint64
 		if cht, ok := params.TrustedCheckpoints[blockchain.Genesis().Hash()]; ok {
 			checkpoint = (cht.SectionIndex+1)*params.CHTFrequency - 1
@@ -193,7 +196,7 @@ func (pm *ProtocolManager) removePeer(id string) {
 
 func (pm *ProtocolManager) Start(maxPeers int) {
 	pm.maxPeers = maxPeers
-	if pm.lightSync {
+	if pm.client {
 		go pm.syncer()
 	} else {
 		go func() {
@@ -268,10 +271,13 @@ func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgRea
 func (pm *ProtocolManager) handle(p *peer) error {
 	// Ignore maxPeers if this is a trusted peer
 	// In server mode we try to check into the client pool after handshake
-	if pm.lightSync && pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted {
+	if pm.client && pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted {
 		return p2p.DiscTooManyPeers
 	}
-
+	// Reject light clients if server is not synced.
+	if !pm.client && !pm.synced() {
+		return p2p.DiscRequested
+	}
 	p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
 
 	// Execute the LES handshake
@@ -304,7 +310,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
 	}()
 
 	// Register the peer in the downloader. If the downloader considers it banned, we disconnect
-	if pm.lightSync {
+	if pm.client {
 		p.lock.Lock()
 		head := p.headInfo
 		p.lock.Unlock()
diff --git a/les/helper_test.go b/les/helper_test.go
index 9a302f8370be3f3bd0d36f4a49ddecd937598775..878e44404c28fc562dc9c5bb14834cd2f61552b8 100644
--- a/les/helper_test.go
+++ b/les/helper_test.go
@@ -170,7 +170,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
 	if lightSync {
 		indexConfig = light.TestClientIndexerConfig
 	}
-	pm, err := NewProtocolManager(gspec.Config, indexConfig, lightSync, NetworkId, evmux, engine, peers, chain, pool, db, odr, nil, nil, make(chan struct{}), new(sync.WaitGroup), ulcConfig)
+	pm, err := NewProtocolManager(gspec.Config, indexConfig, lightSync, NetworkId, evmux, engine, peers, chain, pool, db, odr, nil, nil, make(chan struct{}), new(sync.WaitGroup), ulcConfig, func() bool { return true })
 	if err != nil {
 		return nil, err
 	}
diff --git a/les/server.go b/les/server.go
index 6c2b227f424eef5491a2d959b93d8fc793b99aa3..6b93b846dbc525d56cd28d0e3bb6c9f6e5222b98 100644
--- a/les/server.go
+++ b/les/server.go
@@ -74,7 +74,7 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
 		nil,
 		quitSync,
 		new(sync.WaitGroup),
-		config.ULC)
+		config.ULC, eth.Synced)
 	if err != nil {
 		return nil, err
 	}
@@ -243,6 +243,7 @@ func (s *LesServer) Stop() {
 	s.protocolManager.Stop()
 }
 
+// todo(rjl493456442) separate client and server implementation.
 func (pm *ProtocolManager) blockLoop() {
 	pm.wg.Add(1)
 	headCh := make(chan core.ChainHeadEvent, 10)
diff --git a/les/transactions.rlp b/les/transactions.rlp
new file mode 100755
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/les/ulc.go b/les/ulc.go
index d9f7dc76c994e267ba93d36920c98c843a9b29a6..c6d41555296e55ef2d99a9fa17dd76a5d766492d 100644
--- a/les/ulc.go
+++ b/les/ulc.go
@@ -1,9 +1,24 @@
+// Copyright 2019 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 les
 
 import (
-	"fmt"
-
 	"github.com/ethereum/go-ethereum/eth"
+	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 
@@ -12,24 +27,24 @@ type ulc struct {
 	minTrustedFraction int
 }
 
+// newULC creates and returns a ultra light client instance.
 func newULC(ulcConfig *eth.ULCConfig) *ulc {
 	if ulcConfig == nil {
 		return nil
 	}
-
 	m := make(map[string]struct{}, len(ulcConfig.TrustedServers))
 	for _, id := range ulcConfig.TrustedServers {
 		node, err := enode.ParseV4(id)
 		if err != nil {
-			fmt.Println("node:", id, " err:", err)
+			log.Debug("Failed to parse trusted server", "id", id, "err", err)
 			continue
 		}
 		m[node.ID().String()] = struct{}{}
 	}
-
 	return &ulc{m, ulcConfig.MinTrustedFraction}
 }
 
+// isTrusted return an indicator that whether the specified peer is trusted.
 func (u *ulc) isTrusted(p enode.ID) bool {
 	if u.trustedKeys == nil {
 		return false
diff --git a/les/ulc_test.go b/les/ulc_test.go
index 81986fa1e8449113cde8bc963648e8823a79e2e7..38adeb95f1178bc2a09ecd8c3d46689004bc72eb 100644
--- a/les/ulc_test.go
+++ b/les/ulc_test.go
@@ -1,3 +1,19 @@
+// Copyright 2019 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 les
 
 import (