2019-04-13 09:23:43 -04:00
|
|
|
package blockchain
|
2019-03-26 09:58:30 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
flow "github.com/tendermint/tendermint/libs/flowrate"
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
|
|
"github.com/tendermint/tendermint/p2p"
|
2019-04-04 00:13:32 +02:00
|
|
|
"github.com/tendermint/tendermint/types"
|
2019-03-26 09:58:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
//--------
|
|
|
|
// Peer
|
2019-06-02 19:03:51 +02:00
|
|
|
type bpPeerParams struct {
|
|
|
|
peerTimeout time.Duration
|
|
|
|
minRecvRate int64
|
|
|
|
peerSampleRate time.Duration
|
|
|
|
peerWindowSize time.Duration
|
|
|
|
}
|
2019-03-26 09:58:30 +01:00
|
|
|
|
|
|
|
type bpPeer struct {
|
2019-05-07 21:06:43 -04:00
|
|
|
logger log.Logger
|
|
|
|
id p2p.ID
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
height int64 // the peer reported height
|
|
|
|
numPendingBlockRequests int32 // number of requests still waiting for block responses
|
|
|
|
blocks map[int64]*types.Block // blocks received or expected to be received from this peer
|
|
|
|
blockResponseTimer *time.Timer
|
|
|
|
recvMonitor *flow.Monitor
|
|
|
|
parameters *bpPeerParams // parameters for timer and monitor
|
|
|
|
|
|
|
|
errFunc func(onErr error, peerID p2p.ID) // function to call on error
|
2019-03-26 09:58:30 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func NewBPPeer(
|
|
|
|
peerID p2p.ID, height int64, errFunc func(err error, peerID p2p.ID), parameters *bpPeerParams) *bpPeer {
|
|
|
|
|
|
|
|
if parameters == nil {
|
|
|
|
parameters = bpPeerDefaultParams()
|
|
|
|
}
|
|
|
|
return &bpPeer{
|
2019-03-26 09:58:30 +01:00
|
|
|
id: peerID,
|
|
|
|
height: height,
|
2019-04-04 00:13:32 +02:00
|
|
|
blocks: make(map[int64]*types.Block, maxRequestsPerPeer),
|
2019-03-26 09:58:30 +01:00
|
|
|
logger: log.NewNopLogger(),
|
|
|
|
errFunc: errFunc,
|
2019-06-02 19:03:51 +02:00
|
|
|
parameters: parameters,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func bpPeerDefaultParams() *bpPeerParams {
|
|
|
|
return &bpPeerParams{
|
|
|
|
// Timeout for a peer to respond to a block request.
|
|
|
|
peerTimeout: 15 * time.Second,
|
|
|
|
|
|
|
|
// Minimum recv rate to ensure we're receiving blocks from a peer fast
|
|
|
|
// enough. If a peer is not sending data at at least that rate, we
|
|
|
|
// consider them to have timedout and we disconnect.
|
|
|
|
//
|
|
|
|
// Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s,
|
|
|
|
// sending data across atlantic ~ 7.5 KB/s.
|
|
|
|
minRecvRate: int64(7680),
|
|
|
|
|
|
|
|
// Monitor parameters
|
|
|
|
peerSampleRate: time.Second,
|
|
|
|
peerWindowSize: 40 * time.Second,
|
2019-03-26 09:58:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func (peer *bpPeer) ID() p2p.ID {
|
|
|
|
return peer.id
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) SetLogger(l log.Logger) {
|
2019-03-26 09:58:30 +01:00
|
|
|
peer.logger = l
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func (peer *bpPeer) GetHeight() int64 {
|
|
|
|
return peer.height
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) SetHeight(height int64) {
|
|
|
|
peer.height = height
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) GetNumPendingBlockRequests() int32 {
|
|
|
|
return peer.numPendingBlockRequests
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) GetBlockAtHeight(height int64) (*types.Block, error) {
|
|
|
|
block, ok := peer.blocks[height]
|
|
|
|
if !ok {
|
|
|
|
return nil, errMissingRequest
|
|
|
|
}
|
|
|
|
if block == nil {
|
|
|
|
return nil, errMissingBlock
|
|
|
|
}
|
|
|
|
return peer.blocks[height], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) SetBlockAtHeight(height int64, block *types.Block) {
|
|
|
|
peer.blocks[height] = block
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) RemoveBlockAtHeight(height int64) {
|
|
|
|
if _, ok := peer.blocks[height]; !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
delete(peer.blocks, height)
|
|
|
|
}
|
|
|
|
|
2019-03-26 09:58:30 +01:00
|
|
|
func (peer *bpPeer) resetMonitor() {
|
2019-06-02 19:03:51 +02:00
|
|
|
peer.recvMonitor = flow.New(peer.parameters.peerSampleRate, peer.parameters.peerWindowSize)
|
|
|
|
initialValue := float64(peer.parameters.minRecvRate) * math.E
|
2019-03-26 09:58:30 +01:00
|
|
|
peer.recvMonitor.SetREMA(initialValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) resetTimeout() {
|
2019-06-02 19:03:51 +02:00
|
|
|
if peer.blockResponseTimer == nil {
|
|
|
|
peer.blockResponseTimer = time.AfterFunc(peer.parameters.peerTimeout, peer.onTimeout)
|
2019-03-26 09:58:30 +01:00
|
|
|
} else {
|
2019-06-02 19:03:51 +02:00
|
|
|
peer.blockResponseTimer.Reset(peer.parameters.peerTimeout)
|
2019-03-26 09:58:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func (peer *bpPeer) IncrPending() {
|
|
|
|
if peer.numPendingBlockRequests == 0 {
|
2019-03-26 09:58:30 +01:00
|
|
|
peer.resetMonitor()
|
|
|
|
peer.resetTimeout()
|
|
|
|
}
|
2019-06-02 19:03:51 +02:00
|
|
|
peer.numPendingBlockRequests++
|
2019-03-26 09:58:30 +01:00
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func (peer *bpPeer) StopBlockResponseTimer() bool {
|
|
|
|
if peer.blockResponseTimer == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return peer.blockResponseTimer.Stop()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) DecrPending(recvSize int) {
|
|
|
|
if peer.GetNumPendingBlockRequests() == 0 {
|
2019-03-26 09:58:30 +01:00
|
|
|
panic("cannot decrement, peer does not have pending requests")
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
peer.numPendingBlockRequests--
|
|
|
|
if peer.GetNumPendingBlockRequests() == 0 {
|
|
|
|
_ = peer.StopBlockResponseTimer()
|
2019-03-26 09:58:30 +01:00
|
|
|
} else {
|
|
|
|
peer.recvMonitor.Update(recvSize)
|
|
|
|
peer.resetTimeout()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (peer *bpPeer) onTimeout() {
|
|
|
|
peer.errFunc(errNoPeerResponse, peer.id)
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func (peer *bpPeer) IsGood() error {
|
|
|
|
if peer.numPendingBlockRequests == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
curRate := peer.recvMonitor.Status().CurRate
|
|
|
|
// curRate can be 0 on start
|
|
|
|
if curRate != 0 && curRate < peer.parameters.minRecvRate {
|
|
|
|
err := errSlowPeer
|
|
|
|
peer.logger.Error("SendTimeout", "peer", peer,
|
|
|
|
"reason", err,
|
|
|
|
"curRate", fmt.Sprintf("%d KB/s", curRate/1024),
|
|
|
|
"minRate", fmt.Sprintf("%d KB/s", peer.parameters.minRecvRate/1024))
|
|
|
|
return err
|
2019-03-26 09:58:30 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:03:51 +02:00
|
|
|
func (peer *bpPeer) Cleanup() {
|
|
|
|
_ = peer.StopBlockResponseTimer()
|
2019-03-26 09:58:30 +01:00
|
|
|
}
|
2019-03-27 19:54:08 +01:00
|
|
|
|
|
|
|
func (peer *bpPeer) String() string {
|
2019-06-02 19:03:51 +02:00
|
|
|
return fmt.Sprintf("peer: %v height: %v pending: %v", peer.id, peer.height, peer.numPendingBlockRequests)
|
2019-03-27 19:54:08 +01:00
|
|
|
}
|