diff --git a/network/p2p/gossip/gossip.go b/network/p2p/gossip/gossip.go index 94d49260da4..0fb042323db 100644 --- a/network/p2p/gossip/gossip.go +++ b/network/p2p/gossip/gossip.go @@ -5,6 +5,7 @@ package gossip import ( "context" + "sync" "time" "github.com/prometheus/client_golang/prometheus" @@ -23,6 +24,7 @@ import ( var ( _ Gossiper = (*ValidatorGossiper)(nil) _ Gossiper = (*PullGossiper[testTx, *testTx])(nil) + _ Gossiper = (*PushGossiper[*testTx])(nil) ) // Gossiper gossips Gossipables to other nodes @@ -179,6 +181,94 @@ func (p *PullGossiper[T, U]) handleResponse( p.receivedBytes.Add(float64(receivedBytes)) } +// NewPushGossiper returns an instance of PushGossiper +func NewPushGossiper[T Gossipable](client *p2p.Client) *PushGossiper[T] { + return &PushGossiper[T]{ + sender: &gossipClient{ + client: client, + }, + } +} + +// PushGossiper broadcasts gossip to peers randomly in the network +type PushGossiper[T Gossipable] struct { + sender gossipSender + + lock sync.Mutex + queued []T +} + +// Add queues gossipables to be gossiped +func (p *PushGossiper[T]) Add(gossipables ...T) { + p.lock.Lock() + defer p.lock.Unlock() + + p.queued = append(p.queued, gossipables...) +} + +// Gossip flushes any queued gossipables +func (p *PushGossiper[T]) Gossip(ctx context.Context) error { + p.lock.Lock() + defer p.lock.Unlock() + + if len(p.queued) == 0 { + return nil + } + + msg := &sdk.PushGossip{ + Gossip: make([][]byte, 0, len(p.queued)), + } + + for _, tx := range p.queued { + bytes, err := tx.Marshal() + if err != nil { + return err + } + + msg.Gossip = append(msg.Gossip, bytes) + } + + p.queued = nil + + msgBytes, err := proto.Marshal(msg) + if err != nil { + return err + } + + return p.sender.sendGossip(ctx, msgBytes) +} + +// Subscribe gossips a gossipable whenever one is made available +func Subscribe[T Gossipable]( + ctx context.Context, + log logging.Logger, + gossiper *PushGossiper[T], + gossipables <-chan T, +) { + for { + select { + case gossipable, ok := <-gossipables: + if !ok { + log.Debug("shutting down push gossip", + zap.String("reason", "channel closed"), + ) + return + } + + gossiper.Add(gossipable) + + if err := gossiper.Gossip(ctx); err != nil { + log.Warn("push gossip failed", zap.Error(err)) + } + case <-ctx.Done(): + log.Debug("shutting down push gossip", + zap.String("reason", "context cancelled"), + ) + return + } + } +} + // Every calls [Gossip] every [frequency] amount of time. func Every(ctx context.Context, log logging.Logger, gossiper Gossiper, frequency time.Duration) { ticker := time.NewTicker(frequency) diff --git a/network/p2p/gossip/gossip_test.go b/network/p2p/gossip/gossip_test.go index 6585e09d031..b05efa3b599 100644 --- a/network/p2p/gossip/gossip_test.go +++ b/network/p2p/gossip/gossip_test.go @@ -11,10 +11,13 @@ import ( "github.com/prometheus/client_golang/prometheus" + "google.golang.org/protobuf/proto" + "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/proto/pb/sdk" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" @@ -120,7 +123,7 @@ func TestGossiperGossip(t *testing.T) { responseNetwork := p2p.NewNetwork(logging.NoLog{}, responseSender, prometheus.NewRegistry(), "") responseBloom, err := NewBloomFilter(1000, 0.01) require.NoError(err) - responseSet := testSet{ + responseSet := &testSet{ set: set.Set[*testTx]{}, bloom: responseBloom, } @@ -128,7 +131,13 @@ func TestGossiperGossip(t *testing.T) { require.NoError(responseSet.Add(item)) } - handler, err := NewHandler[*testTx](responseSet, tt.config, prometheus.NewRegistry()) + handler, err := NewHandler[testTx, *testTx]( + logging.NoLog{}, + nil, + responseSet, + tt.config, + prometheus.NewRegistry(), + ) require.NoError(err) require.NoError(responseNetwork.AddHandler(0x0, handler)) @@ -153,8 +162,7 @@ func TestGossiperGossip(t *testing.T) { bloom, err := NewBloomFilter(1000, 0.01) require.NoError(err) - requestSet := testSet{ - set: set.Set[*testTx]{}, + requestSet := &testSet{ bloom: bloom, } for _, item := range tt.requester { @@ -245,6 +253,251 @@ func TestValidatorGossiper(t *testing.T) { require.Equal(2, calls) } +func TestPushGossiper(t *testing.T) { + tests := []struct { + name string + cycles [][]*testTx + }{ + { + name: "single cycle", + cycles: [][]*testTx{ + { + &testTx{ + id: ids.ID{0}, + }, + &testTx{ + id: ids.ID{1}, + }, + &testTx{ + id: ids.ID{2}, + }, + }, + }, + }, + { + name: "multiple cycles", + cycles: [][]*testTx{ + { + &testTx{ + id: ids.ID{0}, + }, + }, + { + &testTx{ + id: ids.ID{1}, + }, + &testTx{ + id: ids.ID{2}, + }, + }, + { + &testTx{ + id: ids.ID{3}, + }, + &testTx{ + id: ids.ID{4}, + }, + &testTx{ + id: ids.ID{5}, + }, + }, + { + &testTx{ + id: ids.ID{6}, + }, + &testTx{ + id: ids.ID{7}, + }, + &testTx{ + id: ids.ID{8}, + }, + &testTx{ + id: ids.ID{9}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + ctx := context.Background() + + sent := make(chan []byte, 1) + gossiper := NewPushGossiper[*testTx](nil) + gossiper.sender = &fakeGossipClient{ + sent: sent, + } + + for _, gossipables := range tt.cycles { + gossiper.Add(gossipables...) + require.NoError(gossiper.Gossip(ctx)) + + want := &sdk.PushGossip{ + Gossip: make([][]byte, 0, len(tt.cycles)), + } + + for _, gossipable := range gossipables { + bytes, err := gossipable.Marshal() + require.NoError(err) + + want.Gossip = append(want.Gossip, bytes) + } + + got := &sdk.PushGossip{} + sentMsg := <-sent + // remove the handler prefix + require.NoError(proto.Unmarshal(sentMsg[1:], got)) + require.Equal(want.Gossip, got.Gossip) + } + }) + } +} + +func TestSubscribe(t *testing.T) { + require := require.New(t) + + sent := make(chan []byte) + gossiper := NewPushGossiper[*testTx](nil) + gossiper.sender = &fakeGossipClient{ + sent: sent, + } + + ctx := context.Background() + toGossip := make(chan *testTx) + + go Subscribe(ctx, logging.NoLog{}, gossiper, toGossip) + + tx := &testTx{id: ids.ID{1}} + toGossip <- tx + + txBytes, err := tx.Marshal() + require.NoError(err) + want := [][]byte{txBytes} + + gotMsg := &sdk.PushGossip{} + // remove the handler prefix + sentMsg := <-sent + require.NoError(proto.Unmarshal(sentMsg[1:], gotMsg)) + + require.Equal(want, gotMsg.Gossip) +} + +func TestSubscribeCloseChannel(*testing.T) { + gossiper := &PushGossiper[*testTx]{} + + wg := &sync.WaitGroup{} + ctx := context.Background() + toGossip := make(chan *testTx) + + wg.Add(1) + go func() { + Subscribe(ctx, logging.NoLog{}, gossiper, toGossip) + wg.Done() + }() + + close(toGossip) + wg.Wait() +} + +func TestSubscribeCancelContext(*testing.T) { + gossiper := &PushGossiper[*testTx]{} + + wg := &sync.WaitGroup{} + ctx, cancel := context.WithCancel(context.Background()) + toGossip := make(chan *testTx) + + wg.Add(1) + go func() { + Subscribe(ctx, logging.NoLog{}, gossiper, toGossip) + wg.Done() + }() + + cancel() + wg.Wait() +} + +func TestPushGossipE2E(t *testing.T) { + require := require.New(t) + + // tx known by both the sender and the receiver which should not be + // forwarded + knownTx := &testTx{id: ids.GenerateTestID()} + + log := logging.NoLog{} + bloom, err := NewBloomFilter(100, 0.01) + require.NoError(err) + set := &testSet{ + bloom: bloom, + } + require.NoError(set.Add(knownTx)) + + metrics := prometheus.NewRegistry() + handler, err := NewHandler[testTx, *testTx]( + log, + nil, + set, + HandlerConfig{}, + metrics, + ) + require.NoError(err) + + handlerID := uint64(123) + forwarded := make(chan []byte, 1) + handler.gossipSender = &fakeGossipClient{ + handlerID: handlerID, + sent: forwarded, + } + + network := p2p.NewNetwork(log, nil, metrics, "") + _, err = network.NewAppProtocol(handlerID, handler) + require.NoError(err) + + sendGossiper := NewPushGossiper[*testTx](nil) + sent := make(chan []byte, 1) + sendGossiper.sender = &fakeGossipClient{ + handlerID: handlerID, + sent: sent, + } + + want := []*testTx{ + {id: ids.GenerateTestID()}, + {id: ids.GenerateTestID()}, + {id: ids.GenerateTestID()}, + } + + // gossip both the new tx and the one the peer already knows about + gossiped := append(want, knownTx) + sendGossiper.Add(gossiped...) + got := make([]*testTx, 0, len(want)) + set.onAdd = func(tx *testTx) { + got = append(got, tx) + } + + ctx := context.Background() + require.NoError(sendGossiper.Gossip(ctx)) + + // make sure that we only add new txs someone gossips to us + require.NoError(network.AppGossip(ctx, ids.EmptyNodeID, <-sent)) + require.Equal(want, got) + + // make sure that we only forward txs we have not already seen before + forwardedBytes := <-forwarded + forwardedMsg := &sdk.PushGossip{} + require.NoError(proto.Unmarshal(forwardedBytes[1:], forwardedMsg)) + require.Len(forwardedMsg.Gossip, len(want)) + + gotForwarded := make([]*testTx, 0, len(got)) + for _, bytes := range forwardedMsg.Gossip { + tx := &testTx{} + require.NoError(tx.Unmarshal(bytes)) + gotForwarded = append(gotForwarded, tx) + } + + require.Equal(want, gotForwarded) +} + type testGossiper struct { gossipF func(ctx context.Context) error } diff --git a/network/p2p/gossip/gossipable.go b/network/p2p/gossip/gossipable.go index 84c37e2d6b8..8a9b6585a79 100644 --- a/network/p2p/gossip/gossipable.go +++ b/network/p2p/gossip/gossipable.go @@ -16,6 +16,8 @@ type Gossipable interface { type Set[T Gossipable] interface { // Add adds a Gossipable to the set Add(gossipable T) error + // Get returns if a Gossipable with id is in the set + Get(id ids.ID) (T, bool) // Iterate iterates over elements until [f] returns false Iterate(f func(gossipable T) bool) // GetFilter returns the byte representation of bloom filter and its diff --git a/network/p2p/gossip/handler.go b/network/p2p/gossip/handler.go index 695551f9dbc..4783f655baf 100644 --- a/network/p2p/gossip/handler.go +++ b/network/p2p/gossip/handler.go @@ -8,6 +8,7 @@ import ( "time" bloomfilter "github.com/holiman/bloomfilter/v2" + "go.uber.org/zap" "github.com/prometheus/client_golang/prometheus" @@ -17,22 +18,29 @@ import ( "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/proto/pb/sdk" "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/logging" ) -var _ p2p.Handler = (*Handler[Gossipable])(nil) +var _ p2p.Handler = (*Handler[testTx, *testTx])(nil) type HandlerConfig struct { Namespace string TargetResponseSize int } -func NewHandler[T Gossipable]( - set Set[T], +func NewHandler[T any, U GossipableAny[T]]( + log logging.Logger, + client *p2p.Client, + set Set[U], config HandlerConfig, metrics prometheus.Registerer, -) (*Handler[T], error) { - h := &Handler[T]{ - Handler: p2p.NoOpHandler{}, +) (*Handler[T, U], error) { + h := &Handler[T, U]{ + Handler: p2p.NoOpHandler{}, + log: log, + gossipSender: &gossipClient{ + client: client, + }, set: set, targetResponseSize: config.TargetResponseSize, sentN: prometheus.NewCounter(prometheus.CounterOpts{ @@ -45,25 +53,41 @@ func NewHandler[T Gossipable]( Name: "gossip_sent_bytes", Help: "amount of gossip sent (bytes)", }), + pushGossipReceivedN: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: config.Namespace, + Name: "push_gossip_received_n", + Help: "amount of push gossip received (n)", + }), + pushGossipReceivedBytes: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: config.Namespace, + Name: "push_gossip_received_bytes", + Help: "amount of push gossip received (n)", + }), } err := utils.Err( metrics.Register(h.sentN), metrics.Register(h.sentBytes), + metrics.Register(h.pushGossipReceivedN), + metrics.Register(h.pushGossipReceivedBytes), ) return h, err } -type Handler[T Gossipable] struct { +type Handler[T any, U GossipableAny[T]] struct { p2p.Handler - set Set[T] + log logging.Logger + set Set[U] + gossipSender gossipSender targetResponseSize int - sentN prometheus.Counter - sentBytes prometheus.Counter + sentN prometheus.Counter + sentBytes prometheus.Counter + pushGossipReceivedN prometheus.Counter + pushGossipReceivedBytes prometheus.Counter } -func (h Handler[T]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, requestBytes []byte) ([]byte, error) { +func (h Handler[T, U]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, requestBytes []byte) ([]byte, error) { request := &sdk.PullGossipRequest{} if err := proto.Unmarshal(requestBytes, request); err != nil { return nil, err @@ -84,7 +108,7 @@ func (h Handler[T]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, req responseSize := 0 gossipBytes := make([][]byte, 0) - h.set.Iterate(func(gossipable T) bool { + h.set.Iterate(func(gossipable U) bool { // filter out what the requesting peer already knows about if filter.Has(gossipable) { return true @@ -117,3 +141,58 @@ func (h Handler[T]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, req return proto.Marshal(response) } + +func (h Handler[T, U]) AppGossip(ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) { + msg := &sdk.PushGossip{} + if err := proto.Unmarshal(gossipBytes, msg); err != nil { + h.log.Debug("failed to unmarshal gossip", zap.Error(err)) + return + } + + forward := make([][]byte, 0, len(msg.Gossip)) + for _, bytes := range msg.Gossip { + gossipable := U(new(T)) + if err := gossipable.Unmarshal(bytes); err != nil { + h.log.Debug("failed to unmarshal gossip", + zap.Stringer("nodeID", nodeID), + zap.Error(err), + ) + } + + if _, ok := h.set.Get(gossipable.GetID()); ok { + continue + } + + if err := h.set.Add(gossipable); err != nil { + h.log.Debug( + "failed to add gossip to the known set", + zap.Stringer("nodeID", nodeID), + zap.Stringer("id", gossipable.GetID()), + zap.Error(err), + ) + continue + } + + // continue gossiping messages we have not seen to other peers + forward = append(forward, bytes) + } + + forwardMsg := &sdk.PushGossip{ + Gossip: forward, + } + + forwardMsgBytes, err := proto.Marshal(forwardMsg) + if err != nil { + h.log.Debug( + "failed to marshal forward gossip message", + zap.Error(err), + ) + } + + if err := h.gossipSender.sendGossip(ctx, forwardMsgBytes); err != nil { + h.log.Debug( + "failed to forward gossip", + zap.Error(err), + ) + } +} diff --git a/network/p2p/gossip/sender.go b/network/p2p/gossip/sender.go new file mode 100644 index 00000000000..413d53a52d9 --- /dev/null +++ b/network/p2p/gossip/sender.go @@ -0,0 +1,36 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package gossip + +import ( + "context" + "encoding/binary" + + "github.com/ava-labs/avalanchego/network/p2p" +) + +var _ gossipSender = (*gossipClient)(nil) + +type gossipSender interface { + sendGossip(ctx context.Context, bytes []byte) error +} + +type gossipClient struct { + client *p2p.Client +} + +func (g *gossipClient) sendGossip(ctx context.Context, bytes []byte) error { + return g.client.AppGossip(ctx, bytes) +} + +type fakeGossipClient struct { + handlerID uint64 + sent chan<- []byte +} + +func (f *fakeGossipClient) sendGossip(_ context.Context, bytes []byte) error { + msg := append(binary.AppendUvarint(nil, f.handlerID), bytes...) + f.sent <- msg + return nil +} diff --git a/network/p2p/gossip/test_gossip.go b/network/p2p/gossip/test_gossip.go index ba114adf377..907a705d7b2 100644 --- a/network/p2p/gossip/test_gossip.go +++ b/network/p2p/gossip/test_gossip.go @@ -5,7 +5,6 @@ package gossip import ( "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/set" ) var ( @@ -32,13 +31,13 @@ func (t *testTx) Unmarshal(bytes []byte) error { } type testSet struct { - set set.Set[*testTx] + txs map[ids.ID]*testTx bloom *BloomFilter onAdd func(tx *testTx) } -func (t testSet) Add(gossipable *testTx) error { - t.set.Add(gossipable) +func (t *testSet) Add(gossipable *testTx) error { + t.txs[gossipable.id] = gossipable t.bloom.Add(gossipable) if t.onAdd != nil { t.onAdd(gossipable) @@ -47,15 +46,20 @@ func (t testSet) Add(gossipable *testTx) error { return nil } -func (t testSet) Iterate(f func(gossipable *testTx) bool) { - for tx := range t.set { +func (t *testSet) Get(id ids.ID) (*testTx, bool) { + tx, ok := t.txs[id] + return tx, ok +} + +func (t *testSet) Iterate(f func(gossipable *testTx) bool) { + for _, tx := range t.txs { if !f(tx) { return } } } -func (t testSet) GetFilter() ([]byte, []byte, error) { +func (t *testSet) GetFilter() ([]byte, []byte, error) { bloom, err := t.bloom.Bloom.MarshalBinary() return bloom, t.bloom.Salt[:], err } diff --git a/proto/pb/sdk/sdk.pb.go b/proto/pb/sdk/sdk.pb.go index 120974ee597..b828c4026d9 100644 --- a/proto/pb/sdk/sdk.pb.go +++ b/proto/pb/sdk/sdk.pb.go @@ -122,6 +122,53 @@ func (x *PullGossipResponse) GetGossip() [][]byte { return nil } +type PushGossip struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Gossip [][]byte `protobuf:"bytes,1,rep,name=gossip,proto3" json:"gossip,omitempty"` +} + +func (x *PushGossip) Reset() { + *x = PushGossip{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_sdk_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushGossip) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushGossip) ProtoMessage() {} + +func (x *PushGossip) ProtoReflect() protoreflect.Message { + mi := &file_sdk_sdk_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushGossip.ProtoReflect.Descriptor instead. +func (*PushGossip) Descriptor() ([]byte, []int) { + return file_sdk_sdk_proto_rawDescGZIP(), []int{2} +} + +func (x *PushGossip) GetGossip() [][]byte { + if x != nil { + return x.Gossip + } + return nil +} + var File_sdk_sdk_proto protoreflect.FileDescriptor var file_sdk_sdk_proto_rawDesc = []byte{ @@ -133,10 +180,13 @@ var file_sdk_sdk_proto_rawDesc = []byte{ 0x04, 0x73, 0x61, 0x6c, 0x74, 0x22, 0x2c, 0x0a, 0x12, 0x50, 0x75, 0x6c, 0x6c, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x67, 0x6f, 0x73, - 0x73, 0x69, 0x70, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, - 0x73, 0x64, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x69, 0x70, 0x22, 0x24, 0x0a, 0x0a, 0x50, 0x75, 0x73, 0x68, 0x47, 0x6f, 0x73, 0x73, 0x69, + 0x70, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x06, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, + 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x73, 0x64, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -151,10 +201,11 @@ func file_sdk_sdk_proto_rawDescGZIP() []byte { return file_sdk_sdk_proto_rawDescData } -var file_sdk_sdk_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_sdk_sdk_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_sdk_sdk_proto_goTypes = []interface{}{ (*PullGossipRequest)(nil), // 0: sdk.PullGossipRequest (*PullGossipResponse)(nil), // 1: sdk.PullGossipResponse + (*PushGossip)(nil), // 2: sdk.PushGossip } var file_sdk_sdk_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -194,6 +245,18 @@ func file_sdk_sdk_proto_init() { return nil } } + file_sdk_sdk_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushGossip); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -201,7 +264,7 @@ func file_sdk_sdk_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_sdk_sdk_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/sdk/sdk.proto b/proto/sdk/sdk.proto index 20bfca08185..6841cfb8b02 100644 --- a/proto/sdk/sdk.proto +++ b/proto/sdk/sdk.proto @@ -12,3 +12,7 @@ message PullGossipRequest { message PullGossipResponse { repeated bytes gossip = 1; } + +message PushGossip { + repeated bytes gossip = 1; +}