218 lines
6.7 KiB
Go
218 lines
6.7 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package discovery
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
protolib "github.com/golang/protobuf/proto"
|
|
proto "github.com/hyperledger/fabric-protos-go/gossip"
|
|
"github.com/hyperledger/fabric/gossip/common"
|
|
"github.com/hyperledger/fabric/gossip/protoext"
|
|
)
|
|
|
|
// CryptoService is an interface that the discovery expects to be implemented and passed on creation
|
|
type CryptoService interface {
|
|
// ValidateAliveMsg validates that an Alive message is authentic
|
|
ValidateAliveMsg(message *protoext.SignedGossipMessage) bool
|
|
|
|
// SignMessage signs a message
|
|
SignMessage(m *proto.GossipMessage, internalEndpoint string) *proto.Envelope
|
|
}
|
|
|
|
// EnvelopeFilter may or may not remove part of the Envelope
|
|
// that the given SignedGossipMessage originates from.
|
|
type EnvelopeFilter func(message *protoext.SignedGossipMessage) *proto.Envelope
|
|
|
|
// Sieve defines the messages that are allowed to be sent to some remote peer,
|
|
// based on some criteria.
|
|
// Returns whether the sieve permits sending a given message.
|
|
type Sieve func(message *protoext.SignedGossipMessage) bool
|
|
|
|
// DisclosurePolicy defines which messages a given remote peer
|
|
// is eligible of knowing about, and also what is it eligible
|
|
// to know about out of a given SignedGossipMessage.
|
|
// Returns:
|
|
// 1. A Sieve for a given remote peer.
|
|
// The Sieve is applied for each peer in question and outputs
|
|
// whether the message should be disclosed to the remote peer.
|
|
// 2. A EnvelopeFilter for a given SignedGossipMessage, which may remove
|
|
// part of the Envelope the SignedGossipMessage originates from
|
|
type DisclosurePolicy func(remotePeer *NetworkMember) (Sieve, EnvelopeFilter)
|
|
|
|
// CommService is an interface that the discovery expects to be implemented and passed on creation
|
|
type CommService interface {
|
|
// Gossip gossips a message
|
|
Gossip(msg *protoext.SignedGossipMessage)
|
|
|
|
// SendToPeer sends to a given peer a message.
|
|
// The nonce can be anything since the communication module handles the nonce itself
|
|
SendToPeer(peer *NetworkMember, msg *protoext.SignedGossipMessage)
|
|
|
|
// Ping probes a remote peer and returns if it's responsive or not
|
|
Ping(peer *NetworkMember) bool
|
|
|
|
// Accept returns a read-only channel for membership messages sent from remote peers
|
|
Accept() <-chan protoext.ReceivedMessage
|
|
|
|
// PresumedDead returns a read-only channel for peers that are presumed to be dead
|
|
PresumedDead() <-chan common.PKIidType
|
|
|
|
// CloseConn orders to close the connection with a certain peer
|
|
CloseConn(peer *NetworkMember)
|
|
|
|
// Forward sends message to the next hop, excluding the hop
|
|
// from which message was initially received
|
|
Forward(msg protoext.ReceivedMessage)
|
|
|
|
// IdentitySwitch returns a read-only channel about identity change events
|
|
IdentitySwitch() <-chan common.PKIidType
|
|
}
|
|
|
|
// AnchorPeerTracker is an interface that is passed to discovery to check if an endpoint is an anchor peer
|
|
type AnchorPeerTracker interface {
|
|
IsAnchorPeer(endpoint string) bool
|
|
}
|
|
|
|
// NetworkMember is a peer's representation
|
|
type NetworkMember struct {
|
|
Endpoint string
|
|
Metadata []byte
|
|
PKIid common.PKIidType
|
|
InternalEndpoint string
|
|
Properties *proto.Properties
|
|
*proto.Envelope
|
|
}
|
|
|
|
// Clone clones the NetworkMember
|
|
func (n NetworkMember) Clone() NetworkMember {
|
|
pkiIDClone := make(common.PKIidType, len(n.PKIid))
|
|
copy(pkiIDClone, n.PKIid)
|
|
nmClone := NetworkMember{
|
|
Endpoint: n.Endpoint,
|
|
Metadata: n.Metadata,
|
|
InternalEndpoint: n.InternalEndpoint,
|
|
PKIid: pkiIDClone,
|
|
}
|
|
|
|
if n.Properties != nil {
|
|
nmClone.Properties = protolib.Clone(n.Properties).(*proto.Properties)
|
|
}
|
|
|
|
if n.Envelope != nil {
|
|
nmClone.Envelope = protolib.Clone(n.Envelope).(*proto.Envelope)
|
|
}
|
|
|
|
return nmClone
|
|
}
|
|
|
|
// String returns a string representation of the NetworkMember
|
|
func (n NetworkMember) String() string {
|
|
return fmt.Sprintf("Endpoint: %s, InternalEndpoint: %s, PKI-ID: %s, Metadata: %x", n.Endpoint, n.InternalEndpoint, n.PKIid, n.Metadata)
|
|
}
|
|
|
|
// PreferredEndpoint computes the endpoint to connect to,
|
|
// while preferring internal endpoint over the standard
|
|
// endpoint
|
|
func (n NetworkMember) PreferredEndpoint() string {
|
|
if n.InternalEndpoint != "" {
|
|
return n.InternalEndpoint
|
|
}
|
|
return n.Endpoint
|
|
}
|
|
|
|
// PeerIdentification encompasses a remote peer's
|
|
// PKI-ID and whether its in the same org as the current
|
|
// peer or not
|
|
type PeerIdentification struct {
|
|
ID common.PKIidType
|
|
SelfOrg bool
|
|
}
|
|
|
|
type identifier func() (*PeerIdentification, error)
|
|
|
|
// Discovery is the interface that represents a discovery module
|
|
type Discovery interface {
|
|
// Lookup returns a network member, or nil if not found
|
|
Lookup(PKIID common.PKIidType) *NetworkMember
|
|
|
|
// Self returns this instance's membership information
|
|
Self() NetworkMember
|
|
|
|
// UpdateMetadata updates this instance's metadata
|
|
UpdateMetadata([]byte)
|
|
|
|
// UpdateEndpoint updates this instance's endpoint
|
|
UpdateEndpoint(string)
|
|
|
|
// Stops this instance
|
|
Stop()
|
|
|
|
// GetMembership returns the alive members in the view
|
|
GetMembership() []NetworkMember
|
|
|
|
// InitiateSync makes the instance ask a given number of peers
|
|
// for their membership information
|
|
InitiateSync(peerNum int)
|
|
|
|
// Connect makes this instance to connect to a remote instance
|
|
// The identifier param is a function that can be used to identify
|
|
// the peer, and to assert its PKI-ID, whether its in the peer's org or not,
|
|
// and whether the action was successful or not
|
|
Connect(member NetworkMember, id identifier)
|
|
}
|
|
|
|
// Members represents an aggregation of NetworkMembers
|
|
type Members []NetworkMember
|
|
|
|
// ByID returns a mapping from the PKI-IDs (in string form)
|
|
// to NetworkMember
|
|
func (members Members) ByID() map[string]NetworkMember {
|
|
res := make(map[string]NetworkMember, len(members))
|
|
for _, peer := range members {
|
|
res[string(peer.PKIid)] = peer
|
|
}
|
|
return res
|
|
}
|
|
|
|
// Intersect returns the intersection of 2 Members
|
|
func (members Members) Intersect(otherMembers Members) Members {
|
|
var res Members
|
|
m := otherMembers.ByID()
|
|
for _, member := range members {
|
|
if _, exists := m[string(member.PKIid)]; exists {
|
|
res = append(res, member)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
// Filter returns only members that satisfy the given filter
|
|
func (members Members) Filter(filter func(member NetworkMember) bool) Members {
|
|
var res Members
|
|
for _, member := range members {
|
|
if filter(member) {
|
|
res = append(res, member)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
// Map invokes the given function to every NetworkMember among the Members
|
|
func (members Members) Map(f func(member NetworkMember) NetworkMember) Members {
|
|
var res Members
|
|
for _, m := range members {
|
|
res = append(res, f(m))
|
|
}
|
|
return res
|
|
}
|
|
|
|
// HaveExternalEndpoints selects network members that have external endpoints
|
|
func HasExternalEndpoint(member NetworkMember) bool {
|
|
return member.Endpoint != ""
|
|
}
|