|
package p2p
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
|
|
"github.com/tendermint/tendermint/crypto"
|
|
"github.com/tendermint/tendermint/p2p/conn"
|
|
)
|
|
|
|
// Transport is an arbitrary mechanism for exchanging bytes with a peer.
|
|
type Transport interface {
|
|
// Accept waits for the next inbound connection on a listening endpoint.
|
|
Accept(context.Context) (Connection, error)
|
|
|
|
// Dial creates an outbound connection to an endpoint.
|
|
Dial(context.Context, Endpoint) (Connection, error)
|
|
|
|
// Endpoints lists endpoints the transport is listening on. Any endpoint IP
|
|
// addresses do not need to be normalized in any way (e.g. 0.0.0.0 is
|
|
// valid), as they should be preprocessed before being advertised.
|
|
Endpoints() []Endpoint
|
|
|
|
// Close stops accepting new connections, but does not close active connections.
|
|
Close() error
|
|
|
|
// SetChannelDescriptors sets the channel descriptors for the transport.
|
|
// FIXME: This is only here for compatibility with the current Switch code.
|
|
SetChannelDescriptors(chDescs []*conn.ChannelDescriptor)
|
|
}
|
|
|
|
// Protocol identifies a transport protocol.
|
|
type Protocol string
|
|
|
|
// Endpoint represents a transport connection endpoint, either local or remote.
|
|
type Endpoint struct {
|
|
// PeerID specifies the peer ID of the endpoint.
|
|
//
|
|
// FIXME: This is here for backwards-compatibility with the existing MConn
|
|
// protocol, we should consider moving this higher in the stack (i.e. to
|
|
// the router).
|
|
PeerID ID
|
|
|
|
// Protocol specifies the transport protocol, used by the router to pick a
|
|
// transport for an endpoint.
|
|
Protocol Protocol
|
|
|
|
// Path is an optional, arbitrary transport-specific path or identifier.
|
|
Path string
|
|
|
|
// IP is an IP address (v4 or v6) to connect to. If set, this defines the
|
|
// endpoint as a networked endpoint.
|
|
IP net.IP
|
|
|
|
// Port is a network port (either TCP or UDP). If not set, a default port
|
|
// may be used depending on the protocol.
|
|
Port uint16
|
|
}
|
|
|
|
// String formats an endpoint as a URL string.
|
|
func (e Endpoint) String() string {
|
|
u := url.URL{Scheme: string(e.Protocol)}
|
|
if e.PeerID != "" {
|
|
u.User = url.User(string(e.PeerID))
|
|
}
|
|
if len(e.IP) > 0 {
|
|
u.Host = e.IP.String()
|
|
if e.Port > 0 {
|
|
u.Host += fmt.Sprintf(":%v", e.Port)
|
|
}
|
|
} else if e.Path != "" {
|
|
u.Opaque = e.Path
|
|
}
|
|
return u.String()
|
|
}
|
|
|
|
// Validate validates an endpoint.
|
|
func (e Endpoint) Validate() error {
|
|
switch {
|
|
case e.PeerID == "":
|
|
return errors.New("endpoint has no peer ID")
|
|
case e.Protocol == "":
|
|
return errors.New("endpoint has no protocol")
|
|
case len(e.IP) == 0 && len(e.Path) == 0:
|
|
return errors.New("endpoint must have either IP or path")
|
|
case e.Port > 0 && len(e.IP) == 0:
|
|
return fmt.Errorf("endpoint has port %v but no IP", e.Port)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// NetAddress returns a NetAddress for the endpoint.
|
|
// FIXME: This is temporary for compatibility with the old P2P stack.
|
|
func (e Endpoint) NetAddress() *NetAddress {
|
|
return &NetAddress{
|
|
ID: e.PeerID,
|
|
IP: e.IP,
|
|
Port: e.Port,
|
|
}
|
|
}
|
|
|
|
// Connection represents an established connection between two endpoints.
|
|
//
|
|
// FIXME: This is a temporary interface while we figure out whether we'll be
|
|
// adopting QUIC or not. If we do, this should be a byte-oriented multi-stream
|
|
// interface with one goroutine consuming each stream, and the MConnection
|
|
// transport either needs protocol changes or a shim. For details, see:
|
|
// https://github.com/tendermint/spec/pull/227
|
|
//
|
|
// FIXME: The interface is currently very broad in order to accommodate
|
|
// MConnection behavior that the rest of the P2P stack relies on. This should be
|
|
// removed once the P2P core is rewritten.
|
|
type Connection interface {
|
|
// ReceiveMessage returns the next message received on the connection,
|
|
// blocking until one is available. io.EOF is returned when closed.
|
|
ReceiveMessage() (chID byte, msg []byte, err error)
|
|
|
|
// SendMessage sends a message on the connection.
|
|
// FIXME: For compatibility with the current Peer, it returns an additional
|
|
// boolean false if the message timed out waiting to be accepted into the
|
|
// send buffer.
|
|
SendMessage(chID byte, msg []byte) (bool, error)
|
|
|
|
// TrySendMessage is a non-blocking version of SendMessage that returns
|
|
// immediately if the message buffer is full. It returns true if the message
|
|
// was accepted.
|
|
//
|
|
// FIXME: This is here for backwards-compatibility with the current Peer
|
|
// code, and should be removed when possible.
|
|
TrySendMessage(chID byte, msg []byte) (bool, error)
|
|
|
|
// LocalEndpoint returns the local endpoint for the connection.
|
|
LocalEndpoint() Endpoint
|
|
|
|
// RemoteEndpoint returns the remote endpoint for the connection.
|
|
RemoteEndpoint() Endpoint
|
|
|
|
// PubKey returns the remote peer's public key.
|
|
PubKey() crypto.PubKey
|
|
|
|
// NodeInfo returns the remote peer's node info.
|
|
NodeInfo() NodeInfo
|
|
|
|
// Close closes the connection.
|
|
Close() error
|
|
|
|
// FlushClose flushes all pending sends and then closes the connection.
|
|
//
|
|
// FIXME: This only exists for backwards-compatibility with the current
|
|
// MConnection implementation. There should really be a separate Flush()
|
|
// method, but there is no easy way to synchronously flush pending data with
|
|
// the current MConnection structure.
|
|
FlushClose() error
|
|
|
|
// Status returns the current connection status.
|
|
// FIXME: Only here for compatibility with the current Peer code.
|
|
Status() conn.ConnectionStatus
|
|
}
|