les, les/flowcontrol: implement LES/3 (#19329)
les, les/flowcontrol: implement LES/3
This commit is contained in:
110
les/peer.go
110
les/peer.go
@ -20,11 +20,14 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
||||
@ -47,10 +50,16 @@ const (
|
||||
allowedUpdateRate = time.Millisecond * 10 // time constant for recharging one byte of allowance
|
||||
)
|
||||
|
||||
const (
|
||||
freezeTimeBase = time.Millisecond * 700 // fixed component of client freeze time
|
||||
freezeTimeRandom = time.Millisecond * 600 // random component of client freeze time
|
||||
freezeCheckPeriod = time.Millisecond * 100 // buffer value recheck period after initial freeze time has elapsed
|
||||
)
|
||||
|
||||
// if the total encoded size of a sent transaction batch is over txSizeCostLimit
|
||||
// per transaction then the request cost is calculated as proportional to the
|
||||
// encoded size instead of the transaction count
|
||||
const txSizeCostLimit = 0x10000
|
||||
const txSizeCostLimit = 0x4000
|
||||
|
||||
const (
|
||||
announceTypeNone = iota
|
||||
@ -86,14 +95,17 @@ type peer struct {
|
||||
responseErrors int
|
||||
updateCounter uint64
|
||||
updateTime mclock.AbsTime
|
||||
frozen uint32 // 1 if client is in frozen state
|
||||
|
||||
fcClient *flowcontrol.ClientNode // nil if the peer is server only
|
||||
fcServer *flowcontrol.ServerNode // nil if the peer is client only
|
||||
fcParams flowcontrol.ServerParams
|
||||
fcCosts requestCostTable
|
||||
|
||||
isTrusted bool
|
||||
isOnlyAnnounce bool
|
||||
isTrusted bool
|
||||
isOnlyAnnounce bool
|
||||
chainSince, chainRecent uint64
|
||||
stateSince, stateRecent uint64
|
||||
}
|
||||
|
||||
func newPeer(version int, network uint64, isTrusted bool, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
@ -129,8 +141,59 @@ func (p *peer) rejectUpdate(size uint64) bool {
|
||||
return p.updateCounter > allowedUpdateBytes
|
||||
}
|
||||
|
||||
// freezeClient temporarily puts the client in a frozen state which means all
|
||||
// unprocessed and subsequent requests are dropped. Unfreezing happens automatically
|
||||
// after a short time if the client's buffer value is at least in the slightly positive
|
||||
// region. The client is also notified about being frozen/unfrozen with a Stop/Resume
|
||||
// message.
|
||||
func (p *peer) freezeClient() {
|
||||
if p.version < lpv3 {
|
||||
// if Stop/Resume is not supported then just drop the peer after setting
|
||||
// its frozen status permanently
|
||||
atomic.StoreUint32(&p.frozen, 1)
|
||||
p.Peer.Disconnect(p2p.DiscUselessPeer)
|
||||
return
|
||||
}
|
||||
if atomic.SwapUint32(&p.frozen, 1) == 0 {
|
||||
go func() {
|
||||
p.SendStop()
|
||||
time.Sleep(freezeTimeBase + time.Duration(rand.Int63n(int64(freezeTimeRandom))))
|
||||
for {
|
||||
bufValue, bufLimit := p.fcClient.BufferStatus()
|
||||
if bufLimit == 0 {
|
||||
return
|
||||
}
|
||||
if bufValue <= bufLimit/8 {
|
||||
time.Sleep(freezeCheckPeriod)
|
||||
} else {
|
||||
atomic.StoreUint32(&p.frozen, 0)
|
||||
p.SendResume(bufValue)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// freezeServer processes Stop/Resume messages from the given server
|
||||
func (p *peer) freezeServer(frozen bool) {
|
||||
var f uint32
|
||||
if frozen {
|
||||
f = 1
|
||||
}
|
||||
if atomic.SwapUint32(&p.frozen, f) != f && frozen {
|
||||
p.sendQueue.clear()
|
||||
}
|
||||
}
|
||||
|
||||
// isFrozen returns true if the client is frozen or the server has put our
|
||||
// client in frozen state
|
||||
func (p *peer) isFrozen() bool {
|
||||
return atomic.LoadUint32(&p.frozen) != 0
|
||||
}
|
||||
|
||||
func (p *peer) canQueue() bool {
|
||||
return p.sendQueue.canQueue()
|
||||
return p.sendQueue.canQueue() && !p.isFrozen()
|
||||
}
|
||||
|
||||
func (p *peer) queueSend(f func()) {
|
||||
@ -265,10 +328,21 @@ func (p *peer) GetTxRelayCost(amount, size int) uint64 {
|
||||
|
||||
// HasBlock checks if the peer has a given block
|
||||
func (p *peer) HasBlock(hash common.Hash, number uint64, hasState bool) bool {
|
||||
var head, since, recent uint64
|
||||
p.lock.RLock()
|
||||
if p.headInfo != nil {
|
||||
head = p.headInfo.Number
|
||||
}
|
||||
if hasState {
|
||||
since = p.stateSince
|
||||
recent = p.stateRecent
|
||||
} else {
|
||||
since = p.chainSince
|
||||
recent = p.chainRecent
|
||||
}
|
||||
hasBlock := p.hasBlock
|
||||
p.lock.RUnlock()
|
||||
return hasBlock != nil && hasBlock(hash, number, hasState)
|
||||
return head >= number && number >= since && (recent == 0 || number+recent+4 > head) && hasBlock != nil && hasBlock(hash, number, hasState)
|
||||
}
|
||||
|
||||
// SendAnnounce announces the availability of a number of blocks through
|
||||
@ -277,6 +351,16 @@ func (p *peer) SendAnnounce(request announceData) error {
|
||||
return p2p.Send(p.rw, AnnounceMsg, request)
|
||||
}
|
||||
|
||||
// SendStop notifies the client about being in frozen state
|
||||
func (p *peer) SendStop() error {
|
||||
return p2p.Send(p.rw, StopMsg, struct{}{})
|
||||
}
|
||||
|
||||
// SendResume notifies the client about getting out of frozen state
|
||||
func (p *peer) SendResume(bv uint64) error {
|
||||
return p2p.Send(p.rw, ResumeMsg, bv)
|
||||
}
|
||||
|
||||
// ReplyBlockHeaders creates a reply with a batch of block headers
|
||||
func (p *peer) ReplyBlockHeaders(reqID uint64, headers []*types.Header) *reply {
|
||||
data, _ := rlp.EncodeToBytes(headers)
|
||||
@ -464,19 +548,19 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
||||
send = send.add("genesisHash", genesis)
|
||||
if server != nil {
|
||||
if !server.onlyAnnounce {
|
||||
//only announce server. It sends only announse requests
|
||||
send = send.add("serveHeaders", nil)
|
||||
send = send.add("serveChainSince", uint64(0))
|
||||
send = send.add("serveStateSince", uint64(0))
|
||||
send = send.add("serveRecentState", uint64(core.TriesInMemory-4))
|
||||
send = send.add("txRelay", nil)
|
||||
}
|
||||
send = send.add("flowControl/BL", server.defParams.BufLimit)
|
||||
send = send.add("flowControl/MRR", server.defParams.MinRecharge)
|
||||
var costList RequestCostList
|
||||
if server.costTracker != nil {
|
||||
costList = server.costTracker.makeCostList()
|
||||
costList = server.costTracker.makeCostList(server.costTracker.globalFactor())
|
||||
} else {
|
||||
costList = testCostList()
|
||||
costList = testCostList(server.testCost)
|
||||
}
|
||||
send = send.add("flowControl/MRC", costList)
|
||||
p.fcCosts = costList.decode(ProtocolLengths[uint(p.version)])
|
||||
@ -544,12 +628,18 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
||||
p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams)
|
||||
} else {
|
||||
//mark OnlyAnnounce server if "serveHeaders", "serveChainSince", "serveStateSince" or "txRelay" fields don't exist
|
||||
if recv.get("serveChainSince", nil) != nil {
|
||||
if recv.get("serveChainSince", &p.chainSince) != nil {
|
||||
p.isOnlyAnnounce = true
|
||||
}
|
||||
if recv.get("serveStateSince", nil) != nil {
|
||||
if recv.get("serveRecentChain", &p.chainRecent) != nil {
|
||||
p.chainRecent = 0
|
||||
}
|
||||
if recv.get("serveStateSince", &p.stateSince) != nil {
|
||||
p.isOnlyAnnounce = true
|
||||
}
|
||||
if recv.get("serveRecentState", &p.stateRecent) != nil {
|
||||
p.stateRecent = 0
|
||||
}
|
||||
if recv.get("txRelay", nil) != nil {
|
||||
p.isOnlyAnnounce = true
|
||||
}
|
||||
|
Reference in New Issue
Block a user