2017-02-14 00:00:02 +01:00
|
|
|
// Copyright (c) 2013-2016 The btcsuite developers
|
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package spvchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/addrmgr"
|
|
|
|
"github.com/btcsuite/btcd/connmgr"
|
2017-04-19 01:51:56 +02:00
|
|
|
"github.com/btcsuite/btcutil/gcs"
|
2017-02-14 00:00:02 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type getConnCountMsg struct {
|
|
|
|
reply chan int32
|
|
|
|
}
|
|
|
|
|
|
|
|
type getPeersMsg struct {
|
|
|
|
reply chan []*serverPeer
|
|
|
|
}
|
|
|
|
|
|
|
|
type getOutboundGroup struct {
|
|
|
|
key string
|
|
|
|
reply chan int
|
|
|
|
}
|
|
|
|
|
|
|
|
type getAddedNodesMsg struct {
|
|
|
|
reply chan []*serverPeer
|
|
|
|
}
|
|
|
|
|
|
|
|
type disconnectNodeMsg struct {
|
|
|
|
cmp func(*serverPeer) bool
|
|
|
|
reply chan error
|
|
|
|
}
|
|
|
|
|
|
|
|
type connectNodeMsg struct {
|
|
|
|
addr string
|
|
|
|
permanent bool
|
|
|
|
reply chan error
|
|
|
|
}
|
|
|
|
|
|
|
|
type removeNodeMsg struct {
|
|
|
|
cmp func(*serverPeer) bool
|
|
|
|
reply chan error
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:24:26 +02:00
|
|
|
type forAllPeersMsg struct {
|
|
|
|
closure func(*serverPeer)
|
|
|
|
}
|
|
|
|
|
2017-04-19 01:51:56 +02:00
|
|
|
type processCFilterMsg struct {
|
|
|
|
filter *gcs.Filter
|
|
|
|
extended bool
|
|
|
|
}
|
|
|
|
|
2017-02-14 00:00:02 +01:00
|
|
|
// handleQuery is the central handler for all queries and commands from other
|
|
|
|
// goroutines related to peer state.
|
|
|
|
func (s *ChainService) handleQuery(state *peerState, querymsg interface{}) {
|
|
|
|
switch msg := querymsg.(type) {
|
|
|
|
case getConnCountMsg:
|
|
|
|
nconnected := int32(0)
|
|
|
|
state.forAllPeers(func(sp *serverPeer) {
|
|
|
|
if sp.Connected() {
|
|
|
|
nconnected++
|
|
|
|
}
|
|
|
|
})
|
|
|
|
msg.reply <- nconnected
|
|
|
|
|
|
|
|
case getPeersMsg:
|
|
|
|
peers := make([]*serverPeer, 0, state.Count())
|
|
|
|
state.forAllPeers(func(sp *serverPeer) {
|
|
|
|
if !sp.Connected() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
peers = append(peers, sp)
|
|
|
|
})
|
|
|
|
msg.reply <- peers
|
|
|
|
|
|
|
|
case connectNodeMsg:
|
|
|
|
// TODO: duplicate oneshots?
|
|
|
|
// Limit max number of total peers.
|
|
|
|
if state.Count() >= MaxPeers {
|
|
|
|
msg.reply <- errors.New("max peers reached")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, peer := range state.persistentPeers {
|
|
|
|
if peer.Addr() == msg.addr {
|
|
|
|
if msg.permanent {
|
|
|
|
msg.reply <- errors.New("peer already connected")
|
|
|
|
} else {
|
|
|
|
msg.reply <- errors.New("peer exists as a permanent peer")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
netAddr, err := addrStringToNetAddr(msg.addr)
|
|
|
|
if err != nil {
|
|
|
|
msg.reply <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: if too many, nuke a non-perm peer.
|
|
|
|
go s.connManager.Connect(&connmgr.ConnReq{
|
|
|
|
Addr: netAddr,
|
|
|
|
Permanent: msg.permanent,
|
|
|
|
})
|
|
|
|
msg.reply <- nil
|
|
|
|
case removeNodeMsg:
|
|
|
|
found := disconnectPeer(state.persistentPeers, msg.cmp, func(sp *serverPeer) {
|
|
|
|
// Keep group counts ok since we remove from
|
|
|
|
// the list now.
|
|
|
|
state.outboundGroups[addrmgr.GroupKey(sp.NA())]--
|
|
|
|
})
|
|
|
|
|
|
|
|
if found {
|
|
|
|
msg.reply <- nil
|
|
|
|
} else {
|
|
|
|
msg.reply <- errors.New("peer not found")
|
|
|
|
}
|
|
|
|
case getOutboundGroup:
|
|
|
|
count, ok := state.outboundGroups[msg.key]
|
|
|
|
if ok {
|
|
|
|
msg.reply <- count
|
|
|
|
} else {
|
|
|
|
msg.reply <- 0
|
|
|
|
}
|
|
|
|
// Request a list of the persistent (added) peers.
|
|
|
|
case getAddedNodesMsg:
|
|
|
|
// Respond with a slice of the relavent peers.
|
|
|
|
peers := make([]*serverPeer, 0, len(state.persistentPeers))
|
|
|
|
for _, sp := range state.persistentPeers {
|
|
|
|
peers = append(peers, sp)
|
|
|
|
}
|
|
|
|
msg.reply <- peers
|
|
|
|
case disconnectNodeMsg:
|
|
|
|
// Check outbound peers.
|
|
|
|
found := disconnectPeer(state.outboundPeers, msg.cmp, func(sp *serverPeer) {
|
|
|
|
// Keep group counts ok since we remove from
|
|
|
|
// the list now.
|
|
|
|
state.outboundGroups[addrmgr.GroupKey(sp.NA())]--
|
|
|
|
})
|
|
|
|
if found {
|
|
|
|
// If there are multiple outbound connections to the same
|
|
|
|
// ip:port, continue disconnecting them all until no such
|
|
|
|
// peers are found.
|
|
|
|
for found {
|
|
|
|
found = disconnectPeer(state.outboundPeers, msg.cmp, func(sp *serverPeer) {
|
|
|
|
state.outboundGroups[addrmgr.GroupKey(sp.NA())]--
|
|
|
|
})
|
|
|
|
}
|
|
|
|
msg.reply <- nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.reply <- errors.New("peer not found")
|
2017-04-11 21:24:26 +02:00
|
|
|
case forAllPeersMsg:
|
|
|
|
// Run the closure on all peers in the passed state.
|
|
|
|
state.forAllPeers(msg.closure)
|
|
|
|
// Even though this is a query, there's no reply channel as the
|
|
|
|
// forAllPeers method doesn't return anything. An error might be
|
|
|
|
// useful in the future.
|
2017-02-14 00:00:02 +01:00
|
|
|
}
|
2017-04-19 01:51:56 +02:00
|
|
|
//case processCFilterMsg:
|
|
|
|
// TODO: make this work
|
2017-02-14 00:00:02 +01:00
|
|
|
}
|