2018-01-26 18:04:13 +00:00
|
|
|
package network
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
2018-03-09 15:55:25 +00:00
|
|
|
"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"
|
2018-03-09 15:55:25 +00:00
|
|
|
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-03-09 15:55:25 +00:00
|
|
|
|
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
|
|
|
)
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
var dialTimeout = 4 * time.Second
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// Config holds the server configuration.
|
|
|
|
type Config struct {
|
|
|
|
// MaxPeers it the maximum numbers of peers that can
|
|
|
|
// be connected to the server.
|
|
|
|
MaxPeers int
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// The user agent of the server.
|
|
|
|
UserAgent string
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// The listen address of the TCP server.
|
|
|
|
ListenTCP uint16
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// The listen address of the RPC server.
|
|
|
|
ListenRPC uint16
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// The network mode this server will operate on.
|
|
|
|
// ModePrivNet docker private network.
|
|
|
|
// ModeTestNet NEO test network.
|
|
|
|
// ModeMainNet NEO main network.
|
|
|
|
Net NetMode
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// Relay determins whether the server is forwarding its inventory.
|
|
|
|
Relay bool
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// Seeds are a list of initial nodes used to establish connectivity.
|
|
|
|
Seeds []string
|
2018-02-01 20:28:45 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// Maximum duration a single dial may take.
|
|
|
|
DialTimeout time.Duration
|
2018-01-26 18:04:13 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +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
|
|
|
|
2018-03-09 15:55:25 +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
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
// Unique id of this server.
|
|
|
|
id uint32
|
2018-02-06 06:43:32 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
logger log.Logger
|
|
|
|
listener net.Listener
|
2018-01-26 18:04:13 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
register chan Peer
|
|
|
|
unregister chan Peer
|
2018-01-26 18:04:13 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
badAddrOp chan func(map[string]bool)
|
|
|
|
badAddrOpDone chan struct{}
|
2018-01-26 20:39:34 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
peerOp chan func(map[Peer]bool)
|
|
|
|
peerOpDone chan struct{}
|
2018-01-26 18:04:13 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
quit chan struct{}
|
|
|
|
}
|
2018-01-26 18:04:13 +00:00
|
|
|
|
2018-03-09 15:55:25 +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
|
2018-02-01 08:00:42 +00:00
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
if cfg.Net == 0 {
|
|
|
|
cfg.Net = ModeTestNet
|
2018-01-26 18:04:13 +00:00
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
if cfg.DialTimeout == 0 {
|
|
|
|
cfg.DialTimeout = dialTimeout
|
2018-01-26 18:04:13 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
logger := log.NewLogfmtLogger(os.Stderr)
|
|
|
|
logger = log.With(logger, "component", "server")
|
2018-01-31 19:11:08 +00:00
|
|
|
|
2018-03-09 15:55:25 +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
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
s.proto = newNode(s, cfg)
|
2018-01-26 18:04:13 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
return s
|
2018-01-30 10:56:36 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +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
|
2018-02-01 08:00:42 +00:00
|
|
|
return nil
|
2018-01-31 19:11:08 +00:00
|
|
|
}
|
2018-01-30 10:56:36 +00:00
|
|
|
|
2018-03-09 15:55:25 +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
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
s.Quit()
|
2018-01-27 12:39:07 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
func (s *Server) setupConnection(conn net.Conn) {
|
|
|
|
if !s.hasCapacity() {
|
|
|
|
s.logger.Log("msg", "server reached maximum capacity")
|
|
|
|
return
|
2018-02-01 20:28:45 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
p := NewTCPPeer(conn, s.proto.handleProto)
|
|
|
|
s.register <- p
|
|
|
|
if err := p.run(); err != nil {
|
|
|
|
s.unregister <- p
|
|
|
|
}
|
2018-02-01 20:28:45 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
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-02-01 08:00:42 +00:00
|
|
|
}
|
2018-01-31 19:11:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +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
|
2018-02-06 06:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
|
|
|
<-s.peerOpDone
|
|
|
|
if !canConnect {
|
|
|
|
return false
|
|
|
|
}
|
2018-02-04 19:54:51 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
s.badAddrOp <- func(badAddrs map[string]bool) {
|
|
|
|
_, ok := badAddrs[addr]
|
|
|
|
canConnect = !ok
|
|
|
|
}
|
|
|
|
<-s.badAddrOpDone
|
|
|
|
return canConnect
|
2018-02-04 19:54:51 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
func (s *Server) hasCapacity() bool {
|
|
|
|
return s.PeerCount() != s.MaxPeers
|
|
|
|
}
|
2018-02-04 19:54:51 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
func (s *Server) sendVersion(peer Peer) {
|
|
|
|
peer.Send(NewMessage(s.Net, CMDVersion, s.proto.version()))
|
2018-02-01 20:28:45 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +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
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
<-s.peerOpDone
|
|
|
|
return
|
2018-01-28 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
func (s *Server) Start() error {
|
|
|
|
fmt.Println(logo())
|
|
|
|
fmt.Println("")
|
|
|
|
s.printConfiguration()
|
2018-02-01 08:00:42 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
if err := s.createListener(); err != nil {
|
|
|
|
return err
|
2018-02-01 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
go s.run()
|
|
|
|
go s.listenTCP()
|
|
|
|
go s.connectToPeers(s.Seeds...)
|
|
|
|
select {}
|
|
|
|
}
|
2018-02-01 08:00:42 +00:00
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
func (s *Server) Quit() {
|
|
|
|
s.quit <- struct{}{}
|
2018-02-01 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
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-02-01 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2018-01-26 18:04:13 +00:00
|
|
|
func logo() string {
|
|
|
|
return `
|
2018-03-04 13:47:56 +00:00
|
|
|
_ ____________ __________
|
2018-01-26 18:04:13 +00:00
|
|
|
/ | / / ____/ __ \ / ____/ __ \
|
|
|
|
/ |/ / __/ / / / /_____/ / __/ / / /
|
2018-03-04 13:47:56 +00:00
|
|
|
/ /| / /___/ /_/ /_____/ /_/ / /_/ /
|
|
|
|
/_/ |_/_____/\____/ \____/\____/
|
2018-01-26 18:04:13 +00:00
|
|
|
`
|
|
|
|
}
|