2019-08-21 09:29:34 +00:00
|
|
|
// 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 (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/common/mclock"
|
|
|
|
"github.com/ethereum/go-ethereum/core"
|
2020-12-10 16:20:55 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core/forkid"
|
2019-08-21 09:29:34 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
|
|
"github.com/ethereum/go-ethereum/core/state"
|
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
2021-04-27 07:44:59 +00:00
|
|
|
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
2019-08-21 09:29:34 +00:00
|
|
|
"github.com/ethereum/go-ethereum/light"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/ethereum/go-ethereum/metrics"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p"
|
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
|
|
"github.com/ethereum/go-ethereum/trie"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
|
|
|
|
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
|
|
|
|
|
|
|
|
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
|
|
|
|
MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request
|
|
|
|
MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request
|
|
|
|
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
|
|
|
|
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
|
|
|
MaxHelperTrieProofsFetch = 64 // Amount of helper tries to be fetched per retrieval request
|
|
|
|
MaxTxSend = 64 // Amount of transactions to be send per request
|
|
|
|
MaxTxStatus = 256 // Amount of transactions to queried per request
|
|
|
|
)
|
|
|
|
|
2019-08-27 11:07:25 +00:00
|
|
|
var (
|
|
|
|
errTooManyInvalidRequest = errors.New("too many invalid requests made")
|
|
|
|
)
|
2019-08-21 09:29:34 +00:00
|
|
|
|
|
|
|
// serverHandler is responsible for serving light client and process
|
|
|
|
// all incoming light requests.
|
|
|
|
type serverHandler struct {
|
2020-12-10 16:20:55 +00:00
|
|
|
forkFilter forkid.Filter
|
2019-08-21 09:29:34 +00:00
|
|
|
blockchain *core.BlockChain
|
|
|
|
chainDb ethdb.Database
|
|
|
|
txpool *core.TxPool
|
|
|
|
server *LesServer
|
|
|
|
|
|
|
|
closeCh chan struct{} // Channel used to exit all background routines of handler.
|
|
|
|
wg sync.WaitGroup // WaitGroup used to track all background routines of handler.
|
|
|
|
synced func() bool // Callback function used to determine whether local node is synced.
|
|
|
|
|
|
|
|
// Testing fields
|
|
|
|
addTxsSync bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool) *serverHandler {
|
|
|
|
handler := &serverHandler{
|
2020-12-10 16:20:55 +00:00
|
|
|
forkFilter: forkid.NewFilter(blockchain),
|
2019-08-21 09:29:34 +00:00
|
|
|
server: server,
|
|
|
|
blockchain: blockchain,
|
|
|
|
chainDb: chainDb,
|
|
|
|
txpool: txpool,
|
|
|
|
closeCh: make(chan struct{}),
|
|
|
|
synced: synced,
|
|
|
|
}
|
|
|
|
return handler
|
|
|
|
}
|
|
|
|
|
|
|
|
// start starts the server handler.
|
|
|
|
func (h *serverHandler) start() {
|
|
|
|
h.wg.Add(1)
|
2020-10-21 08:56:33 +00:00
|
|
|
go h.broadcastLoop()
|
2019-08-21 09:29:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// stop stops the server handler.
|
|
|
|
func (h *serverHandler) stop() {
|
|
|
|
close(h.closeCh)
|
|
|
|
h.wg.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// runPeer is the p2p protocol run function for the given version.
|
|
|
|
func (h *serverHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
2020-02-26 09:41:24 +00:00
|
|
|
peer := newClientPeer(int(version), h.server.config.NetworkId, p, newMeteredMsgWriter(rw, int(version)))
|
|
|
|
defer peer.close()
|
2019-08-21 09:29:34 +00:00
|
|
|
h.wg.Add(1)
|
|
|
|
defer h.wg.Done()
|
|
|
|
return h.handle(peer)
|
|
|
|
}
|
|
|
|
|
2020-02-26 09:41:24 +00:00
|
|
|
func (h *serverHandler) handle(p *clientPeer) error {
|
2019-08-21 09:29:34 +00:00
|
|
|
p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
|
|
|
|
|
|
|
|
// Execute the LES handshake
|
|
|
|
var (
|
|
|
|
head = h.blockchain.CurrentHeader()
|
|
|
|
hash = head.Hash()
|
|
|
|
number = head.Number.Uint64()
|
|
|
|
td = h.blockchain.GetTd(hash, number)
|
2020-12-10 16:20:55 +00:00
|
|
|
forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), h.blockchain.CurrentBlock().NumberU64())
|
2019-08-21 09:29:34 +00:00
|
|
|
)
|
2020-12-10 16:20:55 +00:00
|
|
|
if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil {
|
2019-08-21 09:29:34 +00:00
|
|
|
p.Log().Debug("Light Ethereum handshake failed", "err", err)
|
|
|
|
return err
|
|
|
|
}
|
2021-04-27 07:44:59 +00:00
|
|
|
// Connected to another server, no messages expected, just wait for disconnection
|
2020-10-21 08:56:33 +00:00
|
|
|
if p.server {
|
2021-04-06 18:42:50 +00:00
|
|
|
if err := h.server.serverset.register(p); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-21 08:56:33 +00:00
|
|
|
_, err := p.rw.ReadMsg()
|
2021-04-06 18:42:50 +00:00
|
|
|
h.server.serverset.unregister(p)
|
2020-10-21 08:56:33 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-04-27 07:44:59 +00:00
|
|
|
// Setup flow control mechanism for the peer
|
|
|
|
p.fcClient = flowcontrol.NewClientNode(h.server.fcManager, p.fcParams)
|
|
|
|
defer p.fcClient.Disconnect()
|
2021-04-06 18:42:50 +00:00
|
|
|
|
2021-04-27 07:44:59 +00:00
|
|
|
// Reject light clients if server is not synced. Put this checking here, so
|
|
|
|
// that "non-synced" les-server peers are still allowed to keep the connection.
|
les, p2p/simulations/adapters: fix issues found while simulating les (#21761)
This adds a few tiny fixes for les and the p2p simulation framework:
LES Parts
- Keep the LES-SERVER connection even it's non-synced
We had this idea to reject the connections in LES protocol if the les-server itself is
not synced. However, in LES protocol we will also receive the connection from another
les-server. In this case even the local node is not synced yet, we should keep the tcp
connection for other protocols(e.g. eth protocol).
- Don't count "invalid message" for non-existing GetBlockHeadersMsg request
In the eth syncing mechanism (full sync, fast sync, light sync), it will try to fetch
some non-existent blocks or headers(to ensure we indeed download all the missing chain).
In this case, it's possible that the les-server will receive the request for
non-existent headers. So don't count it as the "invalid message" for scheduling
dropping.
- Copy the announce object in the closure
Before the les-server pushes the latest headers to all connected clients, it will create
a closure and queue it in the underlying request scheduler. In some scenarios it's
problematic. E.g, in private networks, the block can be mined very fast. So before the
first closure is executed, we may already update the latest_announce object. So actually
the "announce" object we want to send is replaced.
The downsize is the client will receive two announces with the same td and then drop the
server.
P2P Simulation Framework
- Don't double register the protocol services in p2p-simulation "Start".
The protocols upon the devp2p are registered in the "New node stage". So don't reigster
them again when starting a node in the p2p simulation framework
- Add one more new config field "ExternalSigner", in order to use clef service in the
framework.
2020-10-30 17:04:38 +00:00
|
|
|
if !h.synced() {
|
|
|
|
p.Log().Debug("Light server not synced, rejecting peer")
|
|
|
|
return p2p.DiscRequested
|
|
|
|
}
|
2021-04-27 07:44:59 +00:00
|
|
|
|
|
|
|
// Register the peer into the peerset and clientpool
|
2021-04-06 18:42:50 +00:00
|
|
|
if err := h.server.peers.register(p); err != nil {
|
|
|
|
return err
|
2019-08-27 11:07:25 +00:00
|
|
|
}
|
2021-04-06 18:42:50 +00:00
|
|
|
if p.balance = h.server.clientPool.Register(p); p.balance == nil {
|
|
|
|
h.server.peers.unregister(p.ID())
|
|
|
|
p.Log().Debug("Client pool already closed")
|
2020-09-14 20:44:20 +00:00
|
|
|
return p2p.DiscRequested
|
|
|
|
}
|
2021-04-06 18:42:50 +00:00
|
|
|
p.connectedAt = mclock.Now()
|
2019-08-21 09:29:34 +00:00
|
|
|
|
2019-08-27 11:07:25 +00:00
|
|
|
var wg sync.WaitGroup // Wait group used to track all in-flight task routines.
|
2019-08-21 09:29:34 +00:00
|
|
|
defer func() {
|
2019-08-27 11:07:25 +00:00
|
|
|
wg.Wait() // Ensure all background task routines have exited.
|
2021-04-06 18:42:50 +00:00
|
|
|
h.server.clientPool.Unregister(p)
|
|
|
|
h.server.peers.unregister(p.ID())
|
2020-09-14 20:44:20 +00:00
|
|
|
p.balance = nil
|
2021-04-06 18:42:50 +00:00
|
|
|
connectionTimer.Update(time.Duration(mclock.Now() - p.connectedAt))
|
2019-08-21 09:29:34 +00:00
|
|
|
}()
|
2021-04-06 18:42:50 +00:00
|
|
|
|
|
|
|
// Mark the peer as being served.
|
2020-03-12 10:25:52 +00:00
|
|
|
atomic.StoreUint32(&p.serving, 1)
|
|
|
|
defer atomic.StoreUint32(&p.serving, 0)
|
2019-08-21 09:29:34 +00:00
|
|
|
|
|
|
|
// Spawn a main loop to handle all incoming messages.
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case err := <-p.errCh:
|
|
|
|
p.Log().Debug("Failed to send light ethereum response", "err", err)
|
|
|
|
return err
|
|
|
|
default:
|
|
|
|
}
|
2019-08-27 11:07:25 +00:00
|
|
|
if err := h.handleMsg(p, &wg); err != nil {
|
2019-08-21 09:29:34 +00:00
|
|
|
p.Log().Debug("Light Ethereum message handling failed", "err", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 13:33:11 +00:00
|
|
|
// beforeHandle will do a series of prechecks before handling message.
|
|
|
|
func (h *serverHandler) beforeHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, reqCnt uint64, maxCount uint64) (*servingTask, uint64) {
|
|
|
|
// Ensure that the request sent by client peer is valid
|
|
|
|
inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0)
|
|
|
|
if reqCnt == 0 || reqCnt > maxCount {
|
|
|
|
p.fcClient.OneTimeCost(inSizeCost)
|
|
|
|
return nil, 0
|
|
|
|
}
|
|
|
|
// Ensure that the client peer complies with the flow control
|
|
|
|
// rules agreed by both sides.
|
|
|
|
if p.isFrozen() {
|
|
|
|
p.fcClient.OneTimeCost(inSizeCost)
|
|
|
|
return nil, 0
|
|
|
|
}
|
|
|
|
maxCost := p.fcCosts.getMaxCost(msg.Code, reqCnt)
|
|
|
|
accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost)
|
|
|
|
if !accepted {
|
|
|
|
p.freeze()
|
|
|
|
p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge)))
|
|
|
|
p.fcClient.OneTimeCost(inSizeCost)
|
|
|
|
return nil, 0
|
|
|
|
}
|
|
|
|
// Create a multi-stage task, estimate the time it takes for the task to
|
|
|
|
// execute, and cache it in the request service queue.
|
|
|
|
factor := h.server.costTracker.globalFactor()
|
|
|
|
if factor < 0.001 {
|
|
|
|
factor = 1
|
|
|
|
p.Log().Error("Invalid global cost factor", "factor", factor)
|
|
|
|
}
|
|
|
|
maxTime := uint64(float64(maxCost) / factor)
|
|
|
|
task := h.server.servingQueue.newTask(p, maxTime, priority)
|
|
|
|
if !task.start() {
|
|
|
|
p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost)
|
|
|
|
return nil, 0
|
|
|
|
}
|
|
|
|
return task, maxCost
|
|
|
|
}
|
|
|
|
|
|
|
|
// Afterhandle will perform a series of operations after message handling,
|
|
|
|
// such as updating flow control data, sending reply, etc.
|
|
|
|
func (h *serverHandler) afterHandle(p *clientPeer, reqID, responseCount uint64, msg p2p.Msg, maxCost uint64, reqCnt uint64, task *servingTask, reply *reply) {
|
|
|
|
if reply != nil {
|
|
|
|
task.done()
|
|
|
|
}
|
|
|
|
p.responseLock.Lock()
|
|
|
|
defer p.responseLock.Unlock()
|
|
|
|
|
|
|
|
// Short circuit if the client is already frozen.
|
|
|
|
if p.isFrozen() {
|
|
|
|
realCost := h.server.costTracker.realCost(task.servingTime, msg.Size, 0)
|
|
|
|
p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Positive correction buffer value with real cost.
|
|
|
|
var replySize uint32
|
|
|
|
if reply != nil {
|
|
|
|
replySize = reply.size()
|
|
|
|
}
|
|
|
|
var realCost uint64
|
|
|
|
if h.server.costTracker.testing {
|
|
|
|
realCost = maxCost // Assign a fake cost for testing purpose
|
|
|
|
} else {
|
|
|
|
realCost = h.server.costTracker.realCost(task.servingTime, msg.Size, replySize)
|
|
|
|
if realCost > maxCost {
|
|
|
|
realCost = maxCost
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost)
|
|
|
|
if reply != nil {
|
|
|
|
// Feed cost tracker request serving statistic.
|
|
|
|
h.server.costTracker.updateStats(msg.Code, reqCnt, task.servingTime, realCost)
|
|
|
|
// Reduce priority "balance" for the specific peer.
|
|
|
|
p.balance.RequestServed(realCost)
|
|
|
|
p.queueSend(func() {
|
|
|
|
if err := reply.send(bv); err != nil {
|
|
|
|
select {
|
|
|
|
case p.errCh <- err:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-21 09:29:34 +00:00
|
|
|
// handleMsg is invoked whenever an inbound message is received from a remote
|
|
|
|
// peer. The remote connection is torn down upon returning any error.
|
2020-02-26 09:41:24 +00:00
|
|
|
func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
|
2019-08-21 09:29:34 +00:00
|
|
|
// Read the next message from the remote peer, and ensure it's fully consumed
|
|
|
|
msg, err := p.rw.ReadMsg()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size)
|
|
|
|
|
|
|
|
// Discard large message which exceeds the limitation.
|
|
|
|
if msg.Size > ProtocolMaxMsgSize {
|
|
|
|
clientErrorMeter.Mark(1)
|
|
|
|
return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
|
|
|
|
}
|
|
|
|
defer msg.Discard()
|
|
|
|
|
2021-02-22 13:33:11 +00:00
|
|
|
// Lookup the request handler table, ensure it's supported
|
|
|
|
// message type by the protocol.
|
2021-02-20 09:40:38 +00:00
|
|
|
req, ok := Les3[msg.Code]
|
|
|
|
if !ok {
|
|
|
|
p.Log().Trace("Received invalid message", "code", msg.Code)
|
|
|
|
clientErrorMeter.Mark(1)
|
|
|
|
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
|
|
|
}
|
|
|
|
p.Log().Trace("Received " + req.Name)
|
2019-08-21 09:29:34 +00:00
|
|
|
|
2021-02-22 13:33:11 +00:00
|
|
|
// Decode the p2p message, resolve the concrete handler for it.
|
2021-02-20 09:40:38 +00:00
|
|
|
serve, reqID, reqCnt, err := req.Handle(msg)
|
|
|
|
if err != nil {
|
|
|
|
clientErrorMeter.Mark(1)
|
|
|
|
return errResp(ErrDecode, "%v: %v", msg, err)
|
|
|
|
}
|
|
|
|
if metrics.EnabledExpensive {
|
|
|
|
req.InPacketsMeter.Mark(1)
|
|
|
|
req.InTrafficMeter.Mark(int64(msg.Size))
|
|
|
|
}
|
2021-02-22 13:33:11 +00:00
|
|
|
p.responseCount++
|
|
|
|
responseCount := p.responseCount
|
2019-08-21 09:29:34 +00:00
|
|
|
|
2021-02-22 13:33:11 +00:00
|
|
|
// First check this client message complies all rules before
|
|
|
|
// handling it and return a processor if all checks are passed.
|
|
|
|
task, maxCost := h.beforeHandle(p, reqID, responseCount, msg, reqCnt, req.MaxCount)
|
|
|
|
if task == nil {
|
2021-02-20 09:40:38 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-22 13:33:11 +00:00
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
2019-08-21 09:29:34 +00:00
|
|
|
|
2021-02-22 13:33:11 +00:00
|
|
|
reply := serve(h, p, task.waitOrStop)
|
|
|
|
h.afterHandle(p, reqID, responseCount, msg, maxCost, reqCnt, task, reply)
|
2019-08-21 09:29:34 +00:00
|
|
|
|
2021-02-22 13:33:11 +00:00
|
|
|
if metrics.EnabledExpensive {
|
|
|
|
size := uint32(0)
|
2021-02-20 09:40:38 +00:00
|
|
|
if reply != nil {
|
2021-02-22 13:33:11 +00:00
|
|
|
size = reply.size()
|
2021-02-20 09:40:38 +00:00
|
|
|
}
|
2021-02-22 13:33:11 +00:00
|
|
|
req.OutPacketsMeter.Mark(1)
|
|
|
|
req.OutTrafficMeter.Mark(int64(size))
|
|
|
|
req.ServingTimeMeter.Update(time.Duration(task.servingTime))
|
|
|
|
}
|
|
|
|
}()
|
2020-07-28 15:02:35 +00:00
|
|
|
// If the client has made too much invalid request(e.g. request a non-existent data),
|
2019-08-21 09:29:34 +00:00
|
|
|
// reject them to prevent SPAM attack.
|
2020-07-28 15:02:35 +00:00
|
|
|
if p.getInvalid() > maxRequestErrors {
|
2019-08-21 09:29:34 +00:00
|
|
|
clientErrorMeter.Mark(1)
|
|
|
|
return errTooManyInvalidRequest
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-20 09:40:38 +00:00
|
|
|
// BlockChain implements serverBackend
|
|
|
|
func (h *serverHandler) BlockChain() *core.BlockChain {
|
|
|
|
return h.blockchain
|
|
|
|
}
|
|
|
|
|
|
|
|
// TxPool implements serverBackend
|
|
|
|
func (h *serverHandler) TxPool() *core.TxPool {
|
|
|
|
return h.txpool
|
|
|
|
}
|
|
|
|
|
|
|
|
// ArchiveMode implements serverBackend
|
|
|
|
func (h *serverHandler) ArchiveMode() bool {
|
|
|
|
return h.server.archiveMode
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddTxsSync implements serverBackend
|
|
|
|
func (h *serverHandler) AddTxsSync() bool {
|
|
|
|
return h.addTxsSync
|
|
|
|
}
|
|
|
|
|
2019-08-21 09:29:34 +00:00
|
|
|
// getAccount retrieves an account from the state based on root.
|
2021-02-20 09:40:38 +00:00
|
|
|
func getAccount(triedb *trie.Database, root, hash common.Hash) (state.Account, error) {
|
2019-08-21 09:29:34 +00:00
|
|
|
trie, err := trie.New(root, triedb)
|
|
|
|
if err != nil {
|
|
|
|
return state.Account{}, err
|
|
|
|
}
|
|
|
|
blob, err := trie.TryGet(hash[:])
|
|
|
|
if err != nil {
|
|
|
|
return state.Account{}, err
|
|
|
|
}
|
|
|
|
var account state.Account
|
|
|
|
if err = rlp.DecodeBytes(blob, &account); err != nil {
|
|
|
|
return state.Account{}, err
|
|
|
|
}
|
|
|
|
return account, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getHelperTrie returns the post-processed trie root for the given trie ID and section index
|
2021-02-20 09:40:38 +00:00
|
|
|
func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie {
|
|
|
|
var (
|
|
|
|
root common.Hash
|
|
|
|
prefix string
|
|
|
|
)
|
2019-08-21 09:29:34 +00:00
|
|
|
switch typ {
|
|
|
|
case htCanonical:
|
|
|
|
sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1)
|
2021-02-20 09:40:38 +00:00
|
|
|
root, prefix = light.GetChtRoot(h.chainDb, index, sectionHead), light.ChtTablePrefix
|
2019-08-21 09:29:34 +00:00
|
|
|
case htBloomBits:
|
|
|
|
sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1)
|
2021-02-20 09:40:38 +00:00
|
|
|
root, prefix = light.GetBloomTrieRoot(h.chainDb, index, sectionHead), light.BloomTrieTablePrefix
|
2019-08-21 09:29:34 +00:00
|
|
|
}
|
2021-02-20 09:40:38 +00:00
|
|
|
if root == (common.Hash{}) {
|
|
|
|
return nil
|
2019-08-21 09:29:34 +00:00
|
|
|
}
|
2021-02-20 09:40:38 +00:00
|
|
|
trie, _ := trie.New(root, trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix)))
|
|
|
|
return trie
|
2019-08-21 09:29:34 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 08:56:33 +00:00
|
|
|
// broadcastLoop broadcasts new block information to all connected light
|
2019-08-21 09:29:34 +00:00
|
|
|
// 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.
|
2020-10-21 08:56:33 +00:00
|
|
|
func (h *serverHandler) broadcastLoop() {
|
2019-08-21 09:29:34 +00:00
|
|
|
defer h.wg.Done()
|
|
|
|
|
|
|
|
headCh := make(chan core.ChainHeadEvent, 10)
|
|
|
|
headSub := h.blockchain.SubscribeChainHeadEvent(headCh)
|
|
|
|
defer headSub.Unsubscribe()
|
|
|
|
|
|
|
|
var (
|
|
|
|
lastHead *types.Header
|
|
|
|
lastTd = common.Big0
|
|
|
|
)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case ev := <-headCh:
|
|
|
|
header := ev.Block.Header()
|
|
|
|
hash, number := header.Hash(), header.Number.Uint64()
|
|
|
|
td := h.blockchain.GetTd(hash, number)
|
|
|
|
if td == nil || td.Cmp(lastTd) <= 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var reorg uint64
|
|
|
|
if lastHead != nil {
|
|
|
|
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)
|
2021-04-06 18:42:50 +00:00
|
|
|
h.server.peers.broadcast(announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg})
|
2019-08-21 09:29:34 +00:00
|
|
|
case <-h.closeCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|