From c6b5ca20cab646c44b4357972a5415e74c33f101 Mon Sep 17 00:00:00 2001 From: Pop Chunhapanya Date: Sun, 15 Dec 2024 15:55:29 +0700 Subject: [PATCH 1/4] GossipSub v2.0: Params and control protobuf --- gossipsub.go | 23 +- gossipsub_feat.go | 12 +- gossipsub_feat_test.go | 25 ++ pb/rpc.pb.go | 617 ++++++++++++++++++++++++++++++++++--- pb/rpc.proto | 13 + pb/trace.pb.go | 680 ++++++++++++++++++++++++++++++++++++----- pb/trace.proto | 11 + trace.go | 25 ++ 8 files changed, 1296 insertions(+), 110 deletions(-) diff --git a/gossipsub.go b/gossipsub.go index 56b6886c..59efec83 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -25,19 +25,25 @@ import ( const ( // GossipSubID_v10 is the protocol ID for version 1.0.0 of the GossipSub protocol. - // It is advertised along with GossipSubID_v11 and GossipSubID_v12 for backwards compatibility. + // It is advertised along with GossipSubID_v11, GossipSubID_v12, and GossipSubID_v20 for backwards compatibility. GossipSubID_v10 = protocol.ID("/meshsub/1.0.0") // GossipSubID_v11 is the protocol ID for version 1.1.0 of the GossipSub protocol. - // It is advertised along with GossipSubID_v12 for backwards compatibility. + // It is advertised along with GossipSubID_v12 and GossipSubID_v20 for backwards compatibility. // See the spec for details about how v1.1.0 compares to v1.0.0: // https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md GossipSubID_v11 = protocol.ID("/meshsub/1.1.0") // GossipSubID_v12 is the protocol ID for version 1.2.0 of the GossipSub protocol. + // It is advertised along with GossipSubID_v20 for backwards compatibility. // See the spec for details about how v1.2.0 compares to v1.1.0: // https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md GossipSubID_v12 = protocol.ID("/meshsub/1.2.0") + + // GossipSubID_v20 is the protocol ID for version 2.0.0 of the GossipSub protocol. + // See the spec for details about how v2.0.0 compares to v1.2.0: + // https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v2.0.md + GossipSubID_v20 = protocol.ID("/meshsub/2.0.0") ) // Defines the default gossipsub parameters. @@ -49,7 +55,9 @@ var ( GossipSubDout = 2 GossipSubHistoryLength = 5 GossipSubHistoryGossip = 3 + GossipSubDannounce = 4 GossipSubDlazy = 6 + GossipSubTimeout = 400 * time.Millisecond GossipSubGossipFactor = 0.25 GossipSubGossipRetransmission = 3 GossipSubHeartbeatInitialDelay = 100 * time.Millisecond @@ -127,12 +135,21 @@ type GossipSubParams struct { // avoid a runtime panic. HistoryGossip int + // Dannounce controls how many times a message is sent lazily to mesh peers. This number + // must be at most equal to D. Every time we want to forward a message to a particular + // peer, we will decide to forward it lazily with a probability Dannounce/D. + // Otherwise, we will forward eagerly. + Dannounce int + // Dlazy affects how many peers we will emit gossip to at each heartbeat. // We will send gossip to at least Dlazy peers outside our mesh. The actual // number may be more, depending on GossipFactor and how many peers we're // connected to. Dlazy int + // Timeout is the time limit to receive the message back after sending INEED out. + Timeout time.Duration + // GossipFactor affects how many peers we will emit gossip to at each heartbeat. // We will send gossip to GossipFactor * (total number of non-mesh peers), or // Dlazy, whichever is greater. @@ -289,7 +306,9 @@ func DefaultGossipSubParams() GossipSubParams { Dout: GossipSubDout, HistoryLength: GossipSubHistoryLength, HistoryGossip: GossipSubHistoryGossip, + Dannounce: GossipSubDannounce, Dlazy: GossipSubDlazy, + Timeout: GossipSubTimeout, GossipFactor: GossipSubGossipFactor, GossipRetransmission: GossipSubGossipRetransmission, HeartbeatInitialDelay: GossipSubHeartbeatInitialDelay, diff --git a/gossipsub_feat.go b/gossipsub_feat.go index 49c7423c..8a46195e 100644 --- a/gossipsub_feat.go +++ b/gossipsub_feat.go @@ -20,20 +20,24 @@ const ( GossipSubFeaturePX // Protocol supports IDONTWANT -- gossipsub-v1.2 compatible GossipSubFeatureIdontwant + // Protocol supports IANNOUNCE/INEED -- gossipsub-v2.0 compatible + GossipSubFeatureAnnounce ) // GossipSubDefaultProtocols is the default gossipsub router protocol list -var GossipSubDefaultProtocols = []protocol.ID{GossipSubID_v12, GossipSubID_v11, GossipSubID_v10, FloodSubID} +var GossipSubDefaultProtocols = []protocol.ID{GossipSubID_v20, GossipSubID_v12, GossipSubID_v11, GossipSubID_v10, FloodSubID} // GossipSubDefaultFeatures is the feature test function for the default gossipsub protocols func GossipSubDefaultFeatures(feat GossipSubFeature, proto protocol.ID) bool { switch feat { case GossipSubFeatureMesh: - return proto == GossipSubID_v12 || proto == GossipSubID_v11 || proto == GossipSubID_v10 + return proto == GossipSubID_v20 || proto == GossipSubID_v12 || proto == GossipSubID_v11 || proto == GossipSubID_v10 case GossipSubFeaturePX: - return proto == GossipSubID_v12 || proto == GossipSubID_v11 + return proto == GossipSubID_v20 || proto == GossipSubID_v12 || proto == GossipSubID_v11 case GossipSubFeatureIdontwant: - return proto == GossipSubID_v12 + return proto == GossipSubID_v20 || proto == GossipSubID_v12 + case GossipSubFeatureAnnounce: + return proto == GossipSubID_v20 default: return false } diff --git a/gossipsub_feat_test.go b/gossipsub_feat_test.go index ff3709a3..bd053982 100644 --- a/gossipsub_feat_test.go +++ b/gossipsub_feat_test.go @@ -24,6 +24,9 @@ func TestDefaultGossipSubFeatures(t *testing.T) { if !GossipSubDefaultFeatures(GossipSubFeatureMesh, GossipSubID_v12) { t.Fatal("gossipsub-v1.2 should support Mesh") } + if !GossipSubDefaultFeatures(GossipSubFeatureMesh, GossipSubID_v20) { + t.Fatal("gossipsub-v2.0 should support Mesh") + } if GossipSubDefaultFeatures(GossipSubFeaturePX, FloodSubID) { t.Fatal("floodsub should not support PX") @@ -37,6 +40,9 @@ func TestDefaultGossipSubFeatures(t *testing.T) { if !GossipSubDefaultFeatures(GossipSubFeaturePX, GossipSubID_v12) { t.Fatal("gossipsub-v1.2 should support PX") } + if !GossipSubDefaultFeatures(GossipSubFeaturePX, GossipSubID_v20) { + t.Fatal("gossipsub-v2.0 should support PX") + } if GossipSubDefaultFeatures(GossipSubFeatureIdontwant, FloodSubID) { t.Fatal("floodsub should not support IDONTWANT") @@ -50,6 +56,25 @@ func TestDefaultGossipSubFeatures(t *testing.T) { if !GossipSubDefaultFeatures(GossipSubFeatureIdontwant, GossipSubID_v12) { t.Fatal("gossipsub-v1.2 should support IDONTWANT") } + if !GossipSubDefaultFeatures(GossipSubFeatureIdontwant, GossipSubID_v20) { + t.Fatal("gossipsub-v2.0 should support IDONTWANT") + } + + if GossipSubDefaultFeatures(GossipSubFeatureAnnounce, FloodSubID) { + t.Fatal("floodsub should not support IANNOUNCE/INEED") + } + if GossipSubDefaultFeatures(GossipSubFeatureAnnounce, GossipSubID_v10) { + t.Fatal("gossipsub-v1.0 should not support IANNOUNCE/INEED") + } + if GossipSubDefaultFeatures(GossipSubFeatureAnnounce, GossipSubID_v11) { + t.Fatal("gossipsub-v1.1 should not support IANNOUNCE/INEED") + } + if GossipSubDefaultFeatures(GossipSubFeatureAnnounce, GossipSubID_v12) { + t.Fatal("gossipsub-v1.2 should not support IANNOUNCE/INEED") + } + if !GossipSubDefaultFeatures(GossipSubFeatureAnnounce, GossipSubID_v20) { + t.Fatal("gossipsub-v2.0 should support IANNOUNCE/INEED") + } } func TestGossipSubCustomProtocols(t *testing.T) { diff --git a/pb/rpc.pb.go b/pb/rpc.pb.go index 213cdcc3..99009975 100644 --- a/pb/rpc.pb.go +++ b/pb/rpc.pb.go @@ -5,11 +5,10 @@ package pubsub_pb import ( fmt "fmt" + proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" - - proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. @@ -234,6 +233,8 @@ type ControlMessage struct { Graft []*ControlGraft `protobuf:"bytes,3,rep,name=graft" json:"graft,omitempty"` Prune []*ControlPrune `protobuf:"bytes,4,rep,name=prune" json:"prune,omitempty"` Idontwant []*ControlIDontWant `protobuf:"bytes,5,rep,name=idontwant" json:"idontwant,omitempty"` + Iannounce []*ControlIAnnounce `protobuf:"bytes,6,rep,name=iannounce" json:"iannounce,omitempty"` + Ineed []*ControlINeed `protobuf:"bytes,7,rep,name=ineed" json:"ineed,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -307,6 +308,20 @@ func (m *ControlMessage) GetIdontwant() []*ControlIDontWant { return nil } +func (m *ControlMessage) GetIannounce() []*ControlIAnnounce { + if m != nil { + return m.Iannounce + } + return nil +} + +func (m *ControlMessage) GetIneed() []*ControlINeed { + if m != nil { + return m.Ineed + } + return nil +} + type ControlIHave struct { TopicID *string `protobuf:"bytes,1,opt,name=topicID" json:"topicID,omitempty"` // implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings @@ -569,6 +584,110 @@ func (m *ControlIDontWant) GetMessageIDs() []string { return nil } +type ControlIAnnounce struct { + TopicID *string `protobuf:"bytes,1,opt,name=topicID" json:"topicID,omitempty"` + // implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings + MessageID *string `protobuf:"bytes,2,opt,name=messageID" json:"messageID,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ControlIAnnounce) Reset() { *m = ControlIAnnounce{} } +func (m *ControlIAnnounce) String() string { return proto.CompactTextString(m) } +func (*ControlIAnnounce) ProtoMessage() {} +func (*ControlIAnnounce) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{8} +} +func (m *ControlIAnnounce) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ControlIAnnounce) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ControlIAnnounce.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ControlIAnnounce) XXX_Merge(src proto.Message) { + xxx_messageInfo_ControlIAnnounce.Merge(m, src) +} +func (m *ControlIAnnounce) XXX_Size() int { + return m.Size() +} +func (m *ControlIAnnounce) XXX_DiscardUnknown() { + xxx_messageInfo_ControlIAnnounce.DiscardUnknown(m) +} + +var xxx_messageInfo_ControlIAnnounce proto.InternalMessageInfo + +func (m *ControlIAnnounce) GetTopicID() string { + if m != nil && m.TopicID != nil { + return *m.TopicID + } + return "" +} + +func (m *ControlIAnnounce) GetMessageID() string { + if m != nil && m.MessageID != nil { + return *m.MessageID + } + return "" +} + +type ControlINeed struct { + // implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings + MessageID *string `protobuf:"bytes,1,opt,name=messageID" json:"messageID,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ControlINeed) Reset() { *m = ControlINeed{} } +func (m *ControlINeed) String() string { return proto.CompactTextString(m) } +func (*ControlINeed) ProtoMessage() {} +func (*ControlINeed) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{9} +} +func (m *ControlINeed) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ControlINeed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ControlINeed.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ControlINeed) XXX_Merge(src proto.Message) { + xxx_messageInfo_ControlINeed.Merge(m, src) +} +func (m *ControlINeed) XXX_Size() int { + return m.Size() +} +func (m *ControlINeed) XXX_DiscardUnknown() { + xxx_messageInfo_ControlINeed.DiscardUnknown(m) +} + +var xxx_messageInfo_ControlINeed proto.InternalMessageInfo + +func (m *ControlINeed) GetMessageID() string { + if m != nil && m.MessageID != nil { + return *m.MessageID + } + return "" +} + type PeerInfo struct { PeerID []byte `protobuf:"bytes,1,opt,name=peerID" json:"peerID,omitempty"` SignedPeerRecord []byte `protobuf:"bytes,2,opt,name=signedPeerRecord" json:"signedPeerRecord,omitempty"` @@ -581,7 +700,7 @@ func (m *PeerInfo) Reset() { *m = PeerInfo{} } func (m *PeerInfo) String() string { return proto.CompactTextString(m) } func (*PeerInfo) ProtoMessage() {} func (*PeerInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{8} + return fileDescriptor_77a6da22d6a3feb1, []int{10} } func (m *PeerInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,45 +753,51 @@ func init() { proto.RegisterType((*ControlGraft)(nil), "pubsub.pb.ControlGraft") proto.RegisterType((*ControlPrune)(nil), "pubsub.pb.ControlPrune") proto.RegisterType((*ControlIDontWant)(nil), "pubsub.pb.ControlIDontWant") + proto.RegisterType((*ControlIAnnounce)(nil), "pubsub.pb.ControlIAnnounce") + proto.RegisterType((*ControlINeed)(nil), "pubsub.pb.ControlINeed") proto.RegisterType((*PeerInfo)(nil), "pubsub.pb.PeerInfo") } func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 511 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcd, 0x6e, 0x13, 0x31, - 0x10, 0xc7, 0xe5, 0x7c, 0x34, 0xdd, 0xe9, 0x82, 0x22, 0x83, 0x8a, 0xf9, 0x50, 0x14, 0xed, 0x29, - 0x20, 0xd8, 0x43, 0x38, 0x21, 0x71, 0x81, 0x44, 0xa2, 0x39, 0x00, 0x91, 0x39, 0x70, 0xde, 0xdd, - 0x38, 0xe9, 0xaa, 0x8d, 0x6d, 0x6c, 0x6f, 0x11, 0x4f, 0xc0, 0x89, 0xf7, 0xe2, 0xc8, 0x23, 0xa0, - 0xdc, 0x78, 0x0b, 0xe4, 0x59, 0xe7, 0xa3, 0x4d, 0x03, 0x37, 0xcf, 0xf8, 0x37, 0xfe, 0xff, 0x67, - 0xc6, 0x10, 0x19, 0x5d, 0xa4, 0xda, 0x28, 0xa7, 0x68, 0xa4, 0xab, 0xdc, 0x56, 0x79, 0xaa, 0xf3, - 0xe4, 0x0f, 0x81, 0x26, 0x9f, 0x8e, 0xe8, 0x6b, 0xb8, 0x63, 0xab, 0xdc, 0x16, 0xa6, 0xd4, 0xae, - 0x54, 0xd2, 0x32, 0xd2, 0x6f, 0x0e, 0x4e, 0x86, 0xa7, 0xe9, 0x06, 0x4d, 0xf9, 0x74, 0x94, 0x7e, - 0xaa, 0xf2, 0x8f, 0xda, 0x59, 0x7e, 0x1d, 0xa6, 0xcf, 0xa1, 0xa3, 0xab, 0xfc, 0xb2, 0xb4, 0xe7, - 0xac, 0x81, 0x75, 0x74, 0xa7, 0xee, 0xbd, 0xb0, 0x36, 0x5b, 0x08, 0xbe, 0x46, 0xe8, 0x4b, 0xe8, - 0x14, 0x4a, 0x3a, 0xa3, 0x2e, 0x59, 0xb3, 0x4f, 0x06, 0x27, 0xc3, 0x87, 0x3b, 0xf4, 0xa8, 0xbe, - 0xd9, 0x14, 0x05, 0xf2, 0xd1, 0x1b, 0xe8, 0x04, 0x71, 0xfa, 0x04, 0xa2, 0x20, 0x9f, 0x0b, 0x46, - 0xfa, 0x64, 0x70, 0xcc, 0xb7, 0x09, 0xca, 0xa0, 0xe3, 0x94, 0x2e, 0x8b, 0x72, 0xc6, 0x1a, 0x7d, - 0x32, 0x88, 0xf8, 0x3a, 0x4c, 0x7e, 0x10, 0xe8, 0x84, 0x77, 0x29, 0x85, 0xd6, 0xdc, 0xa8, 0x25, - 0x96, 0xc7, 0x1c, 0xcf, 0x3e, 0x37, 0xcb, 0x5c, 0x86, 0x65, 0x31, 0xc7, 0x33, 0xbd, 0x0f, 0x6d, - 0x2b, 0xbe, 0x48, 0x85, 0x4e, 0x63, 0x5e, 0x07, 0x3e, 0x8b, 0x8f, 0xb2, 0x16, 0x2a, 0xd4, 0x01, - 0xfa, 0x2a, 0x17, 0x32, 0x73, 0x95, 0x11, 0xac, 0x8d, 0xfc, 0x36, 0x41, 0xbb, 0xd0, 0xbc, 0x10, - 0xdf, 0xd8, 0x11, 0xe6, 0xfd, 0x31, 0xf9, 0xde, 0x80, 0xbb, 0xd7, 0xdb, 0xa5, 0x2f, 0xa0, 0x5d, - 0x9e, 0x67, 0x57, 0x22, 0x8c, 0xff, 0xc1, 0xfe, 0x60, 0x26, 0x67, 0xd9, 0x95, 0xe0, 0x35, 0x85, - 0xf8, 0xd7, 0x4c, 0xba, 0x30, 0xf5, 0xdb, 0xf0, 0xcf, 0x99, 0x74, 0xbc, 0xa6, 0x3c, 0xbe, 0x30, - 0xd9, 0xdc, 0xb1, 0xe6, 0x21, 0xfc, 0x9d, 0xbf, 0xe6, 0x35, 0xe5, 0x71, 0x6d, 0x2a, 0x29, 0x58, - 0xeb, 0x10, 0x3e, 0xf5, 0xd7, 0xbc, 0xa6, 0xe8, 0x2b, 0x88, 0xca, 0x99, 0x92, 0x0e, 0x0d, 0xb5, - 0xb1, 0xe4, 0xf1, 0x2d, 0x86, 0xc6, 0x4a, 0x3a, 0x34, 0xb5, 0xa5, 0x93, 0x33, 0x88, 0x77, 0xdb, - 0xdb, 0xec, 0x70, 0x32, 0xc6, 0x05, 0xad, 0x77, 0x38, 0x19, 0xd3, 0x1e, 0xc0, 0xb2, 0x9e, 0xd5, - 0x64, 0x6c, 0xb1, 0xed, 0x88, 0xef, 0x64, 0x92, 0x74, 0xfb, 0x92, 0x17, 0xb9, 0xc1, 0x93, 0x3d, - 0x7e, 0xb0, 0xe1, 0xb1, 0xf5, 0xc3, 0xca, 0xc9, 0x72, 0x43, 0x62, 0xd7, 0xff, 0xf0, 0xf8, 0x14, - 0xda, 0x5a, 0x08, 0x63, 0xc3, 0x56, 0xee, 0xed, 0x0c, 0x61, 0x2a, 0x84, 0x99, 0xc8, 0xb9, 0xe2, - 0x35, 0xe1, 0x1f, 0xc9, 0xb3, 0xe2, 0x42, 0xcd, 0xe7, 0xf8, 0xc1, 0x5a, 0x7c, 0x1d, 0x26, 0x43, - 0xe8, 0xde, 0x9c, 0xd8, 0x7f, 0x9b, 0xf9, 0x00, 0xc7, 0x6b, 0x01, 0x7a, 0x0a, 0x47, 0x5e, 0x22, - 0xb8, 0x8b, 0x79, 0x88, 0xe8, 0x33, 0xe8, 0xfa, 0x3f, 0x29, 0x66, 0x9e, 0xe4, 0xa2, 0x50, 0x66, - 0x16, 0x3e, 0xfc, 0x5e, 0xfe, 0x6d, 0xfc, 0x73, 0xd5, 0x23, 0xbf, 0x56, 0x3d, 0xf2, 0x7b, 0xd5, - 0x23, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xba, 0x73, 0x8e, 0xbf, 0x41, 0x04, 0x00, 0x00, + // 567 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xcd, 0x6e, 0x13, 0x31, + 0x10, 0xc7, 0xe5, 0x7c, 0x6d, 0x77, 0xba, 0xa0, 0xc8, 0xa0, 0x62, 0xa0, 0x8a, 0xa2, 0x3d, 0x05, + 0x54, 0xf6, 0x10, 0x4e, 0x48, 0x5c, 0x4a, 0x22, 0xd1, 0x20, 0x51, 0x22, 0x73, 0xe0, 0xbc, 0x1f, + 0x4e, 0xba, 0x6a, 0x63, 0x2f, 0x6b, 0x6f, 0x11, 0x0f, 0xc1, 0x7b, 0x71, 0xe4, 0x11, 0x50, 0x6e, + 0x1c, 0x79, 0x03, 0x64, 0xaf, 0x77, 0x37, 0x1f, 0x4d, 0xb9, 0x79, 0xc6, 0xbf, 0xbf, 0x3d, 0xf3, + 0x1f, 0x1b, 0xdc, 0x3c, 0x8b, 0x83, 0x2c, 0x17, 0x4a, 0x60, 0x37, 0x2b, 0x22, 0x59, 0x44, 0x41, + 0x16, 0xf9, 0x7f, 0x10, 0xb4, 0xe9, 0x7c, 0x82, 0xdf, 0xc2, 0x03, 0x59, 0x44, 0x32, 0xce, 0xd3, + 0x4c, 0xa5, 0x82, 0x4b, 0x82, 0x86, 0xed, 0xd1, 0xf1, 0xf8, 0x24, 0xa8, 0xd1, 0x80, 0xce, 0x27, + 0xc1, 0xe7, 0x22, 0xfa, 0x94, 0x29, 0x49, 0xb7, 0x61, 0x7c, 0x06, 0x4e, 0x56, 0x44, 0x37, 0xa9, + 0xbc, 0x22, 0x2d, 0xa3, 0xc3, 0x1b, 0xba, 0x8f, 0x4c, 0xca, 0x70, 0xc9, 0x68, 0x85, 0xe0, 0xd7, + 0xe0, 0xc4, 0x82, 0xab, 0x5c, 0xdc, 0x90, 0xf6, 0x10, 0x8d, 0x8e, 0xc7, 0x4f, 0x37, 0xe8, 0x49, + 0xb9, 0x53, 0x8b, 0x2c, 0xf9, 0xec, 0x1c, 0x1c, 0x7b, 0x39, 0x3e, 0x05, 0xd7, 0x5e, 0x1f, 0x31, + 0x82, 0x86, 0x68, 0x74, 0x44, 0x9b, 0x04, 0x26, 0xe0, 0x28, 0x91, 0xa5, 0x71, 0x9a, 0x90, 0xd6, + 0x10, 0x8d, 0x5c, 0x5a, 0x85, 0xfe, 0x0f, 0x04, 0x8e, 0x3d, 0x17, 0x63, 0xe8, 0x2c, 0x72, 0xb1, + 0x32, 0x72, 0x8f, 0x9a, 0xb5, 0xce, 0x25, 0xa1, 0x0a, 0x8d, 0xcc, 0xa3, 0x66, 0x8d, 0x1f, 0x43, + 0x57, 0xb2, 0xaf, 0x5c, 0x98, 0x4a, 0x3d, 0x5a, 0x06, 0x3a, 0x6b, 0x0e, 0x25, 0x1d, 0x73, 0x43, + 0x19, 0x98, 0xba, 0xd2, 0x25, 0x0f, 0x55, 0x91, 0x33, 0xd2, 0x35, 0x7c, 0x93, 0xc0, 0x7d, 0x68, + 0x5f, 0xb3, 0xef, 0xa4, 0x67, 0xf2, 0x7a, 0xe9, 0xff, 0x6d, 0xc1, 0xc3, 0xed, 0x76, 0xf1, 0x2b, + 0xe8, 0xa6, 0x57, 0xe1, 0x2d, 0xb3, 0xf6, 0x3f, 0xd9, 0x37, 0x66, 0x76, 0x11, 0xde, 0x32, 0x5a, + 0x52, 0x06, 0xff, 0x16, 0x72, 0x65, 0x5d, 0xbf, 0x0b, 0xff, 0x12, 0x72, 0x45, 0x4b, 0x4a, 0xe3, + 0xcb, 0x3c, 0x5c, 0x28, 0xd2, 0x3e, 0x84, 0xbf, 0xd7, 0xdb, 0xb4, 0xa4, 0x34, 0x9e, 0xe5, 0x05, + 0x67, 0xa4, 0x73, 0x08, 0x9f, 0xeb, 0x6d, 0x5a, 0x52, 0xf8, 0x0d, 0xb8, 0x69, 0x22, 0xb8, 0x32, + 0x05, 0x75, 0x8d, 0xe4, 0xf9, 0x1d, 0x05, 0x4d, 0x05, 0x57, 0xa6, 0xa8, 0x86, 0x36, 0xd2, 0x90, + 0x73, 0x51, 0xf0, 0x98, 0x91, 0xde, 0x41, 0xe9, 0xb9, 0x45, 0x68, 0x43, 0x1b, 0x0b, 0x38, 0x63, + 0x09, 0x71, 0x0e, 0x5a, 0x70, 0xc9, 0x58, 0x42, 0x4b, 0xca, 0xbf, 0x00, 0x6f, 0xd3, 0xc8, 0xfa, + 0xb5, 0xcc, 0xa6, 0xe6, 0x29, 0x54, 0xaf, 0x65, 0x36, 0xc5, 0x03, 0x80, 0x55, 0x39, 0x95, 0xd9, + 0x54, 0x1a, 0x83, 0x5d, 0xba, 0x91, 0xf1, 0x83, 0xe6, 0x24, 0xdd, 0xce, 0x0e, 0x8f, 0xf6, 0xf8, + 0x51, 0xcd, 0x1b, 0x93, 0x0f, 0xdf, 0xec, 0xaf, 0x6a, 0xd2, 0xf8, 0x7b, 0x4f, 0x8d, 0x2f, 0xa0, + 0x9b, 0x31, 0x96, 0x4b, 0x3b, 0xff, 0x47, 0x1b, 0xcd, 0xcf, 0x19, 0xcb, 0x67, 0x7c, 0x21, 0x68, + 0x49, 0xe8, 0x43, 0xa2, 0x30, 0xbe, 0x16, 0x8b, 0x85, 0x79, 0xca, 0x1d, 0x5a, 0x85, 0xfe, 0x18, + 0xfa, 0xbb, 0xb3, 0xf9, 0x6f, 0x33, 0x1f, 0x1a, 0x4d, 0x35, 0x94, 0x7b, 0xca, 0x3c, 0x05, 0xb7, + 0xd6, 0xda, 0x4f, 0xd9, 0x24, 0xfc, 0xb3, 0xc6, 0x48, 0x3d, 0xa9, 0x6d, 0x1a, 0xed, 0xd2, 0x97, + 0x70, 0x54, 0xb5, 0x86, 0x4f, 0xa0, 0xa7, 0x9b, 0xb3, 0x98, 0x47, 0x6d, 0x84, 0x5f, 0x42, 0x5f, + 0xff, 0x3b, 0x96, 0x68, 0x92, 0xb2, 0x58, 0xe4, 0x89, 0xfd, 0xd4, 0x7b, 0xf9, 0x77, 0xde, 0xcf, + 0xf5, 0x00, 0xfd, 0x5a, 0x0f, 0xd0, 0xef, 0xf5, 0x00, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xe6, + 0xa7, 0xbb, 0x02, 0x25, 0x05, 0x00, 0x00, } func (m *RPC) Marshal() (dAtA []byte, err error) { @@ -879,6 +1004,34 @@ func (m *ControlMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Ineed) > 0 { + for iNdEx := len(m.Ineed) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Ineed[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRpc(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.Iannounce) > 0 { + for iNdEx := len(m.Iannounce) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Iannounce[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRpc(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } if len(m.Idontwant) > 0 { for iNdEx := len(m.Idontwant) - 1; iNdEx >= 0; iNdEx-- { { @@ -1154,6 +1307,81 @@ func (m *ControlIDontWant) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ControlIAnnounce) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ControlIAnnounce) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ControlIAnnounce) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MessageID != nil { + i -= len(*m.MessageID) + copy(dAtA[i:], *m.MessageID) + i = encodeVarintRpc(dAtA, i, uint64(len(*m.MessageID))) + i-- + dAtA[i] = 0x12 + } + if m.TopicID != nil { + i -= len(*m.TopicID) + copy(dAtA[i:], *m.TopicID) + i = encodeVarintRpc(dAtA, i, uint64(len(*m.TopicID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ControlINeed) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ControlINeed) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ControlINeed) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MessageID != nil { + i -= len(*m.MessageID) + copy(dAtA[i:], *m.MessageID) + i = encodeVarintRpc(dAtA, i, uint64(len(*m.MessageID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *PeerInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1325,6 +1553,18 @@ func (m *ControlMessage) Size() (n int) { n += 1 + l + sovRpc(uint64(l)) } } + if len(m.Iannounce) > 0 { + for _, e := range m.Iannounce { + l = e.Size() + n += 1 + l + sovRpc(uint64(l)) + } + } + if len(m.Ineed) > 0 { + for _, e := range m.Ineed { + l = e.Size() + n += 1 + l + sovRpc(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -1430,6 +1670,42 @@ func (m *ControlIDontWant) Size() (n int) { return n } +func (m *ControlIAnnounce) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TopicID != nil { + l = len(*m.TopicID) + n += 1 + l + sovRpc(uint64(l)) + } + if m.MessageID != nil { + l = len(*m.MessageID) + n += 1 + l + sovRpc(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ControlINeed) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MessageID != nil { + l = len(*m.MessageID) + n += 1 + l + sovRpc(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *PeerInfo) Size() (n int) { if m == nil { return 0 @@ -2169,6 +2445,74 @@ func (m *ControlMessage) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Iannounce", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRpc + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRpc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Iannounce = append(m.Iannounce, &ControlIAnnounce{}) + if err := m.Iannounce[len(m.Iannounce)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ineed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRpc + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRpc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ineed = append(m.Ineed, &ControlINeed{}) + if err := m.Ineed[len(m.Ineed)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRpc(dAtA[iNdEx:]) @@ -2695,6 +3039,207 @@ func (m *ControlIDontWant) Unmarshal(dAtA []byte) error { } return nil } +func (m *ControlIAnnounce) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ControlIAnnounce: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ControlIAnnounce: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TopicID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRpc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRpc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.TopicID = &s + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRpc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRpc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.MessageID = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRpc(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRpc + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ControlINeed) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ControlINeed: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ControlINeed: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRpc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRpc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.MessageID = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRpc(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRpc + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *PeerInfo) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pb/rpc.proto b/pb/rpc.proto index bd0234c3..dd8bd9a0 100644 --- a/pb/rpc.proto +++ b/pb/rpc.proto @@ -29,6 +29,8 @@ message ControlMessage { repeated ControlGraft graft = 3; repeated ControlPrune prune = 4; repeated ControlIDontWant idontwant = 5; + repeated ControlIAnnounce iannounce = 6; + repeated ControlINeed ineed = 7; } message ControlIHave { @@ -57,6 +59,17 @@ message ControlIDontWant { repeated string messageIDs = 1; } +message ControlIAnnounce { + optional string topicID = 1; + // implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings + optional string messageID = 2; +} + +message ControlINeed { + // implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings + optional string messageID = 1; +} + message PeerInfo { optional bytes peerID = 1; optional bytes signedPeerRecord = 2; diff --git a/pb/trace.pb.go b/pb/trace.pb.go index 9361c393..c2a06204 100644 --- a/pb/trace.pb.go +++ b/pb/trace.pb.go @@ -5,11 +5,10 @@ package pubsub_pb import ( fmt "fmt" + proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" - - proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. @@ -1165,6 +1164,8 @@ type TraceEvent_ControlMeta struct { Graft []*TraceEvent_ControlGraftMeta `protobuf:"bytes,3,rep,name=graft" json:"graft,omitempty"` Prune []*TraceEvent_ControlPruneMeta `protobuf:"bytes,4,rep,name=prune" json:"prune,omitempty"` Idontwant []*TraceEvent_ControlIDontWantMeta `protobuf:"bytes,5,rep,name=idontwant" json:"idontwant,omitempty"` + Iannounce []*TraceEvent_ControlIAnnounceMeta `protobuf:"bytes,6,rep,name=iannounce" json:"iannounce,omitempty"` + Ineed []*TraceEvent_ControlINeedMeta `protobuf:"bytes,7,rep,name=ineed" json:"ineed,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1238,6 +1239,20 @@ func (m *TraceEvent_ControlMeta) GetIdontwant() []*TraceEvent_ControlIDontWantMe return nil } +func (m *TraceEvent_ControlMeta) GetIannounce() []*TraceEvent_ControlIAnnounceMeta { + if m != nil { + return m.Iannounce + } + return nil +} + +func (m *TraceEvent_ControlMeta) GetIneed() []*TraceEvent_ControlINeedMeta { + if m != nil { + return m.Ineed + } + return nil +} + type TraceEvent_ControlIHaveMeta struct { Topic *string `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` MessageIDs [][]byte `protobuf:"bytes,2,rep,name=messageIDs" json:"messageIDs,omitempty"` @@ -1489,6 +1504,108 @@ func (m *TraceEvent_ControlIDontWantMeta) GetMessageIDs() [][]byte { return nil } +type TraceEvent_ControlIAnnounceMeta struct { + Topic *string `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` + MessageID []byte `protobuf:"bytes,2,opt,name=messageID" json:"messageID,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TraceEvent_ControlIAnnounceMeta) Reset() { *m = TraceEvent_ControlIAnnounceMeta{} } +func (m *TraceEvent_ControlIAnnounceMeta) String() string { return proto.CompactTextString(m) } +func (*TraceEvent_ControlIAnnounceMeta) ProtoMessage() {} +func (*TraceEvent_ControlIAnnounceMeta) Descriptor() ([]byte, []int) { + return fileDescriptor_0571941a1d628a80, []int{0, 22} +} +func (m *TraceEvent_ControlIAnnounceMeta) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TraceEvent_ControlIAnnounceMeta) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TraceEvent_ControlIAnnounceMeta.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TraceEvent_ControlIAnnounceMeta) XXX_Merge(src proto.Message) { + xxx_messageInfo_TraceEvent_ControlIAnnounceMeta.Merge(m, src) +} +func (m *TraceEvent_ControlIAnnounceMeta) XXX_Size() int { + return m.Size() +} +func (m *TraceEvent_ControlIAnnounceMeta) XXX_DiscardUnknown() { + xxx_messageInfo_TraceEvent_ControlIAnnounceMeta.DiscardUnknown(m) +} + +var xxx_messageInfo_TraceEvent_ControlIAnnounceMeta proto.InternalMessageInfo + +func (m *TraceEvent_ControlIAnnounceMeta) GetTopic() string { + if m != nil && m.Topic != nil { + return *m.Topic + } + return "" +} + +func (m *TraceEvent_ControlIAnnounceMeta) GetMessageID() []byte { + if m != nil { + return m.MessageID + } + return nil +} + +type TraceEvent_ControlINeedMeta struct { + MessageID []byte `protobuf:"bytes,1,opt,name=messageID" json:"messageID,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TraceEvent_ControlINeedMeta) Reset() { *m = TraceEvent_ControlINeedMeta{} } +func (m *TraceEvent_ControlINeedMeta) String() string { return proto.CompactTextString(m) } +func (*TraceEvent_ControlINeedMeta) ProtoMessage() {} +func (*TraceEvent_ControlINeedMeta) Descriptor() ([]byte, []int) { + return fileDescriptor_0571941a1d628a80, []int{0, 23} +} +func (m *TraceEvent_ControlINeedMeta) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TraceEvent_ControlINeedMeta) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TraceEvent_ControlINeedMeta.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TraceEvent_ControlINeedMeta) XXX_Merge(src proto.Message) { + xxx_messageInfo_TraceEvent_ControlINeedMeta.Merge(m, src) +} +func (m *TraceEvent_ControlINeedMeta) XXX_Size() int { + return m.Size() +} +func (m *TraceEvent_ControlINeedMeta) XXX_DiscardUnknown() { + xxx_messageInfo_TraceEvent_ControlINeedMeta.DiscardUnknown(m) +} + +var xxx_messageInfo_TraceEvent_ControlINeedMeta proto.InternalMessageInfo + +func (m *TraceEvent_ControlINeedMeta) GetMessageID() []byte { + if m != nil { + return m.MessageID + } + return nil +} + type TraceEventBatch struct { Batch []*TraceEvent `protobuf:"bytes,1,rep,name=batch" json:"batch,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1561,78 +1678,83 @@ func init() { proto.RegisterType((*TraceEvent_ControlGraftMeta)(nil), "pubsub.pb.TraceEvent.ControlGraftMeta") proto.RegisterType((*TraceEvent_ControlPruneMeta)(nil), "pubsub.pb.TraceEvent.ControlPruneMeta") proto.RegisterType((*TraceEvent_ControlIDontWantMeta)(nil), "pubsub.pb.TraceEvent.ControlIDontWantMeta") + proto.RegisterType((*TraceEvent_ControlIAnnounceMeta)(nil), "pubsub.pb.TraceEvent.ControlIAnnounceMeta") + proto.RegisterType((*TraceEvent_ControlINeedMeta)(nil), "pubsub.pb.TraceEvent.ControlINeedMeta") proto.RegisterType((*TraceEventBatch)(nil), "pubsub.pb.TraceEventBatch") } func init() { proto.RegisterFile("trace.proto", fileDescriptor_0571941a1d628a80) } var fileDescriptor_0571941a1d628a80 = []byte{ - // 1027 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xdf, 0x6e, 0xe2, 0x46, - 0x14, 0xc6, 0xeb, 0x80, 0x03, 0x1c, 0x08, 0x71, 0xa7, 0xd9, 0xd6, 0x72, 0x77, 0x23, 0x9a, 0xae, - 0x56, 0xa8, 0x95, 0x90, 0x36, 0x52, 0xbb, 0x17, 0xdd, 0x5d, 0x95, 0x60, 0x6f, 0x42, 0x44, 0x12, - 0x6b, 0x20, 0xe9, 0x65, 0x6a, 0x60, 0xba, 0x71, 0x04, 0xb6, 0x65, 0x0f, 0x54, 0x7b, 0xd5, 0xd7, - 0xdb, 0xbb, 0xed, 0x23, 0x54, 0x79, 0x92, 0x6a, 0x66, 0xfc, 0x07, 0x83, 0xed, 0xec, 0x46, 0xb9, - 0xf3, 0x19, 0xbe, 0xdf, 0x99, 0x33, 0x67, 0xce, 0x37, 0x02, 0xea, 0xd4, 0xb7, 0x26, 0xa4, 0xe3, - 0xf9, 0x2e, 0x75, 0x51, 0xcd, 0x5b, 0x8c, 0x83, 0xc5, 0xb8, 0xe3, 0x8d, 0x0f, 0xee, 0xbe, 0x03, - 0x18, 0xb1, 0x9f, 0x8c, 0x25, 0x71, 0x28, 0xea, 0x40, 0x99, 0x7e, 0xf0, 0x88, 0x2a, 0xb5, 0xa4, - 0x76, 0xf3, 0x50, 0xeb, 0xc4, 0xc2, 0x4e, 0x22, 0xea, 0x8c, 0x3e, 0x78, 0x04, 0x73, 0x1d, 0xfa, - 0x16, 0xb6, 0x3d, 0x42, 0xfc, 0xbe, 0xae, 0x6e, 0xb5, 0xa4, 0x76, 0x03, 0x87, 0x11, 0x7a, 0x0a, - 0x35, 0x6a, 0xcf, 0x49, 0x40, 0xad, 0xb9, 0xa7, 0x96, 0x5a, 0x52, 0xbb, 0x84, 0x93, 0x05, 0x34, - 0x80, 0xa6, 0xb7, 0x18, 0xcf, 0xec, 0xe0, 0xe6, 0x8c, 0x04, 0x81, 0xf5, 0x9e, 0xa8, 0xe5, 0x96, - 0xd4, 0xae, 0x1f, 0x3e, 0xcf, 0xde, 0xcf, 0x4c, 0x69, 0xf1, 0x1a, 0x8b, 0xfa, 0xb0, 0xe3, 0x93, - 0x5b, 0x32, 0xa1, 0x51, 0x32, 0x99, 0x27, 0xfb, 0x31, 0x3b, 0x19, 0x5e, 0x95, 0xe2, 0x34, 0x89, - 0x30, 0x28, 0xd3, 0x85, 0x37, 0xb3, 0x27, 0x16, 0x25, 0x51, 0xb6, 0x6d, 0x9e, 0xed, 0x45, 0x76, - 0x36, 0x7d, 0x4d, 0x8d, 0x37, 0x78, 0x76, 0xd8, 0x29, 0x99, 0xd9, 0x4b, 0xe2, 0x47, 0x19, 0x2b, - 0x45, 0x87, 0xd5, 0x53, 0x5a, 0xbc, 0xc6, 0xa2, 0x57, 0x50, 0xb1, 0xa6, 0x53, 0x93, 0x10, 0x5f, - 0xad, 0xf2, 0x34, 0xcf, 0xb2, 0xd3, 0x74, 0x85, 0x08, 0x47, 0x6a, 0xf4, 0x3b, 0x80, 0x4f, 0xe6, - 0xee, 0x92, 0x70, 0xb6, 0xc6, 0xd9, 0x56, 0x5e, 0x8b, 0x22, 0x1d, 0x5e, 0x61, 0xd8, 0xd6, 0x3e, - 0x99, 0x2c, 0xb1, 0xd9, 0x53, 0xa1, 0x68, 0x6b, 0x2c, 0x44, 0x38, 0x52, 0x33, 0x30, 0x20, 0xce, - 0x94, 0x81, 0xf5, 0x22, 0x70, 0x28, 0x44, 0x38, 0x52, 0x33, 0x70, 0xea, 0xbb, 0x1e, 0x03, 0x1b, - 0x45, 0xa0, 0x2e, 0x44, 0x38, 0x52, 0xb3, 0x31, 0xbe, 0x75, 0x6d, 0x47, 0xdd, 0xe1, 0x54, 0xce, - 0x18, 0x9f, 0xba, 0xb6, 0x83, 0xb9, 0x0e, 0xbd, 0x04, 0x79, 0x46, 0xac, 0x25, 0x51, 0x9b, 0x1c, - 0xf8, 0x3e, 0x1b, 0x18, 0x30, 0x09, 0x16, 0x4a, 0x86, 0xbc, 0xf7, 0xad, 0xbf, 0xa8, 0xba, 0x5b, - 0x84, 0x1c, 0x33, 0x09, 0x16, 0x4a, 0x86, 0x78, 0xfe, 0xc2, 0x21, 0xaa, 0x52, 0x84, 0x98, 0x4c, - 0x82, 0x85, 0x52, 0xd3, 0xa1, 0x99, 0x9e, 0x7e, 0xe6, 0xac, 0xb9, 0xf8, 0xec, 0xeb, 0xdc, 0xa6, - 0x0d, 0x9c, 0x2c, 0xa0, 0x3d, 0x90, 0xa9, 0xeb, 0xd9, 0x13, 0x6e, 0xc7, 0x1a, 0x16, 0x81, 0xf6, - 0x0f, 0xec, 0xa4, 0xc6, 0xfe, 0x9e, 0x24, 0x07, 0xd0, 0xf0, 0xc9, 0x84, 0xd8, 0x4b, 0x32, 0x7d, - 0xe7, 0xbb, 0xf3, 0xd0, 0xda, 0xa9, 0x35, 0x66, 0x7c, 0x9f, 0x58, 0x81, 0xeb, 0x70, 0x77, 0xd7, - 0x70, 0x18, 0x25, 0x05, 0x94, 0x57, 0x0b, 0xb8, 0x05, 0x65, 0xdd, 0x29, 0x8f, 0x50, 0x43, 0xbc, - 0x57, 0x69, 0x75, 0xaf, 0x1b, 0x68, 0xa6, 0x3d, 0xf4, 0x90, 0x96, 0x6d, 0xec, 0x5f, 0xda, 0xdc, - 0x5f, 0x7b, 0x05, 0x95, 0xd0, 0x66, 0x2b, 0xef, 0xa0, 0x94, 0x7a, 0x07, 0xf7, 0xd8, 0x95, 0xbb, - 0xd4, 0x8d, 0x92, 0xf3, 0x40, 0x7b, 0x0e, 0x90, 0x78, 0x2c, 0x8f, 0xd5, 0xfe, 0x84, 0x4a, 0x68, - 0xa5, 0x8d, 0x6a, 0xa4, 0x8c, 0x6e, 0xbc, 0x84, 0xf2, 0x9c, 0x50, 0x8b, 0xef, 0x94, 0xef, 0x4d, - 0xb3, 0x77, 0x46, 0xa8, 0x85, 0xb9, 0x54, 0x1b, 0x41, 0x25, 0xf4, 0x1c, 0x2b, 0x82, 0xb9, 0x6e, - 0xe4, 0x46, 0x45, 0x88, 0xe8, 0x81, 0x59, 0x43, 0x43, 0x3e, 0x66, 0xd6, 0xa7, 0x50, 0x66, 0x86, - 0x4d, 0xae, 0x4b, 0x5a, 0xbd, 0xf4, 0x67, 0x20, 0x73, 0x77, 0xe6, 0x18, 0xe0, 0x17, 0x90, 0xb9, - 0x13, 0x8b, 0xee, 0x29, 0x1b, 0xe3, 0x6e, 0xfc, 0x42, 0xec, 0xa3, 0x04, 0x95, 0xb0, 0x78, 0xf4, - 0x06, 0xaa, 0xe1, 0xa8, 0x05, 0xaa, 0xd4, 0x2a, 0xb5, 0xeb, 0x87, 0x3f, 0x64, 0x9f, 0x36, 0x1c, - 0x56, 0x7e, 0xe2, 0x18, 0x41, 0x5d, 0x68, 0x04, 0x8b, 0x71, 0x30, 0xf1, 0x6d, 0x8f, 0xda, 0xae, - 0xa3, 0x6e, 0xf1, 0x14, 0x79, 0xef, 0xe7, 0x62, 0xcc, 0xf1, 0x14, 0x82, 0x7e, 0x83, 0xca, 0xc4, - 0x75, 0xa8, 0xef, 0xce, 0xf8, 0x10, 0xe7, 0x16, 0xd0, 0x13, 0x22, 0x9e, 0x21, 0x22, 0xb4, 0x2e, - 0xd4, 0x57, 0x0a, 0x7b, 0xd0, 0xe3, 0xf3, 0x06, 0x2a, 0x61, 0x61, 0x0c, 0x0f, 0x4b, 0x1b, 0x8b, - 0xbf, 0x18, 0x55, 0x9c, 0x2c, 0xe4, 0xe0, 0x9f, 0xb6, 0xa0, 0xbe, 0x52, 0x1a, 0x7a, 0x0d, 0xb2, - 0x7d, 0xc3, 0x9e, 0x6a, 0xd1, 0xcd, 0x17, 0x85, 0x87, 0xe9, 0x9f, 0x58, 0x4b, 0xd1, 0x52, 0x01, - 0x71, 0xfa, 0x6f, 0xcb, 0xa1, 0x61, 0x23, 0xef, 0xa1, 0xff, 0xb0, 0x1c, 0x1a, 0xd2, 0x0c, 0x62, - 0xb4, 0x78, 0xf3, 0x4b, 0x9f, 0x41, 0xf3, 0x81, 0x13, 0xb4, 0x78, 0xfe, 0x5f, 0x47, 0xcf, 0x7f, - 0xf9, 0x33, 0x68, 0x3e, 0x77, 0x82, 0xe6, 0x10, 0x3a, 0x81, 0x9a, 0x3d, 0x75, 0x1d, 0xca, 0xab, - 0x97, 0x79, 0x86, 0x9f, 0x8a, 0xab, 0xd7, 0x5d, 0x87, 0xc6, 0x27, 0x48, 0x60, 0xed, 0x04, 0x94, - 0xf5, 0xf6, 0x64, 0xbb, 0x0a, 0xed, 0x03, 0xc4, 0xb7, 0x1b, 0xf0, 0x96, 0x35, 0xf0, 0xca, 0x8a, - 0x76, 0x98, 0x64, 0x8a, 0x36, 0x5a, 0x63, 0xa4, 0x0d, 0xa6, 0x1d, 0x33, 0x71, 0x83, 0x72, 0x3c, - 0xfd, 0x36, 0x56, 0xc6, 0xcd, 0xc8, 0xa9, 0x93, 0xbd, 0xb2, 0x84, 0xf8, 0x51, 0x89, 0x22, 0xd0, - 0x7e, 0x85, 0xbd, 0xac, 0x56, 0xdc, 0x57, 0xe1, 0xc1, 0x27, 0x09, 0xca, 0xec, 0x2f, 0x2e, 0xfa, - 0x06, 0x76, 0xcd, 0xcb, 0xa3, 0x41, 0x7f, 0x78, 0x72, 0x7d, 0x66, 0x0c, 0x87, 0xdd, 0x63, 0x43, - 0xf9, 0x0a, 0x21, 0x68, 0x62, 0xe3, 0xd4, 0xe8, 0x8d, 0xe2, 0x35, 0x09, 0x3d, 0x81, 0xaf, 0xf5, - 0x4b, 0x73, 0xd0, 0xef, 0x75, 0x47, 0x46, 0xbc, 0xbc, 0xc5, 0x78, 0xdd, 0x18, 0xf4, 0xaf, 0x0c, - 0x1c, 0x2f, 0x96, 0x50, 0x03, 0xaa, 0x5d, 0x5d, 0xbf, 0x36, 0x0d, 0x03, 0x2b, 0x65, 0xb4, 0x0b, - 0x75, 0x6c, 0x9c, 0x5d, 0x5c, 0x19, 0x62, 0x41, 0x66, 0x3f, 0x63, 0xa3, 0x77, 0x75, 0x8d, 0xcd, - 0x9e, 0xb2, 0xcd, 0xa2, 0xa1, 0x71, 0xae, 0xf3, 0xa8, 0xc2, 0x22, 0x1d, 0x5f, 0x98, 0x3c, 0xaa, - 0xa2, 0x2a, 0x94, 0x4f, 0x2f, 0xfa, 0xe7, 0x4a, 0x0d, 0xd5, 0x40, 0x1e, 0x18, 0xdd, 0x2b, 0x43, - 0x01, 0xf6, 0x79, 0x8c, 0xbb, 0xef, 0x46, 0x4a, 0x9d, 0x7d, 0x9a, 0xf8, 0xf2, 0xdc, 0x50, 0x1a, - 0x07, 0x6f, 0x61, 0x37, 0x99, 0x8f, 0x23, 0x8b, 0x4e, 0x6e, 0xd0, 0xcf, 0x20, 0x8f, 0xd9, 0x47, - 0x68, 0xa3, 0x27, 0x99, 0xa3, 0x84, 0x85, 0xe6, 0xa8, 0xf1, 0xf1, 0x6e, 0x5f, 0xfa, 0xf7, 0x6e, - 0x5f, 0xfa, 0xef, 0x6e, 0x5f, 0xfa, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x17, 0x7f, 0xbd, 0x0d, 0x4b, - 0x0c, 0x00, 0x00, + // 1088 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0x4f, 0x6f, 0xdb, 0x46, + 0x13, 0xc6, 0x5f, 0x5a, 0x92, 0x25, 0x8d, 0x64, 0x99, 0xef, 0xd6, 0x29, 0x58, 0x36, 0x31, 0x54, + 0x37, 0x08, 0x84, 0x16, 0x10, 0x1a, 0x03, 0x6d, 0x0e, 0x4d, 0x82, 0xca, 0x22, 0x63, 0xcb, 0x90, + 0x6d, 0x62, 0x2d, 0xbb, 0x47, 0x97, 0x12, 0xb7, 0x31, 0x0d, 0x8b, 0x24, 0xc8, 0x95, 0x8a, 0x9c, + 0xfa, 0xf5, 0x72, 0x6b, 0x6f, 0xbd, 0x16, 0x3e, 0xf5, 0x63, 0x14, 0xbb, 0xcb, 0xbf, 0x12, 0x49, + 0xbb, 0x46, 0x6e, 0xdc, 0xd5, 0xf3, 0x9b, 0x9d, 0xdd, 0x99, 0x67, 0x20, 0x68, 0x51, 0xdf, 0x9c, + 0x91, 0xbe, 0xe7, 0xbb, 0xd4, 0x45, 0x4d, 0x6f, 0x31, 0x0d, 0x16, 0xd3, 0xbe, 0x37, 0xdd, 0xfb, + 0xe7, 0x0b, 0x80, 0x09, 0xfb, 0x49, 0x5f, 0x12, 0x87, 0xa2, 0x3e, 0x54, 0xe9, 0x07, 0x8f, 0x28, + 0x52, 0x57, 0xea, 0x75, 0xf6, 0xd5, 0x7e, 0x2c, 0xec, 0x27, 0xa2, 0xfe, 0xe4, 0x83, 0x47, 0x30, + 0xd7, 0xa1, 0xcf, 0x61, 0xd3, 0x23, 0xc4, 0x1f, 0x69, 0xca, 0x46, 0x57, 0xea, 0xb5, 0x71, 0xb8, + 0x42, 0x4f, 0xa1, 0x49, 0xed, 0x39, 0x09, 0xa8, 0x39, 0xf7, 0x94, 0x4a, 0x57, 0xea, 0x55, 0x70, + 0xb2, 0x81, 0xc6, 0xd0, 0xf1, 0x16, 0xd3, 0x5b, 0x3b, 0xb8, 0x3e, 0x21, 0x41, 0x60, 0xbe, 0x27, + 0x4a, 0xb5, 0x2b, 0xf5, 0x5a, 0xfb, 0xcf, 0xf3, 0xcf, 0x33, 0x32, 0x5a, 0xbc, 0xc2, 0xa2, 0x11, + 0x6c, 0xf9, 0xe4, 0x86, 0xcc, 0x68, 0x14, 0xac, 0xc6, 0x83, 0x7d, 0x9d, 0x1f, 0x0c, 0xa7, 0xa5, + 0x38, 0x4b, 0x22, 0x0c, 0xb2, 0xb5, 0xf0, 0x6e, 0xed, 0x99, 0x49, 0x49, 0x14, 0x6d, 0x93, 0x47, + 0x7b, 0x91, 0x1f, 0x4d, 0x5b, 0x51, 0xe3, 0x35, 0x9e, 0x5d, 0xd6, 0x22, 0xb7, 0xf6, 0x92, 0xf8, + 0x51, 0xc4, 0x7a, 0xd9, 0x65, 0xb5, 0x8c, 0x16, 0xaf, 0xb0, 0xe8, 0x15, 0xd4, 0x4d, 0xcb, 0x32, + 0x08, 0xf1, 0x95, 0x06, 0x0f, 0xf3, 0x2c, 0x3f, 0xcc, 0x40, 0x88, 0x70, 0xa4, 0x46, 0x3f, 0x01, + 0xf8, 0x64, 0xee, 0x2e, 0x09, 0x67, 0x9b, 0x9c, 0xed, 0x16, 0x3d, 0x51, 0xa4, 0xc3, 0x29, 0x86, + 0x1d, 0xed, 0x93, 0xd9, 0x12, 0x1b, 0x43, 0x05, 0xca, 0x8e, 0xc6, 0x42, 0x84, 0x23, 0x35, 0x03, + 0x03, 0xe2, 0x58, 0x0c, 0x6c, 0x95, 0x81, 0xe7, 0x42, 0x84, 0x23, 0x35, 0x03, 0x2d, 0xdf, 0xf5, + 0x18, 0xd8, 0x2e, 0x03, 0x35, 0x21, 0xc2, 0x91, 0x9a, 0xb5, 0xf1, 0x8d, 0x6b, 0x3b, 0xca, 0x16, + 0xa7, 0x0a, 0xda, 0xf8, 0xd8, 0xb5, 0x1d, 0xcc, 0x75, 0xe8, 0x25, 0xd4, 0x6e, 0x89, 0xb9, 0x24, + 0x4a, 0x87, 0x03, 0x5f, 0xe6, 0x03, 0x63, 0x26, 0xc1, 0x42, 0xc9, 0x90, 0xf7, 0xbe, 0xf9, 0x2b, + 0x55, 0xb6, 0xcb, 0x90, 0x43, 0x26, 0xc1, 0x42, 0xc9, 0x10, 0xcf, 0x5f, 0x38, 0x44, 0x91, 0xcb, + 0x10, 0x83, 0x49, 0xb0, 0x50, 0xaa, 0x1a, 0x74, 0xb2, 0xdd, 0xcf, 0x9c, 0x35, 0x17, 0x9f, 0x23, + 0x8d, 0xdb, 0xb4, 0x8d, 0x93, 0x0d, 0xb4, 0x03, 0x35, 0xea, 0x7a, 0xf6, 0x8c, 0xdb, 0xb1, 0x89, + 0xc5, 0x42, 0xfd, 0x1d, 0xb6, 0x32, 0x6d, 0x7f, 0x4f, 0x90, 0x3d, 0x68, 0xfb, 0x64, 0x46, 0xec, + 0x25, 0xb1, 0xde, 0xf9, 0xee, 0x3c, 0xb4, 0x76, 0x66, 0x8f, 0x19, 0xdf, 0x27, 0x66, 0xe0, 0x3a, + 0xdc, 0xdd, 0x4d, 0x1c, 0xae, 0x92, 0x04, 0xaa, 0xe9, 0x04, 0x6e, 0x40, 0x5e, 0x75, 0xca, 0x27, + 0xc8, 0x21, 0x3e, 0xab, 0x92, 0x3e, 0xeb, 0x1a, 0x3a, 0x59, 0x0f, 0x3d, 0xe6, 0xc9, 0xd6, 0xce, + 0xaf, 0xac, 0x9f, 0xaf, 0xbe, 0x82, 0x7a, 0x68, 0xb3, 0xd4, 0x1c, 0x94, 0x32, 0x73, 0x70, 0x87, + 0x95, 0xdc, 0xa5, 0x6e, 0x14, 0x9c, 0x2f, 0xd4, 0xe7, 0x00, 0x89, 0xc7, 0x8a, 0x58, 0xf5, 0x17, + 0xa8, 0x87, 0x56, 0x5a, 0xcb, 0x46, 0xca, 0x79, 0x8d, 0x97, 0x50, 0x9d, 0x13, 0x6a, 0xf2, 0x93, + 0x8a, 0xbd, 0x69, 0x0c, 0x4f, 0x08, 0x35, 0x31, 0x97, 0xaa, 0x13, 0xa8, 0x87, 0x9e, 0x63, 0x49, + 0x30, 0xd7, 0x4d, 0xdc, 0x28, 0x09, 0xb1, 0x7a, 0x64, 0xd4, 0xd0, 0x90, 0x9f, 0x32, 0xea, 0x53, + 0xa8, 0x32, 0xc3, 0x26, 0xe5, 0x92, 0xd2, 0x45, 0x7f, 0x06, 0x35, 0xee, 0xce, 0x02, 0x03, 0x7c, + 0x0f, 0x35, 0xee, 0xc4, 0xb2, 0x3a, 0xe5, 0x63, 0xdc, 0x8d, 0xff, 0x11, 0xfb, 0x28, 0x41, 0x3d, + 0x4c, 0x1e, 0xbd, 0x81, 0x46, 0xd8, 0x6a, 0x81, 0x22, 0x75, 0x2b, 0xbd, 0xd6, 0xfe, 0x57, 0xf9, + 0xb7, 0x0d, 0x9b, 0x95, 0xdf, 0x38, 0x46, 0xd0, 0x00, 0xda, 0xc1, 0x62, 0x1a, 0xcc, 0x7c, 0xdb, + 0xa3, 0xb6, 0xeb, 0x28, 0x1b, 0x3c, 0x44, 0xd1, 0xfc, 0x5c, 0x4c, 0x39, 0x9e, 0x41, 0xd0, 0x8f, + 0x50, 0x9f, 0xb9, 0x0e, 0xf5, 0xdd, 0x5b, 0xde, 0xc4, 0x85, 0x09, 0x0c, 0x85, 0x88, 0x47, 0x88, + 0x08, 0x75, 0x00, 0xad, 0x54, 0x62, 0x8f, 0x1a, 0x3e, 0x6f, 0xa0, 0x1e, 0x26, 0xc6, 0xf0, 0x30, + 0xb5, 0xa9, 0xf8, 0x8b, 0xd1, 0xc0, 0xc9, 0x46, 0x01, 0xfe, 0x57, 0x05, 0x5a, 0xa9, 0xd4, 0xd0, + 0x6b, 0xa8, 0xd9, 0xd7, 0x6c, 0x54, 0x8b, 0xd7, 0x7c, 0x51, 0x7a, 0x99, 0xd1, 0x91, 0xb9, 0x14, + 0x4f, 0x2a, 0x20, 0x4e, 0xff, 0x66, 0x3a, 0x34, 0x7c, 0xc8, 0x7b, 0xe8, 0x9f, 0x4d, 0x87, 0x86, + 0x34, 0x83, 0x18, 0x2d, 0x66, 0x7e, 0xe5, 0x01, 0x34, 0x6f, 0x38, 0x41, 0x8b, 0xf1, 0xff, 0x3a, + 0x1a, 0xff, 0xd5, 0x07, 0xd0, 0xbc, 0xef, 0x04, 0xcd, 0x21, 0x74, 0x04, 0x4d, 0xdb, 0x72, 0x1d, + 0xca, 0xb3, 0xaf, 0xf1, 0x08, 0xdf, 0x94, 0x67, 0xaf, 0xb9, 0x0e, 0x8d, 0x6f, 0x90, 0xc0, 0x3c, + 0x92, 0xe9, 0x38, 0xee, 0xc2, 0x99, 0xb1, 0x7f, 0x37, 0x0f, 0x88, 0x34, 0x08, 0xd5, 0x61, 0xa4, + 0x08, 0xe6, 0xaf, 0xe9, 0x10, 0x62, 0x29, 0xf5, 0x87, 0xbc, 0xe6, 0x29, 0x21, 0x56, 0xf8, 0x9a, + 0x0c, 0x52, 0x8f, 0x40, 0x5e, 0x2d, 0x53, 0xbe, 0xbb, 0xd1, 0x2e, 0x40, 0xdc, 0x65, 0x01, 0x2f, + 0x5d, 0x1b, 0xa7, 0x76, 0xd4, 0xfd, 0x24, 0x52, 0x74, 0xe1, 0x15, 0x46, 0x5a, 0x63, 0x7a, 0x31, + 0x13, 0x17, 0xaa, 0x60, 0xb6, 0xbc, 0x8d, 0x95, 0x71, 0x51, 0x0a, 0xf2, 0x64, 0xd3, 0x9e, 0x10, + 0x3f, 0x4a, 0x51, 0x2c, 0xd4, 0x1f, 0x60, 0x27, 0xaf, 0x24, 0xf7, 0x66, 0x78, 0x9c, 0x70, 0xe9, + 0x02, 0x14, 0x9c, 0x9d, 0xb1, 0xe6, 0xc6, 0x8a, 0x35, 0xd5, 0xef, 0x92, 0x17, 0x8a, 0xca, 0x50, + 0x6e, 0xe6, 0xbd, 0x3f, 0x24, 0xa8, 0xb2, 0x3f, 0xfa, 0xe8, 0x33, 0xd8, 0x36, 0x2e, 0x0e, 0xc6, + 0xa3, 0xf3, 0xa3, 0xab, 0x13, 0xfd, 0xfc, 0x7c, 0x70, 0xa8, 0xcb, 0xff, 0x43, 0x08, 0x3a, 0x58, + 0x3f, 0xd6, 0x87, 0x93, 0x78, 0x4f, 0x42, 0x4f, 0xe0, 0xff, 0xda, 0x85, 0x31, 0x1e, 0x0d, 0x07, + 0x13, 0x3d, 0xde, 0xde, 0x60, 0xbc, 0xa6, 0x8f, 0x47, 0x97, 0x3a, 0x8e, 0x37, 0x2b, 0xa8, 0x0d, + 0x8d, 0x81, 0xa6, 0x5d, 0x19, 0xba, 0x8e, 0xe5, 0x2a, 0xda, 0x86, 0x16, 0xd6, 0x4f, 0xce, 0x2e, + 0x75, 0xb1, 0x51, 0x63, 0x3f, 0x63, 0x7d, 0x78, 0x79, 0x85, 0x8d, 0xa1, 0xbc, 0xc9, 0x56, 0xe7, + 0xfa, 0xa9, 0xc6, 0x57, 0x75, 0xb6, 0xd2, 0xf0, 0x99, 0xc1, 0x57, 0x0d, 0xd4, 0x80, 0xea, 0xf1, + 0xd9, 0xe8, 0x54, 0x6e, 0xa2, 0x26, 0xd4, 0xc6, 0xfa, 0xe0, 0x52, 0x97, 0x81, 0x7d, 0x1e, 0xe2, + 0xc1, 0xbb, 0x89, 0xdc, 0x62, 0x9f, 0x06, 0xbe, 0x38, 0xd5, 0xe5, 0xf6, 0xde, 0x5b, 0xd8, 0x4e, + 0xba, 0xf2, 0xc0, 0xa4, 0xb3, 0x6b, 0xf4, 0x2d, 0xd4, 0xa6, 0xec, 0x23, 0x1c, 0x26, 0x4f, 0x72, + 0x1b, 0x18, 0x0b, 0xcd, 0x41, 0xfb, 0xe3, 0xdd, 0xae, 0xf4, 0xe7, 0xdd, 0xae, 0xf4, 0xf7, 0xdd, + 0xae, 0xf4, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x39, 0x10, 0x87, 0x2b, 0x51, 0x0d, 0x00, 0x00, } func (m *TraceEvent) Marshal() (dAtA []byte, err error) { @@ -2568,6 +2690,34 @@ func (m *TraceEvent_ControlMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Ineed) > 0 { + for iNdEx := len(m.Ineed) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Ineed[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTrace(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.Iannounce) > 0 { + for iNdEx := len(m.Iannounce) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Iannounce[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTrace(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } if len(m.Idontwant) > 0 { for iNdEx := len(m.Idontwant) - 1; iNdEx >= 0; iNdEx-- { { @@ -2833,6 +2983,81 @@ func (m *TraceEvent_ControlIDontWantMeta) MarshalToSizedBuffer(dAtA []byte) (int return len(dAtA) - i, nil } +func (m *TraceEvent_ControlIAnnounceMeta) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TraceEvent_ControlIAnnounceMeta) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TraceEvent_ControlIAnnounceMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MessageID != nil { + i -= len(m.MessageID) + copy(dAtA[i:], m.MessageID) + i = encodeVarintTrace(dAtA, i, uint64(len(m.MessageID))) + i-- + dAtA[i] = 0x12 + } + if m.Topic != nil { + i -= len(*m.Topic) + copy(dAtA[i:], *m.Topic) + i = encodeVarintTrace(dAtA, i, uint64(len(*m.Topic))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TraceEvent_ControlINeedMeta) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TraceEvent_ControlINeedMeta) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TraceEvent_ControlINeedMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MessageID != nil { + i -= len(m.MessageID) + copy(dAtA[i:], m.MessageID) + i = encodeVarintTrace(dAtA, i, uint64(len(m.MessageID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *TraceEventBatch) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -3326,6 +3551,18 @@ func (m *TraceEvent_ControlMeta) Size() (n int) { n += 1 + l + sovTrace(uint64(l)) } } + if len(m.Iannounce) > 0 { + for _, e := range m.Iannounce { + l = e.Size() + n += 1 + l + sovTrace(uint64(l)) + } + } + if len(m.Ineed) > 0 { + for _, e := range m.Ineed { + l = e.Size() + n += 1 + l + sovTrace(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3428,6 +3665,42 @@ func (m *TraceEvent_ControlIDontWantMeta) Size() (n int) { return n } +func (m *TraceEvent_ControlIAnnounceMeta) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Topic != nil { + l = len(*m.Topic) + n += 1 + l + sovTrace(uint64(l)) + } + if m.MessageID != nil { + l = len(m.MessageID) + n += 1 + l + sovTrace(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *TraceEvent_ControlINeedMeta) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MessageID != nil { + l = len(m.MessageID) + n += 1 + l + sovTrace(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *TraceEventBatch) Size() (n int) { if m == nil { return 0 @@ -6199,6 +6472,74 @@ func (m *TraceEvent_ControlMeta) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Iannounce", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTrace + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTrace + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Iannounce = append(m.Iannounce, &TraceEvent_ControlIAnnounceMeta{}) + if err := m.Iannounce[len(m.Iannounce)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ineed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTrace + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTrace + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ineed = append(m.Ineed, &TraceEvent_ControlINeedMeta{}) + if err := m.Ineed[len(m.Ineed)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTrace(dAtA[iNdEx:]) @@ -6703,6 +7044,209 @@ func (m *TraceEvent_ControlIDontWantMeta) Unmarshal(dAtA []byte) error { } return nil } +func (m *TraceEvent_ControlIAnnounceMeta) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ControlIAnnounceMeta: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ControlIAnnounceMeta: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTrace + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTrace + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Topic = &s + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageID", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTrace + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTrace + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MessageID = append(m.MessageID[:0], dAtA[iNdEx:postIndex]...) + if m.MessageID == nil { + m.MessageID = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTrace(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTrace + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TraceEvent_ControlINeedMeta) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ControlINeedMeta: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ControlINeedMeta: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageID", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTrace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTrace + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTrace + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MessageID = append(m.MessageID[:0], dAtA[iNdEx:postIndex]...) + if m.MessageID == nil { + m.MessageID = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTrace(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTrace + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *TraceEventBatch) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pb/trace.proto b/pb/trace.proto index 5ee8401c..f21f22a7 100644 --- a/pb/trace.proto +++ b/pb/trace.proto @@ -125,6 +125,8 @@ message TraceEvent { repeated ControlGraftMeta graft = 3; repeated ControlPruneMeta prune = 4; repeated ControlIDontWantMeta idontwant = 5; + repeated ControlIAnnounceMeta iannounce = 6; + repeated ControlINeedMeta ineed = 7; } message ControlIHaveMeta { @@ -148,6 +150,15 @@ message TraceEvent { message ControlIDontWantMeta { repeated bytes messageIDs = 1; } + + message ControlIAnnounceMeta { + optional string topic = 1; + optional bytes messageID = 2; + } + + message ControlINeedMeta { + optional bytes messageID = 1; + } } message TraceEventBatch { diff --git a/trace.go b/trace.go index 7dbb5409..fa3b5e37 100644 --- a/trace.go +++ b/trace.go @@ -413,12 +413,37 @@ func (t *pubsubTracer) traceRPCMeta(rpc *RPC) *pb.TraceEvent_RPCMeta { }) } + var iannounce []*pb.TraceEvent_ControlIAnnounceMeta + for _, ctl := range rpc.Control.Iannounce { + var mid []byte + if ctl.MessageID != nil { + mid = []byte(*ctl.MessageID) + } + iannounce = append(iannounce, &pb.TraceEvent_ControlIAnnounceMeta{ + Topic: ctl.TopicID, + MessageID: mid, + }) + } + + var ineed []*pb.TraceEvent_ControlINeedMeta + for _, ctl := range rpc.Control.Ineed { + var mid []byte + if ctl.MessageID != nil { + mid = []byte(*ctl.MessageID) + } + ineed = append(ineed, &pb.TraceEvent_ControlINeedMeta{ + MessageID: mid, + }) + } + rpcMeta.Control = &pb.TraceEvent_ControlMeta{ Ihave: ihave, Iwant: iwant, Graft: graft, Prune: prune, Idontwant: idontwant, + Iannounce: iannounce, + Ineed: ineed, } } From ec781ae26c5080ffb890f3812dc10ac2f5d185f3 Mon Sep 17 00:00:00 2001 From: Pop Chunhapanya Date: Sun, 15 Dec 2024 19:41:13 +0700 Subject: [PATCH 2/4] Gossipsub v2.0: Send IANNOUNCE out --- comm.go | 6 +- gossipsub.go | 52 ++++- gossipsub_spam_test.go | 25 ++- gossipsub_test.go | 460 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 524 insertions(+), 19 deletions(-) diff --git a/comm.go b/comm.go index d38cce08..3df377c4 100644 --- a/comm.go +++ b/comm.go @@ -207,7 +207,9 @@ func rpcWithControl(msgs []*pb.Message, iwant []*pb.ControlIWant, graft []*pb.ControlGraft, prune []*pb.ControlPrune, - idontwant []*pb.ControlIDontWant) *RPC { + idontwant []*pb.ControlIDontWant, + iannounce []*pb.ControlIAnnounce, + ineed []*pb.ControlINeed) *RPC { return &RPC{ RPC: pb.RPC{ Publish: msgs, @@ -217,6 +219,8 @@ func rpcWithControl(msgs []*pb.Message, Graft: graft, Prune: prune, Idontwant: idontwant, + Iannounce: iannounce, + Ineed: ineed, }, }, } diff --git a/gossipsub.go b/gossipsub.go index 59efec83..8d31854b 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -746,7 +746,7 @@ func (gs *GossipSubRouter) PreValidation(msgs []*Message) { // send to only peers that support IDONTWANT if gs.feature(GossipSubFeatureIdontwant, gs.peers[p]) { idontwant := []*pb.ControlIDontWant{{MessageIDs: mids}} - out := rpcWithControl(nil, nil, nil, nil, nil, idontwant) + out := rpcWithControl(nil, nil, nil, nil, nil, idontwant, nil, nil) gs.sendRPC(p, out, true) } } @@ -769,7 +769,7 @@ func (gs *GossipSubRouter) HandleRPC(rpc *RPC) { return } - out := rpcWithControl(ihave, nil, iwant, nil, prune, nil) + out := rpcWithControl(ihave, nil, iwant, nil, prune, nil, nil, nil) gs.sendRPC(rpc.from, out, false) } @@ -1226,13 +1226,43 @@ func (gs *GossipSubRouter) Publish(msg *Message) { } } - out := rpcWithMessages(msg.Message) + iannounce := []*pb.ControlIAnnounce{{TopicID: &topic, MessageID: &msg.ID}} + lazyOut := rpcWithControl(nil, nil, nil, nil, nil, nil, iannounce, nil) + + eagerOut := rpcWithMessages(msg.Message) + for pid := range tosend { if pid == from || pid == peer.ID(msg.GetFrom()) { continue } - gs.sendRPC(pid, out, false) + // if the peer doesn't support IANNOUNCE, send eagerly + if !gs.feature(GossipSubFeatureAnnounce, gs.peers[pid]) { + gs.sendRPC(pid, eagerOut, false) + continue + } + + gmap, ok := gs.mesh[topic] + // if sending using fanout peers, send eagerly + if !ok { + gs.sendRPC(pid, eagerOut, false) + continue + } + + _, isMesh := gmap[pid] + _, isDirect := gs.direct[pid] + // if the peer is neither in mesh nor a direct peer, send eagerly + if !isMesh && !isDirect { + gs.sendRPC(pid, eagerOut, false) + continue + } + + // toss a coin to decide whether to send eagerly or lazily + if gs.params.D > 0 && rand.Intn(gs.params.D) < gs.params.Dannounce { + gs.sendRPC(pid, lazyOut, false) + } else { + gs.sendRPC(pid, eagerOut, false) + } } } @@ -1317,13 +1347,13 @@ func (gs *GossipSubRouter) Leave(topic string) { func (gs *GossipSubRouter) sendGraft(p peer.ID, topic string) { graft := []*pb.ControlGraft{{TopicID: &topic}} - out := rpcWithControl(nil, nil, nil, graft, nil, nil) + out := rpcWithControl(nil, nil, nil, graft, nil, nil, nil, nil) gs.sendRPC(p, out, false) } func (gs *GossipSubRouter) sendPrune(p peer.ID, topic string, isUnsubscribe bool) { prune := []*pb.ControlPrune{gs.makePrune(p, topic, gs.doPX, isUnsubscribe)} - out := rpcWithControl(nil, nil, nil, nil, prune, nil) + out := rpcWithControl(nil, nil, nil, nil, prune, nil, nil, nil) gs.sendRPC(p, out, false) } @@ -1925,7 +1955,7 @@ func (gs *GossipSubRouter) sendGraftPrune(tograft, toprune map[peer.ID][]string, } } - out := rpcWithControl(nil, nil, nil, graft, prune, nil) + out := rpcWithControl(nil, nil, nil, graft, prune, nil, nil, nil) gs.sendRPC(p, out, false) } @@ -1935,7 +1965,7 @@ func (gs *GossipSubRouter) sendGraftPrune(tograft, toprune map[peer.ID][]string, prune = append(prune, gs.makePrune(p, topic, gs.doPX && !noPX[p], false)) } - out := rpcWithControl(nil, nil, nil, nil, prune, nil) + out := rpcWithControl(nil, nil, nil, nil, prune, nil, nil, nil) gs.sendRPC(p, out, false) } } @@ -2002,14 +2032,14 @@ func (gs *GossipSubRouter) flush() { // send gossip first, which will also piggyback pending control for p, ihave := range gs.gossip { delete(gs.gossip, p) - out := rpcWithControl(nil, ihave, nil, nil, nil, nil) + out := rpcWithControl(nil, ihave, nil, nil, nil, nil, nil, nil) gs.sendRPC(p, out, false) } // send the remaining control messages that wasn't merged with gossip for p, ctl := range gs.control { delete(gs.control, p) - out := rpcWithControl(nil, nil, nil, ctl.Graft, ctl.Prune, nil) + out := rpcWithControl(nil, nil, nil, ctl.Graft, ctl.Prune, nil, nil, nil) gs.sendRPC(p, out, false) } } @@ -2173,7 +2203,7 @@ func (gs *GossipSubRouter) WithDefaultTagTracer() Option { // // nothing. func (gs *GossipSubRouter) SendControl(p peer.ID, ctl *pb.ControlMessage, msgs ...*pb.Message) { - out := rpcWithControl(msgs, ctl.Ihave, ctl.Iwant, ctl.Graft, ctl.Prune, ctl.Idontwant) + out := rpcWithControl(msgs, ctl.Ihave, ctl.Iwant, ctl.Graft, ctl.Prune, ctl.Idontwant, nil, nil) gs.sendRPC(p, out, false) } diff --git a/gossipsub_spam_test.go b/gossipsub_spam_test.go index 9f6f0f94..4a5f7a20 100644 --- a/gossipsub_spam_test.go +++ b/gossipsub_spam_test.go @@ -33,7 +33,9 @@ func TestGossipsubAttackSpamIWANT(t *testing.T) { attacker := hosts[1] // Set up gossipsub on the legit host - ps, err := NewGossipSub(ctx, legit) + params := DefaultGossipSubParams() + params.Dannounce = 0 + ps, err := NewGossipSub(ctx, legit, WithGossipSubParams(params)) if err != nil { t.Fatal(err) } @@ -123,7 +125,7 @@ func TestGossipsubAttackSpamIWANT(t *testing.T) { // being spammy) iwantlst := []string{DefaultMsgIdFn(msg)} iwant := []*pb.ControlIWant{{MessageIDs: iwantlst}} - orpc := rpcWithControl(nil, nil, iwant, nil, nil, nil) + orpc := rpcWithControl(nil, nil, iwant, nil, nil, nil, nil, nil) writeMsg(&orpc.RPC) } }) @@ -150,7 +152,10 @@ func TestGossipsubAttackSpamIHAVE(t *testing.T) { attacker := hosts[1] // Set up gossipsub on the legit host + params := DefaultGossipSubParams() + params.Dannounce = 0 ps, err := NewGossipSub(ctx, legit, + WithGossipSubParams(params), WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(peer.ID) float64 { return 0 }, @@ -210,7 +215,7 @@ func TestGossipsubAttackSpamIHAVE(t *testing.T) { for i := 0; i < 3*GossipSubMaxIHaveLength; i++ { ihavelst := []string{"someid" + strconv.Itoa(i)} ihave := []*pb.ControlIHave{{TopicID: sub.Topicid, MessageIDs: ihavelst}} - orpc := rpcWithControl(nil, ihave, nil, nil, nil, nil) + orpc := rpcWithControl(nil, ihave, nil, nil, nil, nil, nil, nil) writeMsg(&orpc.RPC) } @@ -240,7 +245,7 @@ func TestGossipsubAttackSpamIHAVE(t *testing.T) { for i := 0; i < 3*GossipSubMaxIHaveLength; i++ { ihavelst := []string{"someid" + strconv.Itoa(i+100)} ihave := []*pb.ControlIHave{{TopicID: sub.Topicid, MessageIDs: ihavelst}} - orpc := rpcWithControl(nil, ihave, nil, nil, nil, nil) + orpc := rpcWithControl(nil, ihave, nil, nil, nil, nil, nil, nil) writeMsg(&orpc.RPC) } @@ -301,7 +306,9 @@ func TestGossipsubAttackGRAFTNonExistentTopic(t *testing.T) { attacker := hosts[1] // Set up gossipsub on the legit host - ps, err := NewGossipSub(ctx, legit) + params := DefaultGossipSubParams() + params.Dannounce = 0 + ps, err := NewGossipSub(ctx, legit, WithGossipSubParams(params)) if err != nil { t.Fatal(err) } @@ -385,7 +392,10 @@ func TestGossipsubAttackGRAFTDuringBackoff(t *testing.T) { attacker := hosts[1] // Set up gossipsub on the legit host + params := DefaultGossipSubParams() + params.Dannounce = 0 ps, err := NewGossipSub(ctx, legit, + WithGossipSubParams(params), WithPeerScore( &PeerScoreParams{ AppSpecificScore: func(peer.ID) float64 { return 0 }, @@ -666,7 +676,10 @@ func TestGossipsubAttackInvalidMessageSpam(t *testing.T) { // Set up gossipsub on the legit host tracer := &gsAttackInvalidMsgTracer{} + gossipsubParams := DefaultGossipSubParams() + gossipsubParams.Dannounce = 0 ps, err := NewGossipSub(ctx, legit, + WithGossipSubParams(gossipsubParams), WithEventTracer(tracer), WithPeerScore(params, thresholds), ) @@ -942,7 +955,7 @@ func TestGossipsubHandleIDontwantSpam(t *testing.T) { type mockGSOnRead func(writeMsg func(*pb.RPC), irpc *pb.RPC) func newMockGS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg mockGSOnRead) { - newMockGSWithVersion(ctx, t, attacker, protocol.ID("/meshsub/1.2.0"), onReadMsg) + newMockGSWithVersion(ctx, t, attacker, protocol.ID("/meshsub/2.0.0"), onReadMsg) } func newMockGSWithVersion(ctx context.Context, t *testing.T, attacker host.Host, gossipSubID protocol.ID, onReadMsg mockGSOnRead) { diff --git a/gossipsub_test.go b/gossipsub_test.go index 675d164c..235a894b 100644 --- a/gossipsub_test.go +++ b/gossipsub_test.go @@ -28,6 +28,11 @@ import ( ) func getGossipsub(ctx context.Context, h host.Host, opts ...Option) *PubSub { + params := DefaultGossipSubParams() + // since there was no lazy mesh propagation in gossipsub 1.x, we need to set Dannounce to 0 for all the old tests + params.Dannounce = 0 + // prepend the options to allow overrides by users + opts = append([]Option{WithGossipSubParams(params)}, opts...) ps, err := NewGossipSub(ctx, h, opts...) if err != nil { panic(err) @@ -593,6 +598,7 @@ func TestGossipsubPruneBackoffTime(t *testing.T) { currentScoreForHost0 := int32(0) params := DefaultGossipSubParams() + params.Dannounce = 0 params.HeartbeatInitialDelay = time.Millisecond * 10 params.HeartbeatInterval = time.Millisecond * 100 @@ -1492,6 +1498,7 @@ func TestGossipsubCustomParams(t *testing.T) { defer cancel() params := DefaultGossipSubParams() + params.Dannounce = 0 wantedFollowTime := 1 * time.Second params.IWantFollowupTime = wantedFollowTime @@ -2329,7 +2336,7 @@ func (iwe *iwantEverything) handleStream(s network.Stream) { } } - out := rpcWithControl(nil, nil, iwants, nil, prunes, nil) + out := rpcWithControl(nil, nil, iwants, nil, prunes, nil, nil, nil) err = w.WriteMsg(out) if err != nil { panic(err) @@ -2612,6 +2619,7 @@ func TestGossipsubIdontwantSend(t *testing.T) { } params := DefaultGossipSubParams() + params.Dannounce = 0 params.IDontWantMessageThreshold = 16 psubs := make([]*PubSub, 2) @@ -2822,6 +2830,7 @@ func TestGossipsubIdontwantNonMesh(t *testing.T) { hosts := getDefaultHosts(t, 3) params := DefaultGossipSubParams() + params.Dannounce = 0 params.IDontWantMessageThreshold = 16 psubs := getGossipsubs(ctx, hosts[:2], WithGossipSubParams(params)) @@ -2910,6 +2919,7 @@ func TestGossipsubIdontwantIncompat(t *testing.T) { hosts := getDefaultHosts(t, 3) params := DefaultGossipSubParams() + params.Dannounce = 0 params.IDontWantMessageThreshold = 16 psubs := getGossipsubs(ctx, hosts[:2], WithGossipSubParams(params)) @@ -2998,6 +3008,7 @@ func TestGossipsubIdontwantSmallMessage(t *testing.T) { hosts := getDefaultHosts(t, 3) params := DefaultGossipSubParams() + params.Dannounce = 0 params.IDontWantMessageThreshold = 16 psubs := getGossipsubs(ctx, hosts[:2], WithGossipSubParams(params)) @@ -3327,6 +3338,453 @@ func TestGossipsubPruneMeshCorrectly(t *testing.T) { } } +// Test that IANNOUNCE is sent to mesh peers +func TestGossipsubIannounceMeshPeer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + msgID := func(pmsg *pb.Message) string { + // silly content-based test message-ID: just use the data as whole + return base64.URLEncoding.EncodeToString(pmsg.Data) + } + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params), WithMessageIdFn(msgID)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + iannounceCount := 0 + checkMsgs := func() { + if msgCount != 0 { + t.Fatalf("Expected no message received, got %d", msgCount) + } + if iannounceCount != 1 { + t.Fatalf("Expected 1 IANNOUNCE received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + var mid string + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + mid = msgID(&pb.Message{Data: data}) + psub.Publish("foobar", data) + }() + } + } + msgCount += len(irpc.GetPublish()) + iannounceCount += len(irpc.GetControl().GetIannounce()) + if len(irpc.GetControl().GetIannounce()) > 0 { + announce_mid := irpc.GetControl().GetIannounce()[0].GetMessageID() + if mid != announce_mid { + t.Fatalf("Wrong message id in IANNOUNCE expected %s got %s", mid, announce_mid) + } + } + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that IANNOUNCE is sent to direct peers +func TestGossipsubIannounceDirectPeer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + msgID := func(pmsg *pb.Message) string { + // silly content-based test message-ID: just use the data as whole + return base64.URLEncoding.EncodeToString(pmsg.Data) + } + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], + WithGossipSubParams(params), + WithMessageIdFn(msgID), + WithDirectPeers([]peer.AddrInfo{{ID: hosts[1].ID(), Addrs: hosts[1].Addrs()}})) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + iannounceCount := 0 + checkMsgs := func() { + if msgCount != 0 { + t.Fatalf("Expected no message received, got %d", msgCount) + } + if iannounceCount != 1 { + t.Fatalf("Expected 1 IANNOUNCE received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + var mid string + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + mid = msgID(&pb.Message{Data: data}) + psub.Publish("foobar", data) + }() + } + } + msgCount += len(irpc.GetPublish()) + iannounceCount += len(irpc.GetControl().GetIannounce()) + if len(irpc.GetControl().GetIannounce()) > 0 { + announce_mid := irpc.GetControl().GetIannounce()[0].GetMessageID() + if mid != announce_mid { + t.Fatalf("Wrong message id in IANNOUNCE expected %s got %s", mid, announce_mid) + } + } + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that IANNOUNCE is not sent to incompatible peers +func TestGossipsubIannounceIncompatiblePeer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + iannounceCount := 0 + checkMsgs := func() { + if msgCount != 1 { + t.Fatalf("Expected 1 message received, got %d", msgCount) + } + if iannounceCount != 0 { + t.Fatalf("Expected no IANNOUNCE received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + newMockGSWithVersion(ctx, t, hosts[1], protocol.ID("/meshsub/1.2.0"), func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + psub.Publish("foobar", data) + }() + } + } + msgCount += len(irpc.GetPublish()) + iannounceCount += len(irpc.GetControl().GetIannounce()) + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that IANNOUNCE is not sent to indirect non-mesh peers when flood publish +func TestGossipsubIannounceFloodPublish(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params), WithFloodPublish(true)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + iannounceCount := 0 + checkMsgs := func() { + if msgCount != 1 { + t.Fatalf("Expected 1 message received, got %d", msgCount) + } + if iannounceCount != 0 { + t.Fatalf("Expected no IANNOUNCE received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and pruning to the first peer to make sure + // that it's not in the mesh + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Prune: []*pb.ControlPrune{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + prune + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + psub.Publish("foobar", data) + }() + } + } + msgCount += len(irpc.GetPublish()) + iannounceCount += len(irpc.GetControl().GetIannounce()) + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that no IANNOUNCE is sent if Dannounce is zero +func TestGossipsubIannounceZeroDannounce(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + params := DefaultGossipSubParams() + params.Dannounce = 0 + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + iannounceCount := 0 + checkMsgs := func() { + if msgCount != 1 { + t.Fatalf("Expected 1 message received, got %d", msgCount) + } + if iannounceCount != 0 { + t.Fatalf("Expected no IANNOUNCE received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + psub.Publish("foobar", data) + }() + } + } + msgCount += len(irpc.GetPublish()) + iannounceCount += len(irpc.GetControl().GetIannounce()) + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that no IANNOUNCE is sent if publishing to fanout peers +func TestGossipsubIannounceFanout(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params)) + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + iannounceCount := 0 + checkMsgs := func() { + if msgCount != 1 { + t.Fatalf("Expected 1 message received, got %d", msgCount) + } + if iannounceCount != 0 { + t.Fatalf("Expected no IANNOUNCE received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + var sentSubscribe bool + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + // Reply by subcribing to the topic + topic := "foobar" + subscribe := true + if !sentSubscribe { + sentSubscribe = true + + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: &subscribe, Topicid: &topic}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + psub.Publish(topic, data) + }() + } + msgCount += len(irpc.GetPublish()) + iannounceCount += len(irpc.GetControl().GetIannounce()) + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + func BenchmarkAllocDoDropRPC(b *testing.B) { gs := GossipSubRouter{tracer: &pubsubTracer{}} From 5768259fde51510ff7e9e098b808606a7b2e9035 Mon Sep 17 00:00:00 2001 From: Pop Chunhapanya Date: Sat, 21 Dec 2024 23:21:05 +0700 Subject: [PATCH 3/4] Gossipsub v2.0: Handle IANNOUNCE and send INEED --- acache.go | 217 +++++++++++++++++++++++ acache_test.go | 443 ++++++++++++++++++++++++++++++++++++++++++++++ gossipsub.go | 51 +++++- gossipsub_test.go | 233 ++++++++++++++++++++++++ 4 files changed, 943 insertions(+), 1 deletion(-) create mode 100644 acache.go create mode 100644 acache_test.go diff --git a/acache.go b/acache.go new file mode 100644 index 00000000..37f926e2 --- /dev/null +++ b/acache.go @@ -0,0 +1,217 @@ +package pubsub + +import ( + "container/list" + "fmt" + "sync" + "time" + + "github.com/libp2p/go-libp2p/core/peer" +) + +type IneedMeta struct { + pid peer.ID + mid string +} + +type sendList struct { + // Timeout + t time.Duration + // List of message ids + l *list.List + // Elements in l indexed by message ids. + es map[string]*list.Element +} + +type sendListEntry struct { + meta *IneedMeta + // Send time + sendTime time.Time + // Timeout time + expiryTime time.Time +} + +func newSendList(timeout time.Duration) *sendList { + return &sendList{ + t: timeout, + l: list.New(), + es: make(map[string]*list.Element), + } +} + +// Front returns the first message id in the list. +func (sl *sendList) Front() *sendListEntry { + e := sl.l.Front() + if e != nil { + entry := e.Value.(sendListEntry) + return &entry + } else { + return nil + } +} + +// Push pushes the message id and the peer to the list with send time set to now and expiry time set to now plus timeout. +func (sl *sendList) Push(meta *IneedMeta) { + // there shouldn't already be a message id in the list + if _, ok := sl.es[meta.mid]; ok { + panic(fmt.Errorf("there is already a message id in the list: %s", meta.mid)) + } + // push to the back and remember the element + sl.es[meta.mid] = sl.l.PushBack(sendListEntry{ + meta: meta, + sendTime: time.Now(), + expiryTime: time.Now().Add(sl.t), + }) +} + +// Remove removes the message id from the list. +func (sl *sendList) Remove(mid string) { + // there shouldn already be a message id in the list + if _, ok := sl.es[mid]; !ok { + panic(fmt.Errorf("there is no message id in the list to remove: %s", mid)) + } + // remove it from both the list and the indexing map + sl.l.Remove(sl.es[mid]) + delete(sl.es, mid) +} + +// Has checks if the message id is in the list. +func (sl *sendList) Has(mid string) bool { + _, ok := sl.es[mid] + return ok +} + +type AnnounceCache struct { + lk sync.RWMutex + // Queues indexed by messages ids containing the peers from whom we already receive IANNOUNCE, but not yet send INEED. + m map[string][]peer.ID + // List of pairs of peers and message ids that we already send INEED, but the timeout hasn't occured and the message is not received yet. + // There is supposed to be at most one element per message id in the list at any time. + sl *sendList + // Channel to wake up the background routine and try to send INEED. + c chan<- *IneedMeta + // Channel used to notify a request to send INEED from the cache. + R <-chan *IneedMeta + // Channel used to notify a timeout of INEED from the cache. + T <-chan *IneedMeta + // Used to indicate that the cache is stopped + stopped chan struct{} +} + +func NewAnnounceCache(timeout time.Duration) *AnnounceCache { + c := make(chan *IneedMeta) + R := make(chan *IneedMeta) + T := make(chan *IneedMeta) + ac := &AnnounceCache{ + c: c, + R: R, + T: T, + m: make(map[string][]peer.ID), + sl: newSendList(timeout), + + stopped: make(chan struct{}), + } + go ac.background(c, R, T) + + return ac +} + +func (ac *AnnounceCache) background(c <-chan *IneedMeta, R chan<- *IneedMeta, T chan<- *IneedMeta) { + timer := time.NewTimer(0) + for { + select { + case <-ac.stopped: + return + case meta := <-c: + ac.lk.Lock() + if !ac.sl.Has(meta.mid) { + // If there is no INEED on flight, just send INEED right away by putting it in the list + ac.sl.Push(meta) + // Send the meta data to the cache user, so they can send INEED using that + select { + case R <- meta: + case <-ac.stopped: + ac.lk.Unlock() + return + } + } else { + ac.m[meta.mid] = append(ac.m[meta.mid], meta.pid) + } + case <-timer.C: + ac.lk.Lock() + } + entry := ac.sl.Front() + for entry != nil && entry.expiryTime.Before(time.Now()) { + // If the ongoing INEED times out + mid := entry.meta.mid + + // Remove it from the list + ac.sl.Remove(mid) + + // Notify the cache user that the ongoing INEED times out + select { + case T <- entry.meta: + case <-ac.stopped: + ac.lk.Unlock() + return + } + + // If there is another peer waiting for INEED + if len(ac.m[mid]) > 0 { + meta := &IneedMeta{ + pid: ac.m[mid][0], + mid: mid, + } + ac.m[mid] = ac.m[mid][1:] + ac.sl.Push(meta) + + // Send the meta data to the cache user, so they can send INEED using that + select { + case R <- meta: + case <-ac.stopped: + ac.lk.Unlock() + return + } + } else { + delete(ac.m, mid) + } + + // Look at the next entry + entry = ac.sl.Front() + } + timer.Stop() + if entry = ac.sl.Front(); entry != nil { + // If there still the next entry, wake this background routine correspondingly + timer.Reset(time.Until(entry.expiryTime)) + } + ac.lk.Unlock() + } +} + +func (ac *AnnounceCache) Add(mid string, pid peer.ID) { + meta := &IneedMeta{ + mid: mid, + pid: pid, + } + select { + case ac.c <- meta: + case <-ac.stopped: + } +} + +// Clear clears all the pending IANNOUNCE and remove the ongoing INEED from the list so the the timeout +// will not be triggered +func (ac *AnnounceCache) Clear(mid string) { + ac.lk.Lock() + defer ac.lk.Unlock() + + // Clear the cache for the given message id + ac.m[mid] = []peer.ID{} + if ac.sl.Has(mid) { + ac.sl.Remove(mid) + } +} + +func (ac *AnnounceCache) Stop() { + close(ac.stopped) +} diff --git a/acache_test.go b/acache_test.go new file mode 100644 index 00000000..b092a46a --- /dev/null +++ b/acache_test.go @@ -0,0 +1,443 @@ +package pubsub + +import ( + "github.com/libp2p/go-libp2p/core/peer" + + "testing" + "time" +) + +type Event struct { + // True if it's a send event, false if it's a timeout event + send bool + meta *IneedMeta + time time.Time +} + +func closeTimes(a time.Time, b time.Time) bool { + return a.Sub(b) < 2*time.Millisecond && b.Sub(a) < 2*time.Millisecond +} + +func TestAnnounceCache(t *testing.T) { + var events []Event + timeout := 50 * time.Millisecond + ac := NewAnnounceCache(timeout) + pidA := peer.ID("A") + pidB := peer.ID("B") + pidC := peer.ID("C") + pidD := peer.ID("D") + + done := make(chan struct{}) + go func() { + timer := time.After(200 * time.Millisecond) + for { + select { + case meta := <-ac.T: + events = append(events, Event{ + send: false, + meta: meta, + time: time.Now(), + }) + case meta := <-ac.R: + events = append(events, Event{ + send: true, + meta: meta, + time: time.Now(), + }) + case <-timer: + done <- struct{}{} + return + } + } + }() + + start := time.Now() + ac.Add("mid1", pidA) + ac.Add("mid1", pidB) + ac.Add("mid2", pidC) + ac.Add("mid2", pidD) + + <-done + + // Check the number of events + if len(events) != 8 { + t.Fatal("incorrect number of events") + } + msgList := map[string][]peer.ID{ + "mid1": {pidA, pidB}, + "mid2": {pidC, pidD}, + } + sentTime := make(map[string]time.Time) + expiryTime := make(map[string]time.Time) + for _, event := range events { + if event.send { + if _, ok := sentTime[event.meta.mid]; ok { + t.Fatal("there shouldn't be an ongoing INEED when send event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + expiryT, ok := expiryTime[event.meta.mid] + var expectedSentTime time.Time + if ok { + expectedSentTime = expiryT + } else { + expectedSentTime = start + } + if !closeTimes(expectedSentTime, event.time) { + t.Fatal("send event is not sent timely") + } + sentTime[event.meta.mid] = event.time + } else { + sentT, ok := sentTime[event.meta.mid] + if !ok { + t.Fatal("there shouldn be an ongoing INEED when timeout event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + if !closeTimes(sentT.Add(timeout), event.time) { + t.Fatal("timeout event is not sent timely") + } + delete(sentTime, event.meta.mid) + expiryTime[event.meta.mid] = event.time + msgList[event.meta.mid] = msgList[event.meta.mid][1:] + } + } + // Make sure that all send events have corresponding timeout events + if len(sentTime) != 0 { + t.Fatal("there is some send event that doesn't have corresponding timeout event") + } + // Make sure that all peer ids are consumed + for mid, list := range msgList { + if len(list) != 0 { + t.Fatalf("%s has some peer id that doesn't have events", mid) + } + } +} + +func TestAnnounceCacheClear(t *testing.T) { + var events []Event + timeout := 50 * time.Millisecond + ac := NewAnnounceCache(timeout) + pidA := peer.ID("A") + pidB := peer.ID("B") + pidC := peer.ID("C") + pidD := peer.ID("D") + + done := make(chan struct{}) + go func() { + timer := time.After(200 * time.Millisecond) + for { + select { + case meta := <-ac.T: + events = append(events, Event{ + send: false, + meta: meta, + time: time.Now(), + }) + case meta := <-ac.R: + events = append(events, Event{ + send: true, + meta: meta, + time: time.Now(), + }) + case <-timer: + done <- struct{}{} + return + } + } + }() + + go func() { + time.Sleep(80 * time.Millisecond) + ac.Clear("mid1") + }() + + start := time.Now() + ac.Add("mid1", pidA) + ac.Add("mid1", pidB) + ac.Add("mid1", pidC) + ac.Add("mid2", pidD) + + <-done + + // Check the number of events + if len(events) != 5 { + t.Fatal("incorrect number of events") + } + msgList := map[string][]peer.ID{ + "mid1": {pidA, pidB}, + "mid2": {pidD}, + } + sentTime := make(map[string]time.Time) + expiryTime := make(map[string]time.Time) + for _, event := range events { + if event.send { + if _, ok := sentTime[event.meta.mid]; ok { + t.Fatal("there shouldn't be an ongoing INEED when send event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + expiryT, ok := expiryTime[event.meta.mid] + var expectedSentTime time.Time + if ok { + expectedSentTime = expiryT + } else { + expectedSentTime = start + } + if !closeTimes(expectedSentTime, event.time) { + t.Fatal("send event is not sent timely") + } + sentTime[event.meta.mid] = event.time + } else { + sentT, ok := sentTime[event.meta.mid] + if !ok { + t.Fatal("there shouldn be an ongoing INEED when timeout event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + if !closeTimes(sentT.Add(timeout), event.time) { + t.Fatal("timeout event is not sent timely") + } + delete(sentTime, event.meta.mid) + expiryTime[event.meta.mid] = event.time + msgList[event.meta.mid] = msgList[event.meta.mid][1:] + } + } + // Make sure that there is only one send event that doesn't have corresponding timeout event + if len(sentTime) != 1 { + t.Fatal("there should only be one send event that doesn't have corresponding timeout event") + } + if _, ok := sentTime["mid1"]; !ok { + t.Fatal("One send event that doesn't have corresponding timeout event should be mid1") + } + // Make sure that all peer ids are consumed + for mid, list := range msgList { + if mid == "mid1" { + if len(list) != 1 || list[0] != pidB { + t.Fatal("there should be only pidB that isn't consumed and has no timeout event") + } + } else { + if len(list) != 0 { + t.Fatalf("%s has some peer id that doesn't have events", mid) + } + } + } +} + +func TestAnnounceCacheReAdd(t *testing.T) { + var events []Event + timeout := 50 * time.Millisecond + ac := NewAnnounceCache(timeout) + pidA := peer.ID("A") + pidB := peer.ID("B") + pidC := peer.ID("C") + pidD := peer.ID("D") + + done := make(chan struct{}) + go func() { + timer := time.After(300 * time.Millisecond) + for { + select { + case meta := <-ac.T: + events = append(events, Event{ + send: false, + meta: meta, + time: time.Now(), + }) + case meta := <-ac.R: + events = append(events, Event{ + send: true, + meta: meta, + time: time.Now(), + }) + case <-timer: + done <- struct{}{} + return + } + } + }() + + start := time.Now() + ac.Add("mid1", pidA) + ac.Add("mid1", pidB) + ac.Add("mid2", pidC) + time.Sleep(220 * time.Millisecond) + start2 := time.Now() + ac.Add("mid1", pidD) + + <-done + + // Check the number of events + if len(events) != 8 { + t.Fatal("incorrect number of events") + } + msgList := map[string][]peer.ID{ + "mid1": {pidA, pidB, pidD}, + "mid2": {pidC}, + } + sentTime := make(map[string]time.Time) + expiryTime := make(map[string]time.Time) + for _, event := range events { + if event.send { + if _, ok := sentTime[event.meta.mid]; ok { + t.Fatal("there shouldn't be an ongoing INEED when send event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + expiryT, ok := expiryTime[event.meta.mid] + var expectedSentTime time.Time + if event.meta.pid == pidD { + expectedSentTime = start2 + } else if ok { + expectedSentTime = expiryT + } else { + expectedSentTime = start + } + if !closeTimes(expectedSentTime, event.time) { + t.Fatal("send event is not sent timely") + } + sentTime[event.meta.mid] = event.time + } else { + sentT, ok := sentTime[event.meta.mid] + if !ok { + t.Fatal("there shouldn be an ongoing INEED when timeout event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + if !closeTimes(sentT.Add(timeout), event.time) { + t.Fatal("timeout event is not sent timely") + } + delete(sentTime, event.meta.mid) + expiryTime[event.meta.mid] = event.time + msgList[event.meta.mid] = msgList[event.meta.mid][1:] + } + } + // Make sure that all send events have corresponding timeout events + if len(sentTime) != 0 { + t.Fatal("there is some send event that doesn't have corresponding timeout event") + } + // Make sure that all peer ids are consumed + for mid, list := range msgList { + if len(list) != 0 { + t.Fatalf("%s has some peer id that doesn't have events", mid) + } + } +} + +func TestAnnounceCacheStop(t *testing.T) { + var events []Event + timeout := 50 * time.Millisecond + ac := NewAnnounceCache(timeout) + pidA := peer.ID("A") + pidB := peer.ID("B") + pidC := peer.ID("C") + pidD := peer.ID("D") + + done := make(chan struct{}) + go func() { + timer := time.After(200 * time.Millisecond) + for { + select { + case meta := <-ac.T: + events = append(events, Event{ + send: false, + meta: meta, + time: time.Now(), + }) + case meta := <-ac.R: + events = append(events, Event{ + send: true, + meta: meta, + time: time.Now(), + }) + case <-timer: + done <- struct{}{} + return + } + } + }() + + go func() { + time.Sleep(80 * time.Millisecond) + ac.Stop() + }() + + start := time.Now() + ac.Add("mid1", pidA) + ac.Add("mid1", pidB) + ac.Add("mid1", pidC) + ac.Add("mid2", pidD) + + <-done + + // Check the number of events + if len(events) != 5 { + t.Fatal("incorrect number of events") + } + msgList := map[string][]peer.ID{ + "mid1": {pidA, pidB}, + "mid2": {pidD}, + } + sentTime := make(map[string]time.Time) + expiryTime := make(map[string]time.Time) + for _, event := range events { + if event.send { + if _, ok := sentTime[event.meta.mid]; ok { + t.Fatal("there shouldn't be an ongoing INEED when send event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + expiryT, ok := expiryTime[event.meta.mid] + var expectedSentTime time.Time + if ok { + expectedSentTime = expiryT + } else { + expectedSentTime = start + } + if !closeTimes(expectedSentTime, event.time) { + t.Fatal("send event is not sent timely") + } + sentTime[event.meta.mid] = event.time + } else { + sentT, ok := sentTime[event.meta.mid] + if !ok { + t.Fatal("there shouldn be an ongoing INEED when timeout event is received") + } + if msgList[event.meta.mid][0] != event.meta.pid { + t.Fatal("wrong peer id in the send event") + } + if !closeTimes(sentT.Add(timeout), event.time) { + t.Fatal("timeout event is not sent timely") + } + delete(sentTime, event.meta.mid) + expiryTime[event.meta.mid] = event.time + msgList[event.meta.mid] = msgList[event.meta.mid][1:] + } + } + // Make sure that there is only one send event that doesn't have corresponding timeout event + if len(sentTime) != 1 { + t.Fatal("there should only be one send event that doesn't have corresponding timeout event") + } + if _, ok := sentTime["mid1"]; !ok { + t.Fatal("One send event that doesn't have corresponding timeout event should be mid1") + } + // Make sure that all peer ids are consumed + for mid, list := range msgList { + if mid == "mid1" { + if len(list) != 1 || list[0] != pidB { + t.Fatal("there should be only pidB that isn't consumed and has no timeout event") + } + } else { + if len(list) != 0 { + t.Fatalf("%s has some peer id that doesn't have events", mid) + } + } + } +} diff --git a/gossipsub.go b/gossipsub.go index 8d31854b..7f85a1d7 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -287,6 +287,7 @@ func DefaultGossipSubRouter(h host.Host) *GossipSubRouter { outbound: make(map[peer.ID]bool), connect: make(chan connectInfo, params.MaxPendingConnections), cab: pstoremem.NewAddrBook(), + acache: NewAnnounceCache(params.Timeout), mcache: NewMessageCache(params.HistoryGossip, params.HistoryLength), protos: GossipSubDefaultProtocols, feature: GossipSubDefaultFeatures, @@ -500,6 +501,7 @@ type GossipSubRouter struct { protos []protocol.ID feature GossipSubFeatureTest + acache *AnnounceCache mcache *MessageCache tracer *pubsubTracer score *peerScore @@ -577,6 +579,9 @@ func (gs *GossipSubRouter) Attach(p *PubSub) { // Manage our address book from events emitted by libp2p go gs.manageAddrBook() + // Manage events from the announce cache + go gs.manageAnnounceCache() + // connect to direct peers if len(gs.direct) > 0 { go func() { @@ -637,6 +642,22 @@ func (gs *GossipSubRouter) manageAddrBook() { } } +func (gs *GossipSubRouter) manageAnnounceCache() { + for { + select { + case <-gs.p.ctx.Done(): + gs.acache.Stop() + return + case r := <-gs.acache.R: + ineed := []*pb.ControlINeed{{MessageID: &r.mid}} + out := rpcWithControl(nil, nil, nil, nil, nil, nil, nil, ineed) + gs.sendRPC(r.pid, out, false) + case <-gs.acache.T: + // TODO: penalize peers when timeout + } + } +} + func (gs *GossipSubRouter) AddPeer(p peer.ID, proto protocol.ID) { log.Debugf("PEERUP: Add new peer %s using %s", p, proto) gs.tracer.AddPeer(p, proto) @@ -726,6 +747,8 @@ func (gs *GossipSubRouter) AcceptFrom(p peer.ID) AcceptStatus { // 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. +// +// It also clear the announce cache to prevent sending further INEEDs. func (gs *GossipSubRouter) PreValidation(msgs []*Message) { tmids := make(map[string][]string) for _, msg := range msgs { @@ -733,7 +756,10 @@ func (gs *GossipSubRouter) PreValidation(msgs []*Message) { continue } topic := msg.GetTopic() - tmids[topic] = append(tmids[topic], gs.p.idGen.ID(msg)) + mid := gs.p.idGen.ID(msg) + tmids[topic] = append(tmids[topic], mid) + // Clear the announce cache + gs.acache.Clear(mid) } for topic, mids := range tmids { if len(mids) == 0 { @@ -762,6 +788,7 @@ func (gs *GossipSubRouter) HandleRPC(rpc *RPC) { iwant := gs.handleIHave(rpc.from, ctl) ihave := gs.handleIWant(rpc.from, ctl) prune := gs.handleGraft(rpc.from, ctl) + gs.handleIAnnounce(rpc.from, ctl) gs.handlePrune(rpc.from, ctl) gs.handleIDontWant(rpc.from, ctl) @@ -1056,6 +1083,28 @@ mainIDWLoop: } } +func (gs *GossipSubRouter) handleIAnnounce(p peer.ID, ctl *pb.ControlMessage) { + for _, iannounce := range ctl.GetIannounce() { + gmap, ok := gs.mesh[iannounce.GetTopicID()] + if !ok { + // Not in mesh, no need ot handle IANNOUNCE + continue + } + _, isMesh := gmap[p] + _, isDirect := gs.direct[p] + // We handle IANNOUNCE only from mesh peers or direct peers + if !isMesh && !isDirect { + continue + } + + mid := iannounce.GetMessageID() + if !gs.p.seenMessage(mid) { + // If the message has not been seen, add it to the announce cache + gs.acache.Add(mid, p) + } + } +} + func (gs *GossipSubRouter) addBackoff(p peer.ID, topic string, isUnsubscribe bool) { backoff := gs.params.PruneBackoff if isUnsubscribe { diff --git a/gossipsub_test.go b/gossipsub_test.go index 235a894b..1724512f 100644 --- a/gossipsub_test.go +++ b/gossipsub_test.go @@ -3785,6 +3785,239 @@ func TestGossipsubIannounceFanout(t *testing.T) { <-ctx.Done() } +// Test that INEED is sent to mesh peers +func TestGossipsubIneedMeshPeers(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + msgID := func(pmsg *pb.Message) string { + // silly content-based test message-ID: just use the data as whole + return base64.URLEncoding.EncodeToString(pmsg.Data) + } + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params), WithMessageIdFn(msgID)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + ineedCount := 0 + checkMsgs := func() { + if ineedCount != 1 { + t.Fatalf("Expected 1 INEED received, got %d", ineedCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + var mid string + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Send IANNOUNCE + mid = msgID(&pb.Message{Data: []byte("mymessage")}) + writeMsg(&pb.RPC{ + Control: &pb.ControlMessage{Iannounce: []*pb.ControlIAnnounce{{TopicID: sub.Topicid, MessageID: &mid}}}, + }) + }() + } + } + ineedCount += len(irpc.GetControl().GetIneed()) + if len(irpc.GetControl().GetIneed()) > 0 { + ineed_mid := irpc.GetControl().GetIneed()[0].GetMessageID() + if mid != ineed_mid { + t.Fatalf("Wrong message id in INEED expected %s got %s", mid, ineed_mid) + } + } + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that INEED is sent to direct peers +func TestGossipsubIneedDirectPeers(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + msgID := func(pmsg *pb.Message) string { + // silly content-based test message-ID: just use the data as whole + return base64.URLEncoding.EncodeToString(pmsg.Data) + } + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], + WithGossipSubParams(params), + WithMessageIdFn(msgID), + WithDirectPeers([]peer.AddrInfo{{ID: hosts[1].ID(), Addrs: hosts[1].Addrs()}})) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + ineedCount := 0 + checkMsgs := func() { + if ineedCount != 1 { + t.Fatalf("Expected 1 INEED received, got %d", ineedCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + var mid string + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Send IANNOUNCE + mid = msgID(&pb.Message{Data: []byte("mymessage")}) + writeMsg(&pb.RPC{ + Control: &pb.ControlMessage{Iannounce: []*pb.ControlIAnnounce{{TopicID: sub.Topicid, MessageID: &mid}}}, + }) + }() + } + } + ineedCount += len(irpc.GetControl().GetIneed()) + if len(irpc.GetControl().GetIneed()) > 0 { + ineed_mid := irpc.GetControl().GetIneed()[0].GetMessageID() + if mid != ineed_mid { + t.Fatalf("Wrong message id in INEED expected %s got %s", mid, ineed_mid) + } + } + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + +// Test that INEED is not sent to indirect non-mesh peers +func TestGossipsubIneedIndirectNonmeshPeers(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + msgID := func(pmsg *pb.Message) string { + // silly content-based test message-ID: just use the data as whole + return base64.URLEncoding.EncodeToString(pmsg.Data) + } + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params), WithMessageIdFn(msgID)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + ineedCount := 0 + checkMsgs := func() { + if ineedCount != 0 { + t.Fatalf("Expected no INEED received, got %d", ineedCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and pruning to the first peer to make sure + // that it's not in the mesh + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Prune: []*pb.ControlPrune{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Send IANNOUNCE + mid := msgID(&pb.Message{Data: []byte("mymessage")}) + writeMsg(&pb.RPC{ + Control: &pb.ControlMessage{Iannounce: []*pb.ControlIAnnounce{{TopicID: sub.Topicid, MessageID: &mid}}}, + }) + }() + } + } + ineedCount += len(irpc.GetControl().GetIneed()) + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + func BenchmarkAllocDoDropRPC(b *testing.B) { gs := GossipSubRouter{tracer: &pubsubTracer{}} From 04587eb24accd52872bd8d43decf6a0581c97336 Mon Sep 17 00:00:00 2001 From: Pop Chunhapanya Date: Sun, 19 Jan 2025 07:18:00 +0700 Subject: [PATCH 4/4] Gossipsub v2.0: Handle INEED and send the message --- gossipsub.go | 32 +++++++++ gossipsub_test.go | 171 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 1 deletion(-) diff --git a/gossipsub.go b/gossipsub.go index 7f85a1d7..47d9d058 100644 --- a/gossipsub.go +++ b/gossipsub.go @@ -787,6 +787,7 @@ func (gs *GossipSubRouter) HandleRPC(rpc *RPC) { iwant := gs.handleIHave(rpc.from, ctl) ihave := gs.handleIWant(rpc.from, ctl) + ihave = append(ihave, gs.handleINeed(rpc.from, ctl)...) prune := gs.handleGraft(rpc.from, ctl) gs.handleIAnnounce(rpc.from, ctl) gs.handlePrune(rpc.from, ctl) @@ -922,6 +923,37 @@ func (gs *GossipSubRouter) handleIWant(p peer.ID, ctl *pb.ControlMessage) []*pb. return msgs } +func (gs *GossipSubRouter) handleINeed(p peer.ID, ctl *pb.ControlMessage) []*pb.Message { + ihave := make(map[string]*pb.Message) + for _, ineed := range ctl.GetIneed() { + mid := ineed.GetMessageID() + // Check if that peer has sent IDONTWANT before, if so don't send them the message + if _, ok := gs.unwanted[p][computeChecksum(mid)]; ok { + continue + } + + msg, _, ok := gs.mcache.GetForPeer(mid, p) + if !ok { + continue + } + if !gs.p.peerFilter(p, msg.GetTopic()) { + continue + } + ihave[mid] = msg.Message + } + + if len(ihave) == 0 { + return nil + } + + msgs := make([]*pb.Message, 0, len(ihave)) + for _, msg := range ihave { + msgs = append(msgs, msg) + } + + return msgs +} + func (gs *GossipSubRouter) handleGraft(p peer.ID, ctl *pb.ControlMessage) []*pb.ControlPrune { var prune []string diff --git a/gossipsub_test.go b/gossipsub_test.go index 1724512f..b5040fbf 100644 --- a/gossipsub_test.go +++ b/gossipsub_test.go @@ -3338,7 +3338,7 @@ func TestGossipsubPruneMeshCorrectly(t *testing.T) { } } -// Test that IANNOUNCE is sent to mesh peers +// Test that IANNOUNCE is sent to mesh peers and no message is sent if it doesn't send INEED func TestGossipsubIannounceMeshPeer(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -3421,6 +3421,89 @@ func TestGossipsubIannounceMeshPeer(t *testing.T) { <-ctx.Done() } +// Test that IANNOUNCE is sent to mesh peers and the message is sent after sending INEED +func TestGossipsubIannounceIneedMeshPeer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 2) + + msgID := func(pmsg *pb.Message) string { + // silly content-based test message-ID: just use the data as whole + return base64.URLEncoding.EncodeToString(pmsg.Data) + } + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psub := getGossipsub(ctx, hosts[0], WithGossipSubParams(params), WithMessageIdFn(msgID)) + _, err := psub.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + // Wait a bit after the last message before checking we got the right messages + msgTimer := time.NewTimer(1 * time.Second) + + // Checks we received the right messages + msgCount := 0 + checkMsgs := func() { + if msgCount != 1 { + t.Fatalf("Expected one message received, got %d", msgCount) + } + } + + // Wait for the timer to expire + go func() { + select { + case <-msgTimer.C: + checkMsgs() + cancel() + return + case <-ctx.Done(): + checkMsgs() + } + }() + + newMockGS(ctx, t, hosts[1], func(writeMsg func(*pb.RPC), irpc *pb.RPC) { + // When the first peer connects it will send us its subscriptions + for _, sub := range irpc.GetSubscriptions() { + if sub.GetSubscribe() { + // Reply by subcribing to the topic and grafting to the first peer + writeMsg(&pb.RPC{ + Subscriptions: []*pb.RPC_SubOpts{{Subscribe: sub.Subscribe, Topicid: sub.Topicid}}, + Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{TopicID: sub.Topicid}}}, + }) + + go func() { + // Wait for a short interval to make sure the first peer + // received and processed the subscribe + graft + time.Sleep(100 * time.Millisecond) + // Publish messages from the first peer + data := []byte("mymessage") + psub.Publish("foobar", data) + }() + } + } + if len(irpc.GetControl().GetIannounce()) > 0 { + var ineeds []*pb.ControlINeed + for _, iannounce := range irpc.GetControl().GetIannounce() { + mid := iannounce.GetMessageID() + ineed := &pb.ControlINeed{ + MessageID: &mid, + } + ineeds = append(ineeds, ineed) + } + writeMsg(&pb.RPC{ + Control: &pb.ControlMessage{Ineed: ineeds}, + }) + } + msgCount += len(irpc.GetPublish()) + }) + + connect(t, hosts[0], hosts[1]) + + <-ctx.Done() +} + // Test that IANNOUNCE is sent to direct peers func TestGossipsubIannounceDirectPeer(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -4018,6 +4101,92 @@ func TestGossipsubIneedIndirectNonmeshPeers(t *testing.T) { <-ctx.Done() } +func TestSparseGossipsubV2(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 20) + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psubs := getGossipsubs(ctx, hosts, WithGossipSubParams(params)) + + var msgs []*Subscription + for _, ps := range psubs { + subch, err := ps.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + msgs = append(msgs, subch) + } + + sparseConnect(t, hosts) + + // wait for heartbeats to build mesh + time.Sleep(time.Second * 2) + + for i := 0; i < 100; i++ { + msg := []byte(fmt.Sprintf("%d it's not a floooooood %d", i, i)) + + owner := mrand.Intn(len(psubs)) + + psubs[owner].Publish("foobar", msg) + + for _, sub := range msgs { + got, err := sub.Next(ctx) + if err != nil { + t.Fatal(sub.err) + } + if !bytes.Equal(msg, got.Data) { + t.Fatal("got wrong message!") + } + } + } +} + +func TestDenseGossipsubV2(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hosts := getDefaultHosts(t, 20) + + params := DefaultGossipSubParams() + params.Dannounce = params.D + psubs := getGossipsubs(ctx, hosts, WithGossipSubParams(params)) + + var msgs []*Subscription + for _, ps := range psubs { + subch, err := ps.Subscribe("foobar") + if err != nil { + t.Fatal(err) + } + + msgs = append(msgs, subch) + } + + denseConnect(t, hosts) + + // wait for heartbeats to build mesh + time.Sleep(time.Second * 2) + + for i := 0; i < 100; i++ { + msg := []byte(fmt.Sprintf("%d it's not a floooooood %d", i, i)) + + owner := mrand.Intn(len(psubs)) + + psubs[owner].Publish("foobar", msg) + + for _, sub := range msgs { + got, err := sub.Next(ctx) + if err != nil { + t.Fatal(sub.err) + } + if !bytes.Equal(msg, got.Data) { + t.Fatal("got wrong message!") + } + } + } +} + func BenchmarkAllocDoDropRPC(b *testing.B) { gs := GossipSubRouter{tracer: &pubsubTracer{}}