les: remove clientPeerSet and serverSet (#21566)
* les: move NodeStateMachine from clientPool to LesServer * les: new header broadcaster * les: peerCommons.headInfo always contains last announced head * les: remove clientPeerSet and serverSet * les: fixed panic * les: fixed --nodiscover option * les: disconnect all peers at ns.Stop() * les: added comments and fixed signed broadcasts * les: removed unused parameter, fixed tests
This commit is contained in:
@ -17,6 +17,7 @@
|
||||
package les
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -36,6 +37,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nodestate"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
@ -91,7 +94,7 @@ func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb et
|
||||
// start starts the server handler.
|
||||
func (h *serverHandler) start() {
|
||||
h.wg.Add(1)
|
||||
go h.broadcastHeaders()
|
||||
go h.broadcastLoop()
|
||||
}
|
||||
|
||||
// stop stops the server handler.
|
||||
@ -123,47 +126,58 @@ func (h *serverHandler) handle(p *clientPeer) error {
|
||||
p.Log().Debug("Light Ethereum handshake failed", "err", err)
|
||||
return err
|
||||
}
|
||||
if p.server {
|
||||
if err := h.server.serverset.register(p); err != nil {
|
||||
return err
|
||||
}
|
||||
// connected to another server, no messages expected, just wait for disconnection
|
||||
_, err := p.rw.ReadMsg()
|
||||
return err
|
||||
}
|
||||
// Reject light clients if server is not synced.
|
||||
if !h.synced() {
|
||||
p.Log().Debug("Light server not synced, rejecting peer")
|
||||
return p2p.DiscRequested
|
||||
}
|
||||
defer p.fcClient.Disconnect()
|
||||
var registered bool
|
||||
if err := h.server.ns.Operation(func() {
|
||||
if h.server.ns.GetField(p.Node(), clientPeerField) != nil {
|
||||
registered = true
|
||||
} else {
|
||||
h.server.ns.SetFieldSub(p.Node(), clientPeerField, p)
|
||||
}
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if registered {
|
||||
return errAlreadyRegistered
|
||||
}
|
||||
|
||||
defer func() {
|
||||
h.server.ns.SetField(p.Node(), clientPeerField, nil)
|
||||
if p.fcClient != nil { // is nil when connecting another server
|
||||
p.fcClient.Disconnect()
|
||||
}
|
||||
}()
|
||||
if p.server {
|
||||
// connected to another server, no messages expected, just wait for disconnection
|
||||
_, err := p.rw.ReadMsg()
|
||||
return err
|
||||
}
|
||||
|
||||
// Disconnect the inbound peer if it's rejected by clientPool
|
||||
if cap, err := h.server.clientPool.connect(p); cap != p.fcParams.MinRecharge || err != nil {
|
||||
p.Log().Debug("Light Ethereum peer rejected", "err", errFullClientPool)
|
||||
return errFullClientPool
|
||||
}
|
||||
p.balance, _ = h.server.clientPool.ns.GetField(p.Node(), h.server.clientPool.BalanceField).(*lps.NodeBalance)
|
||||
p.balance, _ = h.server.ns.GetField(p.Node(), h.server.clientPool.BalanceField).(*lps.NodeBalance)
|
||||
if p.balance == nil {
|
||||
return p2p.DiscRequested
|
||||
}
|
||||
// Register the peer locally
|
||||
if err := h.server.peers.register(p); err != nil {
|
||||
h.server.clientPool.disconnect(p)
|
||||
p.Log().Error("Light Ethereum peer registration failed", "err", err)
|
||||
return err
|
||||
}
|
||||
clientConnectionGauge.Update(int64(h.server.peers.len()))
|
||||
activeCount, _ := h.server.clientPool.pp.Active()
|
||||
clientConnectionGauge.Update(int64(activeCount))
|
||||
|
||||
var wg sync.WaitGroup // Wait group used to track all in-flight task routines.
|
||||
|
||||
connectedAt := mclock.Now()
|
||||
defer func() {
|
||||
wg.Wait() // Ensure all background task routines have exited.
|
||||
h.server.peers.unregister(p.id)
|
||||
h.server.clientPool.disconnect(p)
|
||||
p.balance = nil
|
||||
clientConnectionGauge.Update(int64(h.server.peers.len()))
|
||||
activeCount, _ := h.server.clientPool.pp.Active()
|
||||
clientConnectionGauge.Update(int64(activeCount))
|
||||
connectionTimer.Update(time.Duration(mclock.Now() - connectedAt))
|
||||
}()
|
||||
// Mark the peer starts to be served.
|
||||
@ -911,11 +925,11 @@ func (h *serverHandler) txStatus(hash common.Hash) light.TxStatus {
|
||||
return stat
|
||||
}
|
||||
|
||||
// broadcastHeaders broadcasts new block information to all connected light
|
||||
// broadcastLoop broadcasts new block information to all connected light
|
||||
// clients. According to the agreement between client and server, server should
|
||||
// only broadcast new announcement if the total difficulty is higher than the
|
||||
// last one. Besides server will add the signature if client requires.
|
||||
func (h *serverHandler) broadcastHeaders() {
|
||||
func (h *serverHandler) broadcastLoop() {
|
||||
defer h.wg.Done()
|
||||
|
||||
headCh := make(chan core.ChainHeadEvent, 10)
|
||||
@ -929,10 +943,6 @@ func (h *serverHandler) broadcastHeaders() {
|
||||
for {
|
||||
select {
|
||||
case ev := <-headCh:
|
||||
peers := h.server.peers.allPeers()
|
||||
if len(peers) == 0 {
|
||||
continue
|
||||
}
|
||||
header := ev.Block.Header()
|
||||
hash, number := header.Hash(), header.Number.Uint64()
|
||||
td := h.blockchain.GetTd(hash, number)
|
||||
@ -944,33 +954,73 @@ func (h *serverHandler) broadcastHeaders() {
|
||||
reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(h.chainDb, header, lastHead).Number.Uint64()
|
||||
}
|
||||
lastHead, lastTd = header, td
|
||||
|
||||
log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
|
||||
var (
|
||||
signed bool
|
||||
signedAnnounce announceData
|
||||
)
|
||||
announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
|
||||
for _, p := range peers {
|
||||
p := p
|
||||
switch p.announceType {
|
||||
case announceTypeSimple:
|
||||
if !p.queueSend(func() { p.sendAnnounce(announce) }) {
|
||||
log.Debug("Drop announcement because queue is full", "number", number, "hash", hash)
|
||||
}
|
||||
case announceTypeSigned:
|
||||
if !signed {
|
||||
signedAnnounce = announce
|
||||
signedAnnounce.sign(h.server.privateKey)
|
||||
signed = true
|
||||
}
|
||||
if !p.queueSend(func() { p.sendAnnounce(signedAnnounce) }) {
|
||||
log.Debug("Drop announcement because queue is full", "number", number, "hash", hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
h.server.broadcaster.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg})
|
||||
case <-h.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// broadcaster sends new header announcements to active client peers
|
||||
type broadcaster struct {
|
||||
ns *nodestate.NodeStateMachine
|
||||
privateKey *ecdsa.PrivateKey
|
||||
lastAnnounce, signedAnnounce announceData
|
||||
}
|
||||
|
||||
// newBroadcaster creates a new broadcaster
|
||||
func newBroadcaster(ns *nodestate.NodeStateMachine) *broadcaster {
|
||||
b := &broadcaster{ns: ns}
|
||||
ns.SubscribeState(priorityPoolSetup.ActiveFlag, func(node *enode.Node, oldState, newState nodestate.Flags) {
|
||||
if newState.Equals(priorityPoolSetup.ActiveFlag) {
|
||||
// send last announcement to activated peers
|
||||
b.sendTo(node)
|
||||
}
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
// setSignerKey sets the signer key for signed announcements. Should be called before
|
||||
// starting the protocol handler.
|
||||
func (b *broadcaster) setSignerKey(privateKey *ecdsa.PrivateKey) {
|
||||
b.privateKey = privateKey
|
||||
}
|
||||
|
||||
// broadcast sends the given announcements to all active peers
|
||||
func (b *broadcaster) broadcast(announce announceData) {
|
||||
b.ns.Operation(func() {
|
||||
// iterate in an Operation to ensure that the active set does not change while iterating
|
||||
b.lastAnnounce = announce
|
||||
b.ns.ForEach(priorityPoolSetup.ActiveFlag, nodestate.Flags{}, func(node *enode.Node, state nodestate.Flags) {
|
||||
b.sendTo(node)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// sendTo sends the most recent announcement to the given node unless the same or higher Td
|
||||
// announcement has already been sent.
|
||||
func (b *broadcaster) sendTo(node *enode.Node) {
|
||||
if b.lastAnnounce.Td == nil {
|
||||
return
|
||||
}
|
||||
if p, _ := b.ns.GetField(node, clientPeerField).(*clientPeer); p != nil {
|
||||
if p.headInfo.Td == nil || b.lastAnnounce.Td.Cmp(p.headInfo.Td) > 0 {
|
||||
switch p.announceType {
|
||||
case announceTypeSimple:
|
||||
if !p.queueSend(func() { p.sendAnnounce(b.lastAnnounce) }) {
|
||||
log.Debug("Drop announcement because queue is full", "number", b.lastAnnounce.Number, "hash", b.lastAnnounce.Hash)
|
||||
}
|
||||
case announceTypeSigned:
|
||||
if b.signedAnnounce.Hash != b.lastAnnounce.Hash {
|
||||
b.signedAnnounce = b.lastAnnounce
|
||||
b.signedAnnounce.sign(b.privateKey)
|
||||
}
|
||||
if !p.queueSend(func() { p.sendAnnounce(b.signedAnnounce) }) {
|
||||
log.Debug("Drop announcement because queue is full", "number", b.lastAnnounce.Number, "hash", b.lastAnnounce.Hash)
|
||||
}
|
||||
}
|
||||
p.headInfo = blockInfo{b.lastAnnounce.Hash, b.lastAnnounce.Number, b.lastAnnounce.Td}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user