neoneo-go/pkg/network/server.go

291 lines
6.2 KiB
Go
Raw Normal View History

2018-01-26 18:04:13 +00:00
package network
import (
"fmt"
"net"
"os"
"text/tabwriter"
2018-01-28 10:12:05 +00:00
"time"
2018-01-27 15:00:28 +00:00
2018-02-01 18:54:23 +00:00
"github.com/CityOfZion/neo-go/pkg/util"
log "github.com/go-kit/kit/log"
2018-01-26 18:04:13 +00:00
)
const (
2018-01-26 20:39:34 +00:00
// node version
version = "2.6.0"
2018-01-26 20:39:34 +00:00
// official ports according to the protocol.
2018-01-26 18:04:13 +00:00
portMainNet = 10333
portTestNet = 20333
2018-01-31 19:11:08 +00:00
maxPeers = 50
2018-01-26 18:04:13 +00:00
)
var dialTimeout = 4 * time.Second
// Config holds the server configuration.
type Config struct {
// MaxPeers it the maximum numbers of peers that can
// be connected to the server.
MaxPeers int
// The user agent of the server.
UserAgent string
// The listen address of the TCP server.
ListenTCP uint16
// The listen address of the RPC server.
ListenRPC uint16
// The network mode this server will operate on.
// ModePrivNet docker private network.
// ModeTestNet NEO test network.
// ModeMainNet NEO main network.
Net NetMode
// Relay determins whether the server is forwarding its inventory.
Relay bool
// Seeds are a list of initial nodes used to establish connectivity.
Seeds []string
// Maximum duration a single dial may take.
DialTimeout time.Duration
2018-01-26 18:04:13 +00:00
}
// Server manages all incoming peer connections.
type Server struct {
// Config fields may not be modified while the server is running.
Config
2018-01-26 18:04:13 +00:00
// Proto is just about anything that can handle the NEO protocol.
// In production enviroments the ProtoHandler is mostly the local node.
proto ProtoHandler
2018-01-26 18:04:13 +00:00
// Unique id of this server.
id uint32
logger log.Logger
listener net.Listener
2018-01-26 18:04:13 +00:00
register chan Peer
unregister chan Peer
2018-01-26 18:04:13 +00:00
badAddrOp chan func(map[string]bool)
badAddrOpDone chan struct{}
2018-01-26 20:39:34 +00:00
peerOp chan func(map[Peer]bool)
peerOpDone chan struct{}
2018-01-26 18:04:13 +00:00
quit chan struct{}
}
2018-01-26 18:04:13 +00:00
// NewServer returns a new Server object created from the
// given config.
func NewServer(cfg Config) *Server {
if cfg.MaxPeers == 0 {
cfg.MaxPeers = maxPeers
}
if cfg.Net == 0 {
cfg.Net = ModeTestNet
2018-01-26 18:04:13 +00:00
}
if cfg.DialTimeout == 0 {
cfg.DialTimeout = dialTimeout
2018-01-26 18:04:13 +00:00
}
logger := log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "component", "server")
2018-01-31 19:11:08 +00:00
s := &Server{
Config: cfg,
logger: logger,
id: util.RandUint32(1000000, 9999999),
quit: make(chan struct{}, 1),
register: make(chan Peer),
unregister: make(chan Peer),
badAddrOp: make(chan func(map[string]bool)),
badAddrOpDone: make(chan struct{}),
peerOp: make(chan func(map[Peer]bool)),
peerOpDone: make(chan struct{}),
2018-01-26 18:04:13 +00:00
}
s.proto = newNode(s, cfg)
2018-01-26 18:04:13 +00:00
return s
2018-01-30 10:56:36 +00:00
}
func (s *Server) createListener() error {
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", s.ListenTCP))
if err != nil {
return err
}
s.listener = ln
return nil
2018-01-31 19:11:08 +00:00
}
2018-01-30 10:56:36 +00:00
func (s *Server) listenTCP() {
for {
conn, err := s.listener.Accept()
if err != nil {
s.logger.Log("msg", "conn read error", "err", err)
break
}
go s.setupConnection(conn)
2018-01-31 19:11:08 +00:00
}
s.Quit()
2018-01-27 12:39:07 +00:00
}
func (s *Server) setupConnection(conn net.Conn) {
if !s.hasCapacity() {
s.logger.Log("msg", "server reached maximum capacity")
return
}
p := NewTCPPeer(conn, s.proto.handleProto)
s.register <- p
if err := p.run(); err != nil {
s.unregister <- p
}
}
func (s *Server) connectToPeers(addrs ...string) {
for _, addr := range addrs {
if s.hasCapacity() && s.canConnectWith(addr) {
go func(addr string) {
conn, err := net.DialTimeout("tcp", addr, s.DialTimeout)
if err != nil {
s.badAddrOp <- func(badAddrs map[string]bool) {
badAddrs[addr] = true
}
<-s.badAddrOpDone
return
}
go s.setupConnection(conn)
}(addr)
}
2018-01-31 19:11:08 +00:00
}
}
func (s *Server) canConnectWith(addr string) bool {
canConnect := true
s.peerOp <- func(peers map[Peer]bool) {
for peer := range peers {
if peer.Endpoint().String() == addr {
canConnect = false
break
}
}
}
<-s.peerOpDone
if !canConnect {
return false
}
s.badAddrOp <- func(badAddrs map[string]bool) {
_, ok := badAddrs[addr]
canConnect = !ok
}
<-s.badAddrOpDone
return canConnect
}
func (s *Server) hasCapacity() bool {
return s.PeerCount() != s.MaxPeers
}
func (s *Server) sendVersion(peer Peer) {
peer.Send(NewMessage(s.Net, CMDVersion, s.proto.version()))
}
func (s *Server) run() {
var (
ticker = time.NewTicker(30 * time.Second).C
peers = make(map[Peer]bool)
badAddrs = make(map[string]bool)
)
for {
select {
case op := <-s.badAddrOp:
op(badAddrs)
s.badAddrOpDone <- struct{}{}
case op := <-s.peerOp:
op(peers)
s.peerOpDone <- struct{}{}
case p := <-s.register:
peers[p] = true
// When a new peer connection is established, we send
// out our version immediately.
s.sendVersion(p)
s.logger.Log("event", "peer connected", "endpoint", p.Endpoint())
case p := <-s.unregister:
delete(peers, p)
s.logger.Log("event", "peer disconnected", "endpoint", p.Endpoint())
case <-ticker:
s.printState()
case <-s.quit:
return
2018-01-28 13:59:32 +00:00
}
}
}
// PeerCount returns the number of current connected peers.
func (s *Server) PeerCount() (n int) {
s.peerOp <- func(peers map[Peer]bool) {
n = len(peers)
2018-01-28 10:12:05 +00:00
}
<-s.peerOpDone
return
2018-01-28 10:12:05 +00:00
}
func (s *Server) Start() error {
fmt.Println(logo())
fmt.Println("")
s.printConfiguration()
if err := s.createListener(); err != nil {
return err
}
go s.run()
go s.listenTCP()
go s.connectToPeers(s.Seeds...)
select {}
}
func (s *Server) Quit() {
s.quit <- struct{}{}
}
func (s *Server) printState() {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
fmt.Fprintf(w, "connected peers:\t%d/%d\n", s.PeerCount(), s.MaxPeers)
w.Flush()
}
func (s *Server) printConfiguration() {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
fmt.Fprintf(w, "user agent:\t%s\n", s.UserAgent)
fmt.Fprintf(w, "id:\t%d\n", s.id)
fmt.Fprintf(w, "network:\t%s\n", s.Net)
fmt.Fprintf(w, "listen TCP:\t%d\n", s.ListenTCP)
fmt.Fprintf(w, "listen RPC:\t%d\n", s.ListenRPC)
fmt.Fprintf(w, "relay:\t%v\n", s.Relay)
fmt.Fprintf(w, "max peers:\t%d\n", s.MaxPeers)
chainer := s.proto.(Noder)
fmt.Fprintf(w, "current height:\t%d\n", chainer.blockchain().HeaderHeight())
fmt.Fprintln(w, "")
w.Flush()
}
2018-01-26 18:04:13 +00:00
func logo() string {
return `
_ ____________ __________
2018-01-26 18:04:13 +00:00
/ | / / ____/ __ \ / ____/ __ \
/ |/ / __/ / / / /_____/ / __/ / / /
/ /| / /___/ /_/ /_____/ /_/ / /_/ /
/_/ |_/_____/\____/ \____/\____/
2018-01-26 18:04:13 +00:00
`
}