diff --git a/gossip/discovery/discovery.go b/gossip/discovery/discovery.go index 542d0b44dc9..8666471bcf1 100644 --- a/gossip/discovery/discovery.go +++ b/gossip/discovery/discovery.go @@ -54,14 +54,29 @@ type CommService interface { // NetworkMember is a peer's representation type NetworkMember struct { - Endpoint string - Metadata []byte - PKIid common.PKIidType + Endpoint string + Metadata []byte + PKIid common.PKIidType + InternalEndpoint *proto.SignedEndpoint +} + +// PreferredEndpoint computes the endpoint to connect to, +// while preferring internal endpoint over the standard +// endpoint +func (nm NetworkMember) PreferredEndpoint() string { + if nm.InternalEndpoint != nil && nm.InternalEndpoint.Endpoint != "" { + return nm.InternalEndpoint.Endpoint + } + return nm.Endpoint } // Discovery is the interface that represents a discovery module type Discovery interface { + // Exists returns whether a peer with given + // PKI-ID is known + Exists(PKIID common.PKIidType) bool + // Self returns this instance's membership information Self() NetworkMember diff --git a/gossip/discovery/discovery_impl.go b/gossip/discovery/discovery_impl.go index 2140d7b8c3a..9cc14f82135 100644 --- a/gossip/discovery/discovery_impl.go +++ b/gossip/discovery/discovery_impl.go @@ -71,13 +71,9 @@ func (ts *timestamp) String() string { } type gossipDiscoveryImpl struct { - pkiID common.PKIidType - endpoint string - incTime uint64 - metadata []byte - - seqNum uint64 - + incTime uint64 + seqNum uint64 + self NetworkMember deadLastTS map[string]*timestamp // H aliveLastTS map[string]*timestamp // V id2Member map[string]*NetworkMember // all known members @@ -97,10 +93,8 @@ type gossipDiscoveryImpl struct { // NewDiscoveryService returns a new discovery service with the comm module passed and the crypto service passed func NewDiscoveryService(bootstrapPeers []string, self NetworkMember, comm CommService, crypt CryptoService) Discovery { d := &gossipDiscoveryImpl{ - endpoint: self.Endpoint, + self: self, incTime: uint64(time.Now().UnixNano()), - metadata: self.Metadata, - pkiID: self.PKIid, seqNum: uint64(0), deadLastTS: make(map[string]*timestamp), aliveLastTS: make(map[string]*timestamp), @@ -109,13 +103,12 @@ func NewDiscoveryService(bootstrapPeers []string, self NetworkMember, comm CommS Alive: make([]*proto.GossipMessage, 0), Dead: make([]*proto.GossipMessage, 0), }, - crypt: crypt, - bootstrapPeers: bootstrapPeers, - comm: comm, - lock: &sync.RWMutex{}, - toDieChan: make(chan struct{}, 1), - toDieFlag: int32(0), - logger: util.GetLogger(util.LoggingDiscoveryModule, self.Endpoint), + crypt: crypt, + comm: comm, + lock: &sync.RWMutex{}, + toDieChan: make(chan struct{}, 1), + toDieFlag: int32(0), + logger: util.GetLogger(util.LoggingDiscoveryModule, self.InternalEndpoint.Endpoint), } go d.periodicalSendAlive() @@ -131,6 +124,15 @@ func NewDiscoveryService(bootstrapPeers []string, self NetworkMember, comm CommS return d } +// Exists returns whether a peer with given +// PKI-ID is known +func (d *gossipDiscoveryImpl) Exists(PKIID common.PKIidType) bool { + d.lock.RLock() + defer d.lock.RUnlock() + _, exists := d.id2Member[string(PKIID)] + return exists +} + func (d *gossipDiscoveryImpl) Connect(member NetworkMember) { d.logger.Debug("Entering", member) defer d.logger.Debug("Exiting") @@ -167,6 +169,9 @@ func (d *gossipDiscoveryImpl) connect2BootstrapPeers(endpoints []string) { defer wg.Done() peer := &NetworkMember{ Endpoint: endpoint, + InternalEndpoint: &proto.SignedEndpoint{ + Endpoint: endpoint, + }, } d.comm.SendToPeer(peer, req) }(endpoint) @@ -192,9 +197,10 @@ func (d *gossipDiscoveryImpl) InitiateSync(peerNum int) { for _, i := range util.GetRandomIndices(k, n-1) { pulledPeer := d.cachedMembership.Alive[i].GetAliveMsg().Membership netMember := &NetworkMember{ - Endpoint: pulledPeer.Endpoint, - Metadata: pulledPeer.Metadata, - PKIid: pulledPeer.PkiID, + Endpoint: pulledPeer.Endpoint, + Metadata: pulledPeer.Metadata, + PKIid: pulledPeer.PkiID, + InternalEndpoint: pulledPeer.InternalEndpoint, } peers2SendTo = append(peers2SendTo, netMember) } @@ -285,7 +291,7 @@ func (d *gossipDiscoveryImpl) handleMsgFromComm(m *proto.GossipMessage) { for _, dm := range memResp.Dead { if !d.crypt.ValidateAliveMsg(m) { - d.logger.Warningf("Alive message isn't authentic, someone spoofed %s's identity", dm.GetAliveMsg().Membership.Endpoint) + d.logger.Warningf("Alive message isn't authentic, someone spoofed %s's identity", dm.GetAliveMsg().Membership) continue } @@ -308,9 +314,10 @@ func (d *gossipDiscoveryImpl) sendMemResponse(member *proto.Member, known [][]by defer d.logger.Debug("Exiting, replying with", memResp) d.comm.SendToPeer(&NetworkMember{ - Endpoint: member.Endpoint, - Metadata: member.Metadata, - PKIid: member.PkiID, + Endpoint: member.Endpoint, + Metadata: member.Metadata, + PKIid: member.PkiID, + InternalEndpoint: member.InternalEndpoint, }, &proto.GossipMessage{ Tag: proto.GossipMessage_EMPTY, Nonce: uint64(0), @@ -353,12 +360,12 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) { defer d.logger.Debug("Exiting") if !d.crypt.ValidateAliveMsg(m) { - d.logger.Warningf("Alive message isn't authentic, someone must be spoofing %s's identity", m.GetAliveMsg().Membership.Endpoint) + d.logger.Warningf("Alive message isn't authentic, someone must be spoofing %s's identity", m.GetAliveMsg()) return } pkiID := m.GetAliveMsg().Membership.PkiID - if equalPKIid(pkiID, d.pkiID) { + if equalPKIid(pkiID, d.self.PKIid) { d.logger.Debug("Got alive message about ourselves,", m) return } @@ -385,7 +392,7 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) { } if isAlive && isDead { - d.logger.Panicf("Member %s is both alive and dead at the same time", m.GetAliveMsg().Membership.Endpoint) + d.logger.Panicf("Member %s is both alive and dead at the same time", m.GetAliveMsg().Membership) return } @@ -394,7 +401,7 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) { // resurrect peer d.resurrectMember(m, *ts) } else if !same(lastDeadTS, ts) { - d.logger.Debug(m.GetAliveMsg().Membership.Endpoint, "lastDeadTS:", lastDeadTS, "but got ts:", ts) + d.logger.Debug(m.GetAliveMsg().Membership, "lastDeadTS:", lastDeadTS, "but got ts:", ts) } return } @@ -407,7 +414,7 @@ func (d *gossipDiscoveryImpl) handleAliveMessage(m *proto.GossipMessage) { if before(lastAliveTS, ts) { d.learnExistingMembers([]*proto.GossipMessage{m}) } else if !same(lastAliveTS, ts) { - d.logger.Debug(m.GetAliveMsg().Membership.Endpoint, "lastAliveTS:", lastAliveTS, "but got ts:", ts) + d.logger.Debug(m.GetAliveMsg().Membership, "lastAliveTS:", lastAliveTS, "but got ts:", ts) } } @@ -429,9 +436,10 @@ func (d *gossipDiscoveryImpl) resurrectMember(am *proto.GossipMessage, t proto.P } d.id2Member[string(pkiID)] = &NetworkMember{ - Endpoint: member.Endpoint, - Metadata: member.Metadata, - PKIid: member.PkiID, + Endpoint: member.Endpoint, + Metadata: member.Metadata, + PKIid: member.PkiID, + InternalEndpoint: member.InternalEndpoint, } delete(d.deadLastTS, string(pkiID)) @@ -570,7 +578,7 @@ func (d *gossipDiscoveryImpl) expireDeadMembers(dead []common.PKIidType) { d.lock.Unlock() for _, member2Expire := range deadMembers2Expire { - d.logger.Warning("Closing connection to", member2Expire.Endpoint) + d.logger.Warning("Closing connection to", member2Expire) d.comm.CloseConn(member2Expire) } } @@ -605,9 +613,10 @@ func (d *gossipDiscoveryImpl) createAliveMessage() *proto.GossipMessage { d.seqNum++ seqNum := d.seqNum - endpoint := d.endpoint - meta := d.metadata - pkiID := d.pkiID + endpoint := d.self.Endpoint + meta := d.self.Metadata + pkiID := d.self.PKIid + internalEndpoint := d.self.InternalEndpoint d.lock.Unlock() @@ -616,9 +625,10 @@ func (d *gossipDiscoveryImpl) createAliveMessage() *proto.GossipMessage { Content: &proto.GossipMessage_AliveMsg{ AliveMsg: &proto.AliveMessage{ Membership: &proto.Member{ - Endpoint: endpoint, - Metadata: meta, - PkiID: pkiID, + Endpoint: endpoint, + Metadata: meta, + PkiID: pkiID, + InternalEndpoint: internalEndpoint, }, Timestamp: &proto.PeerTime{ IncNumber: uint64(d.incTime), @@ -649,14 +659,15 @@ func (d *gossipDiscoveryImpl) learnExistingMembers(aliveArr []*proto.GossipMessa member := d.id2Member[string(am.Membership.PkiID)] member.Endpoint = am.Membership.Endpoint member.Metadata = am.Membership.Metadata + member.InternalEndpoint = am.Membership.InternalEndpoint if _, isKnownAsDead := d.deadLastTS[string(am.Membership.PkiID)]; isKnownAsDead { - d.logger.Warning(am.Membership.Endpoint, "has already expired") + d.logger.Warning(am.Membership, "has already expired") continue } if _, isKnownAsAlive := d.aliveLastTS[string(am.Membership.PkiID)]; !isKnownAsAlive { - d.logger.Warning(am.Membership.Endpoint, "has already expired") + d.logger.Warning(am.Membership, "has already expired") continue } else { d.logger.Debug("Updating aliveness data:", am) @@ -686,7 +697,7 @@ func (d *gossipDiscoveryImpl) learnNewMembers(aliveMembers []*proto.GossipMessag defer d.lock.Unlock() for _, am := range aliveMembers { - if equalPKIid(am.GetAliveMsg().Membership.PkiID, d.pkiID) { + if equalPKIid(am.GetAliveMsg().Membership.PkiID, d.self.PKIid) { continue } d.aliveLastTS[string(am.GetAliveMsg().Membership.PkiID)] = ×tamp{ @@ -700,7 +711,7 @@ func (d *gossipDiscoveryImpl) learnNewMembers(aliveMembers []*proto.GossipMessag } for _, dm := range deadMembers { - if equalPKIid(dm.GetAliveMsg().Membership.PkiID, d.pkiID) { + if equalPKIid(dm.GetAliveMsg().Membership.PkiID, d.self.PKIid) { continue } d.deadLastTS[string(dm.GetAliveMsg().Membership.PkiID)] = ×tamp{ @@ -722,9 +733,10 @@ func (d *gossipDiscoveryImpl) learnNewMembers(aliveMembers []*proto.GossipMessag return } d.id2Member[string(member.Membership.PkiID)] = &NetworkMember{ - Endpoint: member.Membership.Endpoint, - Metadata: member.Membership.Metadata, - PKIid: member.Membership.PkiID, + Endpoint: member.Membership.Endpoint, + Metadata: member.Membership.Metadata, + PKIid: member.Membership.PkiID, + InternalEndpoint: member.Membership.InternalEndpoint, } } } @@ -741,9 +753,10 @@ func (d *gossipDiscoveryImpl) GetMembership() []NetworkMember { for _, m := range d.cachedMembership.Alive { member := m.GetAliveMsg() response = append(response, NetworkMember{ - PKIid: member.Membership.PkiID, - Endpoint: member.Membership.Endpoint, - Metadata: member.Membership.Metadata, + PKIid: member.Membership.PkiID, + Endpoint: member.Membership.Endpoint, + Metadata: member.Membership.Metadata, + InternalEndpoint: member.Membership.InternalEndpoint, }) } return response @@ -757,18 +770,23 @@ func tsToTime(ts uint64) time.Time { func (d *gossipDiscoveryImpl) UpdateMetadata(md []byte) { d.lock.Lock() defer d.lock.Unlock() - d.metadata = md + d.self.Metadata = md } func (d *gossipDiscoveryImpl) UpdateEndpoint(endpoint string) { d.lock.Lock() defer d.lock.Unlock() - d.endpoint = endpoint + d.self.Endpoint = endpoint } func (d *gossipDiscoveryImpl) Self() NetworkMember { - return NetworkMember{Endpoint: d.endpoint, Metadata: d.metadata, PKIid: d.pkiID} + return NetworkMember{ + Endpoint: d.self.Endpoint, + Metadata: d.self.Metadata, + PKIid: d.self.PKIid, + InternalEndpoint: d.self.InternalEndpoint, + } } func (d *gossipDiscoveryImpl) toDie() bool { @@ -788,7 +806,7 @@ func equalPKIid(a, b common.PKIidType) bool { } func same(a *timestamp, b *proto.PeerTime) bool { - return (uint64(a.incTime.UnixNano()) == b.IncNumber && a.seqNum == b.SeqNum) + return uint64(a.incTime.UnixNano()) == b.IncNumber && a.seqNum == b.SeqNum } func before(a *timestamp, b *proto.PeerTime) bool { diff --git a/gossip/discovery/discovery_test.go b/gossip/discovery/discovery_test.go index a03ab96ffd1..6ecbac0bb0b 100644 --- a/gossip/discovery/discovery_test.go +++ b/gossip/discovery/discovery_test.go @@ -234,6 +234,10 @@ func createDiscoveryInstanceThatGossips(port int, id string, bootstrapPeers []st Metadata: []byte{}, PKIid: []byte(endpoint), Endpoint: endpoint, + InternalEndpoint: &proto.SignedEndpoint{ + Endpoint: endpoint, + Signature: []byte{}, + }, } listenAddress := fmt.Sprintf("%s:%d", "", port) @@ -414,6 +418,22 @@ func TestGetFullMembership(t *testing.T) { } assertMembership(t, instances, nodeNum-1) + + // Ensure that internal endpoint was propagated to everyone + for _, inst := range instances { + for _, member := range inst.GetMembership() { + assert.NotEmpty(t, member.InternalEndpoint.Endpoint) + assert.NotEmpty(t, member.Endpoint) + } + } + + // Check that Exists() is valid + for _, inst := range instances { + for _, member := range inst.GetMembership() { + assert.True(t, inst.Exists(member.PKIid)) + } + } + stopInstances(t, instances) } diff --git a/gossip/filter/filter.go b/gossip/filter/filter.go index 4b09c57831e..255b5893444 100644 --- a/gossip/filter/filter.go +++ b/gossip/filter/filter.go @@ -44,7 +44,7 @@ func SelectPeers(k int, peerPool []discovery.NetworkMember, filters ...RoutingFi var filteredPeers []*comm.RemotePeer for _, peer := range peerPool { if CombineRoutingFilters(filters...)(peer) { - filteredPeers = append(filteredPeers, &comm.RemotePeer{PKIID: peer.PKIid, Endpoint: peer.Endpoint}) + filteredPeers = append(filteredPeers, &comm.RemotePeer{PKIID: peer.PKIid, Endpoint: peer.PreferredEndpoint()}) } } diff --git a/gossip/gossip/gossip.go b/gossip/gossip/gossip.go index 9a863e70967..23bb7aa2877 100644 --- a/gossip/gossip/gossip.go +++ b/gossip/gossip/gossip.go @@ -69,7 +69,6 @@ type Gossip interface { type Config struct { BindPort int // Port we bind to, used only for tests ID string // ID of this instance - SelfEndpoint string // Endpoint we publish to remote peers BootstrapPeers []string // Peers we connect to at startup PropagateIterations int // Number of times a message is pushed to remote peers PropagatePeerNum int // Number of peers selected to push messages to @@ -89,4 +88,7 @@ type Config struct { PublishStateInfoInterval time.Duration // Determines frequency of pushing state info messages to peers RequestStateInfoInterval time.Duration // Determines frequency of pulling state info messages from peers TLSServerCert *tls.Certificate // TLS certificate of the peer + + InternalEndpoint string // Endpoint we publish to peers in our organization + ExternalEndpoint string // Peer publishes this endpoint instead of SelfEndpoint to foreign organizations } diff --git a/gossip/gossip/gossip_impl.go b/gossip/gossip/gossip_impl.go index 5b49bd9254a..a3567631010 100644 --- a/gossip/gossip/gossip_impl.go +++ b/gossip/gossip/gossip_impl.go @@ -35,6 +35,7 @@ import ( "github.com/hyperledger/fabric/gossip/identity" "github.com/hyperledger/fabric/gossip/util" proto "github.com/hyperledger/fabric/protos/gossip" + "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" "google.golang.org/grpc" ) @@ -115,19 +116,45 @@ func NewGossipService(conf *Config, s *grpc.Server, secAdvisor api.SecurityAdvis conf.MaxPropagationBurstSize, conf.MaxPropagationBurstLatency, g.sendGossipBatch) - g.discAdapter = g.newDiscoveryAdapter(selfIdentity) - g.disSecAdap = newDiscoverySecurityAdapter(idMapper, mcs, c, g.logger) - g.disc = discovery.NewDiscoveryService(conf.BootstrapPeers, discovery.NetworkMember{ - Endpoint: conf.SelfEndpoint, PKIid: g.comm.GetPKIid(), Metadata: []byte{}, - }, g.discAdapter, g.disSecAdap) + g.discAdapter = g.newDiscoveryAdapter() + g.disSecAdap = g.newDiscoverySecurityAdapter() + + g.disc = discovery.NewDiscoveryService(conf.BootstrapPeers, g.selfNetworkMember(), g.discAdapter, g.disSecAdap) g.certStore = newCertStore(g.createCertStorePuller(), idMapper, selfIdentity, mcs) + if g.conf.ExternalEndpoint == "" { + g.logger.Warning("External endpoint is empty, peer will not be accessible outside of its organization") + } + go g.start() return g } +func (g *gossipServiceImpl) selfNetworkMember() discovery.NetworkMember { + var err error + internalEndpoint := &proto.SignedEndpoint{ + Signature: nil, + Endpoint: g.conf.InternalEndpoint, + } + // Sign the internal endpoint + internalEndpoint.Signature = nil + endpointBytes := utils.MarshalOrPanic(internalEndpoint) + internalEndpoint.Signature, err = g.mcs.Sign(endpointBytes) + if err != nil { + g.logger.Panic("Failed signing message:", err) + } + self := discovery.NetworkMember{ + Endpoint: g.conf.ExternalEndpoint, + PKIid: g.comm.GetPKIid(), + Metadata: []byte{}, + InternalEndpoint: internalEndpoint, + } + g.logger.Info("Creating gossip service with self membership of", self) + return self +} + func newChannelState(g *gossipServiceImpl) *channelState { return &channelState{ stopping: int32(0), @@ -161,9 +188,11 @@ func (g *gossipServiceImpl) JoinChan(joinMsg api.JoinChannelMessage, chainID com for _, ap := range joinMsg.AnchorPeers() { if ap.Host == "" { g.logger.Warning("Got empty hostname, skipping connecting to anchor peer", ap) + continue } if ap.Port == 0 { g.logger.Warning("Got invalid port (0), skipping connecting to anchor peer", ap) + continue } pkiID := g.mcs.GetPKIidOfCert(ap.Cert) // Skip connecting to self @@ -172,7 +201,8 @@ func (g *gossipServiceImpl) JoinChan(joinMsg api.JoinChannelMessage, chainID com continue } endpoint := fmt.Sprintf("%s:%d", ap.Host, ap.Port) - g.disc.Connect(discovery.NetworkMember{Endpoint: endpoint, PKIid: pkiID}) + g.disc.Connect(discovery.NetworkMember{ + InternalEndpoint: &proto.SignedEndpoint{Endpoint: endpoint}, PKIid: pkiID}) } } @@ -193,12 +223,10 @@ func (g *gossipServiceImpl) handlePresumedDead() { } func (g *gossipServiceImpl) syncDiscovery() { - g.logger.Debugf("Entering discovery sync with interal %ds", g.conf.PullInterval) + g.logger.Debug("Entering discovery sync with interval", g.conf.PullInterval) defer g.logger.Debug("Exiting discovery sync loop") for !g.toDie() { - //g.logger.Debug("Intiating discovery sync") g.disc.InitiateSync(g.conf.PullPeerNum) - //g.logger.Debug("Sleeping", g.conf.PullInterval) time.Sleep(g.conf.PullInterval) } } @@ -419,7 +447,7 @@ func (g *gossipServiceImpl) gossipBatch(msgs []*proto.GossipMessage) { return gc.IsMemberInChan }) - //Gossip Leadership messages + // Gossip Leadership messages leadershipMsgs, msgs = partitionMessages(isLeadershipMsg, msgs) g.gossipInChan(leadershipMsgs, func(gc channel.GossipChannel) filter.RoutingFilter { return filter.CombineRoutingFilters(gc.IsSubscribed, gc.IsMemberInChan, g.isInMyorg) @@ -615,9 +643,9 @@ func selectOnlyDiscoveryMessages(m interface{}) bool { return selected } -func (g *gossipServiceImpl) newDiscoveryAdapter(identity api.PeerIdentityType) *discoveryAdapter { +func (g *gossipServiceImpl) newDiscoveryAdapter() *discoveryAdapter { return &discoveryAdapter{ - identity: identity, + identity: g.selfIdentity, includeIdentityPeriod: g.includeIdentityPeriod, c: g.comm, stopping: int32(0), @@ -634,7 +662,6 @@ func (g *gossipServiceImpl) newDiscoveryAdapter(identity api.PeerIdentityType) * type discoveryAdapter struct { includeIdentityPeriod time.Time identity api.PeerIdentityType - mcs api.MessageCryptoService stopping int32 c comm.Comm presumedDead chan common.PKIidType @@ -665,11 +692,11 @@ func (da *discoveryAdapter) SendToPeer(peer *discovery.NetworkMember, msg *proto if da.toDie() { return } - da.c.Send(msg, &comm.RemotePeer{PKIID: peer.PKIid, Endpoint: peer.Endpoint}) + da.c.Send(msg, &comm.RemotePeer{PKIID: peer.PKIid, Endpoint: peer.PreferredEndpoint()}) } func (da *discoveryAdapter) Ping(peer *discovery.NetworkMember) bool { - return da.c.Probe(&comm.RemotePeer{Endpoint: peer.Endpoint, PKIID: peer.PKIid}) == nil + return da.c.Probe(&comm.RemotePeer{Endpoint: peer.PreferredEndpoint(), PKIID: peer.PKIid}) == nil } func (da *discoveryAdapter) Accept() <-chan *proto.GossipMessage { @@ -681,22 +708,24 @@ func (da *discoveryAdapter) PresumedDead() <-chan common.PKIidType { } func (da *discoveryAdapter) CloseConn(peer *discovery.NetworkMember) { - da.c.CloseConn(&comm.RemotePeer{Endpoint: peer.Endpoint, PKIID: peer.PKIid}) + da.c.CloseConn(&comm.RemotePeer{PKIID: peer.PKIid}) } type discoverySecurityAdapter struct { idMapper identity.Mapper + sa api.SecurityAdvisor mcs api.MessageCryptoService c comm.Comm logger *logging.Logger } -func newDiscoverySecurityAdapter(idMapper identity.Mapper, mcs api.MessageCryptoService, c comm.Comm, logger *logging.Logger) *discoverySecurityAdapter { +func (g *gossipServiceImpl) newDiscoverySecurityAdapter() *discoverySecurityAdapter { return &discoverySecurityAdapter{ - idMapper: idMapper, - mcs: mcs, - c: c, - logger: logger, + sa: g.secAdvisor, + idMapper: g.idMapper, + mcs: g.mcs, + c: g.comm, + logger: g.logger, } } @@ -736,36 +765,28 @@ func (sa *discoverySecurityAdapter) ValidateAliveMsg(m *proto.GossipMessage) boo return false } - // At this point we got the certificate of the peer, proceed to verifying the AliveMessage - verifier := func(peerIdentity []byte, signature, message []byte) error { - return sa.mcs.Verify(api.PeerIdentityType(peerIdentity), signature, message) - } - - rawIdentity := m.GetAliveMsg().Identity - m.GetAliveMsg().Identity = nil - defer func() { - m.GetAliveMsg().Identity = rawIdentity - }() - err := m.Verify(identity, verifier) - if err != nil { - sa.logger.Warning("Failed verifying:", am, ":", err) - return false - } - return true + return sa.validateAliveMsgSignature(m, identity) } // SignMessage signs an AliveMessage and updates its signature field func (sa *discoverySecurityAdapter) SignMessage(m *proto.GossipMessage) *proto.GossipMessage { + var err error am := m.GetAliveMsg() signer := func(msg []byte) ([]byte, error) { return sa.mcs.Sign(msg) } - identity := am.Identity - am.Identity = nil + + // We omit the inner endpoint when we sign an AliveMessage. + // (1) So we first back it up + endpoint := am.Membership.InternalEndpoint + // (2) And nullify it + am.Membership.InternalEndpoint = nil + // (3) And when we exit the method, we restore it defer func() { - am.Identity = identity + am.Membership.InternalEndpoint = endpoint }() - err := m.Sign(signer) + + err = m.Sign(signer) if err != nil { sa.logger.Error("Failed signing", am, ":", err) return nil @@ -773,23 +794,54 @@ func (sa *discoverySecurityAdapter) SignMessage(m *proto.GossipMessage) *proto.G return m } -func (g *gossipServiceImpl) peersWithEndpoints(endpoints ...string) []*comm.RemotePeer { - peers := []*comm.RemotePeer{} - for _, member := range g.disc.GetMembership() { - for _, endpoint := range endpoints { - if member.Endpoint == endpoint { - peers = append(peers, &comm.RemotePeer{Endpoint: member.Endpoint, PKIID: member.PKIid}) - } +func (sa *discoverySecurityAdapter) validateAliveMsgSignature(m *proto.GossipMessage, identity api.PeerIdentityType) bool { + am := m.GetAliveMsg() + // At this point we got the certificate of the peer, proceed to verifying the AliveMessage + verifier := func(peerIdentity []byte, signature, message []byte) error { + return sa.mcs.Verify(api.PeerIdentityType(peerIdentity), signature, message) + } + + // The message is signed when identity and internalEndpoint are nil + // (1) So we first back them up, + rawIdentity := am.Identity + internalEndpoint := am.Membership.InternalEndpoint + // (2) Nullify them + am.Identity = nil + am.Membership.InternalEndpoint = nil + // (3) And when we exit the method, we restore them + defer func() { + am.Identity = rawIdentity + am.Membership.InternalEndpoint = internalEndpoint + }() + // We verify the signature on the message + err := m.Verify(identity, verifier) + if err != nil { + sa.logger.Warning("Failed verifying:", am, ":", err) + return false + } + if internalEndpoint != nil && len(internalEndpoint.Signature) != 0 { + // Backup sig + sig := internalEndpoint.Signature + // Nullify it + internalEndpoint.Signature = nil + endpointBytes := utils.MarshalOrPanic(internalEndpoint) + // And restore it + internalEndpoint.Signature = sig + // And finally, we verify the signature + err = sa.mcs.Verify(identity, internalEndpoint.Signature, endpointBytes) + if err != nil { + sa.logger.Warning("Failed verifying internal endpoint:", am, err) + return false } } - return peers + return true } func (g *gossipServiceImpl) createCertStorePuller() pull.Mediator { conf := pull.PullConfig{ MsgType: proto.PullMsgType_IdentityMsg, Channel: []byte(""), - ID: g.conf.SelfEndpoint, + ID: g.conf.InternalEndpoint, PeerCountToSelect: g.conf.PullPeerNum, PullInterval: g.conf.PullInterval, Tag: proto.GossipMessage_EMPTY, diff --git a/gossip/gossip/gossip_test.go b/gossip/gossip/gossip_test.go index ed954307ea1..26dc2dec4af 100644 --- a/gossip/gossip/gossip_test.go +++ b/gossip/gossip/gossip_test.go @@ -51,7 +51,6 @@ func init() { } -//var testLock = sync.RWMutex{} var orgInChannelA = api.OrgIdentityType("ORG1") var anchorPeerIdentity = api.PeerIdentityType("identityInOrg1") @@ -164,12 +163,13 @@ func newGossipInstance(portPrefix int, id int, maxMsgCount int, boot ...int) Gos PropagatePeerNum: 3, PullInterval: time.Duration(2) * time.Second, PullPeerNum: 5, - SelfEndpoint: fmt.Sprintf("localhost:%d", port), + InternalEndpoint: fmt.Sprintf("localhost:%d", port), + ExternalEndpoint: fmt.Sprintf("1.2.3.4:%d", port), PublishCertPeriod: time.Duration(4) * time.Second, PublishStateInfoInterval: time.Duration(1) * time.Second, RequestStateInfoInterval: time.Duration(1) * time.Second, } - g := NewGossipServiceWithServer(conf, &orgCryptoService{}, &naiveCryptoService{}, api.PeerIdentityType(conf.SelfEndpoint)) + g := NewGossipServiceWithServer(conf, &orgCryptoService{}, &naiveCryptoService{}, api.PeerIdentityType(conf.InternalEndpoint)) return g } @@ -186,12 +186,13 @@ func newGossipInstanceWithOnlyPull(portPrefix int, id int, maxMsgCount int, boot PropagatePeerNum: 0, PullInterval: time.Duration(1000) * time.Millisecond, PullPeerNum: 20, - SelfEndpoint: fmt.Sprintf("localhost:%d", port), + InternalEndpoint: fmt.Sprintf("localhost:%d", port), + ExternalEndpoint: fmt.Sprintf("1.2.3.4:%d", port), PublishCertPeriod: time.Duration(0) * time.Second, PublishStateInfoInterval: time.Duration(1) * time.Second, RequestStateInfoInterval: time.Duration(1) * time.Second, } - g := NewGossipServiceWithServer(conf, &orgCryptoService{}, &naiveCryptoService{}, api.PeerIdentityType(conf.SelfEndpoint)) + g := NewGossipServiceWithServer(conf, &orgCryptoService{}, &naiveCryptoService{}, api.PeerIdentityType(conf.InternalEndpoint)) return g } @@ -324,7 +325,7 @@ func TestConnectToAnchorPeers(t *testing.T) { }(i) } waitUntilOrFailBlocking(t, wg.Wait) - waitUntilOrFail(t, checkPeersMembership(peers, n-1)) + waitUntilOrFail(t, checkPeersMembership(t, peers, n-1)) channelMembership := func() bool { for _, peer := range peers { @@ -410,6 +411,7 @@ func TestMembership(t *testing.T) { } metadataDisseminationTime := time.Now() waitUntilOrFail(t, metaDataUpdated) + fmt.Println("Metadata updated") t.Log("Metadata dissemination took", time.Since(metadataDisseminationTime)) stop := func() { @@ -479,7 +481,7 @@ func TestDissemination(t *testing.T) { } membershipTime := time.Now() - waitUntilOrFail(t, checkPeersMembership(peers, n)) + waitUntilOrFail(t, checkPeersMembership(t, peers, n)) t.Log("Membership establishment took", time.Since(membershipTime)) for i := 1; i <= msgsCount2Send; i++ { @@ -514,7 +516,7 @@ func TestDissemination(t *testing.T) { incTime := uint64(time.Now().UnixNano()) t3 := time.Now() - leadershipMsg := createLeadershipMsg(true, common.ChainID("A"), incTime, uint64(seqNum), boot.(*gossipServiceImpl).conf.SelfEndpoint, boot.(*gossipServiceImpl).comm.GetPKIid()) + leadershipMsg := createLeadershipMsg(true, common.ChainID("A"), incTime, uint64(seqNum), boot.(*gossipServiceImpl).conf.InternalEndpoint, boot.(*gossipServiceImpl).comm.GetPKIid()) boot.Gossip(leadershipMsg) waitUntilOrFailBlocking(t, wgLeadership.Wait) @@ -569,7 +571,7 @@ func TestMembershipConvergence(t *testing.T) { peers = append(peers, pI) } - waitUntilOrFail(t, checkPeersMembership(peers, 4)) + waitUntilOrFail(t, checkPeersMembership(t, peers, 4)) t.Log("Sets of peers connected successfully") connectorPeer := newGossipInstance(portPrefix, 15, 100, 0, 1, 2) @@ -664,7 +666,7 @@ func TestDataLeakage(t *testing.T) { } waitUntilOrFailBlocking(t, wg.Wait) - waitUntilOrFail(t, checkPeersMembership(peers, n-1)) + waitUntilOrFail(t, checkPeersMembership(t, peers, n-1)) channels := []common.ChainID{common.ChainID("A"), common.ChainID("B")} @@ -771,7 +773,7 @@ func TestDisseminateAll2All(t *testing.T) { }(i) } wg.Wait() - waitUntilOrFail(t, checkPeersMembership(peers, n-1)) + waitUntilOrFail(t, checkPeersMembership(t, peers, n-1)) bMutex := sync.WaitGroup{} bMutex.Add(10 * n * (n - 1)) @@ -837,6 +839,10 @@ func createDataMsg(seqnum uint64, data []byte, hash string, channel common.Chain } func createLeadershipMsg(isDeclaration bool, channel common.ChainID, incTime uint64, seqNum uint64, endpoint string, pkiid []byte) *proto.GossipMessage { + + metadata := []byte{} + metadata = strconv.AppendBool(metadata, isDeclaration) + leadershipMsg := &proto.LeadershipMessage{ IsDeclaration: isDeclaration, PkiID: pkiid, @@ -924,7 +930,7 @@ func ensureGoroutineExit(t *testing.T) { func metadataOfPeer(members []discovery.NetworkMember, endpoint string) []byte { for _, member := range members { - if member.Endpoint == endpoint { + if member.InternalEndpoint.Endpoint == endpoint { return member.Metadata } } @@ -1025,12 +1031,16 @@ func searchInStackTrace(searchTerm string, stack []string) bool { return false } -func checkPeersMembership(peers []Gossip, n int) func() bool { +func checkPeersMembership(t *testing.T, peers []Gossip, n int) func() bool { return func() bool { for _, peer := range peers { if len(peer.Peers()) != n { return false } + for _, p := range peer.Peers() { + assert.NotNil(t, p.InternalEndpoint) + assert.NotEmpty(t, p.Endpoint) + } } return true } diff --git a/gossip/gossip/pull/pullstore.go b/gossip/gossip/pull/pullstore.go index edb3b85281d..94c6a3127f6 100644 --- a/gossip/gossip/pull/pullstore.go +++ b/gossip/gossip/pull/pullstore.go @@ -299,8 +299,8 @@ func (p *pullMediatorImpl) peersWithEndpoints(endpoints ...string) []*comm.Remot peers := []*comm.RemotePeer{} for _, member := range p.memBvc.GetMembership() { for _, endpoint := range endpoints { - if member.Endpoint == endpoint { - peers = append(peers, &comm.RemotePeer{Endpoint: member.Endpoint, PKIID: member.PKIid}) + if member.PreferredEndpoint() == endpoint { + peers = append(peers, &comm.RemotePeer{Endpoint: member.PreferredEndpoint(), PKIID: member.PKIid}) } } } @@ -326,7 +326,7 @@ func SelectEndpoints(k int, peerPool []discovery.NetworkMember) []*comm.RemotePe indices := util.GetRandomIndices(k, len(peerPool)-1) endpoints := make([]*comm.RemotePeer, len(indices)) for i, j := range indices { - endpoints[i] = &comm.RemotePeer{Endpoint: peerPool[j].Endpoint, PKIID: peerPool[j].PKIid} + endpoints[i] = &comm.RemotePeer{Endpoint: peerPool[j].PreferredEndpoint(), PKIID: peerPool[j].PKIid} } return endpoints } diff --git a/gossip/integration/integration.go b/gossip/integration/integration.go index a038cb0907b..f067ecbd1de 100644 --- a/gossip/integration/integration.go +++ b/gossip/integration/integration.go @@ -47,7 +47,7 @@ func getDurationOrDefault(key string, defVal time.Duration) time.Duration { } } -func newConfig(selfEndpoint string, bootPeers ...string) *gossip.Config { +func newConfig(selfEndpoint string, externalEndpoint string, bootPeers ...string) *gossip.Config { port, err := strconv.ParseInt(strings.Split(selfEndpoint, ":")[1], 10, 64) if err != nil { panic(err) @@ -72,7 +72,8 @@ func newConfig(selfEndpoint string, bootPeers ...string) *gossip.Config { PropagatePeerNum: getIntOrDefault("peer.gossip.propagatePeerNum", 3), PullInterval: getDurationOrDefault("peer.gossip.pullInterval", 4*time.Second), PullPeerNum: getIntOrDefault("peer.gossip.pullPeerNum", 3), - SelfEndpoint: selfEndpoint, + InternalEndpoint: selfEndpoint, + ExternalEndpoint: externalEndpoint, PublishCertPeriod: getDurationOrDefault("peer.gossip.publishCertPeriod", 10*time.Second), RequestStateInfoInterval: getDurationOrDefault("peer.gossip.requestStateInfoInterval", 4*time.Second), PublishStateInfoInterval: getDurationOrDefault("peer.gossip.publishStateInfoInterval", 4*time.Second), @@ -87,7 +88,9 @@ func NewGossipComponent(identity []byte, endpoint string, s *grpc.Server, dialOp endpoint = overrideEndpoint } - conf := newConfig(endpoint, bootPeers...) + externalEndpoint := viper.GetString("peer.gossip.externalEndpoint") + + conf := newConfig(endpoint, externalEndpoint, bootPeers...) cryptSvc := mcs.NewMessageCryptoService() secAdv := sa.NewSecurityAdvisor() diff --git a/gossip/state/state.go b/gossip/state/state.go index c00bc2abdbe..e7e4ed6d4b4 100644 --- a/gossip/state/state.go +++ b/gossip/state/state.go @@ -339,11 +339,11 @@ func (s *GossipStateProviderImpl) antiEntropy() { func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64) { var peers []*comm.RemotePeer // Filtering peers which might have relevant blocks - for _, value := range s.gossip.PeersOfChannel(common2.ChainID(s.chainID)) { - nodeMetadata, err := FromBytes(value.Metadata) + for _, netMember := range s.gossip.PeersOfChannel(common2.ChainID(s.chainID)) { + nodeMetadata, err := FromBytes(netMember.Metadata) if err == nil { if nodeMetadata.LedgerHeight >= end { - peers = append(peers, &comm.RemotePeer{Endpoint: value.Endpoint, PKIID: value.PKIid}) + peers = append(peers, &comm.RemotePeer{Endpoint: netMember.PreferredEndpoint(), PKIID: netMember.PKIid}) } } else { s.logger.Errorf("Unable to de-serialize node meta state, error = %s", err) diff --git a/gossip/state/state_test.go b/gossip/state/state_test.go index b3e5c847931..dd3ca8ba435 100644 --- a/gossip/state/state_test.go +++ b/gossip/state/state_test.go @@ -158,7 +158,7 @@ func newGossipConfig(id int, maxMsgCount int, boot ...int) *gossip.Config { PropagatePeerNum: 3, PullInterval: time.Duration(4) * time.Second, PullPeerNum: 5, - SelfEndpoint: fmt.Sprintf("localhost:%d", port), + InternalEndpoint: fmt.Sprintf("localhost:%d", port), PublishCertPeriod: 10 * time.Second, RequestStateInfoInterval: 4 * time.Second, PublishStateInfoInterval: 4 * time.Second, @@ -167,7 +167,7 @@ func newGossipConfig(id int, maxMsgCount int, boot ...int) *gossip.Config { // Create gossip instance func newGossipInstance(config *gossip.Config) gossip.Gossip { - return gossip.NewGossipServiceWithServer(config, &orgCryptoService{}, &naiveCryptoService{}, []byte(config.SelfEndpoint)) + return gossip.NewGossipServiceWithServer(config, &orgCryptoService{}, &naiveCryptoService{}, []byte(config.InternalEndpoint)) } // Create new instance of KVLedger to be used for testing diff --git a/peer/core.yaml b/peer/core.yaml index 94585acc85e..fd5227a1e96 100644 --- a/peer/core.yaml +++ b/peer/core.yaml @@ -101,6 +101,10 @@ peer: # Should we ignore security or not ignoreSecurity: false + # This is an endpoint that is published to peers outside of the organization. + # If this isn't set, the peer will not be known to other organizations. + externalEndpoint: + # Sync related configuration sync: blocks: diff --git a/protos/gossip/message.pb.go b/protos/gossip/message.pb.go index 13f4f37889b..fa514911a41 100644 --- a/protos/gossip/message.pb.go +++ b/protos/gossip/message.pb.go @@ -27,6 +27,7 @@ It has these top-level messages: MembershipRequest MembershipResponse Member + SignedEndpoint Empty RemoteStateRequest RemoteStateResponse @@ -979,6 +980,9 @@ type Member struct { Endpoint string `protobuf:"bytes,1,opt,name=endpoint" json:"endpoint,omitempty"` Metadata []byte `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` PkiID []byte `protobuf:"bytes,3,opt,name=pkiID,proto3" json:"pkiID,omitempty"` + // internalEndpoint is used to connect to the peer + // if its in your own organization + InternalEndpoint *SignedEndpoint `protobuf:"bytes,4,opt,name=internalEndpoint" json:"internalEndpoint,omitempty"` } func (m *Member) Reset() { *m = Member{} } @@ -986,6 +990,28 @@ func (m *Member) String() string { return proto.CompactTextString(m) func (*Member) ProtoMessage() {} func (*Member) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } +func (m *Member) GetInternalEndpoint() *SignedEndpoint { + if m != nil { + return m.InternalEndpoint + } + return nil +} + +// SignedEndpoint is an endpoint that has a signature +// on it. The signature needs to be verified +// by the relevant certificate of the peer +// that sent the message the SignedEndpoint +// is part of. +type SignedEndpoint struct { + Endpoint string `protobuf:"bytes,1,opt,name=endpoint" json:"endpoint,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (m *SignedEndpoint) Reset() { *m = SignedEndpoint{} } +func (m *SignedEndpoint) String() string { return proto.CompactTextString(m) } +func (*SignedEndpoint) ProtoMessage() {} +func (*SignedEndpoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + // Empty is used for pinging and in tests type Empty struct { } @@ -993,7 +1019,7 @@ type Empty struct { func (m *Empty) Reset() { *m = Empty{} } func (m *Empty) String() string { return proto.CompactTextString(m) } func (*Empty) ProtoMessage() {} -func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } +func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } // RemoteStateRequest is used to ask a set of blocks // from a remote peer @@ -1004,7 +1030,7 @@ type RemoteStateRequest struct { func (m *RemoteStateRequest) Reset() { *m = RemoteStateRequest{} } func (m *RemoteStateRequest) String() string { return proto.CompactTextString(m) } func (*RemoteStateRequest) ProtoMessage() {} -func (*RemoteStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } +func (*RemoteStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } // RemoteStateResponse is used to send a set of blocks // to a remote peer @@ -1015,7 +1041,7 @@ type RemoteStateResponse struct { func (m *RemoteStateResponse) Reset() { *m = RemoteStateResponse{} } func (m *RemoteStateResponse) String() string { return proto.CompactTextString(m) } func (*RemoteStateResponse) ProtoMessage() {} -func (*RemoteStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } +func (*RemoteStateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } func (m *RemoteStateResponse) GetPayloads() []*Payload { if m != nil { @@ -1043,6 +1069,7 @@ func init() { proto.RegisterType((*MembershipRequest)(nil), "gossip.MembershipRequest") proto.RegisterType((*MembershipResponse)(nil), "gossip.MembershipResponse") proto.RegisterType((*Member)(nil), "gossip.Member") + proto.RegisterType((*SignedEndpoint)(nil), "gossip.SignedEndpoint") proto.RegisterType((*Empty)(nil), "gossip.Empty") proto.RegisterType((*RemoteStateRequest)(nil), "gossip.RemoteStateRequest") proto.RegisterType((*RemoteStateResponse)(nil), "gossip.RemoteStateResponse") @@ -1195,80 +1222,83 @@ var _Gossip_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("gossip/message.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1198 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x57, 0x5d, 0x6f, 0xdb, 0x36, - 0x17, 0x96, 0xe2, 0x4f, 0x1d, 0xcb, 0x8d, 0xc3, 0xa6, 0x85, 0xde, 0xbc, 0x1d, 0x60, 0x08, 0xdb, - 0xe0, 0x2e, 0xa8, 0xbd, 0xba, 0xbd, 0x18, 0x76, 0xd3, 0x25, 0x75, 0x5a, 0x67, 0xa8, 0xdd, 0x80, - 0x71, 0x07, 0x74, 0x37, 0x01, 0x63, 0x31, 0xb2, 0x16, 0x89, 0x52, 0x4d, 0x7a, 0x43, 0x80, 0x01, - 0xbb, 0xdf, 0x0f, 0xd9, 0xef, 0x1c, 0x48, 0x4a, 0xb2, 0x54, 0x3b, 0x19, 0x32, 0x60, 0x77, 0x3a, - 0x87, 0xcf, 0xf9, 0xe0, 0xe1, 0xe1, 0x73, 0x28, 0xd8, 0xf7, 0x63, 0xce, 0x83, 0x64, 0x10, 0x51, - 0xce, 0x89, 0x4f, 0xfb, 0xc9, 0x32, 0x16, 0x31, 0xaa, 0x6b, 0xad, 0xfb, 0x97, 0x05, 0xed, 0xb7, - 0xea, 0x73, 0xa2, 0xd7, 0xd1, 0x3e, 0xd4, 0x58, 0xcc, 0xe6, 0xd4, 0x31, 0xbb, 0x66, 0xaf, 0x8a, - 0xb5, 0x80, 0x1c, 0x68, 0xcc, 0x17, 0x84, 0x31, 0x1a, 0x3a, 0x3b, 0x5d, 0xb3, 0x67, 0xe3, 0x4c, - 0x44, 0x87, 0x50, 0x11, 0xc4, 0x77, 0x2a, 0x5d, 0xb3, 0xf7, 0x60, 0xf8, 0xbf, 0xbe, 0xf6, 0xdb, - 0x2f, 0xf9, 0xec, 0xcf, 0x88, 0x8f, 0x25, 0x0a, 0x3d, 0x01, 0x8b, 0x07, 0x3e, 0x23, 0x62, 0xb5, - 0xa4, 0x4e, 0x55, 0x39, 0x5a, 0x2b, 0xd0, 0x10, 0x9a, 0x24, 0x0c, 0x7e, 0xa5, 0x13, 0xee, 0x3b, - 0xb5, 0xae, 0xd9, 0x6b, 0x0d, 0xf7, 0x33, 0x7f, 0x47, 0x4a, 0xaf, 0xdd, 0x8d, 0x0d, 0x9c, 0xe3, - 0xd0, 0x0b, 0xa8, 0x47, 0x34, 0xc2, 0xf4, 0x93, 0x53, 0x57, 0x16, 0x79, 0x06, 0x13, 0x1a, 0x5d, - 0xd2, 0x25, 0x5f, 0x04, 0x09, 0xa6, 0x9f, 0x56, 0x94, 0x8b, 0xb1, 0x81, 0x53, 0x28, 0x7a, 0x99, - 0x1a, 0x71, 0xa7, 0xa1, 0x8c, 0x0e, 0xb6, 0x19, 0xf1, 0x24, 0x66, 0x9c, 0xe6, 0x56, 0x1c, 0x0d, - 0xa0, 0xe1, 0x11, 0x41, 0x64, 0x76, 0x4d, 0x65, 0xf6, 0x30, 0x33, 0x1b, 0x49, 0x75, 0x9e, 0x5c, - 0x86, 0x42, 0x87, 0x50, 0x5b, 0xd0, 0x30, 0x8c, 0x1d, 0xab, 0x0c, 0xd7, 0xc5, 0x19, 0xcb, 0xa5, - 0xb1, 0x81, 0x35, 0x06, 0xf5, 0xb5, 0xf7, 0x51, 0xe0, 0x3b, 0xa0, 0xe0, 0xa8, 0xe8, 0x7d, 0x14, - 0xf8, 0x7a, 0x0b, 0x19, 0x28, 0xcb, 0x46, 0xee, 0xbc, 0xb5, 0x99, 0xcd, 0x7a, 0xcf, 0x19, 0x0a, - 0xbd, 0x04, 0x90, 0x9f, 0x1f, 0x12, 0x8f, 0x08, 0xea, 0xd8, 0x9b, 0x31, 0xf4, 0xca, 0xd8, 0xc0, - 0x05, 0x1c, 0xfa, 0x0a, 0x6a, 0x34, 0x4a, 0xc4, 0x8d, 0xd3, 0x56, 0x06, 0xed, 0xcc, 0xe0, 0x44, - 0x2a, 0x65, 0xf6, 0x6a, 0x15, 0x1d, 0x42, 0x75, 0x1e, 0x33, 0xe6, 0x3c, 0x50, 0xa8, 0x47, 0x19, - 0xea, 0x75, 0xcc, 0xd8, 0x09, 0x17, 0xe4, 0x32, 0x0c, 0xf8, 0x62, 0x6c, 0x60, 0x05, 0x42, 0xcf, - 0xc1, 0xe2, 0x82, 0x08, 0x7a, 0xca, 0xae, 0x62, 0x67, 0x57, 0x59, 0xec, 0x65, 0x16, 0xe7, 0xd9, - 0xc2, 0xd8, 0xc0, 0x6b, 0x14, 0x3a, 0x82, 0xb6, 0x12, 0xce, 0x19, 0x49, 0xf8, 0x22, 0x16, 0x4e, - 0xa7, 0x7c, 0xda, 0xb9, 0x59, 0x06, 0x18, 0x1b, 0xb8, 0x6c, 0x81, 0x7e, 0x84, 0x4e, 0xee, 0xef, - 0x6c, 0x15, 0x86, 0xb2, 0x72, 0x7b, 0xca, 0xcb, 0x93, 0x0d, 0x2f, 0xe9, 0x7a, 0x5a, 0xc2, 0x0d, - 0x3b, 0xf4, 0x03, 0xd8, 0x4a, 0x97, 0x62, 0x1c, 0x54, 0x6e, 0x23, 0x4c, 0xa3, 0x58, 0xd0, 0xf3, - 0x02, 0x62, 0x6c, 0xe0, 0x92, 0x05, 0x7a, 0x9d, 0x6e, 0x28, 0xeb, 0x33, 0xe7, 0xa1, 0x72, 0xf1, - 0xff, 0xad, 0x2e, 0xf2, 0x56, 0x2c, 0xdb, 0xc8, 0xaa, 0x84, 0x94, 0x78, 0xba, 0x63, 0x65, 0x5f, - 0xee, 0x97, 0xab, 0xf2, 0x6e, 0xbd, 0x98, 0x77, 0x67, 0xd9, 0x02, 0x7d, 0x0f, 0x76, 0x42, 0xe9, - 0xf2, 0xd4, 0xa3, 0x4c, 0x04, 0xe2, 0xc6, 0x79, 0x54, 0xbe, 0x77, 0x67, 0x85, 0x35, 0xb9, 0x87, - 0x22, 0xd6, 0xbd, 0x80, 0xca, 0x8c, 0xf8, 0xa8, 0x0d, 0xd6, 0x87, 0xe9, 0xe8, 0xe4, 0xcd, 0xe9, - 0xf4, 0x64, 0xd4, 0x31, 0x90, 0x05, 0xb5, 0x93, 0xc9, 0xd9, 0xec, 0x63, 0xc7, 0x44, 0x36, 0x34, - 0xdf, 0xe3, 0xb7, 0x17, 0xef, 0xa7, 0xef, 0x3e, 0x76, 0x76, 0x24, 0xee, 0xf5, 0xf8, 0x68, 0xaa, - 0xc5, 0x0a, 0xea, 0x80, 0xad, 0xc4, 0xa3, 0xe9, 0xe8, 0xe2, 0x3d, 0x7e, 0xdb, 0xa9, 0xa2, 0x5d, - 0x68, 0x69, 0x00, 0x56, 0x8a, 0xda, 0xb1, 0x05, 0x8d, 0x79, 0xcc, 0x04, 0x65, 0xc2, 0x8d, 0xc0, - 0xca, 0x4f, 0x07, 0x1d, 0x40, 0x33, 0xa2, 0x82, 0xc8, 0x36, 0x55, 0x34, 0x65, 0xe3, 0x5c, 0x46, - 0x7d, 0xb0, 0x44, 0x10, 0x51, 0x2e, 0x48, 0x94, 0x28, 0xae, 0x6a, 0x0d, 0x3b, 0xc5, 0xdd, 0xcc, - 0x82, 0x88, 0xe2, 0x35, 0x44, 0xf2, 0x5d, 0x72, 0x1d, 0x9c, 0x8e, 0x14, 0x83, 0xd9, 0x58, 0x0b, - 0xee, 0x1b, 0xd8, 0xdb, 0x68, 0x29, 0xf4, 0x1c, 0x9a, 0x34, 0xa4, 0x11, 0x65, 0x82, 0x3b, 0x66, - 0xb7, 0x52, 0x6c, 0xf4, 0x12, 0xdf, 0xe1, 0x1c, 0xe6, 0x3e, 0x86, 0xfd, 0x6d, 0x4d, 0xe5, 0x4e, - 0xa0, 0x5d, 0xba, 0x1b, 0xeb, 0x34, 0xcc, 0x42, 0x1a, 0x08, 0x41, 0x75, 0x4e, 0x97, 0x22, 0xe5, - 0x5c, 0xf5, 0x2d, 0x75, 0x0b, 0xc2, 0x17, 0x69, 0xbe, 0xea, 0xdb, 0x9d, 0x81, 0x5d, 0x3c, 0xa9, - 0x7b, 0x78, 0x2b, 0x96, 0xb2, 0x52, 0x2e, 0xa5, 0x1b, 0x42, 0xab, 0xc0, 0x25, 0xb7, 0x4f, 0x06, - 0x4f, 0x91, 0x13, 0x77, 0x76, 0xba, 0x95, 0x9e, 0x85, 0x33, 0x11, 0x3d, 0x83, 0x46, 0xc4, 0xfd, - 0xd9, 0x4d, 0x42, 0xd3, 0xe9, 0x90, 0x33, 0x94, 0xac, 0xc4, 0x44, 0x2f, 0xe1, 0x0c, 0xe3, 0x32, - 0x68, 0x15, 0x88, 0xf1, 0x96, 0x68, 0xc5, 0x74, 0x77, 0x3e, 0x3b, 0xf9, 0x7b, 0xc6, 0xfb, 0x1d, - 0x60, 0xcd, 0x7a, 0xb7, 0x84, 0x7b, 0x0a, 0xd5, 0x34, 0xd4, 0x1d, 0xa7, 0x5d, 0xfd, 0x37, 0xd1, - 0xaf, 0x75, 0x74, 0xcd, 0xeb, 0xff, 0x75, 0x69, 0xbf, 0xd3, 0x07, 0x99, 0x8d, 0xf8, 0xa7, 0xd0, - 0x48, 0xc8, 0x4d, 0x18, 0x13, 0x4f, 0xc5, 0x6b, 0x0d, 0x77, 0x73, 0x6b, 0xad, 0xc6, 0xd9, 0xba, - 0x7b, 0x0a, 0x8d, 0x54, 0x87, 0x1e, 0x43, 0x9d, 0xd3, 0x4f, 0xd3, 0x55, 0x94, 0x26, 0x99, 0x4a, - 0x79, 0x3f, 0xca, 0xe3, 0xb0, 0x74, 0x3f, 0x4a, 0x5d, 0xa1, 0xa3, 0xd4, 0xb7, 0xfb, 0xa7, 0x09, - 0x76, 0x71, 0x8c, 0xa3, 0x3e, 0x40, 0x94, 0xcf, 0xdb, 0x34, 0x93, 0x07, 0xe5, 0x49, 0x8c, 0x0b, - 0x88, 0x7b, 0xdf, 0xec, 0x03, 0x68, 0x06, 0x19, 0xad, 0xe9, 0xb7, 0x46, 0x2e, 0xbb, 0x7f, 0xc0, - 0xde, 0x06, 0x39, 0xde, 0x72, 0x6b, 0xee, 0x1b, 0xf6, 0x4b, 0x68, 0x07, 0x7c, 0x44, 0xe7, 0x21, - 0x59, 0x12, 0x11, 0xc4, 0x4c, 0x15, 0xa1, 0x89, 0xcb, 0x4a, 0xf7, 0x08, 0x9a, 0x99, 0x31, 0xfa, - 0x02, 0x20, 0x60, 0xf3, 0x0b, 0xb6, 0x92, 0x5b, 0x4d, 0xab, 0x6b, 0x05, 0x6c, 0x3e, 0x55, 0x8a, - 0x42, 0xe1, 0x77, 0x8a, 0x85, 0x77, 0x7f, 0x81, 0xbd, 0x8d, 0x47, 0x0e, 0x7a, 0x05, 0xbb, 0x9c, - 0x86, 0x57, 0x92, 0x6f, 0x96, 0x91, 0x8e, 0x6f, 0x96, 0x67, 0x72, 0xb9, 0x79, 0x3f, 0x47, 0xcb, - 0x22, 0x5c, 0xb3, 0xf8, 0x37, 0xa6, 0x5a, 0xce, 0xc6, 0x5a, 0x70, 0x43, 0x40, 0x9b, 0x6f, 0x23, - 0xf9, 0xc0, 0x51, 0x0f, 0xb1, 0xbb, 0xd9, 0x50, 0x63, 0xd4, 0x5d, 0xa2, 0xc4, 0xfb, 0xa7, 0xbb, - 0x44, 0x89, 0xe7, 0xfe, 0x04, 0x75, 0x1d, 0x4d, 0x9e, 0x21, 0x65, 0x5e, 0x12, 0x07, 0x4c, 0xa8, - 0x7d, 0x58, 0x38, 0x97, 0xef, 0xe4, 0x82, 0xed, 0xac, 0xde, 0x80, 0x9a, 0x7a, 0xb7, 0xb8, 0x7d, - 0x40, 0x9b, 0x33, 0x5a, 0xde, 0x37, 0x5d, 0x5a, 0x4d, 0xef, 0x55, 0x9c, 0x89, 0xee, 0x31, 0x3c, - 0xdc, 0x32, 0x90, 0xd1, 0x21, 0x34, 0xd3, 0x8b, 0x92, 0x0d, 0x84, 0x8d, 0x9b, 0x94, 0x03, 0xbe, - 0x79, 0x05, 0xad, 0xc2, 0xe5, 0x54, 0x53, 0x93, 0x79, 0xf4, 0x2a, 0x60, 0xd4, 0xeb, 0x18, 0x72, - 0x1a, 0x1e, 0x87, 0xf1, 0xfc, 0x3a, 0x2d, 0x44, 0xc7, 0x94, 0xd3, 0x30, 0xe3, 0xf3, 0x09, 0xf7, - 0x3b, 0x3b, 0x43, 0x01, 0x75, 0x5d, 0x2c, 0x74, 0x0c, 0xb6, 0xfe, 0x3a, 0x17, 0x4b, 0x4a, 0x22, - 0xb4, 0xbd, 0x98, 0x07, 0xdb, 0xd5, 0xae, 0xd1, 0x33, 0xbf, 0x35, 0xd1, 0xd7, 0x50, 0x3d, 0x0b, - 0x98, 0x8f, 0xca, 0x2f, 0xba, 0x83, 0xb2, 0xe8, 0x1a, 0xc7, 0xcf, 0x7e, 0x3e, 0xf4, 0x03, 0xb1, - 0x58, 0x5d, 0xf6, 0xe7, 0x71, 0x34, 0x58, 0xdc, 0x24, 0x74, 0x19, 0x52, 0xcf, 0xa7, 0xcb, 0xc1, - 0x15, 0xb9, 0x5c, 0x06, 0xf3, 0x81, 0xfa, 0xa1, 0xe0, 0x03, 0x6d, 0x76, 0x59, 0x57, 0xe2, 0x8b, - 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xdf, 0xd0, 0xd3, 0x77, 0x0c, 0x00, 0x00, + // 1240 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x6f, 0xdb, 0x36, + 0x10, 0xb7, 0xfc, 0x5f, 0x67, 0x3b, 0x51, 0xd8, 0xb4, 0xd0, 0xb2, 0x0e, 0x08, 0x84, 0x6d, 0x48, + 0x17, 0xd4, 0x59, 0xd3, 0x3e, 0x0c, 0x7b, 0xe9, 0x92, 0x3a, 0xad, 0x53, 0xd4, 0x6e, 0xc0, 0xa4, + 0x0f, 0xdd, 0x4b, 0xc0, 0x58, 0x8c, 0xac, 0x45, 0xa2, 0x54, 0x91, 0xd9, 0x10, 0x60, 0xc0, 0xde, + 0xf7, 0x11, 0xf6, 0x01, 0xf6, 0x39, 0x07, 0x92, 0xa2, 0x2c, 0xd5, 0x4e, 0x86, 0x0c, 0xd8, 0x9b, + 0xee, 0xf8, 0xbb, 0x3f, 0xbc, 0x3b, 0xde, 0x9d, 0x60, 0x33, 0x48, 0x38, 0x0f, 0xd3, 0xbd, 0x98, + 0x72, 0x4e, 0x02, 0x3a, 0x4c, 0xb3, 0x44, 0x24, 0xa8, 0xad, 0xb9, 0xde, 0xdf, 0x36, 0x0c, 0xde, + 0xa8, 0xcf, 0x89, 0x3e, 0x47, 0x9b, 0xd0, 0x62, 0x09, 0x9b, 0x51, 0xd7, 0xda, 0xb6, 0x76, 0x9a, + 0x58, 0x13, 0xc8, 0x85, 0xce, 0x6c, 0x4e, 0x18, 0xa3, 0x91, 0x5b, 0xdf, 0xb6, 0x76, 0xfa, 0xd8, + 0x90, 0x68, 0x17, 0x1a, 0x82, 0x04, 0x6e, 0x63, 0xdb, 0xda, 0x59, 0xdb, 0xff, 0x62, 0xa8, 0xf5, + 0x0e, 0x2b, 0x3a, 0x87, 0x67, 0x24, 0xc0, 0x12, 0x85, 0x1e, 0x83, 0xcd, 0xc3, 0x80, 0x11, 0x71, + 0x9d, 0x51, 0xb7, 0xa9, 0x14, 0x2d, 0x18, 0x68, 0x1f, 0xba, 0x24, 0x0a, 0x7f, 0xa5, 0x13, 0x1e, + 0xb8, 0xad, 0x6d, 0x6b, 0xa7, 0xb7, 0xbf, 0x69, 0xf4, 0x1d, 0x28, 0xbe, 0x56, 0x37, 0xae, 0xe1, + 0x02, 0x87, 0x9e, 0x43, 0x3b, 0xa6, 0x31, 0xa6, 0x9f, 0xdc, 0xb6, 0x92, 0x28, 0x3c, 0x98, 0xd0, + 0xf8, 0x82, 0x66, 0x7c, 0x1e, 0xa6, 0x98, 0x7e, 0xba, 0xa6, 0x5c, 0x8c, 0x6b, 0x38, 0x87, 0xa2, + 0x17, 0xb9, 0x10, 0x77, 0x3b, 0x4a, 0x68, 0x6b, 0x95, 0x10, 0x4f, 0x13, 0xc6, 0x69, 0x21, 0xc5, + 0xd1, 0x1e, 0x74, 0x7c, 0x22, 0x88, 0xf4, 0xae, 0xab, 0xc4, 0x1e, 0x18, 0xb1, 0x91, 0x64, 0x17, + 0xce, 0x19, 0x14, 0xda, 0x85, 0xd6, 0x9c, 0x46, 0x51, 0xe2, 0xda, 0x55, 0xb8, 0x0e, 0xce, 0x58, + 0x1e, 0x8d, 0x6b, 0x58, 0x63, 0xd0, 0x50, 0x6b, 0x1f, 0x85, 0x81, 0x0b, 0x0a, 0x8e, 0xca, 0xda, + 0x47, 0x61, 0xa0, 0xaf, 0x60, 0x40, 0xc6, 0x1b, 0x79, 0xf3, 0xde, 0xb2, 0x37, 0x8b, 0x3b, 0x1b, + 0x14, 0x7a, 0x01, 0x20, 0x3f, 0x3f, 0xa4, 0x3e, 0x11, 0xd4, 0xed, 0x2f, 0xdb, 0xd0, 0x27, 0xe3, + 0x1a, 0x2e, 0xe1, 0xd0, 0x37, 0xd0, 0xa2, 0x71, 0x2a, 0x6e, 0xdc, 0x81, 0x12, 0x18, 0x18, 0x81, + 0x23, 0xc9, 0x94, 0xde, 0xab, 0x53, 0xb4, 0x0b, 0xcd, 0x59, 0xc2, 0x98, 0xbb, 0xa6, 0x50, 0x0f, + 0x0d, 0xea, 0x55, 0xc2, 0xd8, 0x11, 0x17, 0xe4, 0x22, 0x0a, 0xf9, 0x7c, 0x5c, 0xc3, 0x0a, 0x84, + 0x9e, 0x81, 0xcd, 0x05, 0x11, 0xf4, 0x98, 0x5d, 0x26, 0xee, 0xba, 0x92, 0xd8, 0x30, 0x12, 0xa7, + 0xe6, 0x60, 0x5c, 0xc3, 0x0b, 0x14, 0x3a, 0x80, 0x81, 0x22, 0x4e, 0x19, 0x49, 0xf9, 0x3c, 0x11, + 0xae, 0x53, 0xcd, 0x76, 0x21, 0x66, 0x00, 0xe3, 0x1a, 0xae, 0x4a, 0xa0, 0xb7, 0xe0, 0x14, 0xfa, + 0x4e, 0xae, 0xa3, 0x48, 0x46, 0x6e, 0x43, 0x69, 0x79, 0xbc, 0xa4, 0x25, 0x3f, 0xcf, 0x43, 0xb8, + 0x24, 0x87, 0x7e, 0x82, 0xbe, 0xe2, 0xe5, 0x18, 0x17, 0x55, 0xcb, 0x08, 0xd3, 0x38, 0x11, 0xf4, + 0xb4, 0x84, 0x18, 0xd7, 0x70, 0x45, 0x02, 0xbd, 0xca, 0x2f, 0x64, 0xea, 0xcc, 0x7d, 0xa0, 0x54, + 0x7c, 0xb9, 0x52, 0x45, 0x51, 0x8a, 0x55, 0x19, 0x19, 0x95, 0x88, 0x12, 0x5f, 0x57, 0xac, 0xac, + 0xcb, 0xcd, 0x6a, 0x54, 0xde, 0x2d, 0x0e, 0x8b, 0xea, 0xac, 0x4a, 0xa0, 0x1f, 0xa1, 0x9f, 0x52, + 0x9a, 0x1d, 0xfb, 0x94, 0x89, 0x50, 0xdc, 0xb8, 0x0f, 0xab, 0xef, 0xee, 0xa4, 0x74, 0x26, 0xef, + 0x50, 0xc6, 0x7a, 0xe7, 0xd0, 0x38, 0x23, 0x01, 0x1a, 0x80, 0xfd, 0x61, 0x3a, 0x3a, 0x7a, 0x7d, + 0x3c, 0x3d, 0x1a, 0x39, 0x35, 0x64, 0x43, 0xeb, 0x68, 0x72, 0x72, 0xf6, 0xd1, 0xb1, 0x50, 0x1f, + 0xba, 0xef, 0xf1, 0x9b, 0xf3, 0xf7, 0xd3, 0x77, 0x1f, 0x9d, 0xba, 0xc4, 0xbd, 0x1a, 0x1f, 0x4c, + 0x35, 0xd9, 0x40, 0x0e, 0xf4, 0x15, 0x79, 0x30, 0x1d, 0x9d, 0xbf, 0xc7, 0x6f, 0x9c, 0x26, 0x5a, + 0x87, 0x9e, 0x06, 0x60, 0xc5, 0x68, 0x1d, 0xda, 0xd0, 0x99, 0x25, 0x4c, 0x50, 0x26, 0xbc, 0x18, + 0xec, 0x22, 0x3b, 0x68, 0x0b, 0xba, 0x31, 0x15, 0x44, 0x96, 0xa9, 0x6a, 0x53, 0x7d, 0x5c, 0xd0, + 0x68, 0x08, 0xb6, 0x08, 0x63, 0xca, 0x05, 0x89, 0x53, 0xd5, 0xab, 0x7a, 0xfb, 0x4e, 0xf9, 0x36, + 0x67, 0x61, 0x4c, 0xf1, 0x02, 0x22, 0xfb, 0x5d, 0x7a, 0x15, 0x1e, 0x8f, 0x54, 0x07, 0xeb, 0x63, + 0x4d, 0x78, 0xaf, 0x61, 0x63, 0xa9, 0xa4, 0xd0, 0x33, 0xe8, 0xd2, 0x88, 0xc6, 0x94, 0x09, 0xee, + 0x5a, 0xdb, 0x8d, 0x72, 0xa1, 0x57, 0xfa, 0x1d, 0x2e, 0x60, 0xde, 0x23, 0xd8, 0x5c, 0x55, 0x54, + 0xde, 0x04, 0x06, 0x95, 0xb7, 0xb1, 0x70, 0xc3, 0x2a, 0xb9, 0x81, 0x10, 0x34, 0x67, 0x34, 0x13, + 0x79, 0xcf, 0x55, 0xdf, 0x92, 0x37, 0x27, 0x7c, 0x9e, 0xfb, 0xab, 0xbe, 0xbd, 0x33, 0xe8, 0x97, + 0x33, 0x75, 0x0f, 0x6d, 0xe5, 0x50, 0x36, 0xaa, 0xa1, 0xf4, 0x22, 0xe8, 0x95, 0x7a, 0xc9, 0xed, + 0x93, 0xc1, 0x57, 0xcd, 0x89, 0xbb, 0xf5, 0xed, 0xc6, 0x8e, 0x8d, 0x0d, 0x89, 0x9e, 0x42, 0x27, + 0xe6, 0xc1, 0xd9, 0x4d, 0x4a, 0xf3, 0xe9, 0x50, 0x74, 0x28, 0x19, 0x89, 0x89, 0x3e, 0xc2, 0x06, + 0xe3, 0x31, 0xe8, 0x95, 0x1a, 0xe3, 0x2d, 0xd6, 0xca, 0xee, 0xd6, 0x3f, 0xcb, 0xfc, 0x3d, 0xed, + 0xfd, 0x0e, 0xb0, 0xe8, 0x7a, 0xb7, 0x98, 0x7b, 0x02, 0xcd, 0xdc, 0xd4, 0x1d, 0xd9, 0x6e, 0xfe, + 0x17, 0xeb, 0x57, 0xda, 0xba, 0xee, 0xeb, 0xff, 0x77, 0x68, 0x7f, 0xd0, 0x89, 0x34, 0x23, 0xfe, + 0x09, 0x74, 0x52, 0x72, 0x13, 0x25, 0xc4, 0x57, 0xf6, 0x7a, 0xfb, 0xeb, 0x85, 0xb4, 0x66, 0x63, + 0x73, 0xee, 0x1d, 0x43, 0x27, 0xe7, 0xa1, 0x47, 0xd0, 0xe6, 0xf4, 0xd3, 0xf4, 0x3a, 0xce, 0x9d, + 0xcc, 0xa9, 0xa2, 0x1e, 0x65, 0x3a, 0x6c, 0x5d, 0x8f, 0x92, 0x57, 0xaa, 0x28, 0xf5, 0xed, 0xfd, + 0x69, 0x41, 0xbf, 0x3c, 0xc6, 0xd1, 0x10, 0x20, 0x2e, 0xe6, 0x6d, 0xee, 0xc9, 0x5a, 0x75, 0x12, + 0xe3, 0x12, 0xe2, 0xde, 0x2f, 0x7b, 0x0b, 0xba, 0xa1, 0x69, 0x6b, 0x7a, 0xd7, 0x28, 0x68, 0xef, + 0x0f, 0xd8, 0x58, 0x6a, 0x8e, 0xb7, 0xbc, 0x9a, 0xfb, 0x9a, 0xfd, 0x1a, 0x06, 0x21, 0x1f, 0xd1, + 0x59, 0x44, 0x32, 0x22, 0xc2, 0x84, 0xa9, 0x20, 0x74, 0x71, 0x95, 0xe9, 0x1d, 0x40, 0xd7, 0x08, + 0xa3, 0xaf, 0x00, 0x42, 0x36, 0x3b, 0x67, 0xd7, 0xf2, 0xaa, 0x79, 0x74, 0xed, 0x90, 0xcd, 0xa6, + 0x8a, 0x51, 0x0a, 0x7c, 0xbd, 0x1c, 0x78, 0xef, 0x17, 0xd8, 0x58, 0x5a, 0x72, 0xd0, 0x4b, 0x58, + 0xe7, 0x34, 0xba, 0x94, 0xfd, 0x26, 0x8b, 0xb5, 0x7d, 0xab, 0x3a, 0x93, 0xab, 0xc5, 0xfb, 0x39, + 0x5a, 0x06, 0xe1, 0x8a, 0x25, 0xbf, 0x31, 0x55, 0x72, 0x7d, 0xac, 0x09, 0x2f, 0x02, 0xb4, 0xbc, + 0x1b, 0xc9, 0x05, 0x47, 0x2d, 0x62, 0x77, 0x77, 0x43, 0x8d, 0x51, 0x6f, 0x89, 0x12, 0xff, 0xdf, + 0xde, 0x12, 0x25, 0xbe, 0xf7, 0x97, 0x05, 0x6d, 0x6d, 0x4e, 0x26, 0x91, 0x32, 0x3f, 0x4d, 0x42, + 0x26, 0xd4, 0x45, 0x6c, 0x5c, 0xd0, 0x77, 0x36, 0x83, 0x95, 0x6d, 0x1d, 0x1d, 0x82, 0x13, 0x32, + 0x41, 0x33, 0x46, 0xa2, 0x23, 0xa3, 0xb5, 0xa9, 0xc2, 0xf3, 0xa8, 0xd8, 0x01, 0xc2, 0x80, 0x51, + 0xdf, 0x9c, 0xe2, 0x25, 0xbc, 0xf7, 0x16, 0xd6, 0xaa, 0x98, 0x3b, 0x7d, 0xac, 0x6c, 0xbc, 0xf5, + 0xcf, 0x36, 0x5e, 0xaf, 0x03, 0x2d, 0xb5, 0x48, 0x79, 0x43, 0x40, 0xcb, 0x4b, 0x83, 0x6c, 0x00, + 0x3a, 0xd7, 0x7a, 0xde, 0x34, 0xb1, 0x21, 0xbd, 0x43, 0x78, 0xb0, 0x62, 0x43, 0x40, 0xbb, 0xd0, + 0xcd, 0x5f, 0xae, 0x99, 0x50, 0x4b, 0x4f, 0xbb, 0x00, 0x7c, 0xf7, 0x12, 0x7a, 0xa5, 0x6e, 0xa1, + 0xc6, 0x38, 0xf3, 0xe9, 0x65, 0xc8, 0xa8, 0xef, 0xd4, 0xe4, 0x78, 0x3e, 0x8c, 0x92, 0xd9, 0x55, + 0x9e, 0x19, 0xc7, 0x92, 0xe3, 0xd9, 0x0c, 0x98, 0x09, 0x0f, 0x9c, 0xfa, 0xbe, 0x80, 0xb6, 0xce, + 0x1e, 0x3a, 0x84, 0xbe, 0xfe, 0x3a, 0x15, 0x19, 0x25, 0x31, 0x5a, 0x9d, 0xdd, 0xad, 0xd5, 0x6c, + 0xaf, 0xb6, 0x63, 0x7d, 0x6f, 0xa1, 0x6f, 0xa1, 0x79, 0x12, 0xb2, 0x00, 0x55, 0x57, 0xcc, 0xad, + 0x2a, 0xe9, 0xd5, 0x0e, 0x9f, 0xfe, 0xbc, 0x1b, 0x84, 0x62, 0x7e, 0x7d, 0x31, 0x9c, 0x25, 0xf1, + 0xde, 0xfc, 0x26, 0xa5, 0x59, 0x44, 0xfd, 0x80, 0x66, 0x7b, 0x97, 0xe4, 0x22, 0x0b, 0x67, 0x7b, + 0xea, 0x0f, 0x87, 0xef, 0x69, 0xb1, 0x8b, 0xb6, 0x22, 0x9f, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, + 0xb6, 0xae, 0xe4, 0xb6, 0x08, 0x0d, 0x00, 0x00, } diff --git a/protos/gossip/message.proto b/protos/gossip/message.proto index 6dbb3e96f4f..970142c6dc6 100644 --- a/protos/gossip/message.proto +++ b/protos/gossip/message.proto @@ -225,6 +225,19 @@ message Member { string endpoint = 1; bytes metadata = 2; bytes pkiID = 3; + // internalEndpoint is used to connect to the peer + // if its in your own organization + SignedEndpoint internalEndpoint = 4; +} + +// SignedEndpoint is an endpoint that has a signature +// on it. The signature needs to be verified +// by the relevant certificate of the peer +// that sent the message the SignedEndpoint +// is part of. +message SignedEndpoint { + string endpoint = 1; + bytes signature = 2; } // Empty is used for pinging and in tests