mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
eth: implement BlockRangeUpdate sending
This commit is contained in:
parent
482f5cd2d8
commit
a11358df21
2 changed files with 136 additions and 9 deletions
134
eth/handler.go
134
eth/handler.go
|
|
@ -18,8 +18,10 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -47,6 +49,9 @@ const (
|
||||||
// The number is referenced from the size of tx pool.
|
// The number is referenced from the size of tx pool.
|
||||||
txChanSize = 4096
|
txChanSize = 4096
|
||||||
|
|
||||||
|
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||||
|
chainHeadChanSize = 128
|
||||||
|
|
||||||
// txMaxBroadcastSize is the max size of a transaction that will be broadcasted.
|
// txMaxBroadcastSize is the max size of a transaction that will be broadcasted.
|
||||||
// All transactions with a higher size will be announced and need to be fetched
|
// All transactions with a higher size will be announced and need to be fetched
|
||||||
// by the peer.
|
// by the peer.
|
||||||
|
|
@ -117,9 +122,10 @@ type handler struct {
|
||||||
txFetcher *fetcher.TxFetcher
|
txFetcher *fetcher.TxFetcher
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
|
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
txsCh chan core.NewTxsEvent
|
txsCh chan core.NewTxsEvent
|
||||||
txsSub event.Subscription
|
txsSub event.Subscription
|
||||||
|
blockRange *blockRangeState
|
||||||
|
|
||||||
requiredBlocks map[uint64]common.Hash
|
requiredBlocks map[uint64]common.Hash
|
||||||
|
|
||||||
|
|
@ -424,6 +430,11 @@ func (h *handler) Start(maxPeers int) {
|
||||||
h.txsSub = h.txpool.SubscribeTransactions(h.txsCh, false)
|
h.txsSub = h.txpool.SubscribeTransactions(h.txsCh, false)
|
||||||
go h.txBroadcastLoop()
|
go h.txBroadcastLoop()
|
||||||
|
|
||||||
|
// broadcast block range
|
||||||
|
h.wg.Add(1)
|
||||||
|
h.blockRange = newBlockRangeState(h.chain, h.eventMux)
|
||||||
|
go h.blockRangeLoop(h.blockRange)
|
||||||
|
|
||||||
// start sync handlers
|
// start sync handlers
|
||||||
h.txFetcher.Start()
|
h.txFetcher.Start()
|
||||||
|
|
||||||
|
|
@ -434,6 +445,7 @@ func (h *handler) Start(maxPeers int) {
|
||||||
|
|
||||||
func (h *handler) Stop() {
|
func (h *handler) Stop() {
|
||||||
h.txsSub.Unsubscribe() // quits txBroadcastLoop
|
h.txsSub.Unsubscribe() // quits txBroadcastLoop
|
||||||
|
h.blockRange.stop()
|
||||||
h.txFetcher.Stop()
|
h.txFetcher.Stop()
|
||||||
h.downloader.Terminate()
|
h.downloader.Terminate()
|
||||||
|
|
||||||
|
|
@ -555,3 +567,119 @@ func (h *handler) enableSyncedFeatures() {
|
||||||
h.snapSync.Store(false)
|
h.snapSync.Store(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type blockRangeState struct {
|
||||||
|
prev eth.BlockRangeUpdatePacket
|
||||||
|
next eth.BlockRangeUpdatePacket
|
||||||
|
headCh chan core.ChainHeadEvent
|
||||||
|
headSub event.Subscription
|
||||||
|
syncSub *event.TypeMuxSubscription
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBlockRangeState(chain *core.BlockChain, typeMux *event.TypeMux) *blockRangeState {
|
||||||
|
headCh := make(chan core.ChainHeadEvent, chainHeadChanSize)
|
||||||
|
headSub := chain.SubscribeChainHeadEvent(headCh)
|
||||||
|
syncSub := typeMux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
|
||||||
|
st := &blockRangeState{
|
||||||
|
headCh: headCh,
|
||||||
|
headSub: headSub,
|
||||||
|
syncSub: syncSub,
|
||||||
|
}
|
||||||
|
st.update(chain, chain.CurrentBlock())
|
||||||
|
st.prev = st.next
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockRangeBroadcastLoop announces changes in locally-available block range to peers.
|
||||||
|
// The range to announce is the range that is available in the store, so it's not just
|
||||||
|
// about imported blocks.
|
||||||
|
func (h *handler) blockRangeLoop(st *blockRangeState) {
|
||||||
|
defer h.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ev := <-st.syncSub.Chan():
|
||||||
|
if ev == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := ev.Data.(downloader.StartEvent); ok && h.snapSync.Load() {
|
||||||
|
h.blockRangeWhileSnapSyncing(st)
|
||||||
|
}
|
||||||
|
case <-st.headCh:
|
||||||
|
st.update(h.chain, h.chain.CurrentBlock())
|
||||||
|
if st.shouldSend() {
|
||||||
|
h.broadcastBlockRange(st)
|
||||||
|
}
|
||||||
|
case <-st.headSub.Err():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockRangeWhileSnapSyncing announces block range updates during snap sync.
|
||||||
|
// Here we poll the CurrentSnapBlock on a timer and announce updates to it.
|
||||||
|
func (h *handler) blockRangeWhileSnapSyncing(st *blockRangeState) {
|
||||||
|
tick := time.NewTicker(1 * time.Minute)
|
||||||
|
defer tick.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tick.C:
|
||||||
|
st.update(h.chain, h.chain.CurrentSnapBlock())
|
||||||
|
if st.shouldSend() {
|
||||||
|
h.broadcastBlockRange(st)
|
||||||
|
}
|
||||||
|
// back to processing head block updates when sync is done
|
||||||
|
case ev := <-st.syncSub.Chan():
|
||||||
|
if ev == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch ev.Data.(type) {
|
||||||
|
case downloader.FailedEvent, downloader.DoneEvent:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// ignore head updates, but exit when the subscription ends
|
||||||
|
case <-st.headCh:
|
||||||
|
case <-st.headSub.Err():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// broadcastBlockRange sends a range update when one is due.
|
||||||
|
func (h *handler) broadcastBlockRange(state *blockRangeState) {
|
||||||
|
h.peers.lock.Lock()
|
||||||
|
peerlist := slices.Collect(maps.Values(h.peers.peers))
|
||||||
|
h.peers.lock.Unlock()
|
||||||
|
if len(peerlist) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := state.next
|
||||||
|
log.Debug("Sending BlockRangeUpdate message", "peers", len(peerlist), "earliest", msg.EarliestBlock, "latest", msg.LatestBlock)
|
||||||
|
for _, p := range peerlist {
|
||||||
|
p.SendBlockRangeUpdate(msg)
|
||||||
|
}
|
||||||
|
state.prev = state.next
|
||||||
|
}
|
||||||
|
|
||||||
|
// update assigns the values of the next block range update from the chain.
|
||||||
|
func (st *blockRangeState) update(chain *core.BlockChain, latest *types.Header) {
|
||||||
|
earliest, _ := chain.HistoryPruningCutoff()
|
||||||
|
st.next.EarliestBlock = min(latest.Number.Uint64(), earliest)
|
||||||
|
st.next.LatestBlock = latest.Number.Uint64()
|
||||||
|
st.next.LatestBlockHash = latest.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldSend decides whether it is time to send a block range update. We don't want to
|
||||||
|
// send these updates constantly, so they will usually only be sent every 32 blocks.
|
||||||
|
// However, there is a special case: if the range would move back, i.e. due to SetHead, we
|
||||||
|
// want to send it immediately.
|
||||||
|
func (st *blockRangeState) shouldSend() bool {
|
||||||
|
return st.next.LatestBlock < st.prev.LatestBlock ||
|
||||||
|
st.next.LatestBlock-st.prev.LatestBlock >= 32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *blockRangeState) stop() {
|
||||||
|
st.syncSub.Unsubscribe()
|
||||||
|
st.headSub.Unsubscribe()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -344,12 +344,11 @@ func (p *Peer) RequestTxs(hashes []common.Hash) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendBlockRangeUpdate sends a notification about our available block range to the peer.
|
// SendBlockRangeUpdate sends a notification about our available block range to the peer.
|
||||||
func (p *Peer) SendBlockRangeUpdate(earliestBlock, latestBlock uint64, latestBlockHash common.Hash) error {
|
func (p *Peer) SendBlockRangeUpdate(msg BlockRangeUpdatePacket) error {
|
||||||
return p2p.Send(p.rw, BlockRangeUpdateMsg, &BlockRangeUpdatePacket{
|
if p.version < ETH69 {
|
||||||
EarliestBlock: earliestBlock,
|
return nil
|
||||||
LatestBlock: latestBlock,
|
}
|
||||||
LatestBlockHash: latestBlockHash,
|
return p2p.Send(p.rw, BlockRangeUpdateMsg, &msg)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// knownCache is a cache for known hashes.
|
// knownCache is a cache for known hashes.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue