2020-05-07 18:26:33 +00:00
|
|
|
package pubsub
|
|
|
|
|
|
|
|
import (
|
2020-05-07 19:10:24 +00:00
|
|
|
"math/rand"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2020-05-07 18:26:33 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"github.com/libp2p/go-libp2p-core/protocol"
|
|
|
|
)
|
|
|
|
|
|
|
|
// gossipTracer is an internal tracer that tracks IWANT requests in order to penalize
|
|
|
|
// peers who don't follow up on IWANT requests after an IHAVE advertisement.
|
|
|
|
// The tracking of promises is probabilistic to avoid using too much memory.
|
|
|
|
type gossipTracer struct {
|
2020-05-07 19:10:24 +00:00
|
|
|
sync.Mutex
|
|
|
|
msgID MsgIdFunction
|
|
|
|
promises map[string]map[peer.ID]time.Time
|
2020-05-07 18:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newGossipTracer() *gossipTracer {
|
|
|
|
return &gossipTracer{
|
2020-05-07 19:10:24 +00:00
|
|
|
msgID: DefaultMsgIdFn,
|
|
|
|
promises: make(map[string]map[peer.ID]time.Time),
|
2020-05-07 18:26:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gt *gossipTracer) Start(gs *GossipSubRouter) {
|
|
|
|
if gt == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
gt.msgID = gs.p.msgID
|
|
|
|
}
|
|
|
|
|
2020-05-07 19:10:24 +00:00
|
|
|
// track a promise to deliver a message from a list of msgIDs we are requesting
|
2020-05-07 18:26:33 +00:00
|
|
|
func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
|
|
|
|
if gt == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-07 19:10:24 +00:00
|
|
|
idx := rand.Intn(len(msgIDs))
|
|
|
|
mid := msgIDs[idx]
|
|
|
|
|
|
|
|
gt.Lock()
|
|
|
|
defer gt.Unlock()
|
|
|
|
|
|
|
|
peers, ok := gt.promises[mid]
|
|
|
|
if !ok {
|
|
|
|
peers = make(map[peer.ID]time.Time)
|
|
|
|
gt.promises[mid] = peers
|
|
|
|
}
|
|
|
|
|
|
|
|
_, ok = peers[p]
|
|
|
|
if !ok {
|
|
|
|
peers[p] = time.Now().Add(GossipSubIWantFollowupTime)
|
|
|
|
}
|
2020-05-07 18:26:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 19:10:24 +00:00
|
|
|
// returns the number of broken promises for each peer who didn't follow up
|
|
|
|
// on an IWANT request.
|
2020-05-07 18:26:33 +00:00
|
|
|
func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int {
|
2020-05-07 19:10:24 +00:00
|
|
|
if gt == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
gt.Lock()
|
|
|
|
defer gt.Unlock()
|
|
|
|
|
|
|
|
var res map[peer.ID]int
|
|
|
|
now := time.Now()
|
|
|
|
|
2020-09-02 18:32:10 +00:00
|
|
|
// find broken promises from peers
|
2020-05-07 19:10:24 +00:00
|
|
|
for mid, peers := range gt.promises {
|
|
|
|
for p, expire := range peers {
|
|
|
|
if expire.Before(now) {
|
|
|
|
if res == nil {
|
|
|
|
res = make(map[peer.ID]int)
|
|
|
|
}
|
|
|
|
res[p]++
|
|
|
|
|
|
|
|
delete(peers, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(peers) == 0 {
|
|
|
|
delete(gt.promises, mid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
2020-05-07 18:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ internalTracer = (*gossipTracer)(nil)
|
|
|
|
|
2020-09-02 18:32:10 +00:00
|
|
|
func (gt *gossipTracer) fulfillPromise(msg *Message) {
|
2020-05-07 19:10:24 +00:00
|
|
|
mid := gt.msgID(msg.Message)
|
|
|
|
|
|
|
|
gt.Lock()
|
|
|
|
defer gt.Unlock()
|
|
|
|
|
|
|
|
delete(gt.promises, mid)
|
2020-05-07 18:26:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-21 07:19:14 +00:00
|
|
|
func (gt *gossipTracer) DeliverMessage(msg *Message) {
|
2020-09-02 18:32:10 +00:00
|
|
|
// someone delivered a message, fulfill promises for it
|
|
|
|
gt.fulfillPromise(msg)
|
2020-08-21 07:19:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 18:26:33 +00:00
|
|
|
func (gt *gossipTracer) RejectMessage(msg *Message, reason string) {
|
2020-09-02 18:32:10 +00:00
|
|
|
// A message got rejected, so we can fulfill promises and let the score penalty apply
|
2020-05-07 19:10:24 +00:00
|
|
|
// from invalid message delivery.
|
|
|
|
// We do take exception and apply promise penalty regardless in the following cases, where
|
|
|
|
// the peer delivered an obviously invalid message.
|
|
|
|
switch reason {
|
|
|
|
case rejectMissingSignature:
|
|
|
|
return
|
|
|
|
case rejectInvalidSignature:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-02 18:32:10 +00:00
|
|
|
gt.fulfillPromise(msg)
|
2020-08-21 07:19:14 +00:00
|
|
|
}
|
2020-05-07 19:10:24 +00:00
|
|
|
|
2020-08-21 07:19:14 +00:00
|
|
|
func (gt *gossipTracer) ValidateMessage(msg *Message) {
|
2020-09-02 18:32:10 +00:00
|
|
|
// we consider the promise fulfilled as soon as the message begins validation
|
2020-08-21 07:19:14 +00:00
|
|
|
// if it was a case of signature issue it would have been rejected immediately
|
|
|
|
// without triggering the Validate trace
|
2020-09-02 18:32:10 +00:00
|
|
|
gt.fulfillPromise(msg)
|
2020-05-07 18:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (gt *gossipTracer) AddPeer(p peer.ID, proto protocol.ID) {}
|
|
|
|
func (gt *gossipTracer) RemovePeer(p peer.ID) {}
|
|
|
|
func (gt *gossipTracer) Join(topic string) {}
|
|
|
|
func (gt *gossipTracer) Leave(topic string) {}
|
|
|
|
func (gt *gossipTracer) Graft(p peer.ID, topic string) {}
|
|
|
|
func (gt *gossipTracer) Prune(p peer.ID, topic string) {}
|
|
|
|
func (gt *gossipTracer) DuplicateMessage(msg *Message) {}
|
2020-09-02 18:32:10 +00:00
|
|
|
|
|
|
|
func (gt *gossipTracer) ThrottlePeer(p peer.ID) {
|
|
|
|
gt.Lock()
|
|
|
|
defer gt.Unlock()
|
|
|
|
|
|
|
|
// remove promises for peers that have been throttled so that they are not unfairly penalized
|
|
|
|
for mid, peers := range gt.promises {
|
|
|
|
_, hasPromise := peers[p]
|
|
|
|
if hasPromise {
|
|
|
|
delete(peers, p)
|
|
|
|
if len(peers) == 0 {
|
|
|
|
delete(gt.promises, mid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|