Documentation ¶
Index ¶
- Constants
- Variables
- func BlockFreeChannelSend(channel chan interface{}, message interface{}) int
- func LimitListenerAll(l net.Listener) net.Listener
- func LimitListenerSources(l net.Listener) net.Listener
- func RegisterPrometheus()
- type CommandAddPeer
- type CommandAdjustPeerQuality
- type CommandBan
- type CommandDialPeer
- type CommandDisconnect
- type CommandShutdown
- type Connection
- func (c *Connection) ConnectionState() string
- func (c *Connection) CopyMetricsFrom(another *Connection)
- func (c *Connection) Init(peer Peer, persistent bool) *Connection
- func (c *Connection) InitWithConn(conn net.Conn, peer Peer) *Connection
- func (c *Connection) IsOnline() bool
- func (c *Connection) IsOutGoing() bool
- func (c *Connection) IsPersistent() bool
- func (c *Connection) Notes() string
- func (c *Connection) Start()
- func (c *Connection) StatusString() string
- type ConnectionCommand
- type ConnectionManager
- func (cm *ConnectionManager) Add(connection *Connection)
- func (cm *ConnectionManager) All() map[string]*Connection
- func (cm *ConnectionManager) ConnectedTo(address string) bool
- func (cm *ConnectionManager) Count() int
- func (cm *ConnectionManager) GetAllRegular(msgHash [32]byte) []*Connection
- func (cm *ConnectionManager) GetByHash(peerHash string) (*Connection, bool)
- func (cm *ConnectionManager) GetRandom() *Connection
- func (cm *ConnectionManager) GetRandomRegular(sampleSize int, msgHash [32]byte) []*Connection
- func (cm *ConnectionManager) Init() *ConnectionManager
- func (cm *ConnectionManager) Remove(connection *Connection)
- func (cm *ConnectionManager) SendToAll(message interface{})
- func (cm *ConnectionManager) UpdatePrometheusMetrics()
- type ConnectionMetrics
- type ConnectionParcel
- type Controller
- func (c *Controller) AddPeer(conn net.Conn)
- func (c *Controller) AdjustPeerQuality(peerHash string, adjustment int32)
- func (c *Controller) Ban(peerHash string)
- func (c *Controller) DialPeer(peer Peer, persistent bool)
- func (c *Controller) Disconnect(peerHash string)
- func (c *Controller) GetKnownPeers() map[string]Peer
- func (c *Controller) GetNumberOfConnections() int
- func (c *Controller) Init(ci ControllerInit) *Controller
- func (c *Controller) NetworkStop()
- func (c *Controller) ReloadSpecialPeers(newPeersConfig string)
- func (c *Controller) StartNetwork()
- type ControllerInit
- type Discovery
- func (d *Discovery) DiscoverPeersFromSeed()
- func (d *Discovery) GetOutgoingPeers() []Peer
- func (d *Discovery) Init(peersFile string, seed string) *Discovery
- func (d *Discovery) LearnPeers(parcel Parcel)
- func (d *Discovery) LoadPeers()
- func (d *Discovery) SavePeers()
- func (d *Discovery) SharePeers() []byte
- type Last100
- type NetworkID
- type Parcel
- type ParcelCommandType
- type ParcelHeader
- type PartialMessage
- type PartsAssembler
- type Peer
- func (p *Peer) AddressPort() string
- func (p *Peer) Init(address string, port string, quality int32, peerType uint8, connections int) *Peer
- func (p *Peer) IsSamePeerAs(netAddress net.Addr) bool
- func (p *Peer) IsSpecial() bool
- func (p *Peer) LastSource() (result string)
- func (p *Peer) LocationFromAddress() (location uint32)
- func (p *Peer) PeerFixedIdent() string
- func (p *Peer) PeerIdent() string
- func (p *Peer) PeerLogFields() log.Fields
- func (p *Peer) PeerTypeString() string
- type PeerDistanceSort
- type PeerQualitySort
Constants ¶
const ( ConnectionInitialized uint8 = iota //Structure created, have peer info. Dial command moves us to Online or Shutdown (depending) ConnectionOnline // We're connected to the other side. Normal state ConnectionOffline // We've been disconnected for whatever reason. Attempt to reconnect some number of times. Moves to Online if successful, Shutdown if not. ConnectionShuttingDown // We're shutting down, the receives loop exits. ConnectionClosed // We're shut down, the runloop sets this state right before exiting. Controller can clean us up. )
Each connection is a simple state machine. The state is managed by a single goroutine which also does networking. The flow is this: Connection gets initialized, and either has a peer or a net connection (From an accept()) If no network connection, the Connection dials. If the dial is successful, it moves to the Online state If not, it moves to the Shutdown state-- we only dial out once when initialized with a peer. If we are online and get a network error, we shift to offline mode. In offline state we attempt to reconnect for a period defined in protocol.go. IF successful, we go back Online. If too many attempts are made, we go to The ConnectionShutdown state, and exit the runloop. In the Shutdown state we notify the controller so that we can be cleaned up.
const ( ConnectionIsClosed uint8 = iota // Notifies the controller that we are shut down and can be released ConnectionShutdownNow ConnectionUpdatingPeer ConnectionAdjustPeerQuality ConnectionUpdateMetrics ConnectionGoOffline // Notifies the connection it should go offinline (eg from another goroutine) )
These are the commands that connections can send/receive
const ( ParcelValid uint8 = iota InvalidPeerDemerit // The peer sent an invalid message InvalidDisconnectPeer // Eg they are on the wrong network or wrong version of the software )
These constants support the multiple penalties and responses for Parcel validation
const ( RegularPeer uint8 = iota SpecialPeerConfig // special peer defined in the config file SpecialPeerCmdLine // special peer defined via the cmd line params )
const ( // ProtocolVersion is the latest version this package supports ProtocolVersion uint16 = 9 // ProtocolVersionMinimum is the earliest version this package supports ProtocolVersionMinimum uint16 = 9 )
const MaxPayloadSize = 1000000000
MaxPayloadSize is the maximum bytes a message can be at the networking level.
const MaxTimeWaitingForReassembly time.Duration = time.Second * 60 * 10
maximum time we wait for a partial message to arrive, old entries are cleaned up only when new part arrives
const ParcelHeaderSize = 32
ParcelHeaderSize is the number of bytes in a parcel header
Variables ¶
var ( SentToPeers = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "factomd_state_number_of_peers_broadcast", Help: "Number of Peers to which we are broadcasting messages", }) StartingPoint = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "factomd_StartingPoint_peers_broadcast", Help: "Number of msgs broadcasting", }) )
var ( CurrentNetwork = TestNet NetworkListenPort = "8108" BroadcastFlag = "<BROADCAST>" FullBroadcastFlag = "<FULLBORADCAST>" RandomPeerFlag = "<RANDOMPEER>" NodeID uint64 = 0 // Random number used for loopback protection MinumumQualityScore int32 = -200 // if a peer's score is less than this we ignore them. BannedQualityScore int32 = -2147000000 // Used to ban a peer MinumumSharingQualityScore int32 = 20 // if a peer's score is less than this we don't share them. OnlySpecialPeers = false // dial out to special peers only AllowUnknownIncomingPeers = true // allow incoming connections from peers that are not in the special peer list NetworkDeadline = time.Duration(30) * time.Second NumberPeersToConnect = 32 // default value; changeable in cfg and cmd line NumberPeersToBroadcast = 16 // This gets overwritten by command line flag! MaxNumberIncomingConnections = 200 // default value; changeable in cfg and cmd line MaxNumberOfRedialAttempts = 5 // How many missing pings (and other) before we give up and close. StandardChannelSize = 5000 NetworkStatusInterval = time.Second * 9 ConnectionStatusInterval = time.Second * 122 PingInterval = time.Second * 15 TimeBetweenRedials = time.Second * 20 PeerSaveInterval = time.Second * 30 PeerRequestInterval = time.Second * 180 PeerDiscoveryInterval = time.Hour * 4 // Testing metrics TotalMessagesReceived uint64 TotalMessagesSent uint64 ApplicationMessagesReceived uint64 CRCKoopmanTable = crc32.MakeTable(crc32.Koopman) RandomGenerator *rand.Rand // seeded pseudo-random number generator )
Global variables for the p2p protocol
var CommandStrings = map[ParcelCommandType]string{ TypeHeartbeat: "Heartbeat", TypePing: "Ping", TypePong: "Pong", TypePeerRequest: "Peer-Request", TypePeerResponse: "Peer-Response", TypeAlert: "Alert", TypeMessage: "Message", TypeMessagePart: "MessagePart", }
CommandStrings is a Map of command ids to strings for easy printing of network comands
var UpdateKnownPeers sync.Mutex
Functions ¶
func BlockFreeChannelSend ¶
func BlockFreeChannelSend(channel chan interface{}, message interface{}) int
BlockFreeChannelSend will remove things from the queue to make room for new messages if the queue is full. This prevents channel blocking on full.
Returns: The number of elements cleared from the channel to make room
func RegisterPrometheus ¶
func RegisterPrometheus()
RegisterPrometheus registers the variables to be exposed. This can only be run once, hence the boolean flag to prevent panics if launched more than once. This is called in NetStart
Types ¶
type CommandAddPeer ¶
type CommandAddPeer struct {
// contains filtered or unexported fields
}
CommandAddPeer is used to instruct the Controller to add a connection This connection can come from acceptLoop or some other way.
type CommandAdjustPeerQuality ¶
CommandAdjustPeerQuality is used to instruct the Controller to reduce a connections quality score
func (*CommandAdjustPeerQuality) JSONByte ¶
func (e *CommandAdjustPeerQuality) JSONByte() ([]byte, error)
func (*CommandAdjustPeerQuality) JSONString ¶
func (e *CommandAdjustPeerQuality) JSONString() (string, error)
func (*CommandAdjustPeerQuality) String ¶
func (e *CommandAdjustPeerQuality) String() string
type CommandBan ¶
type CommandBan struct {
PeerHash string
}
CommandBan is used to instruct the Controller to disconnect and ban a peer
func (*CommandBan) JSONByte ¶
func (e *CommandBan) JSONByte() ([]byte, error)
func (*CommandBan) JSONString ¶
func (e *CommandBan) JSONString() (string, error)
func (*CommandBan) String ¶
func (e *CommandBan) String() string
type CommandDialPeer ¶
type CommandDialPeer struct {
// contains filtered or unexported fields
}
CommandDialPeer is used to instruct the Controller to dial a peer address
type CommandDisconnect ¶
type CommandDisconnect struct {
PeerHash string
}
CommandDisconnect is used to instruct the Controller to disconnect from a peer
func (*CommandDisconnect) JSONByte ¶
func (e *CommandDisconnect) JSONByte() ([]byte, error)
func (*CommandDisconnect) JSONString ¶
func (e *CommandDisconnect) JSONString() (string, error)
func (*CommandDisconnect) String ¶
func (e *CommandDisconnect) String() string
type CommandShutdown ¶
type CommandShutdown struct {
// contains filtered or unexported fields
}
CommandShutdown is used to instruct the Controller to takve various actions.
type Connection ¶
type Connection struct { Errors chan error // handle errors from connections. Commands chan *ConnectionCommand // handle connection commands SendChannel chan interface{} // Send means "towards the network" Channel sends Parcels and ConnectionCommands ReceiveChannel chan interface{} // Receive means "from the network" Channel receives Parcels and ConnectionCommands ReceiveParcel chan *Parcel // Parcels to be handled. TimeLastpacket time.Time // Time we last successfully received a packet or command. // contains filtered or unexported fields }
Connection represents a single connection to another peer over the network. It communicates with the application via two channels, send and receive. These channels take structs of type ConnectionCommand or ConnectionParcel (defined below).
func (*Connection) ConnectionState ¶
func (c *Connection) ConnectionState() string
func (*Connection) CopyMetricsFrom ¶
func (c *Connection) CopyMetricsFrom(another *Connection)
Copies metrics from another connection to this one.
func (*Connection) Init ¶
func (c *Connection) Init(peer Peer, persistent bool) *Connection
Init is called when we have peer info and need to dial into the peer
func (*Connection) InitWithConn ¶
func (c *Connection) InitWithConn(conn net.Conn, peer Peer) *Connection
InitWithConn is called from our accept loop when a peer dials into us and we already have a network conn
func (*Connection) IsOnline ¶
func (c *Connection) IsOnline() bool
func (*Connection) IsOutGoing ¶
func (c *Connection) IsOutGoing() bool
func (*Connection) IsPersistent ¶
func (c *Connection) IsPersistent() bool
func (*Connection) Notes ¶
func (c *Connection) Notes() string
func (*Connection) Start ¶
func (c *Connection) Start()
func (*Connection) StatusString ¶
func (c *Connection) StatusString() string
type ConnectionCommand ¶
type ConnectionCommand struct { Command uint8 Peer Peer Delta int32 Metrics ConnectionMetrics }
ConnectionCommand is used to instruct the Connection to carry out some functionality.
func (*ConnectionCommand) JSONByte ¶
func (e *ConnectionCommand) JSONByte() ([]byte, error)
func (*ConnectionCommand) JSONString ¶
func (e *ConnectionCommand) JSONString() (string, error)
func (*ConnectionCommand) String ¶
func (e *ConnectionCommand) String() string
type ConnectionManager ¶
type ConnectionManager struct {
// contains filtered or unexported fields
}
func (*ConnectionManager) Add ¶
func (cm *ConnectionManager) Add(connection *Connection)
Add a new connection.
func (*ConnectionManager) All ¶
func (cm *ConnectionManager) All() map[string]*Connection
Get the map of all connections by the peer hash.
func (*ConnectionManager) ConnectedTo ¶
func (cm *ConnectionManager) ConnectedTo(address string) bool
Checks if we are already connected to a specified address.
func (*ConnectionManager) Count ¶
func (cm *ConnectionManager) Count() int
Get the number of all connections
func (*ConnectionManager) GetAllRegular ¶
func (cm *ConnectionManager) GetAllRegular(msgHash [32]byte) []*Connection
Get connections for all online who have not sent me a copy of the message I'm about to send, active regular peers, but in random order.
func (*ConnectionManager) GetByHash ¶
func (cm *ConnectionManager) GetByHash(peerHash string) (*Connection, bool)
Get the connection for a specified peer hash.
func (*ConnectionManager) GetRandom ¶
func (cm *ConnectionManager) GetRandom() *Connection
Get a single random connection from all the online and active connection we have, returns nil if none are found.
func (*ConnectionManager) GetRandomRegular ¶
func (cm *ConnectionManager) GetRandomRegular(sampleSize int, msgHash [32]byte) []*Connection
Get a set of random connections from all the online who have not sent me a copy of the message I'm about to send.
func (*ConnectionManager) Init ¶
func (cm *ConnectionManager) Init() *ConnectionManager
func (*ConnectionManager) Remove ¶
func (cm *ConnectionManager) Remove(connection *Connection)
Remove an existing connection.
func (*ConnectionManager) SendToAll ¶
func (cm *ConnectionManager) SendToAll(message interface{})
Send a message to all the connections.
func (*ConnectionManager) UpdatePrometheusMetrics ¶
func (cm *ConnectionManager) UpdatePrometheusMetrics()
Update connection counts in Prometheus.
type ConnectionMetrics ¶
type ConnectionMetrics struct { MomentConnected time.Time // when the connection started. BytesSent uint32 // Keeping track of the data sent/received for console BytesReceived uint32 // Keeping track of the data sent/received for console MessagesSent uint32 // Keeping track of the data sent/received for console MessagesReceived uint32 // Keeping track of the data sent/received for console PeerAddress string // Peer IP Address PeerQuality int32 // Quality of the connection. PeerType string // Type of the peer (regular, special_config, ...) // Red: Below -50 // Yellow: -50 - 100 // Green: > 100 ConnectionState string // Basic state of the connection ConnectionNotes string // Connectivity notes for the connection }
ConnectionMetrics is used to encapsulate various metrics about the connection.
type ConnectionParcel ¶
type ConnectionParcel struct {
Parcel Parcel
}
ConnectionParcel is sent to convey an application message destined for the network.
func (*ConnectionParcel) JSONByte ¶
func (e *ConnectionParcel) JSONByte() ([]byte, error)
func (*ConnectionParcel) JSONString ¶
func (e *ConnectionParcel) JSONString() (string, error)
func (*ConnectionParcel) String ¶
func (e *ConnectionParcel) String() string
type Controller ¶
type Controller struct { ToNetwork chan interface{} // Parcels from the application for us to route FromNetwork chan interface{} // Parcels from the network for the application NodeID uint64 // contains filtered or unexported fields }
Controller manages the peer to peer network.
func (*Controller) AddPeer ¶
func (c *Controller) AddPeer(conn net.Conn)
func (*Controller) AdjustPeerQuality ¶
func (c *Controller) AdjustPeerQuality(peerHash string, adjustment int32)
func (*Controller) Ban ¶
func (c *Controller) Ban(peerHash string)
func (*Controller) DialPeer ¶
func (c *Controller) DialPeer(peer Peer, persistent bool)
func (*Controller) Disconnect ¶
func (c *Controller) Disconnect(peerHash string)
func (*Controller) GetKnownPeers ¶
func (c *Controller) GetKnownPeers() map[string]Peer
func (*Controller) GetNumberOfConnections ¶
func (c *Controller) GetNumberOfConnections() int
func (*Controller) Init ¶
func (c *Controller) Init(ci ControllerInit) *Controller
func (*Controller) NetworkStop ¶
func (c *Controller) NetworkStop()
func (*Controller) ReloadSpecialPeers ¶
func (c *Controller) ReloadSpecialPeers(newPeersConfig string)
func (*Controller) StartNetwork ¶
func (c *Controller) StartNetwork()
StartNetwork configures the network, starts the runloop
type ControllerInit ¶
type ControllerInit struct { NodeName string // Name of the current node Port string // Port to listen on PeersFile string // Path to file to find / save peers Network NetworkID // Network - eg MainNet, TestNet etc. Exclusive bool // flag to indicate we should only connect to trusted peers ExclusiveIn bool // flag to indicate we should only connect to trusted peers and disallow incoming connections SeedURL string // URL to a source of peer info ConfigPeers string // Peers to always connect to at startup, and stay persistent, passed from the config file CmdLinePeers string // Additional special peers passed from the command line ConnectionMetricsChannel chan interface{} // Channel on which we put the connection metrics map, periodically. LogPath string // Path for logs LogLevel string // Logging level }
type Discovery ¶
type Discovery struct {
// contains filtered or unexported fields
}
func (*Discovery) DiscoverPeersFromSeed ¶
func (d *Discovery) DiscoverPeersFromSeed()
DiscoverPeers gets a set of peers from a DNS Seed
func (*Discovery) GetOutgoingPeers ¶
GetOutgoingPeers gets a set of peers to connect to on startup For now, this gives a set of 12 of the total known peers. We want peers from diverse networks. So,method is this:
-- generate list of candidates (if exclusive, only special peers) -- sort candidates by distance -- if num canddiates is less than desired set, return all candidates -- Otherwise,repeatedly take candidates at the 0%, %25, %50, %75, %100 points in the list -- remove each candidate from the list. -- continue until there are no candidates left, or we have our set.
func (*Discovery) LearnPeers ¶
LearnPeers receives a set of peers from other hosts The unique peers are added to our peer list. The peers are in a json encoded string as a byte slice
func (*Discovery) LoadPeers ¶
func (d *Discovery) LoadPeers()
LoadPeers loads the known peers from disk OVERWRITING PREVIOUS VALUES
func (*Discovery) SavePeers ¶
func (d *Discovery) SavePeers()
SavePeers just saves our known peers out to disk. Called periodically.
func (*Discovery) SharePeers ¶
SharePeers gets a set of peers to send to other hosts For now, this gives a random set of the total known peers. The peers are in a json encoded string as byte slice
type Last100 ¶
type Last100 struct { Msgs map[[32]byte]bool // Look up messages by hash MsgOrder [100][32]byte // keep a list of the order they were added N int }
Keep a short history of messages
type NetworkID ¶
type NetworkID uint32
NetworkIdentifier represents the P2P network we are participating in (eg: test, nmain, etc.)
type Parcel ¶
type Parcel struct { Header ParcelHeader Payload []byte // contains filtered or unexported fields }
Parcel is the atomic level of communication for the p2p network. It contains within it the necessary info for the networking protocol, plus the message that the Application is sending.
func NewParcelMsg ¶
func NewParcelMsg(network NetworkID, payload []byte, msg interfaces.IMsg) *Parcel
func ParcelsForPayload ¶
func ParcelsForPayload(network NetworkID, payload []byte, msg interfaces.IMsg) []Parcel
func ReassembleParcel ¶
func (*Parcel) Init ¶
func (p *Parcel) Init(header ParcelHeader) *Parcel
func (*Parcel) MessageType ¶
func (*Parcel) UpdateHeader ¶
func (p *Parcel) UpdateHeader()
type ParcelCommandType ¶
type ParcelCommandType uint16
const ( TypeHeartbeat ParcelCommandType = iota // "Note, I'm still alive" TypePing // "Are you there?" TypePong // "yes, I'm here" TypePeerRequest // "Please share some peers" TypePeerResponse // "Here's some peers I know about." TypeAlert // network wide alerts (used in bitcoin to indicate criticalities) TypeMessage // Application level message TypeMessagePart // Application level message that was split into multiple parts )
Parcel commands -- all new commands should be added to the *end* of the list!
type ParcelHeader ¶
type ParcelHeader struct { Network NetworkID // 4 bytes - the network we are on (eg testnet, main net, etc.) Version uint16 // 2 bytes - the version of the protocol we are running. Type ParcelCommandType // 2 bytes - network level commands (eg: ping/pong) Length uint32 // 4 bytes - length of the payload (that follows this header) in bytes TargetPeer string // ? bytes - "" or nil for broadcast, otherwise the destination peer's hash. Crc32 uint32 // 4 bytes - data integrity hash (of the payload itself.) PartNo uint16 // 2 bytes - in case of multipart parcels, indicates which part this corresponds to, otherwise should be 0 PartsTotal uint16 // 2 bytes - in case of multipart parcels, indicates the total number of parts that the receiver should expect NodeID uint64 PeerAddress string // address of the peer set by connection to know who sent message (for tracking source of other peers) PeerPort string // port of the peer , or we are listening on AppHash string // Application specific message hash, for tracing AppType string // Application specific message type, for tracing }
func (*ParcelHeader) Init ¶
func (p *ParcelHeader) Init(network NetworkID) *ParcelHeader
type PartialMessage ¶
type PartialMessage struct {
// contains filtered or unexported fields
}
type PartsAssembler ¶
type PartsAssembler struct {
// contains filtered or unexported fields
}
PartsAssembler is responsible for assembling message parts into full messages
func (*PartsAssembler) Init ¶
func (assembler *PartsAssembler) Init() *PartsAssembler
Initializes the assembler
type Peer ¶
type Peer struct { QualityScore int32 // 0 is neutral quality, negative is a bad peer. Address string // Must be in form of x.x.x.x Port string // Must be in form of xxxx NodeID uint64 // a nonce to distinguish multiple nodes behind one IP address Hash string // This is more of a connection ID than hash right now. Location uint32 // IP address as an int. Network NetworkID // The network this peer reference lives on. Type uint8 Connections int // Number of successful connections. LastContact time.Time // Keep track of how long ago we talked to the peer. Source map[string]time.Time // source where we heard from the peer. PrevMsgs Last100 `json:"-"` // contains filtered or unexported fields }
func (*Peer) AddressPort ¶
func (*Peer) LastSource ¶
gets the last source where this peer was seen
func (*Peer) LocationFromAddress ¶
TODO Hadn't considered IPV6 address support. TODO Need to audit all the net code to check IPv6 addresses Here's an IPv6 conversion: Ref: http://stackoverflow.com/questions/23297141/golang-net-ip-to-ipv6-from-mysql-as-decimal39-0-conversion
func ipv6ToInt(IPv6Addr net.IP) *big.Int { IPv6Int := big.NewInt(0) IPv6Int.SetBytes(IPv6Addr) return IPv6Int }
Problem is we're working with string addresses, may never have made a connection. TODO - we might have a DNS address, not iP address and need to resolve it! locationFromAddress converts the peers address into a uint32 "location" numeric
func (*Peer) PeerFixedIdent ¶
func (*Peer) PeerLogFields ¶
func (*Peer) PeerTypeString ¶
type PeerDistanceSort ¶
type PeerDistanceSort []Peer
sort.Sort interface implementation
func (PeerDistanceSort) Len ¶
func (p PeerDistanceSort) Len() int
func (PeerDistanceSort) Less ¶
func (p PeerDistanceSort) Less(i, j int) bool
func (PeerDistanceSort) Swap ¶
func (p PeerDistanceSort) Swap(i, j int)
type PeerQualitySort ¶
type PeerQualitySort []Peer
sort.Sort interface implementation
func (PeerQualitySort) Len ¶
func (p PeerQualitySort) Len() int
func (PeerQualitySort) Less ¶
func (p PeerQualitySort) Less(i, j int) bool
func (PeerQualitySort) Swap ¶
func (p PeerQualitySort) Swap(i, j int)