IDONTWANT Support (#376)

* blossomsub: Remove unused mutex

* blossomsub: Add RPC queue

* blossomsub: Use RPC queue

* blossomsub: Add IDONTWANT control message to protos

* blossomsub: Add IDONTWANT tracing support

* blossomsub: Add pre-validation

* blossomsub: Add IDONTWANT feature flag

* blossomsub: Add IDONTWANT parameters

* blossomsub: Add IDONTWANT observability

* blossomsub: Send IDONTWANT control messages

* blossomsub: Handle IDONTWANT control messages

* blossomsub: Clear maps efficiently

* blossomsub: Increase IDONTWANT parameter defaults

* blossomsub: Do not send IDONTWANT to original sender

* blossomsub: Add IDONTWANT unit tests
This commit is contained in:
petricadaipegsp 2024-11-24 00:15:41 +01:00 committed by GitHub
parent 3b754ea4fb
commit a543a607be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 2138 additions and 406 deletions

View File

@ -656,7 +656,8 @@
"value": null
}
]
}
},
"unit": "short"
},
"overrides": []
},
@ -735,7 +736,7 @@
"type": "prometheus",
"uid": "${datasource}"
},
"description": "The number of message IDs in IHAVE control messages which have been successfully sent to a remote peer.",
"description": "The number of message IDs in IHAVE control messages which have been sent to a remote peer.",
"fieldConfig": {
"defaults": {
"color": {
@ -783,7 +784,8 @@
"value": null
}
]
}
},
"unit": "short"
},
"overrides": []
},
@ -956,7 +958,8 @@
"value": null
}
]
}
},
"unit": "short"
},
"overrides": []
},
@ -1083,7 +1086,8 @@
"value": null
}
]
}
},
"unit": "short"
},
"overrides": []
},
@ -1383,7 +1387,8 @@
"value": null
}
]
}
},
"unit": "short"
},
"overrides": []
},
@ -1502,13 +1507,525 @@
],
"type": "timeseries"
},
{
"datasource": {
"default": false,
"type": "prometheus",
"uid": "${datasource}"
},
"description": "The number of message IDs provided in IDONTWANT control messages sent to remote peers.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 47
},
"id": 19,
"options": {
"legend": {
"calcs": [
"lastNotNull",
"min",
"max",
"mean"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true,
"sortBy": "Name",
"sortDesc": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.99, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"send\"}[$__rate_interval]))",
"hide": false,
"instant": false,
"legendFormat": "P99",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.95, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"send\"}[$__rate_interval]))",
"instant": false,
"legendFormat": "P95",
"range": true,
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.5, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"send\"}[$__rate_interval]))",
"hide": false,
"instant": false,
"legendFormat": "P50",
"range": true,
"refId": "C"
}
],
"title": "Sent IDONTWANT message count histogram",
"type": "timeseries"
},
{
"datasource": {
"default": false,
"type": "prometheus",
"uid": "${datasource}"
},
"description": "The number of message IDs provided in IDONTWANT control messages received from remote peers.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 47
},
"id": 20,
"options": {
"legend": {
"calcs": [
"lastNotNull",
"min",
"max",
"mean"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true,
"sortBy": "Name",
"sortDesc": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.99, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"recv\"}[$__rate_interval]))",
"hide": false,
"instant": false,
"legendFormat": "P99",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.95, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"recv\"}[$__rate_interval]))",
"instant": false,
"legendFormat": "P95",
"range": true,
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.5, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"recv\"}[$__rate_interval]))",
"hide": false,
"instant": false,
"legendFormat": "P50",
"range": true,
"refId": "C"
}
],
"title": "Received IDONTWANT message count histogram",
"type": "timeseries"
},
{
"datasource": {
"default": false,
"type": "prometheus",
"uid": "${datasource}"
},
"description": "The number of message IDs provided in IDONTWANT control messages which have not been sent to a remote peer due to an error (usually a full queue).",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 56
},
"id": 21,
"options": {
"legend": {
"calcs": [
"lastNotNull",
"min",
"max",
"mean"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true,
"sortBy": "Name",
"sortDesc": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.99, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"drop\"}[$__rate_interval]))",
"hide": false,
"instant": false,
"legendFormat": "P99",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.95, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"drop\"}[$__rate_interval]))",
"instant": false,
"legendFormat": "P95",
"range": true,
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.5, rate(blossomsub_idontwant_messages_bucket{job=~\"$job\", instance=~\"$host\", direction=\"drop\"}[$__rate_interval]))",
"hide": false,
"instant": false,
"legendFormat": "P50",
"range": true,
"refId": "C"
}
],
"title": "Dropped IDONTWANT message count histogram",
"type": "timeseries"
},
{
"datasource": {
"default": false,
"type": "prometheus",
"uid": "${datasource}"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "pps"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 56
},
"id": 22,
"options": {
"legend": {
"calcs": [
"lastNotNull",
"min",
"max",
"mean"
],
"displayMode": "table",
"placement": "bottom",
"showLegend": true,
"sortBy": "Name",
"sortDesc": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "rate(blossomsub_idontwant_messages_count{job=~\"$job\", instance=~\"$host\", direction=\"drop\"}[$__rate_interval])",
"hide": false,
"instant": false,
"legendFormat": "Dropped",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"editorMode": "code",
"expr": "rate(blossomsub_idontwant_messages_count{job=~\"$job\", instance=~\"$host\", direction=\"recv\"}[$__rate_interval])",
"hide": false,
"instant": false,
"legendFormat": "Received",
"range": true,
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "rate(blossomsub_idontwant_messages_count{job=~\"$job\", instance=~\"$host\", direction=\"send\"}[$__rate_interval])",
"hide": false,
"instant": false,
"legendFormat": "Sent",
"range": true,
"refId": "C"
}
],
"title": "IDONTWANT message rates",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 47
"y": 65
},
"id": 8,
"panels": [],
@ -1576,7 +2093,7 @@
"h": 9,
"w": 24,
"x": 0,
"y": 48
"y": 66
},
"id": 11,
"options": {
@ -1749,7 +2266,7 @@
"h": 9,
"w": 12,
"x": 0,
"y": 57
"y": 75
},
"id": 9,
"options": {
@ -1922,7 +2439,7 @@
"h": 9,
"w": 12,
"x": 12,
"y": 57
"y": 75
},
"id": 10,
"options": {
@ -2039,7 +2556,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 66
"y": 84
},
"id": 6,
"panels": [],
@ -2108,7 +2625,7 @@
"h": 9,
"w": 24,
"x": 0,
"y": 67
"y": 85
},
"id": 7,
"options": {
@ -2251,6 +2768,6 @@
"timezone": "browser",
"title": "BlossomSub",
"uid": "ee47pcfax962ob",
"version": 39,
"version": 45,
"weekStart": ""
}

View File

@ -8,7 +8,6 @@ import (
"math/rand"
"slices"
"sort"
"sync"
"time"
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
@ -26,6 +25,9 @@ import (
const (
// BlossomSubID_v2 is the protocol ID for version 2.0.0 of the BlossomSub protocol.
BlossomSubID_v2 = protocol.ID("/blossomsub/2.0.0")
// BlossomSubID_v21 is the protocol ID for version 2.1.0 of the BlossomSub protocol.
BlossomSubID_v21 = protocol.ID("/blossomsub/2.1.0")
)
// Defines the default BlossomSub parameters.
@ -56,7 +58,10 @@ var (
BlossomSubGraftFloodThreshold = 10 * time.Second
BlossomSubMaxIHaveLength = 5000
BlossomSubMaxIHaveMessages = 10
BlossomSubMaxIDontWantMessages = 5000
BlossomSubIWantFollowupTime = 3 * time.Second
BlossomSubIDontWantMessageThreshold = 1024 // 1KB
BlossomSubIDontWantMessageTTL = 60 // 60 heartbeats / 42 seconds
)
// BlossomSubParams defines all the BlossomSub specific parameters.
@ -195,10 +200,21 @@ type BlossomSubParams struct {
// MaxIHaveMessages is the maximum number of IHAVE messages to accept from a peer within a heartbeat.
MaxIHaveMessages int
// MaxIDontWantMessages is the maximum number of IDONTWANT messages to accept from a peer within a heartbeat.
MaxIDontWantMessages int
// Time to wait for a message requested through IWANT following an IHAVE advertisement.
// If the message is not received within this window, a broken promise is declared and
// the router may apply bahavioural penalties.
IWantFollowupTime time.Duration
// IDONTWANT is only sent for messages larger than the threshold. This should be greater than
// D_high * the size of the message id. Otherwise, the attacker can do the amplication attack by sending
// small messages while the receiver replies back with larger IDONTWANT messages.
IDontWantMessageThreshold int
// IDONTWANT is cleared when it's older than the TTL.
IDontWantMessageTTL int
}
// NewBlossomSub returns a new PubSub object using the default BlossomSubRouter as the router.
@ -215,31 +231,47 @@ func NewBlossomSubWithRouter(ctx context.Context, h host.Host, rt PubSubRouter,
// NewBlossomSubRouter returns a new BlossomSubRouter with custom parameters.
func NewBlossomSubRouter(h host.Host, params BlossomSubParams, network uint8) *BlossomSubRouter {
protos, feature := BlossomSubDefaultProtocols, BlossomSubDefaultFeatures
if network != 0 {
BlossomSubDefaultProtocols[0] = protocol.ID(
string(BlossomSubID_v2) + fmt.Sprintf("-network-%d", network),
)
protos = append(protos[:0:0], BlossomSubDefaultProtocols...)
for i, p := range protos {
protos[i] = protocol.ID(fmt.Sprintf("%s-network-%d", p, network))
}
feature = func(f BlossomSubFeature, proto protocol.ID) bool {
switch f {
case BlossomSubFeatureMesh:
return proto == protos[0] || proto == protos[1]
case BlossomSubFeaturePX:
return proto == protos[0] || proto == protos[1]
case BlossomSubFeatureIdontwant:
return proto == protos[0]
default:
return false
}
}
}
return &BlossomSubRouter{
peers: make(map[peer.ID]protocol.ID),
mesh: make(map[string]map[peer.ID]struct{}),
fanout: make(map[string]map[peer.ID]struct{}),
lastpub: make(map[string]int64),
gossip: make(map[peer.ID][]*pb.ControlIHave),
control: make(map[peer.ID]*pb.ControlMessage),
cab: pstoremem.NewAddrBook(),
backoff: make(map[string]map[peer.ID]time.Time),
peerhave: make(map[peer.ID]int),
iasked: make(map[peer.ID]int),
outbound: make(map[peer.ID]bool),
connect: make(chan connectInfo, params.MaxPendingConnections),
mcache: NewMessageCache(params.HistoryGossip, params.HistoryLength),
protos: BlossomSubDefaultProtocols,
feature: BlossomSubDefaultFeatures,
tagTracer: newTagTracer(h.ConnManager()),
params: params,
network: network,
peers: make(map[peer.ID]protocol.ID),
mesh: make(map[string]map[peer.ID]struct{}),
fanout: make(map[string]map[peer.ID]struct{}),
lastpub: make(map[string]int64),
gossip: make(map[peer.ID][]*pb.ControlIHave),
control: make(map[peer.ID]*pb.ControlMessage),
cab: pstoremem.NewAddrBook(),
backoff: make(map[string]map[peer.ID]time.Time),
peerhave: make(map[peer.ID]int),
peerdontwant: make(map[peer.ID]int),
unwanted: make(map[peer.ID]map[string]int),
iasked: make(map[peer.ID]int),
outbound: make(map[peer.ID]bool),
connect: make(chan connectInfo, params.MaxPendingConnections),
mcache: NewMessageCache(params.HistoryGossip, params.HistoryLength),
protos: protos,
feature: feature,
tagTracer: newTagTracer(h.ConnManager()),
params: params,
network: network,
}
}
@ -247,23 +279,25 @@ func NewBlossomSubRouter(h host.Host, params BlossomSubParams, network uint8) *B
func DefaultBlossomSubRouter(h host.Host) *BlossomSubRouter {
params := DefaultBlossomSubParams()
return &BlossomSubRouter{
peers: make(map[peer.ID]protocol.ID),
mesh: make(map[string]map[peer.ID]struct{}),
fanout: make(map[string]map[peer.ID]struct{}),
lastpub: make(map[string]int64),
gossip: make(map[peer.ID][]*pb.ControlIHave),
control: make(map[peer.ID]*pb.ControlMessage),
backoff: make(map[string]map[peer.ID]time.Time),
peerhave: make(map[peer.ID]int),
iasked: make(map[peer.ID]int),
outbound: make(map[peer.ID]bool),
connect: make(chan connectInfo, params.MaxPendingConnections),
cab: pstoremem.NewAddrBook(),
mcache: NewMessageCache(params.HistoryGossip, params.HistoryLength),
protos: BlossomSubDefaultProtocols,
feature: BlossomSubDefaultFeatures,
tagTracer: newTagTracer(h.ConnManager()),
params: params,
peers: make(map[peer.ID]protocol.ID),
mesh: make(map[string]map[peer.ID]struct{}),
fanout: make(map[string]map[peer.ID]struct{}),
lastpub: make(map[string]int64),
gossip: make(map[peer.ID][]*pb.ControlIHave),
control: make(map[peer.ID]*pb.ControlMessage),
backoff: make(map[string]map[peer.ID]time.Time),
peerhave: make(map[peer.ID]int),
peerdontwant: make(map[peer.ID]int),
unwanted: make(map[peer.ID]map[string]int),
iasked: make(map[peer.ID]int),
outbound: make(map[peer.ID]bool),
connect: make(chan connectInfo, params.MaxPendingConnections),
cab: pstoremem.NewAddrBook(),
mcache: NewMessageCache(params.HistoryGossip, params.HistoryLength),
protos: BlossomSubDefaultProtocols,
feature: BlossomSubDefaultFeatures,
tagTracer: newTagTracer(h.ConnManager()),
params: params,
}
}
@ -296,7 +330,10 @@ func DefaultBlossomSubParams() BlossomSubParams {
GraftFloodThreshold: BlossomSubGraftFloodThreshold,
MaxIHaveLength: BlossomSubMaxIHaveLength,
MaxIHaveMessages: BlossomSubMaxIHaveMessages,
MaxIDontWantMessages: BlossomSubMaxIDontWantMessages,
IWantFollowupTime: BlossomSubIWantFollowupTime,
IDontWantMessageThreshold: BlossomSubIDontWantMessageThreshold,
IDontWantMessageTTL: BlossomSubIDontWantMessageTTL,
SlowHeartbeatWarning: 0.1,
}
}
@ -445,22 +482,23 @@ func WithBlossomSubParams(cfg BlossomSubParams) Option {
// is the fanout map. Fanout peer lists are expired if we don't publish any
// messages to their bitmask for BlossomSubFanoutTTL.
type BlossomSubRouter struct {
p *PubSub
peers map[peer.ID]protocol.ID // peer protocols
direct map[peer.ID]struct{} // direct peers
mesh map[string]map[peer.ID]struct{} // bitmask meshes
fanout map[string]map[peer.ID]struct{} // bitmask fanout
lastpub map[string]int64 // last publish time for fanout bitmasks
gossip map[peer.ID][]*pb.ControlIHave // pending gossip
control map[peer.ID]*pb.ControlMessage // pending control messages
peerhave map[peer.ID]int // number of IHAVEs received from peer in the last heartbeat
iasked map[peer.ID]int // number of messages we have asked from peer in the last heartbeat
outbound map[peer.ID]bool // connection direction cache, marks peers with outbound connections
backoff map[string]map[peer.ID]time.Time // prune backoff
connect chan connectInfo // px connection requests
cab peerstore.AddrBook
meshMx sync.RWMutex
network uint8
p *PubSub
peers map[peer.ID]protocol.ID // peer protocols
direct map[peer.ID]struct{} // direct peers
mesh map[string]map[peer.ID]struct{} // bitmask meshes
fanout map[string]map[peer.ID]struct{} // bitmask fanout
lastpub map[string]int64 // last publish time for fanout bitmasks
gossip map[peer.ID][]*pb.ControlIHave // pending gossip
control map[peer.ID]*pb.ControlMessage // pending control messages
peerhave map[peer.ID]int // number of IHAVEs received from peer in the last heartbeat
peerdontwant map[peer.ID]int // number of IDONTWANTs received from peer in the last heartbeat
unwanted map[peer.ID]map[string]int // TTL of the message ids peers don't want
iasked map[peer.ID]int // number of messages we have asked from peer in the last heartbeat
outbound map[peer.ID]bool // connection direction cache, marks peers with outbound connections
backoff map[string]map[peer.ID]time.Time // prune backoff
connect chan connectInfo // px connection requests
cab peerstore.AddrBook
network uint8
protos []protocol.ID
feature BlossomSubFeatureTest
@ -633,32 +671,25 @@ loop:
func (bs *BlossomSubRouter) RemovePeer(p peer.ID) {
log.Debugf("PEERDOWN: Remove disconnected peer %s", p)
masks := make([][]byte, 0)
bs.meshMx.Lock()
for bitmask, peers := range bs.mesh {
if _, ok := peers[p]; !ok {
continue
}
masks = append(masks, []byte(bitmask))
}
bs.meshMx.Unlock()
for _, bitmask := range masks {
log.Debugf("PEERDOWN: Pruning peer %s from bitmask %s", p, bitmask)
bs.tracer.Prune(p, bitmask)
bs.tracer.Prune(p, []byte(bitmask))
}
bs.tracer.RemovePeer(p)
delete(bs.peers, p)
bs.meshMx.Lock()
for _, peers := range bs.mesh {
delete(peers, p)
}
bs.meshMx.Unlock()
for _, peers := range bs.fanout {
delete(peers, p)
}
delete(bs.gossip, p)
delete(bs.control, p)
delete(bs.outbound, p)
delete(bs.unwanted, p)
}
func (bs *BlossomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
@ -676,10 +707,8 @@ func (bs *BlossomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
}
}
bs.meshMx.RLock()
// BlossomSub peers
bsPeers = len(bs.mesh[string(bitmask)])
bs.meshMx.RUnlock()
if suggested == 0 {
suggested = bs.params.Dlo
@ -709,6 +738,52 @@ func (bs *BlossomSubRouter) AcceptFrom(p peer.ID) AcceptStatus {
return bs.gate.AcceptFrom(p)
}
// PreValidation sends the IDONTWANT control messages to all the mesh
// peers. They need to be sent right before the validation because they
// should be seen by the peers as soon as possible.
func (bs *BlossomSubRouter) PreValidation(msgs []*Message) {
slicedMessages := make(map[string][]*Message, len(msgs))
for _, msg := range msgs {
if len(msg.GetData()) < bs.params.IDontWantMessageThreshold {
continue
}
for _, bitmask := range SliceBitmask(msg.GetBitmask()) {
bitmask := string(bitmask)
slicedMessages[bitmask] = append(slicedMessages[bitmask], msg)
}
}
toSend := make(map[peer.ID]map[*Message]struct{}, len(slicedMessages))
for bitmask, msgs := range slicedMessages {
// send IDONTWANT to all the mesh peers
for p := range bs.mesh[bitmask] {
// send to only peers that support IDONTWANT
if !bs.feature(BlossomSubFeatureIdontwant, bs.peers[p]) {
continue
}
for _, msg := range msgs {
if msg.ReceivedFrom == p {
continue
}
if toSend[p] == nil {
toSend[p] = make(map[*Message]struct{}, len(msgs))
}
toSend[p][msg] = struct{}{}
}
}
}
for p, msgs := range toSend {
mids := make([][]byte, 0, len(msgs))
for msg := range msgs {
mids = append(mids, bs.p.idGen.ID(msg))
}
// shuffle the messages got from the RPC envelope
shuffleBytes(mids)
idontwant := []*pb.ControlIDontWant{{MessageIDs: mids}}
out := rpcWithControl(nil, nil, nil, nil, nil, idontwant)
bs.sendRPC(p, out, true)
}
}
func (bs *BlossomSubRouter) HandleRPC(rpc *RPC) {
ctl := rpc.GetControl()
if ctl == nil {
@ -719,13 +794,14 @@ func (bs *BlossomSubRouter) HandleRPC(rpc *RPC) {
ihave := bs.handleIWant(rpc.from, ctl)
prune := bs.handleGraft(rpc.from, ctl)
bs.handlePrune(rpc.from, ctl)
bs.handleIDontWant(rpc.from, ctl)
if len(iwant) == 0 && len(ihave) == 0 && len(prune) == 0 {
return
}
out := rpcWithControl(ihave, nil, iwant, nil, prune)
bs.sendRPC(rpc.from, out)
out := rpcWithControl(ihave, nil, iwant, nil, prune, nil)
bs.sendRPC(rpc.from, out, false)
}
func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb.ControlIWant {
@ -751,9 +827,7 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
iwant := make(map[string]struct{})
for _, ihave := range ctl.GetIhave() {
bitmask := ihave.GetBitmask()
bs.meshMx.RLock()
_, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
continue
}
@ -859,9 +933,7 @@ func (bs *BlossomSubRouter) handleGraft(p peer.ID, ctl *pb.ControlMessage) []*pb
continue
}
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
// don't do PX when there is an unknown bitmask to avoid leaking our peers
doPX = false
@ -950,9 +1022,7 @@ func (bs *BlossomSubRouter) handlePrune(p peer.ID, ctl *pb.ControlMessage) {
for _, prune := range ctl.GetPrune() {
bitmask := prune.GetBitmask()
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
continue
}
@ -984,6 +1054,26 @@ func (bs *BlossomSubRouter) handlePrune(p peer.ID, ctl *pb.ControlMessage) {
}
}
func (bs *BlossomSubRouter) handleIDontWant(p peer.ID, ctl *pb.ControlMessage) {
if bs.unwanted[p] == nil {
bs.unwanted[p] = make(map[string]int)
}
// IDONTWANT flood protection
if bs.peerdontwant[p] >= bs.params.MaxIDontWantMessages {
log.Debugf("IDONWANT: peer %s has advertised too many times (%d) within this heartbeat interval; ignoring", p, bs.peerdontwant[p])
return
}
bs.peerdontwant[p]++
// Remember all the unwanted message ids
for _, idontwant := range ctl.GetIdontwant() {
for _, mid := range idontwant.GetMessageIDs() {
bs.unwanted[p][string(mid)] = bs.params.IDontWantMessageTTL
}
}
}
func (bs *BlossomSubRouter) addBackoff(p peer.ID, bitmask []byte, isUnsubscribe bool) {
backoff := bs.params.PruneBackoff
if isUnsubscribe {
@ -1137,9 +1227,7 @@ func (bs *BlossomSubRouter) Publish(msg *Message) {
}
// BlossomSub peers
bs.meshMx.RLock()
gmap, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
// we are not in the mesh for bitmask, use fanout peers
gmap, ok = bs.fanout[string(bitmask)]
@ -1170,14 +1258,19 @@ func (bs *BlossomSubRouter) Publish(msg *Message) {
continue
}
bs.sendRPC(pid, out)
mid := bs.p.idGen.ID(msg)
// Check if it has already received an IDONTWANT for the message.
// If so, don't send it to the peer
if _, ok := bs.unwanted[pid][string(mid)]; ok {
continue
}
bs.sendRPC(pid, out, false)
}
}
func (bs *BlossomSubRouter) Join(bitmask []byte) {
bs.meshMx.RLock()
gmap, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if ok {
return
}
@ -1212,9 +1305,7 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
}
}
bs.meshMx.Lock()
bs.mesh[string(bitmask)] = gmap
bs.meshMx.Unlock()
delete(bs.fanout, string(bitmask))
delete(bs.lastpub, string(bitmask))
} else {
@ -1226,9 +1317,7 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
return !direct && !doBackOff && bs.score.Score(p) >= 0
})
gmap = peerListToMap(peers)
bs.meshMx.Lock()
bs.mesh[string(bitmask)] = gmap
bs.meshMx.Unlock()
}
for p := range gmap {
@ -1239,9 +1328,7 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
}
func (bs *BlossomSubRouter) Leave(bitmask []byte) {
bs.meshMx.RLock()
gmap, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
return
}
@ -1249,9 +1336,7 @@ func (bs *BlossomSubRouter) Leave(bitmask []byte) {
log.Debugf("LEAVE %s", bitmask)
bs.tracer.Leave(bitmask)
bs.meshMx.Lock()
delete(bs.mesh, string(bitmask))
bs.meshMx.Unlock()
for p := range gmap {
log.Debugf("LEAVE: Remove mesh link to %s in %s", p, bitmask)
@ -1266,17 +1351,17 @@ func (bs *BlossomSubRouter) Leave(bitmask []byte) {
func (bs *BlossomSubRouter) sendGraft(p peer.ID, bitmask []byte) {
graft := []*pb.ControlGraft{{Bitmask: bitmask}}
out := rpcWithControl(nil, nil, nil, graft, nil)
bs.sendRPC(p, out)
out := rpcWithControl(nil, nil, nil, graft, nil, nil)
bs.sendRPC(p, out, false)
}
func (bs *BlossomSubRouter) sendPrune(p peer.ID, bitmask []byte, isUnsubscribe bool) {
prune := []*pb.ControlPrune{bs.makePrune(p, bitmask, bs.doPX, isUnsubscribe)}
out := rpcWithControl(nil, nil, nil, nil, prune)
bs.sendRPC(p, out)
out := rpcWithControl(nil, nil, nil, nil, prune, nil)
bs.sendRPC(p, out, false)
}
func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC, fast bool) {
// do we own the RPC?
own := false
@ -1300,16 +1385,14 @@ func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
delete(bs.gossip, p)
}
bs.p.peersMx.RLock()
mch, ok := bs.p.peers[p]
bs.p.peersMx.RUnlock()
if !ok {
return
}
// If we're below the max message size, go ahead and send
if out.Size() < bs.p.maxMessageSize {
bs.doSendRPC(out, p, mch)
bs.doSendRPC(out, p, mch, fast)
return
}
@ -1322,7 +1405,7 @@ func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
bs.doDropRPC(out, p, fmt.Sprintf("Dropping oversized RPC. Size: %d, limit: %d. (Over by %d bytes)", rpc.Size(), bs.p.maxMessageSize, rpc.Size()-bs.p.maxMessageSize))
continue
}
bs.doSendRPC(rpc, p, mch)
bs.doSendRPC(rpc, p, mch, fast)
}
}
@ -1336,13 +1419,12 @@ func (bs *BlossomSubRouter) doDropRPC(rpc *RPC, p peer.ID, reason string) {
}
}
func (bs *BlossomSubRouter) doSendRPC(rpc *RPC, p peer.ID, mch chan *RPC) {
select {
case mch <- rpc:
bs.tracer.SendRPC(rpc, p)
default:
func (bs *BlossomSubRouter) doSendRPC(rpc *RPC, p peer.ID, q *rpcQueue, fast bool) {
if err := q.TryPush(bs.p.ctx, rpc, fast); err != nil {
bs.doDropRPC(rpc, p, "queue full")
return
}
bs.tracer.SendRPC(rpc, p)
}
// appendOrMergeRPC appends the given RPCs to the slice, merging them if possible.
@ -1517,6 +1599,9 @@ func (bs *BlossomSubRouter) heartbeat() {
// clean up iasked counters
bs.clearIHaveCounters()
// clean up IDONTWANT counters
bs.clearIDontWantCounters()
// apply IWANT request penalties
bs.applyIwantPenalties()
@ -1535,7 +1620,6 @@ func (bs *BlossomSubRouter) heartbeat() {
}
// maintain the mesh for bitmasks we have joined
bs.meshMx.Lock()
for bitmask, peers := range bs.mesh {
bitmask := []byte(bitmask)
prunePeer := func(p peer.ID) {
@ -1718,7 +1802,6 @@ func (bs *BlossomSubRouter) heartbeat() {
}
}
}
bs.meshMx.Unlock()
// expire fanout for bitmasks we haven't published to in a while
now := time.Now().UnixNano()
@ -1771,14 +1854,21 @@ func (bs *BlossomSubRouter) heartbeat() {
}
func (bs *BlossomSubRouter) clearIHaveCounters() {
if len(bs.peerhave) > 0 {
// throw away the old map and make a new one
bs.peerhave = make(map[peer.ID]int)
}
clear(bs.peerhave)
clear(bs.iasked)
}
if len(bs.iasked) > 0 {
// throw away the old map and make a new one
bs.iasked = make(map[peer.ID]int)
func (bs *BlossomSubRouter) clearIDontWantCounters() {
clear(bs.peerdontwant)
// decrement TTLs of all the IDONTWANTs and delete it from the cache when it reaches zero
for _, mids := range bs.unwanted {
for mid := range mids {
mids[mid]--
if mids[mid] == 0 {
delete(mids, mid)
}
}
}
}
@ -1856,8 +1946,8 @@ func (bs *BlossomSubRouter) sendGraftPrune(tograft, toprune map[peer.ID][][]byte
}
}
out := rpcWithControl(nil, nil, nil, graft, prune)
bs.sendRPC(p, out)
out := rpcWithControl(nil, nil, nil, graft, prune, nil)
bs.sendRPC(p, out, false)
}
for p, bitmasks := range toprune {
@ -1866,8 +1956,8 @@ func (bs *BlossomSubRouter) sendGraftPrune(tograft, toprune map[peer.ID][][]byte
prune = append(prune, bs.makePrune(p, bitmask, bs.doPX && !noPX[p], false))
}
out := rpcWithControl(nil, nil, nil, nil, prune)
bs.sendRPC(p, out)
out := rpcWithControl(nil, nil, nil, nil, prune, nil)
bs.sendRPC(p, out, false)
}
}
@ -1929,15 +2019,15 @@ func (bs *BlossomSubRouter) flush() {
// send gossip first, which will also piggyback pending control
for p, ihave := range bs.gossip {
delete(bs.gossip, p)
out := rpcWithControl(nil, ihave, nil, nil, nil)
bs.sendRPC(p, out)
out := rpcWithControl(nil, ihave, nil, nil, nil, nil)
bs.sendRPC(p, out, false)
}
// send the remaining control messages that wasn't merged with gossip
for p, ctl := range bs.control {
delete(bs.control, p)
out := rpcWithControl(nil, nil, nil, ctl.Graft, ctl.Prune)
bs.sendRPC(p, out)
out := rpcWithControl(nil, nil, nil, ctl.Graft, ctl.Prune, nil)
bs.sendRPC(p, out, false)
}
}
@ -1973,9 +2063,7 @@ func (bs *BlossomSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.Contro
for _, graft := range ctl.GetGraft() {
bitmask := graft.GetBitmask()
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
continue
}
@ -1987,9 +2075,7 @@ func (bs *BlossomSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.Contro
for _, prune := range ctl.GetPrune() {
bitmask := prune.GetBitmask()
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
toprune = append(toprune, prune)
continue

View File

@ -18,18 +18,22 @@ const (
BlossomSubFeatureMesh = iota
// Protocol supports Peer eXchange on prune -- BlossomSub-v2 compatible
BlossomSubFeaturePX
// Protocol supports IDONTWANT -- BlossomSub-v2.1 compatible
BlossomSubFeatureIdontwant
)
// BlossomSubDefaultProtocols is the default BlossomSub router protocol list
var BlossomSubDefaultProtocols = []protocol.ID{BlossomSubID_v2}
var BlossomSubDefaultProtocols = []protocol.ID{BlossomSubID_v21, BlossomSubID_v2}
// BlossomSubDefaultFeatures is the feature test function for the default BlossomSub protocols
func BlossomSubDefaultFeatures(feat BlossomSubFeature, proto protocol.ID) bool {
switch feat {
case BlossomSubFeatureMesh:
return proto == BlossomSubID_v2
return proto == BlossomSubID_v21 || proto == BlossomSubID_v2
case BlossomSubFeaturePX:
return proto == BlossomSubID_v2
return proto == BlossomSubID_v21 || proto == BlossomSubID_v2
case BlossomSubFeatureIdontwant:
return proto == BlossomSubID_v21
default:
return false
}

View File

@ -15,10 +15,23 @@ func TestDefaultBlossomSubFeatures(t *testing.T) {
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v2) {
t.Fatal("BlossomSub-v2.0 should support Mesh")
}
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v21) {
t.Fatal("BlossomSub-v2.1 should support Mesh")
}
if !BlossomSubDefaultFeatures(BlossomSubFeaturePX, BlossomSubID_v2) {
t.Fatal("BlossomSub-v2.0 should support PX")
}
if !BlossomSubDefaultFeatures(BlossomSubFeaturePX, BlossomSubID_v21) {
t.Fatal("BlossomSub-v2.0 should support PX")
}
if BlossomSubDefaultFeatures(BlossomSubFeatureIdontwant, BlossomSubID_v2) {
t.Fatal("BlossomSub-v2.0 should not support IDONTWANT")
}
if !BlossomSubDefaultFeatures(BlossomSubFeatureIdontwant, BlossomSubID_v21) {
t.Fatal("BlossomSub-v2.1 should support IDONTWANT")
}
}
func TestBlossomSubCustomProtocols(t *testing.T) {

View File

@ -1,9 +1,11 @@
package blossomsub
import (
"bytes"
"context"
"crypto/rand"
"crypto/sha256"
"fmt"
"math/rand"
"strconv"
"sync"
"testing"
@ -12,6 +14,7 @@ import (
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-msgio"
"google.golang.org/protobuf/proto"
@ -122,7 +125,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
// being spammy)
iwantlst := [][]byte{DefaultMsgIdFn(msg)}
iwant := []*pb.ControlIWant{{MessageIDs: iwantlst}}
orpc := rpcWithControl(nil, nil, iwant, nil, nil)
orpc := rpcWithControl(nil, nil, iwant, nil, nil, nil)
writeMsg(orpc.RPC)
}
})
@ -216,7 +219,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
for i := 0; i < 3*BlossomSubMaxIHaveLength; i++ {
ihavelst := [][]byte{[]byte("someid" + strconv.Itoa(i))}
ihave := []*pb.ControlIHave{{Bitmask: sub.Bitmask, MessageIDs: ihavelst}}
orpc := rpcWithControl(nil, ihave, nil, nil, nil)
orpc := rpcWithControl(nil, ihave, nil, nil, nil, nil)
writeMsg(orpc.RPC)
}
@ -246,7 +249,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
for i := 0; i < 3*BlossomSubMaxIHaveLength; i++ {
ihavelst := [][]byte{[]byte("someid" + strconv.Itoa(i+100))}
ihave := []*pb.ControlIHave{{Bitmask: sub.Bitmask, MessageIDs: ihavelst}}
orpc := rpcWithControl(nil, ihave, nil, nil, nil)
orpc := rpcWithControl(nil, ihave, nil, nil, nil, nil)
writeMsg(orpc.RPC)
}
@ -775,16 +778,151 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
<-ctx.Done()
}
// Test that when BlossomSub receives too many IDONTWANT messages from a peer
func TestBlossomSubAttackSpamIDONTWANT(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
msgID := func(pmsg *pb.Message) []byte {
mid := sha256.Sum256(pmsg.GetData())
return mid[:]
}
psubs := make([]*PubSub, 2)
psubs[0] = getBlossomSub(ctx, hosts[0], WithMessageIdFn(msgID))
psubs[1] = getBlossomSub(ctx, hosts[1], WithMessageIdFn(msgID))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking the result
msgWaitMax := time.Second + BlossomSubHeartbeatInterval
msgTimer := time.NewTimer(msgWaitMax)
// Checks we received some messages
var midsMu sync.RWMutex
var expMid []byte
var actMids [][]byte
checkMsgs := func() {
midsMu.RLock()
defer midsMu.RUnlock()
if len(actMids) == 0 {
t.Fatalf("Expected some messages when the maximum number of IDONTWANTs is reached")
}
if !bytes.Equal(actMids[0], expMid) {
t.Fatalf("The expected message is incorrect")
}
if len(actMids) > 1 {
t.Fatalf("The spam prevention should be reset after the heartbeat")
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
newMockBS(ctx, t, hosts[2], func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// Each time the host receives a message
midsMu.Lock()
for _, msg := range irpc.GetPublish() {
actMids = append(actMids, msgID(msg))
}
midsMu.Unlock()
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the middle peer
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe + graft
time.Sleep(100 * time.Millisecond)
// Generate a message and send IDONTWANT to the middle peer
data := make([]byte, 16)
var mid []byte
for i := 0; i < 1+BlossomSubMaxIDontWantMessages; i++ {
rand.Read(data)
mid = msgID(&pb.Message{Data: data})
writeMsg(&pb.RPC{
Control: &pb.ControlMessage{Idontwant: []*pb.ControlIDontWant{{MessageIDs: [][]byte{mid}}}},
})
}
// The host should receives this message id because the maximum was reached
midsMu.Lock()
expMid = mid
midsMu.Unlock()
// Wait for a short interval to make sure the middle peer
// received and processed the IDONTWANTs
time.Sleep(100 * time.Millisecond)
// Publish the message from the first peer
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Error(err)
return // cannot call t.Fatal in a non-test goroutine
}
// Wait for the next heartbeat so that the prevention will be reset
select {
case <-ctx.Done():
return
case <-time.After(BlossomSubHeartbeatInterval):
}
// Test IDONTWANT again to see that it now works again
rand.Read(data)
mid = msgID(&pb.Message{Data: data})
writeMsg(&pb.RPC{
Control: &pb.ControlMessage{Idontwant: []*pb.ControlIDontWant{{MessageIDs: [][]byte{mid}}}},
})
time.Sleep(100 * time.Millisecond)
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Error(err)
return // cannot call t.Fatal in a non-test goroutine
}
}()
}
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
type MockBSOnRead func(writeMsg func(*pb.RPC), irpc *pb.RPC)
func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg MockBSOnRead) {
newMockBSWithVersion(ctx, t, attacker, BlossomSubID_v21, onReadMsg)
}
func newMockBSWithVersion(ctx context.Context, t *testing.T, attacker host.Host, blossomSubID protocol.ID, onReadMsg MockBSOnRead) {
// Listen on the BlossomSub protocol
const BlossomSubID = BlossomSubID_v2
const maxMessageSize = 1024 * 1024
attacker.SetStreamHandler(BlossomSubID, func(stream network.Stream) {
attacker.SetStreamHandler(blossomSubID, func(stream network.Stream) {
// When an incoming stream is opened, set up an outgoing stream
p := stream.Conn().RemotePeer()
ostream, err := attacker.NewStream(ctx, p, BlossomSubID)
ostream, err := attacker.NewStream(ctx, p, blossomSubID)
if err != nil {
t.Fatal(err)
}

View File

@ -3,10 +3,14 @@ package blossomsub
import (
"bytes"
"context"
crand "crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"maps"
"math/rand"
"slices"
"sort"
"sync"
"sync/atomic"
"testing"
@ -41,7 +45,7 @@ func assertPeerLists(t *testing.T, bitmask []byte, hosts []host.Host, ps *PubSub
func checkMessageRouting(t *testing.T, ctx context.Context, bitmasks []*Bitmask, subs [][]*Subscription) {
for _, p := range bitmasks {
data := make([]byte, 16)
rand.Read(data)
crand.Read(data)
err := p.Publish(ctx, p.bitmask, data)
if err != nil {
t.Fatal(err)
@ -2258,10 +2262,14 @@ func TestBlossomSubJoinBitmask(t *testing.T) {
router0 := psubs[0].rt.(*BlossomSubRouter)
// Add in backoff for peer.
peerMap := make(map[peer.ID]time.Time)
peerMap[h[1].ID()] = time.Now().Add(router0.params.UnsubscribeBackoff)
router0.backoff[string([]byte{0x00, 0x00, 0x81, 0x00})] = peerMap
ran := make(chan struct{})
router0.p.eval <- func() {
defer close(ran)
peerMap := make(map[peer.ID]time.Time)
peerMap[h[1].ID()] = time.Now().Add(router0.params.UnsubscribeBackoff)
router0.backoff[string([]byte{0x00, 0x00, 0x81, 0x00})] = peerMap
}
<-ran
// Join all peers
var subs []*Subscription
@ -2275,13 +2283,17 @@ func TestBlossomSubJoinBitmask(t *testing.T) {
time.Sleep(time.Second)
router0.meshMx.RLock()
meshMap := router0.mesh[string([]byte{0x00, 0x00, 0x81, 0x00})]
router0.meshMx.RUnlock()
ran = make(chan struct{})
var meshMap map[peer.ID]struct{}
router0.p.eval <- func() {
defer close(ran)
meshMap = maps.Clone(router0.mesh[string([]byte{0x00, 0x00, 0x81, 0x00})])
}
<-ran
if len(meshMap) != 1 {
t.Fatalf("Unexpect peer included in the mesh")
}
_, ok := meshMap[h[1].ID()]
if ok {
t.Fatalf("Peer that was to be backed off is included in the mesh")
@ -2497,7 +2509,7 @@ func TestBlossomSubRPCFragmentation(t *testing.T) {
msgSize := 20000
for i := 0; i < nMessages; i++ {
msg := make([]byte, msgSize)
rand.Read(msg)
crand.Read(msg)
b[0].Publish(ctx, []byte{0x00, 0x00, 0x81, 0x00}, msg)
time.Sleep(20 * time.Millisecond)
}
@ -2610,7 +2622,7 @@ func (iwe *iwantEverything) handleStream(s network.Stream) {
}
}
msg := rpcWithControl(nil, nil, iwants, nil, prunes)
msg := rpcWithControl(nil, nil, iwants, nil, prunes, nil)
out, err := proto.Marshal(msg)
if err != nil {
@ -2635,7 +2647,7 @@ func TestFragmentRPCFunction(t *testing.T) {
mkMsg := func(size int) *pb.Message {
msg := &pb.Message{}
msg.Data = make([]byte, size-4) // subtract the protobuf overhead, so msg.Size() returns requested size
rand.Read(msg.Data)
crand.Read(msg.Data)
return msg
}
@ -2737,7 +2749,7 @@ func TestFragmentRPCFunction(t *testing.T) {
messageIds := make([][]byte, msgsPerBitmask)
for m := 0; m < msgsPerBitmask; m++ {
mid := make([]byte, messageIdSize)
rand.Read(mid)
crand.Read(mid)
messageIds[m] = mid
}
rpc.Control.Ihave[i] = &pb.ControlIHave{MessageIDs: messageIds}
@ -2754,7 +2766,7 @@ func TestFragmentRPCFunction(t *testing.T) {
// Test the pathological case where a single gossip message ID exceeds the limit.
rpc.Reset()
giantIdBytes := make([]byte, limit*2)
rand.Read(giantIdBytes)
crand.Read(giantIdBytes)
rpc.Control = &pb.ControlMessage{
Iwant: []*pb.ControlIWant{
{MessageIDs: [][]byte{[]byte("hello"), giantIdBytes}},
@ -2780,6 +2792,595 @@ func TestFragmentRPCFunction(t *testing.T) {
}
}
func TestBlossomSubIdontwantSend(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
msgID := func(pmsg *pb.Message) []byte {
mid := sha256.Sum256(pmsg.Data)
return mid[:]
}
var validated atomic.Bool
validate := func(context.Context, peer.ID, *Message) bool {
time.Sleep(100 * time.Millisecond)
validated.Store(true)
return true
}
params := DefaultBlossomSubParams()
params.IDontWantMessageThreshold = 16
psubs := make([]*PubSub, 2)
psubs[0] = getBlossomSub(ctx, hosts[0],
WithBlossomSubParams(params),
WithMessageIdFn(msgID))
psubs[1] = getBlossomSub(ctx, hosts[1],
WithBlossomSubParams(params),
WithMessageIdFn(msgID),
WithDefaultValidator(validate))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
var expMids [][]byte
var actMids [][]byte
// Used to publish a message with random data
publishMsg := func() {
data := make([]byte, 16)
crand.Read(data)
m := &pb.Message{Data: data}
expMids = append(expMids, msgID(m))
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking we got the right messages
msgWaitMax := time.Second
msgTimer := time.NewTimer(msgWaitMax)
// Checks we received the right IDONTWANT messages
checkMsgs := func() {
sort.Slice(actMids, func(i, j int) bool {
return bytes.Compare(actMids[i], actMids[j]) < 0
})
sort.Slice(expMids, func(i, j int) bool {
return bytes.Compare(expMids[i], expMids[j]) < 0
})
if len(actMids) != len(expMids) {
t.Fatalf("Expected %d IDONTWANT messages, got %d", len(expMids), len(actMids))
}
for i, expMid := range expMids {
actMid := actMids[i]
if !bytes.Equal(expMid, actMid) {
t.Fatalf("Expected the id of %x in the %d'th IDONTWANT messages, got %x", expMid, i+1, actMid)
}
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
newMockBS(ctx, t, hosts[2], func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the middle peer
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe + graft
time.Sleep(100 * time.Millisecond)
// Publish messages from the first peer
for i := 0; i < 10; i++ {
publishMsg()
}
}()
}
}
// Each time the middle peer sends an IDONTWANT message
for _, idonthave := range irpc.GetControl().GetIdontwant() {
// If true, it means that, when we get IDONTWANT, the middle peer has done validation
// already, which should not be the case
if validated.Load() {
t.Fatalf("IDONTWANT should be sent before doing validation")
}
for _, mid := range idonthave.GetMessageIDs() {
// Add the message to the list and reset the timer
actMids = append(actMids, mid)
msgTimer.Reset(msgWaitMax)
}
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
func TestBlossomSubIdontwantReceive(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
msgID := func(pmsg *pb.Message) []byte {
mid := sha256.Sum256(pmsg.Data)
return mid[:]
}
psubs := make([]*PubSub, 2)
psubs[0] = getBlossomSub(ctx, hosts[0], WithMessageIdFn(msgID))
psubs[1] = getBlossomSub(ctx, hosts[1], WithMessageIdFn(msgID))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking the result
msgWaitMax := time.Second
msgTimer := time.NewTimer(msgWaitMax)
// Checks we received no messages
received := false
checkMsgs := func() {
if received {
t.Fatalf("Expected no messages received after IDONWANT")
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
newMockBS(ctx, t, hosts[2], func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// Check if it receives any message
if len(irpc.GetPublish()) > 0 {
received = true
}
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the middle peer
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe + graft
time.Sleep(100 * time.Millisecond)
// Generate a message and send IDONTWANT to the middle peer
data := make([]byte, 16)
crand.Read(data)
mid := msgID(&pb.Message{Data: data})
writeMsg(&pb.RPC{
Control: &pb.ControlMessage{Idontwant: []*pb.ControlIDontWant{{MessageIDs: [][]byte{mid}}}},
})
// Wait for a short interval to make sure the middle peer
// received and processed the IDONTWANTs
time.Sleep(100 * time.Millisecond)
// Publish the message from the first peer
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Error(err)
return // cannot call t.Fatal in a non-test goroutine
}
}()
}
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
// Test that non-mesh peers will not get IDONTWANT
func TestBlossomSubIdontwantNonMesh(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
params := DefaultBlossomSubParams()
params.IDontWantMessageThreshold = 16
psubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubParams(params))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// Used to publish a message with random data
publishMsg := func() {
data := make([]byte, 16)
crand.Read(data)
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking we got the right messages
msgWaitMax := time.Second
msgTimer := time.NewTimer(msgWaitMax)
received := false
// Checks if we received any IDONTWANT
checkMsgs := func() {
if received {
t.Fatalf("No IDONTWANT is expected")
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
newMockBS(ctx, t, hosts[2], func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and pruning to the middle peer to make sure
// that it's not in the mesh
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Prune: []*pb.ControlPrune{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe
time.Sleep(100 * time.Millisecond)
// Publish messages from the first peer
for i := 0; i < 10; i++ {
publishMsg()
}
}()
}
}
// Each time the middle peer sends an IDONTWANT message
for range irpc.GetControl().GetIdontwant() {
received = true
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
// Test that peers with incompatible versions will not get IDONTWANT
func TestBlossomSubIdontwantIncompat(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
params := DefaultBlossomSubParams()
params.IDontWantMessageThreshold = 16
psubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubParams(params))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// Used to publish a message with random data
publishMsg := func() {
data := make([]byte, 16)
crand.Read(data)
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking we got the right messages
msgWaitMax := time.Second
msgTimer := time.NewTimer(msgWaitMax)
received := false
// Checks if we received any IDONTWANT
checkMsgs := func() {
if received {
t.Fatalf("No IDONTWANT is expected")
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
// Use the old BlossomSub version
newMockBSWithVersion(ctx, t, hosts[2], BlossomSubID_v2, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the middle peer
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe + graft
time.Sleep(100 * time.Millisecond)
// Publish messages from the first peer
for i := 0; i < 10; i++ {
publishMsg()
}
}()
}
}
// Each time the middle peer sends an IDONTWANT message
for range irpc.GetControl().GetIdontwant() {
received = true
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
// Test that IDONTWANT will not be sent for small messages
func TestBlossomSubIdontwantSmallMessage(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
params := DefaultBlossomSubParams()
params.IDontWantMessageThreshold = 16
psubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubParams(params))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// Used to publish a message with random data
publishMsg := func() {
data := make([]byte, 8)
crand.Read(data)
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking we got the right messages
msgWaitMax := time.Second
msgTimer := time.NewTimer(msgWaitMax)
received := false
// Checks if we received any IDONTWANT
checkMsgs := func() {
if received {
t.Fatalf("No IDONTWANT is expected")
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
newMockBS(ctx, t, hosts[2], func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and pruning to the middle peer to make sure
// that it's not in the mesh
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe
time.Sleep(100 * time.Millisecond)
// Publish messages from the first peer
for i := 0; i < 10; i++ {
publishMsg()
}
}()
}
}
// Each time the middle peer sends an IDONTWANT message
for range irpc.GetControl().GetIdontwant() {
received = true
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
// Test that IDONTWANT will cleared when it's old enough
func TestBlossomSubIdontwantClear(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getDefaultHosts(t, 3)
msgID := func(pmsg *pb.Message) []byte {
mid := sha256.Sum256(pmsg.Data)
return mid[:]
}
params := DefaultBlossomSubParams()
params.IDontWantMessageTTL = 3
psubs := make([]*PubSub, 2)
psubs[0] = getBlossomSub(ctx, hosts[0], WithMessageIdFn(msgID), WithBlossomSubParams(params))
psubs[1] = getBlossomSub(ctx, hosts[1], WithMessageIdFn(msgID), WithBlossomSubParams(params))
bitmask := []byte{0x20, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
}
// Wait a bit after the last message before checking the result
msgWaitMax := 5 * time.Second
msgTimer := time.NewTimer(msgWaitMax)
// Checks we received some message after the IDONTWANT is cleared
var received atomic.Bool
checkMsgs := func() {
if !received.Load() {
t.Fatalf("Expected some message after the IDONTWANT is cleared")
}
}
// Wait for the timer to expire
go func() {
select {
case <-msgTimer.C:
checkMsgs()
cancel()
return
case <-ctx.Done():
checkMsgs()
}
}()
newMockBS(ctx, t, hosts[2], func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// Check if it receives any message
if len(irpc.GetPublish()) > 0 {
received.Store(true)
}
// When the middle peer connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the middle peer
writeMsg(&pb.RPC{
Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Bitmask: sub.Bitmask}},
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: sub.Bitmask}}},
})
go func() {
// Wait for a short interval to make sure the middle peer
// received and processed the subscribe + graft
time.Sleep(100 * time.Millisecond)
// Generate a message and send IDONTWANT to the middle peer
data := make([]byte, 16)
crand.Read(data)
mid := msgID(&pb.Message{Data: data})
writeMsg(&pb.RPC{
Control: &pb.ControlMessage{Idontwant: []*pb.ControlIDontWant{{MessageIDs: [][]byte{mid}}}},
})
// Wait for a short interval to make sure the middle peer
// received and processed the IDONTWANTs
time.Sleep(100 * time.Millisecond)
// Wait for 4 heartbeats to make sure the IDONTWANT is cleared
time.Sleep(4 * time.Second)
// Publish the message from the first peer
if err := psubs[0].Publish(ctx, bitmask, data); err != nil {
t.Error(err)
return // cannot call t.Fatal in a non-test goroutine
}
}()
}
}
})
connect(t, hosts[0], hosts[1])
connect(t, hosts[1], hosts[2])
<-ctx.Done()
}
func TestBloomRouting(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -2933,7 +3534,7 @@ func TestBloomPropagationOverSubTreeTopology(t *testing.T) {
for _, p := range bitmasks {
data := make([]byte, 32)
rand.Read(data)
crand.Read(data)
err := p[0].Publish(ctx, []byte{0x10, 0x10, 0x10, 0x00}, data)
if err != nil {
t.Fatal(err)

View File

@ -126,7 +126,7 @@ func (p *PubSub) notifyPeerDead(pid peer.ID) {
}
}
func (p *PubSub) handleNewPeer(ctx context.Context, pid peer.ID, outgoing <-chan *RPC) {
func (p *PubSub) handleNewPeer(ctx context.Context, pid peer.ID, q *rpcQueue) {
s, err := p.host.NewStream(p.ctx, pid, p.rt.Protocols()...)
if err != nil {
log.Debug("opening new stream to peer: ", err, pid)
@ -139,7 +139,7 @@ func (p *PubSub) handleNewPeer(ctx context.Context, pid peer.ID, outgoing <-chan
return
}
go p.handleSendingMessages(ctx, s, outgoing)
go p.handleSendingMessages(ctx, s, q)
go p.handlePeerDead(s)
select {
case p.newPeerStream <- s:
@ -147,10 +147,10 @@ func (p *PubSub) handleNewPeer(ctx context.Context, pid peer.ID, outgoing <-chan
}
}
func (p *PubSub) handleNewPeerWithBackoff(ctx context.Context, pid peer.ID, backoff time.Duration, outgoing <-chan *RPC) {
func (p *PubSub) handleNewPeerWithBackoff(ctx context.Context, pid peer.ID, backoff time.Duration, q *rpcQueue) {
select {
case <-time.After(backoff):
p.handleNewPeer(ctx, pid, outgoing)
p.handleNewPeer(ctx, pid, q)
case <-ctx.Done():
return
}
@ -168,39 +168,29 @@ func (p *PubSub) handlePeerDead(s network.Stream) {
p.notifyPeerDead(pid)
}
func (p *PubSub) handleSendingMessages(ctx context.Context, s network.Stream, outgoing <-chan *RPC) {
func (p *PubSub) handleSendingMessages(ctx context.Context, s network.Stream, q *rpcQueue) {
writeRPC := func(rpc *RPC) error {
size := uint64(rpc.Size())
buf := pool.Get(varint.UvarintSize(size) + int(size))
defer pool.Put(buf)
n := binary.PutUvarint(buf, size)
_, err := rpc.MarshalTo(buf[n:])
if err != nil {
return err
}
_, err = s.Write(buf)
return err
}
defer s.Close()
defer s.Reset()
for {
select {
case rpc, ok := <-outgoing:
if !ok {
s.Close()
return
}
size := uint64(rpc.Size())
buf := pool.Get(varint.UvarintSize(size) + int(size))
n := binary.PutUvarint(buf, size)
_, err := rpc.MarshalTo(buf[n:])
if err != nil {
s.Reset()
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
s.Close()
pool.Put(buf)
return
}
_, err = s.Write(buf)
pool.Put(buf)
if err != nil {
s.Reset()
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
s.Close()
return
}
case <-ctx.Done():
s.Close()
rpc, err := q.Pop(ctx)
if err != nil {
log.Debugf("pop RPC from queue: %s", err)
return
}
if err := writeRPC(rpc); err != nil {
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
return
}
}
@ -222,15 +212,17 @@ func rpcWithControl(msgs []*pb.Message,
ihave []*pb.ControlIHave,
iwant []*pb.ControlIWant,
graft []*pb.ControlGraft,
prune []*pb.ControlPrune) *RPC {
prune []*pb.ControlPrune,
idontwant []*pb.ControlIDontWant) *RPC {
return &RPC{
RPC: &pb.RPC{
Publish: msgs,
Control: &pb.ControlMessage{
Ihave: ihave,
Iwant: iwant,
Graft: graft,
Prune: prune,
Ihave: ihave,
Iwant: iwant,
Graft: graft,
Prune: prune,
Idontwant: idontwant,
},
},
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// protoc-gen-go v1.34.1
// protoc v5.27.0
// source: rpc.proto
package pb
@ -175,10 +175,11 @@ type ControlMessage struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Ihave []*ControlIHave `protobuf:"bytes,1,rep,name=ihave,proto3" json:"ihave,omitempty"`
Iwant []*ControlIWant `protobuf:"bytes,2,rep,name=iwant,proto3" json:"iwant,omitempty"`
Graft []*ControlGraft `protobuf:"bytes,3,rep,name=graft,proto3" json:"graft,omitempty"`
Prune []*ControlPrune `protobuf:"bytes,4,rep,name=prune,proto3" json:"prune,omitempty"`
Ihave []*ControlIHave `protobuf:"bytes,1,rep,name=ihave,proto3" json:"ihave,omitempty"`
Iwant []*ControlIWant `protobuf:"bytes,2,rep,name=iwant,proto3" json:"iwant,omitempty"`
Graft []*ControlGraft `protobuf:"bytes,3,rep,name=graft,proto3" json:"graft,omitempty"`
Prune []*ControlPrune `protobuf:"bytes,4,rep,name=prune,proto3" json:"prune,omitempty"`
Idontwant []*ControlIDontWant `protobuf:"bytes,5,rep,name=idontwant,proto3" json:"idontwant,omitempty"`
}
func (x *ControlMessage) Reset() {
@ -241,6 +242,13 @@ func (x *ControlMessage) GetPrune() []*ControlPrune {
return nil
}
func (x *ControlMessage) GetIdontwant() []*ControlIDontWant {
if x != nil {
return x.Idontwant
}
return nil
}
type ControlIHave struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -453,6 +461,53 @@ func (x *ControlPrune) GetBackoff() uint64 {
return 0
}
type ControlIDontWant struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MessageIDs [][]byte `protobuf:"bytes,1,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
}
func (x *ControlIDontWant) Reset() {
*x = ControlIDontWant{}
if protoimpl.UnsafeEnabled {
mi := &file_rpc_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ControlIDontWant) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ControlIDontWant) ProtoMessage() {}
func (x *ControlIDontWant) ProtoReflect() protoreflect.Message {
mi := &file_rpc_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ControlIDontWant.ProtoReflect.Descriptor instead.
func (*ControlIDontWant) Descriptor() ([]byte, []int) {
return file_rpc_proto_rawDescGZIP(), []int{7}
}
func (x *ControlIDontWant) GetMessageIDs() [][]byte {
if x != nil {
return x.MessageIDs
}
return nil
}
type PeerInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -465,7 +520,7 @@ type PeerInfo struct {
func (x *PeerInfo) Reset() {
*x = PeerInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_rpc_proto_msgTypes[7]
mi := &file_rpc_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -478,7 +533,7 @@ func (x *PeerInfo) String() string {
func (*PeerInfo) ProtoMessage() {}
func (x *PeerInfo) ProtoReflect() protoreflect.Message {
mi := &file_rpc_proto_msgTypes[7]
mi := &file_rpc_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -491,7 +546,7 @@ func (x *PeerInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use PeerInfo.ProtoReflect.Descriptor instead.
func (*PeerInfo) Descriptor() ([]byte, []int) {
return file_rpc_proto_rawDescGZIP(), []int{7}
return file_rpc_proto_rawDescGZIP(), []int{8}
}
func (x *PeerInfo) GetPeerID() []byte {
@ -520,7 +575,7 @@ type RPC_SubOpts struct {
func (x *RPC_SubOpts) Reset() {
*x = RPC_SubOpts{}
if protoimpl.UnsafeEnabled {
mi := &file_rpc_proto_msgTypes[8]
mi := &file_rpc_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -533,7 +588,7 @@ func (x *RPC_SubOpts) String() string {
func (*RPC_SubOpts) ProtoMessage() {}
func (x *RPC_SubOpts) ProtoReflect() protoreflect.Message {
mi := &file_rpc_proto_msgTypes[8]
mi := &file_rpc_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -592,7 +647,7 @@ var file_rpc_proto_rawDesc = []byte{
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74,
0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61,
0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xdc, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x9b, 0x02, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x68, 0x61,
0x76, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73,
0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
@ -606,37 +661,44 @@ var file_rpc_proto_rawDesc = []byte{
0x66, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70,
0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x52, 0x05,
0x70, 0x72, 0x75, 0x6e, 0x65, 0x22, 0x48, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x49, 0x48, 0x61, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12,
0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
0x2e, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x12,
0x70, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x3d, 0x0a, 0x09, 0x69, 0x64, 0x6f, 0x6e, 0x74, 0x77, 0x61,
0x6e, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73,
0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x49, 0x44, 0x6f, 0x6e, 0x74, 0x57, 0x61, 0x6e, 0x74, 0x52, 0x09, 0x69, 0x64, 0x6f, 0x6e, 0x74,
0x77, 0x61, 0x6e, 0x74, 0x22, 0x48, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49,
0x48, 0x61, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x1e,
0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22, 0x2e,
0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x12, 0x1e,
0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22, 0x28,
0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12, 0x18,
0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x71, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d,
0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x12, 0x2d, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70,
0x62, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x03, 0x20, 0x01,
0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x22, 0x32, 0x0a, 0x10, 0x43,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x44, 0x6f, 0x6e, 0x74, 0x57, 0x61, 0x6e, 0x74, 0x12,
0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
0x28, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12,
0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x71, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74,
0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d,
0x61, 0x73, 0x6b, 0x12, 0x2d, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e,
0x70, 0x62, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x70, 0x65, 0x65,
0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x03, 0x20,
0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x22, 0x78, 0x0a, 0x08,
0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72,
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72,
0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50,
0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x01, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63,
0x6f, 0x72, 0x64, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49,
0x44, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72,
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x42, 0x43, 0x5a, 0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72,
0x65, 0x70, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c,
0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x78, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x06, 0x70,
0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70,
0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e,
0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0c, 0x48, 0x01, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72,
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65,
0x65, 0x72, 0x49, 0x44, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50,
0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x42, 0x43, 0x5a, 0x41, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x6d, 0x6f,
0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70,
0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2f, 0x70, 0x62, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -651,32 +713,34 @@ func file_rpc_proto_rawDescGZIP() []byte {
return file_rpc_proto_rawDescData
}
var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_rpc_proto_goTypes = []interface{}{
(*RPC)(nil), // 0: blossomsub.pb.RPC
(*Message)(nil), // 1: blossomsub.pb.Message
(*ControlMessage)(nil), // 2: blossomsub.pb.ControlMessage
(*ControlIHave)(nil), // 3: blossomsub.pb.ControlIHave
(*ControlIWant)(nil), // 4: blossomsub.pb.ControlIWant
(*ControlGraft)(nil), // 5: blossomsub.pb.ControlGraft
(*ControlPrune)(nil), // 6: blossomsub.pb.ControlPrune
(*PeerInfo)(nil), // 7: blossomsub.pb.PeerInfo
(*RPC_SubOpts)(nil), // 8: blossomsub.pb.RPC.SubOpts
(*RPC)(nil), // 0: blossomsub.pb.RPC
(*Message)(nil), // 1: blossomsub.pb.Message
(*ControlMessage)(nil), // 2: blossomsub.pb.ControlMessage
(*ControlIHave)(nil), // 3: blossomsub.pb.ControlIHave
(*ControlIWant)(nil), // 4: blossomsub.pb.ControlIWant
(*ControlGraft)(nil), // 5: blossomsub.pb.ControlGraft
(*ControlPrune)(nil), // 6: blossomsub.pb.ControlPrune
(*ControlIDontWant)(nil), // 7: blossomsub.pb.ControlIDontWant
(*PeerInfo)(nil), // 8: blossomsub.pb.PeerInfo
(*RPC_SubOpts)(nil), // 9: blossomsub.pb.RPC.SubOpts
}
var file_rpc_proto_depIdxs = []int32{
8, // 0: blossomsub.pb.RPC.subscriptions:type_name -> blossomsub.pb.RPC.SubOpts
9, // 0: blossomsub.pb.RPC.subscriptions:type_name -> blossomsub.pb.RPC.SubOpts
1, // 1: blossomsub.pb.RPC.publish:type_name -> blossomsub.pb.Message
2, // 2: blossomsub.pb.RPC.control:type_name -> blossomsub.pb.ControlMessage
3, // 3: blossomsub.pb.ControlMessage.ihave:type_name -> blossomsub.pb.ControlIHave
4, // 4: blossomsub.pb.ControlMessage.iwant:type_name -> blossomsub.pb.ControlIWant
5, // 5: blossomsub.pb.ControlMessage.graft:type_name -> blossomsub.pb.ControlGraft
6, // 6: blossomsub.pb.ControlMessage.prune:type_name -> blossomsub.pb.ControlPrune
7, // 7: blossomsub.pb.ControlPrune.peers:type_name -> blossomsub.pb.PeerInfo
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
7, // 7: blossomsub.pb.ControlMessage.idontwant:type_name -> blossomsub.pb.ControlIDontWant
8, // 8: blossomsub.pb.ControlPrune.peers:type_name -> blossomsub.pb.PeerInfo
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_rpc_proto_init() }
@ -770,7 +834,7 @@ func file_rpc_proto_init() {
}
}
file_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PeerInfo); i {
switch v := v.(*ControlIDontWant); i {
case 0:
return &v.state
case 1:
@ -782,6 +846,18 @@ func file_rpc_proto_init() {
}
}
file_rpc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PeerInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_rpc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RPC_SubOpts); i {
case 0:
return &v.state
@ -794,14 +870,14 @@ func file_rpc_proto_init() {
}
}
}
file_rpc_proto_msgTypes[7].OneofWrappers = []interface{}{}
file_rpc_proto_msgTypes[8].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_rpc_proto_rawDesc,
NumEnums: 0,
NumMessages: 9,
NumMessages: 10,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -30,6 +30,7 @@ message ControlMessage {
repeated ControlIWant iwant = 2;
repeated ControlGraft graft = 3;
repeated ControlPrune prune = 4;
repeated ControlIDontWant idontwant = 5;
}
message ControlIHave {
@ -51,6 +52,10 @@ message ControlPrune {
uint64 backoff = 3;
}
message ControlIDontWant {
repeated bytes messageIDs = 1;
}
message PeerInfo {
optional bytes peerID = 1;
optional bytes signedPeerRecord = 2;

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// protoc-gen-go v1.34.1
// protoc v5.27.0
// source: trace.proto
package pb
@ -1288,10 +1288,11 @@ type TraceEvent_ControlMeta struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Ihave []*TraceEvent_ControlIHaveMeta `protobuf:"bytes,1,rep,name=ihave,proto3" json:"ihave,omitempty"`
Iwant []*TraceEvent_ControlIWantMeta `protobuf:"bytes,2,rep,name=iwant,proto3" json:"iwant,omitempty"`
Graft []*TraceEvent_ControlGraftMeta `protobuf:"bytes,3,rep,name=graft,proto3" json:"graft,omitempty"`
Prune []*TraceEvent_ControlPruneMeta `protobuf:"bytes,4,rep,name=prune,proto3" json:"prune,omitempty"`
Ihave []*TraceEvent_ControlIHaveMeta `protobuf:"bytes,1,rep,name=ihave,proto3" json:"ihave,omitempty"`
Iwant []*TraceEvent_ControlIWantMeta `protobuf:"bytes,2,rep,name=iwant,proto3" json:"iwant,omitempty"`
Graft []*TraceEvent_ControlGraftMeta `protobuf:"bytes,3,rep,name=graft,proto3" json:"graft,omitempty"`
Prune []*TraceEvent_ControlPruneMeta `protobuf:"bytes,4,rep,name=prune,proto3" json:"prune,omitempty"`
Idontwant []*TraceEvent_ControlIDontWantMeta `protobuf:"bytes,5,rep,name=idontwant,proto3" json:"idontwant,omitempty"`
}
func (x *TraceEvent_ControlMeta) Reset() {
@ -1354,6 +1355,13 @@ func (x *TraceEvent_ControlMeta) GetPrune() []*TraceEvent_ControlPruneMeta {
return nil
}
func (x *TraceEvent_ControlMeta) GetIdontwant() []*TraceEvent_ControlIDontWantMeta {
if x != nil {
return x.Idontwant
}
return nil
}
type TraceEvent_ControlIHaveMeta struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1558,11 +1566,58 @@ func (x *TraceEvent_ControlPruneMeta) GetPeers() [][]byte {
return nil
}
type TraceEvent_ControlIDontWantMeta struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MessageIDs [][]byte `protobuf:"bytes,1,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
}
func (x *TraceEvent_ControlIDontWantMeta) Reset() {
*x = TraceEvent_ControlIDontWantMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TraceEvent_ControlIDontWantMeta) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TraceEvent_ControlIDontWantMeta) ProtoMessage() {}
func (x *TraceEvent_ControlIDontWantMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TraceEvent_ControlIDontWantMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_ControlIDontWantMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 22}
}
func (x *TraceEvent_ControlIDontWantMeta) GetMessageIDs() [][]byte {
if x != nil {
return x.MessageIDs
}
return nil
}
var File_trace_proto protoreflect.FileDescriptor
var file_trace_proto_rawDesc = []byte{
0x0a, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x62,
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x22, 0xca, 0x21, 0x0a,
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x22, 0xd0, 0x22, 0x0a,
0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x6c, 0x6f, 0x73,
0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45,
@ -1766,7 +1821,7 @@ var file_trace_proto_rawDesc = []byte{
0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
0x69, 0x62, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a,
0x95, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x12,
0xe3, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x12,
0x40, 0x0a, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a,
0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54,
0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
@ -1783,64 +1838,72 @@ var file_trace_proto_rawDesc = []byte{
0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61,
0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62,
0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x69, 0x64, 0x6f, 0x6e, 0x74,
0x77, 0x61, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x62, 0x6c, 0x6f,
0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x44, 0x6f,
0x6e, 0x74, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x09, 0x69, 0x64, 0x6f, 0x6e,
0x74, 0x77, 0x61, 0x6e, 0x74, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74,
0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69,
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74,
0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49,
0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x1a, 0x3d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52,
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x1a, 0x3d, 0x0a, 0x10, 0x43, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d,
0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a,
0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a,
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05,
0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65,
0x72, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0xea,
0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x55, 0x42, 0x4c, 0x49,
0x53, 0x48, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e,
0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01,
0x12, 0x15, 0x0a, 0x11, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45,
0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x45, 0x4c, 0x49, 0x56,
0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08,
0x41, 0x44, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45,
0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52,
0x45, 0x43, 0x56, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x45, 0x4e,
0x44, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, 0x50, 0x5f,
0x52, 0x50, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a, 0x04, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x09, 0x12,
0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52,
0x41, 0x46, 0x54, 0x10, 0x0b, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x55, 0x4e, 0x45, 0x10, 0x0c,
0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x41, 0x42, 0x4c,
0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0d, 0x42, 0x07, 0x0a, 0x05, 0x5f,
0x74, 0x79, 0x70, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42,
0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x11, 0x0a,
0x0f, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64, 0x65, 0x6c, 0x69,
0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61,
0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76,
0x65, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x52, 0x50,
0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a,
0x08, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x6f,
0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x42, 0x08, 0x0a, 0x06,
0x5f, 0x67, 0x72, 0x61, 0x66, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x75, 0x6e, 0x65,
0x42, 0x17, 0x0a, 0x15, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62,
0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x42, 0x0a, 0x0f, 0x54, 0x72, 0x61,
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2f, 0x0a, 0x05,
0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x6c,
0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63,
0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x42, 0x43, 0x5a,
0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69,
0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75,
0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69,
0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2f,
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65,
0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73,
0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x36, 0x0a, 0x14,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x44, 0x6f, 0x6e, 0x74, 0x57, 0x61, 0x6e, 0x74,
0x4d, 0x65, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x49, 0x44, 0x73, 0x22, 0xea, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a,
0x0f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x53,
0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43,
0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12, 0x13, 0x0a,
0x0f, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x44, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x04,
0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10,
0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x06, 0x12,
0x0c, 0x0a, 0x08, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x07, 0x12, 0x0c, 0x0a,
0x08, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a, 0x04, 0x4a,
0x4f, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, 0x0a,
0x12, 0x09, 0x0a, 0x05, 0x47, 0x52, 0x41, 0x46, 0x54, 0x10, 0x0b, 0x12, 0x09, 0x0a, 0x05, 0x50,
0x52, 0x55, 0x4e, 0x45, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x44, 0x45, 0x4c, 0x49,
0x56, 0x45, 0x52, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10,
0x0d, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70,
0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63,
0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x64, 0x75, 0x70,
0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x11, 0x0a,
0x0f, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b,
0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x72, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, 0x6e, 0x64,
0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x42,
0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x65, 0x61,
0x76, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x67, 0x72, 0x61, 0x66, 0x74, 0x42, 0x08, 0x0a, 0x06,
0x5f, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c,
0x69, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
0x42, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74,
0x63, 0x68, 0x12, 0x2f, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x19, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70,
0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x62, 0x61,
0x74, 0x63, 0x68, 0x42, 0x43, 0x5a, 0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75,
0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69,
0x6c, 0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f,
0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73,
0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1856,7 +1919,7 @@ func file_trace_proto_rawDescGZIP() []byte {
}
var file_trace_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
var file_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
var file_trace_proto_goTypes = []interface{}{
(TraceEvent_Type)(0), // 0: blossomsub.pb.TraceEvent.Type
(*TraceEvent)(nil), // 1: blossomsub.pb.TraceEvent
@ -1883,6 +1946,7 @@ var file_trace_proto_goTypes = []interface{}{
(*TraceEvent_ControlIWantMeta)(nil), // 22: blossomsub.pb.TraceEvent.ControlIWantMeta
(*TraceEvent_ControlGraftMeta)(nil), // 23: blossomsub.pb.TraceEvent.ControlGraftMeta
(*TraceEvent_ControlPruneMeta)(nil), // 24: blossomsub.pb.TraceEvent.ControlPruneMeta
(*TraceEvent_ControlIDontWantMeta)(nil), // 25: blossomsub.pb.TraceEvent.ControlIDontWantMeta
}
var file_trace_proto_depIdxs = []int32{
0, // 0: blossomsub.pb.TraceEvent.type:type_name -> blossomsub.pb.TraceEvent.Type
@ -1911,11 +1975,12 @@ var file_trace_proto_depIdxs = []int32{
22, // 23: blossomsub.pb.TraceEvent.ControlMeta.iwant:type_name -> blossomsub.pb.TraceEvent.ControlIWantMeta
23, // 24: blossomsub.pb.TraceEvent.ControlMeta.graft:type_name -> blossomsub.pb.TraceEvent.ControlGraftMeta
24, // 25: blossomsub.pb.TraceEvent.ControlMeta.prune:type_name -> blossomsub.pb.TraceEvent.ControlPruneMeta
26, // [26:26] is the sub-list for method output_type
26, // [26:26] is the sub-list for method input_type
26, // [26:26] is the sub-list for extension type_name
26, // [26:26] is the sub-list for extension extendee
0, // [0:26] is the sub-list for field type_name
25, // 26: blossomsub.pb.TraceEvent.ControlMeta.idontwant:type_name -> blossomsub.pb.TraceEvent.ControlIDontWantMeta
27, // [27:27] is the sub-list for method output_type
27, // [27:27] is the sub-list for method input_type
27, // [27:27] is the sub-list for extension type_name
27, // [27:27] is the sub-list for extension extendee
0, // [0:27] is the sub-list for field type_name
}
func init() { file_trace_proto_init() }
@ -2212,6 +2277,18 @@ func file_trace_proto_init() {
return nil
}
}
file_trace_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlIDontWantMeta); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_trace_proto_msgTypes[0].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[2].OneofWrappers = []interface{}{}
@ -2240,7 +2317,7 @@ func file_trace_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_trace_proto_rawDesc,
NumEnums: 1,
NumMessages: 24,
NumMessages: 25,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -134,6 +134,7 @@ message TraceEvent {
repeated ControlIWantMeta iwant = 2;
repeated ControlGraftMeta graft = 3;
repeated ControlPruneMeta prune = 4;
repeated ControlIDontWantMeta idontwant = 5;
}
message ControlIHaveMeta {
@ -153,6 +154,10 @@ message TraceEvent {
optional bytes bitmask = 1;
repeated bytes peers = 2;
}
message ControlIDontWantMeta {
repeated bytes messageIDs = 1;
}
}
message TraceEventBatch {

View File

@ -150,8 +150,7 @@ type PubSub struct {
blacklist Blacklist
blacklistPeer chan peer.ID
peers map[peer.ID]chan *RPC
peersMx sync.RWMutex
peers map[peer.ID]*rpcQueue
inboundStreamsMx sync.Mutex
inboundStreams map[peer.ID]network.Stream
@ -203,11 +202,14 @@ type PubSubRouter interface {
// EnoughPeers returns whether the router needs more peers before it's ready to publish new records.
// Suggested (if greater than 0) is a suggested number of peers that the router should need.
EnoughPeers(bitmask []byte, suggested int) bool
// AcceptFrom is invoked on any incoming message before pushing it to the validation pipeline
// AcceptFrom is invoked on any RPC envelope before pushing it to the validation pipeline
// or processing control information.
// Allows routers with internal scoring to vet peers before committing any processing resources
// to the message and implement an effective graylist and react to validation queue overload.
AcceptFrom(peer.ID) AcceptStatus
// PreValidation is invoked on messages in the RPC envelope right before pushing it to
// the validation pipeline
PreValidation([]*Message)
// HandleRPC is invoked to process control messages in the RPC envelope.
// It is invoked after subscriptions and payload messages have been processed.
HandleRPC(*RPC)
@ -292,7 +294,7 @@ func NewPubSub(ctx context.Context, h host.Host, rt PubSubRouter, opts ...Option
mySubs: make(map[string]map[*Subscription]struct{}),
myRelays: make(map[string]int),
bitmasks: make(map[string]map[peer.ID]struct{}),
peers: make(map[peer.ID]chan *RPC),
peers: make(map[peer.ID]*rpcQueue),
inboundStreams: make(map[peer.ID]network.Stream),
blacklist: NewMapBlacklist(),
blacklistPeer: make(chan peer.ID),
@ -566,13 +568,11 @@ func WithAppSpecificRpcInspector(inspector func(peer.ID, *RPC) error) Option {
// processLoop handles all inputs arriving on the channels
func (p *PubSub) processLoop(ctx context.Context) {
defer func() {
p.peersMx.Lock()
// Clean up go routines.
for _, ch := range p.peers {
close(ch)
for _, q := range p.peers {
_ = q.Close()
}
p.peers = nil
p.peersMx.Unlock()
p.bitmasks = nil
p.seenMessages.Done()
}()
@ -585,9 +585,7 @@ func (p *PubSub) processLoop(ctx context.Context) {
case s := <-p.newPeerStream:
pid := s.Conn().RemotePeer()
p.peersMx.RLock()
ch, ok := p.peers[pid]
p.peersMx.RUnlock()
q, ok := p.peers[pid]
if !ok {
log.Warn("new stream for unknown peer: ", pid)
s.Reset()
@ -596,10 +594,8 @@ func (p *PubSub) processLoop(ctx context.Context) {
if p.blacklist.Contains(pid) {
log.Warn("closing stream for blacklisted peer: ", pid)
close(ch)
p.peersMx.Lock()
_ = q.Close()
delete(p.peers, pid)
p.peersMx.Unlock()
s.Reset()
continue
}
@ -607,9 +603,7 @@ func (p *PubSub) processLoop(ctx context.Context) {
p.rt.AddPeer(pid, s.Protocol())
case pid := <-p.newPeerError:
p.peersMx.Lock()
delete(p.peers, pid)
p.peersMx.Unlock()
case <-p.peerDead:
p.handleDeadPeers()
@ -659,14 +653,10 @@ func (p *PubSub) processLoop(ctx context.Context) {
log.Infof("Blacklisting peer %s", pid)
p.blacklist.Add(pid)
p.peersMx.RLock()
ch, ok := p.peers[pid]
p.peersMx.RUnlock()
q, ok := p.peers[pid]
if ok {
close(ch)
p.peersMx.Lock()
_ = q.Close()
delete(p.peers, pid)
p.peersMx.Unlock()
for t, tmap := range p.bitmasks {
if _, ok := tmap[pid]; ok {
delete(tmap, pid)
@ -695,7 +685,6 @@ peerloop:
}
var peerset []peer.ID
p.peersMx.RLock()
for p := range p.peers {
_, ok := tmap[p]
if !ok {
@ -703,7 +692,6 @@ peerloop:
}
peerset = append(peerset, p)
}
p.peersMx.RUnlock()
if len(peers) == 0 {
peers = peerset
@ -743,25 +731,24 @@ func (p *PubSub) handlePendingPeers() {
continue
}
p.peersMx.RLock()
if _, ok := p.peers[pid]; ok {
p.peersMx.RUnlock()
log.Debug("already have connection to peer: ", pid)
continue
}
p.peersMx.RUnlock()
if p.blacklist.Contains(pid) {
log.Warn("ignoring connection from blacklisted peer: ", pid)
continue
}
messages := make(chan *RPC, p.peerOutboundQueueSize)
messages <- p.getHelloPacket()
go p.handleNewPeer(p.ctx, pid, messages)
p.peersMx.Lock()
p.peers[pid] = messages
p.peersMx.Unlock()
q := newRPCQueue(p.peerOutboundQueueSize, p.peerOutboundQueueSize)
if err := q.Push(p.ctx, p.getHelloPacket(), true); err != nil {
log.Debug("error sending hello packet to new peer: ", err)
_ = q.Close()
continue
}
go p.handleNewPeer(p.ctx, pid, q)
p.peers[pid] = q
}
}
@ -778,17 +765,13 @@ func (p *PubSub) handleDeadPeers() {
p.peerDeadPrioLk.Unlock()
for pid := range deadPeers {
p.peersMx.RLock()
ch, ok := p.peers[pid]
p.peersMx.RUnlock()
q, ok := p.peers[pid]
if !ok {
continue
}
close(ch)
p.peersMx.Lock()
_ = q.Close()
delete(p.peers, pid)
p.peersMx.Unlock()
for t, tmap := range p.bitmasks {
if _, ok := tmap[pid]; ok {
@ -809,12 +792,14 @@ func (p *PubSub) handleDeadPeers() {
// still connected, must be a duplicate connection being closed.
// we respawn the writer as we need to ensure there is a stream active
log.Debugf("peer declared dead but still connected; respawning writer: %s", pid)
messages := make(chan *RPC, p.peerOutboundQueueSize)
messages <- p.getHelloPacket()
p.peersMx.Lock()
p.peers[pid] = messages
p.peersMx.Unlock()
go p.handleNewPeerWithBackoff(p.ctx, pid, backoffDelay, messages)
q := newRPCQueue(p.peerOutboundQueueSize, p.peerOutboundQueueSize)
if err := q.Push(p.ctx, p.getHelloPacket(), true); err != nil {
log.Debug("error sending hello packet to new peer: ", err)
_ = q.Close()
continue
}
p.peers[pid] = q
go p.handleNewPeerWithBackoff(p.ctx, pid, backoffDelay, q)
}
}
}
@ -977,18 +962,15 @@ func (p *PubSub) announce(bitmask []byte, sub bool) {
}
out := rpcWithSubs(subopt)
p.peersMx.RLock()
for pid, peer := range p.peers {
select {
case peer <- out:
p.tracer.SendRPC(out, pid)
default:
for pid, q := range p.peers {
if err := q.TryPush(p.ctx, out, false); err != nil {
log.Infof("Can't send announce message to peer %s: queue full; scheduling retry", pid)
p.tracer.DropRPC(out, pid)
go p.announceRetry(pid, bitmask, sub)
continue
}
p.tracer.SendRPC(out, pid)
}
p.peersMx.RUnlock()
}
func (p *PubSub) announceRetry(pid peer.ID, bitmask []byte, sub bool) {
@ -1012,9 +994,7 @@ func (p *PubSub) announceRetry(pid peer.ID, bitmask []byte, sub bool) {
}
func (p *PubSub) doAnnounceRetry(pid peer.ID, bitmask []byte, sub bool) {
p.peersMx.RLock()
peer, ok := p.peers[pid]
p.peersMx.RUnlock()
q, ok := p.peers[pid]
if !ok {
return
}
@ -1025,14 +1005,13 @@ func (p *PubSub) doAnnounceRetry(pid peer.ID, bitmask []byte, sub bool) {
}
out := rpcWithSubs(subopt)
select {
case peer <- out:
p.tracer.SendRPC(out, pid)
default:
if err := q.TryPush(p.ctx, out, false); err != nil {
log.Infof("Can't send announce message to peer %s: queue full; scheduling retry", pid)
p.tracer.DropRPC(out, pid)
go p.announceRetry(pid, bitmask, sub)
return
}
p.tracer.SendRPC(out, pid)
}
// notifySubs sends a given message to all corresponding subscribers.
@ -1172,13 +1151,21 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
p.tracer.ThrottlePeer(rpc.from)
case AcceptAll:
var toPush []*Message
for _, pmsg := range rpc.GetPublish() {
if !(p.subscribedToMsg(pmsg) || p.canRelayMsg(pmsg)) {
log.Debug("received message in bitmask we didn't subscribe to; ignoring message")
continue
}
p.pushMsg(&Message{pmsg, []byte{}, rpc.from, nil, false})
msg := &Message{pmsg, []byte{}, rpc.from, nil, false}
if p.shouldPush(msg) {
toPush = append(toPush, msg)
}
}
p.rt.PreValidation(toPush)
for _, msg := range toPush {
p.pushMsg(msg)
}
}
@ -1197,27 +1184,28 @@ func DefaultPeerFilter(pid peer.ID, bitmask []byte) bool {
return true
}
// pushMsg pushes a message performing validation as necessary
func (p *PubSub) pushMsg(msg *Message) {
// shouldPush filters a message before validating and pushing it
// It returns true if the message can be further validated and pushed
func (p *PubSub) shouldPush(msg *Message) bool {
src := msg.ReceivedFrom
// reject messages from blacklisted peers
if p.blacklist.Contains(src) {
log.Debugf("dropping message from blacklisted peer %s", src)
p.tracer.RejectMessage(msg, RejectBlacklstedPeer)
return
return false
}
// even if they are forwarded by good peers
if p.blacklist.Contains(msg.GetFrom()) {
log.Debugf("dropping message from blacklisted source %s", src)
p.tracer.RejectMessage(msg, RejectBlacklistedSource)
return
return false
}
err := p.checkSigningPolicy(msg)
if err != nil {
log.Debugf("dropping message from %s: %s", src, err)
return
return false
}
// reject messages claiming to be from ourselves but not locally published
@ -1225,16 +1213,24 @@ func (p *PubSub) pushMsg(msg *Message) {
if peer.ID(msg.GetFrom()) == self && src != self {
log.Debugf("dropping message claiming to be from self but forwarded from %s", src)
p.tracer.RejectMessage(msg, RejectSelfOrigin)
return
return false
}
// have we already seen and validated this message?
id := p.idGen.ID(msg)
if p.seenMessage(id) {
p.tracer.DuplicateMessage(msg)
return
return false
}
return true
}
// pushMsg pushes a message performing validation as necessary
func (p *PubSub) pushMsg(msg *Message) {
src := msg.ReceivedFrom
id := p.idGen.ID(msg)
if !p.val.Push(src, msg) {
return
}

View File

@ -24,7 +24,12 @@ func TestPubSubRemovesBlacklistedPeer(t *testing.T) {
// Bad peer is blacklisted after it has connected.
// Calling p.BlacklistPeer directly does the right thing but we should also clean
// up the peer if it has been added the the blacklist by another means.
bl.Add(hosts[0].ID())
ran := make(chan struct{})
psubs1.eval <- func() {
defer close(ran)
bl.Add(hosts[0].ID())
}
<-ran
bitmasks, err := psubs0.Join([]byte{0x01, 0x00})
if err != nil {
t.Fatal(err)

View File

@ -0,0 +1,94 @@
package blossomsub
import (
"context"
"errors"
)
var ErrQueueFull = errors.New("queue full")
// rpcQueue is a queue of RPCs with two priority levels: fast and slow.
// Fast RPCs are processed before slow RPCs.
type rpcQueue struct {
ctx context.Context
cancel context.CancelFunc
fastQueue chan *RPC
slowQueue chan *RPC
}
// Close closes the queue.
func (q *rpcQueue) Close() error {
q.cancel()
return nil
}
// TryPush tries to push an RPC to the queue.
// Returns ErrQueueFull if the queue is full, or the context error if the context is done.
func (q *rpcQueue) TryPush(ctx context.Context, rpc *RPC, fast bool) error {
ch := q.slowQueue
if fast {
ch = q.fastQueue
}
select {
case <-ctx.Done():
return ctx.Err()
case <-q.ctx.Done():
return q.ctx.Err()
case ch <- rpc:
return nil
default:
return ErrQueueFull
}
}
// Push pushes an RPC to the queue.
// Returns the context error if the context is done.
func (q *rpcQueue) Push(ctx context.Context, rpc *RPC, fast bool) error {
ch := q.slowQueue
if fast {
ch = q.fastQueue
}
select {
case <-ctx.Done():
return ctx.Err()
case <-q.ctx.Done():
return q.ctx.Err()
case ch <- rpc:
return nil
}
}
// Pop pops an RPC from the queue.
// Returns the RPC or the context error.
func (q *rpcQueue) Pop(ctx context.Context) (*RPC, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-q.ctx.Done():
return nil, q.ctx.Err()
case rpc := <-q.fastQueue:
return rpc, nil
default:
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-q.ctx.Done():
return nil, q.ctx.Err()
case rpc := <-q.fastQueue:
return rpc, nil
case rpc := <-q.slowQueue:
return rpc, nil
}
}
// newRPCQueue creates a new RPC queue.
func newRPCQueue(fastSize, slowSize int) *rpcQueue {
ctx, cancel := context.WithCancel(context.Background())
return &rpcQueue{
ctx: ctx,
cancel: cancel,
fastQueue: make(chan *RPC, fastSize),
slowQueue: make(chan *RPC, slowSize),
}
}

View File

@ -0,0 +1,81 @@
package blossomsub
import (
"context"
"errors"
"testing"
)
func TestRPCQueue(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
q := newRPCQueue(32, 32)
defer q.Close()
rpcs := []*RPC{
{from: "a"},
{from: "b"},
{from: "c"},
{from: "d"},
}
for i, tc := range []struct {
fast bool
rpc *RPC
}{
{true, rpcs[0]},
{false, rpcs[1]},
{true, rpcs[2]},
{false, rpcs[3]},
} {
if err := q.TryPush(ctx, tc.rpc, tc.fast); err != nil {
t.Fatal(i, "unexpected error:", err)
}
}
for i, tc := range []struct {
rpc *RPC
}{
{rpcs[0]},
{rpcs[2]},
{rpcs[1]},
{rpcs[3]},
} {
rpc, err := q.Pop(ctx)
if err != nil {
t.Fatal(i, "unexpected error:", err)
}
if rpc != tc.rpc {
t.Fatal(i, "expected rpc", string(tc.rpc.from), "got", string(rpc.from))
}
}
q = newRPCQueue(0, 0)
defer q.Close()
type result struct {
rpc *RPC
err error
}
res := make(chan result, 1)
go func() {
rpc, err := q.Pop(ctx)
res <- result{rpc, err}
}()
if err := q.Push(ctx, rpcs[0], false); err != nil {
t.Fatal("unexpected error:", err)
}
r := <-res
if r.err != nil {
t.Fatal("unexpected error:", r.err)
}
if r.rpc != rpcs[0] {
t.Fatal("expected rpc", string(rpcs[0].from), "got", string(r.rpc.from))
}
if err := q.TryPush(ctx, rpcs[0], false); !errors.Is(err, ErrQueueFull) {
t.Fatal("expected ErrQueueFull, got", err)
}
}

View File

@ -420,11 +420,23 @@ func (t *pubsubTracer) traceRPCMeta(rpc *RPC) *pb.TraceEvent_RPCMeta {
})
}
var idontwant []*pb.TraceEvent_ControlIDontWantMeta
for _, ctl := range rpc.Control.Idontwant {
var mids [][]byte
for _, mid := range ctl.MessageIDs {
mids = append(mids, []byte(mid))
}
idontwant = append(idontwant, &pb.TraceEvent_ControlIDontWantMeta{
MessageIDs: mids,
})
}
rpcMeta.Control = &pb.TraceEvent_ControlMeta{
Ihave: ihave,
Iwant: iwant,
Graft: graft,
Prune: prune,
Ihave: ihave,
Iwant: iwant,
Graft: graft,
Prune: prune,
Idontwant: idontwant,
}
}

View File

@ -30,7 +30,10 @@ type P2PConfig struct {
GraftFloodThreshold time.Duration `yaml:"graftFloodThreshold"`
MaxIHaveLength int `yaml:"maxIHaveLength"`
MaxIHaveMessages int `yaml:"maxIHaveMessages"`
MaxIDontWantMessages int `yaml:"maxIDontWantMessages"`
IWantFollowupTime time.Duration `yaml:"iWantFollowupTime"`
IDontWantMessageThreshold int `yaml:"iDontWantMessageThreshold"`
IDontWantMessageTTL int `yaml:"iDontWantMessageTTL"`
BootstrapPeers []string `yaml:"bootstrapPeers"`
ListenMultiaddr string `yaml:"listenMultiaddr"`
PeerPrivKey string `yaml:"peerPrivKey"`

View File

@ -32,6 +32,7 @@ type blossomSubRawTracer struct {
undeliverableMessageTotal *prometheus.CounterVec
iHaveMessageHistogram *prometheus.HistogramVec
iWantMessageHistogram *prometheus.HistogramVec
iDontWantMessageHistogram *prometheus.HistogramVec
}
func (b *blossomSubRawTracer) observeControl(control *pb.ControlMessage, direction string) {
@ -43,6 +44,9 @@ func (b *blossomSubRawTracer) observeControl(control *pb.ControlMessage, directi
for _, iWant := range control.GetIwant() {
b.iWantMessageHistogram.WithLabelValues(labels...).Observe(float64(len(iWant.GetMessageIDs())))
}
for _, iDontWant := range control.GetIdontwant() {
b.iDontWantMessageHistogram.WithLabelValues(labels...).Observe(float64(len(iDontWant.GetMessageIDs())))
}
}
var _ blossomsub.RawTracer = (*blossomSubRawTracer)(nil)
@ -146,6 +150,7 @@ func (b *blossomSubRawTracer) Describe(ch chan<- *prometheus.Desc) {
b.undeliverableMessageTotal.Describe(ch)
b.iHaveMessageHistogram.Describe(ch)
b.iWantMessageHistogram.Describe(ch)
b.iDontWantMessageHistogram.Describe(ch)
}
// Collect implements prometheus.Collector.
@ -167,6 +172,7 @@ func (b *blossomSubRawTracer) Collect(ch chan<- prometheus.Metric) {
b.undeliverableMessageTotal.Collect(ch)
b.iHaveMessageHistogram.Collect(ch)
b.iWantMessageHistogram.Collect(ch)
b.iDontWantMessageHistogram.Collect(ch)
}
type BlossomSubRawTracer interface {
@ -309,6 +315,15 @@ func NewBlossomSubRawTracer() BlossomSubRawTracer {
},
[]string{"direction"},
),
iDontWantMessageHistogram: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: blossomSubNamespace,
Name: "idontwant_messages",
Help: "Histogram of the number of messages in an IDontWant message.",
Buckets: prometheus.ExponentialBuckets(1, 2, 14),
},
[]string{"direction"},
),
}
return b
}

View File

@ -1033,9 +1033,18 @@ func withDefaults(p2pConfig *config.P2PConfig) *config.P2PConfig {
if p2pConfig.MaxIHaveMessages == 0 {
p2pConfig.MaxIHaveMessages = blossomsub.BlossomSubMaxIHaveMessages
}
if p2pConfig.MaxIDontWantMessages == 0 {
p2pConfig.MaxIDontWantMessages = blossomsub.BlossomSubMaxIDontWantMessages
}
if p2pConfig.IWantFollowupTime == 0 {
p2pConfig.IWantFollowupTime = blossomsub.BlossomSubIWantFollowupTime
}
if p2pConfig.IDontWantMessageThreshold == 0 {
p2pConfig.IDontWantMessageThreshold = blossomsub.BlossomSubIDontWantMessageThreshold
}
if p2pConfig.IDontWantMessageTTL == 0 {
p2pConfig.IDontWantMessageTTL = blossomsub.BlossomSubIDontWantMessageTTL
}
if p2pConfig.LowWatermarkConnections == 0 {
p2pConfig.LowWatermarkConnections = defaultLowWatermarkConnections
}
@ -1099,7 +1108,10 @@ func toBlossomSubParams(p2pConfig *config.P2PConfig) blossomsub.BlossomSubParams
GraftFloodThreshold: p2pConfig.GraftFloodThreshold,
MaxIHaveLength: p2pConfig.MaxIHaveLength,
MaxIHaveMessages: p2pConfig.MaxIHaveMessages,
MaxIDontWantMessages: p2pConfig.MaxIDontWantMessages,
IWantFollowupTime: p2pConfig.IWantFollowupTime,
IDontWantMessageThreshold: p2pConfig.IDontWantMessageThreshold,
IDontWantMessageTTL: p2pConfig.IDontWantMessageTTL,
SlowHeartbeatWarning: 0.1,
}
}