github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/p2p/controller.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package p2p
    18  
    19  //import "os"
    20  import "fmt"
    21  import "net"
    22  import "time"
    23  import "sort"
    24  import "strings"
    25  import "math/big"
    26  import "strconv"
    27  
    28  //import "crypto/rsa"
    29  import "crypto/ecdsa"
    30  import "crypto/elliptic"
    31  
    32  import "crypto/tls"
    33  import "crypto/rand"
    34  import "crypto/x509"
    35  import "encoding/pem"
    36  import "sync/atomic"
    37  import "runtime/debug"
    38  
    39  import "github.com/romana/rlog"
    40  import log "github.com/sirupsen/logrus"
    41  
    42  import "github.com/deroproject/derosuite/config"
    43  import "github.com/deroproject/derosuite/globals"
    44  import "github.com/deroproject/derosuite/blockchain"
    45  import "github.com/deroproject/derosuite/metrics"
    46  
    47  var chain *blockchain.Blockchain // external reference to chain
    48  
    49  var P2P_Port int // this will be exported while doing handshake
    50  
    51  var Exit_Event = make(chan bool) // causes all threads to exit
    52  var Exit_In_Progress bool        // marks we are doing exit
    53  var logger *log.Entry            // global logger, every logger in this package is a child of this
    54  var sync_node bool               // whether sync mode is activated
    55  
    56  var nonbanlist []string // any ips in this list will never be banned
    57  // the list will include seed nodes, any nodes provided at command prompt
    58  
    59  // Initialize P2P subsystem
    60  func P2P_Init(params map[string]interface{}) error {
    61  	logger = globals.Logger.WithFields(log.Fields{"com": "P2P"}) // all components must use this logger
    62  
    63  	GetPeerID() // Initialize peer id once
    64  
    65  	// parse node tag if availble
    66  	if _, ok := globals.Arguments["--node-tag"]; ok {
    67  		if globals.Arguments["--node-tag"] != nil {
    68  
    69  			node_tag = globals.Arguments["--node-tag"].(string)
    70  		}
    71  	}
    72  
    73  	// permanently unban any seed nodes
    74  	if globals.IsMainnet() {
    75  		for i := range config.Mainnet_seed_nodes {
    76  			nonbanlist = append(nonbanlist, strings.ToLower(config.Mainnet_seed_nodes[i]))
    77  		}
    78  	} else { // initial bootstrap
    79  		for i := range config.Testnet_seed_nodes {
    80  			nonbanlist = append(nonbanlist, strings.ToLower(config.Testnet_seed_nodes[i]))
    81  		}
    82  	}
    83  
    84  	chain = params["chain"].(*blockchain.Blockchain)
    85  	load_ban_list()  // load ban list
    86  	load_peer_list() // load old list if availble
    87  
    88  	// if user provided a sync node, connect with it
    89  	if _, ok := globals.Arguments["--sync-node"]; ok { // check if parameter is supported
    90  		if globals.Arguments["--sync-node"].(bool) {
    91  			sync_node = true
    92  			// disable p2p port
    93  			globals.Arguments["--p2p-bind"] = ":0"
    94  
    95  			// disable all connections except seed nodes
    96  			globals.Arguments["--add-exclusive-node"] = []string{"0.0.0.0:0"}
    97  			globals.Arguments["--add-priority-node"] = []string{"0.0.0.0:0"}
    98  
    99  			go maintain_seed_node_connection()
   100  
   101  			logger.Warnf("Sync mode is enabled. Please remove this option after chain syncs successfully")
   102  		}
   103  	}
   104  
   105  	// register the metrics with the metrics registry
   106  	metrics.Registry.MustRegister(block_propagation)
   107  	metrics.Registry.MustRegister(transaction_propagation)
   108  
   109  	go P2P_Server_v2()        // start accepting connections
   110  	go P2P_engine()           // start outgoing engine
   111  	go syncroniser()          // start sync engine
   112  	go clean_up_propagation() // clean up propagation map
   113  	logger.Infof("P2P started")
   114  	atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem
   115  	return nil
   116  }
   117  
   118  // TODO we need to make sure that exclusive/priority nodes are never banned
   119  func P2P_engine() {
   120  
   121  	var end_point_list []string
   122  	if _, ok := globals.Arguments["--add-exclusive-node"]; ok { // check if parameter is supported
   123  		if globals.Arguments["--add-exclusive-node"] != nil {
   124  			tmp_list := globals.Arguments["--add-exclusive-node"].([]string)
   125  			for i := range tmp_list {
   126  				end_point_list = append(end_point_list, tmp_list[i])
   127  				nonbanlist = append(nonbanlist, tmp_list[i])
   128  			}
   129  		}
   130  	}
   131  
   132  	// all prority nodes will be always connected
   133  	if _, ok := globals.Arguments["--add-priority-node"]; ok { // check if parameter is supported
   134  		if globals.Arguments["--add-priority-node"] != nil {
   135  			tmp_list := globals.Arguments["--add-priority-node"].([]string)
   136  			for i := range tmp_list {
   137  				end_point_list = append(end_point_list, tmp_list[i])
   138  				nonbanlist = append(nonbanlist, tmp_list[i])
   139  			}
   140  		}
   141  	}
   142  
   143  	{ // remove duplicates if any
   144  		sort.Strings(end_point_list)
   145  	start_again: // this list is expected to be less  than 100
   146  		for i := range end_point_list {
   147  			if i > 0 && end_point_list[i-1] == end_point_list[i] {
   148  				end_point_list = append(end_point_list[:i-1], end_point_list[i:]...)
   149  				goto start_again
   150  			}
   151  		}
   152  	}
   153  
   154  	logger.Debugf("Priority list %+v", end_point_list)
   155  
   156  	// maintain connection to exclusive/priority nodes
   157  	for i := range end_point_list {
   158  		go maintain_outgoing_priority_connection(end_point_list[i], false)
   159  	}
   160  
   161  	// do not create connections to peers , if requested
   162  	if _, ok := globals.Arguments["--add-exclusive-node"]; ok && len(globals.Arguments["--add-exclusive-node"].([]string)) == 0 { // check if parameter is supported
   163  		go maintain_connection_to_peers()  // maintain certain number of  connections for peer to peers
   164  		go maintain_seed_node_connection() // maintain connection with atleast 1 seed node
   165  
   166  		// this code only triggers when we do not have peer list
   167  		if find_peer_to_connect(1) == nil { // either we donot have a peer list or everyone is banned
   168  			// trigger connection to all seed nodes hoping some will be up
   169  			if globals.IsMainnet() { // initial boot strap should be quick
   170  				for i := range config.Mainnet_seed_nodes {
   171  					go connect_with_endpoint(config.Mainnet_seed_nodes[i], true)
   172  				}
   173  			} else { // initial bootstrap
   174  				for i := range config.Testnet_seed_nodes {
   175  					go connect_with_endpoint(config.Testnet_seed_nodes[i], true)
   176  				}
   177  			}
   178  
   179  		}
   180  
   181  	}
   182  
   183  }
   184  
   185  // will try to connect with given endpoint
   186  // will block until the connection dies or is killed
   187  func connect_with_endpoint(endpoint string, sync_node bool) {
   188  
   189  	defer func() {
   190  		if r := recover(); r != nil {
   191  			rlog.Warnf("Recovered while handling connection, Stack trace below", r)
   192  			rlog.Warnf("Stack trace  \n%s", debug.Stack())
   193  		}
   194  	}()
   195  
   196  	remote_ip, err := net.ResolveTCPAddr("tcp", endpoint)
   197  	if err != nil {
   198  		rlog.Warnf("Resolve address failed:", err.Error())
   199  		return
   200  	}
   201  
   202  	// check whether are already connected to this address if yes, return
   203  	if IsAddressConnected(remote_ip.String()) {
   204  		return
   205  	}
   206  
   207  	// since we may be connecting through socks, grab the remote ip for our purpose rightnow
   208  	conn, err := globals.Dialer.Dial("tcp", remote_ip.String())
   209  
   210  	//conn, err := tls.DialWithDialer(&globals.Dialer, "tcp", remote_ip.String(),&tls.Config{InsecureSkipVerify: true})
   211  	//conn, err := tls.Dial("tcp", remote_ip.String(),&tls.Config{InsecureSkipVerify: true})
   212  	if err != nil {
   213  		rlog.Warnf("Dial failed err %s", err.Error())
   214  		Peer_SetFail(remote_ip.String()) // update peer list as we see
   215  		return
   216  	}
   217  
   218  	tcpc := conn.(*net.TCPConn)
   219  	// detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
   220  	// default on linux:  30 + 8 * 30
   221  	// default on osx:    30 + 8 * 75
   222  	tcpc.SetKeepAlive(true)
   223  	tcpc.SetKeepAlivePeriod(8 * time.Second)
   224  	tcpc.SetLinger(0) // discard any pending data
   225  
   226  
   227  	//conn.SetKeepAlive(true) // set keep alive true
   228  	//conn.SetKeepAlivePeriod(10*time.Second) // keep alive every 10 secs
   229  
   230  	// upgrade connection TO TLS ( tls.Dial does NOT support proxy)
   231  	// TODO we need to choose fastest cipher here ( so both clients/servers are not loaded)
   232  	conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
   233  
   234  	// success is setup after handshake is done
   235  	rlog.Debugf("Connection established to %s", remote_ip)
   236  	Handle_Connection(conn, remote_ip, false, sync_node) // handle  connection
   237  }
   238  
   239  // maintains a persistant connection to endpoint
   240  // if connection drops, tries again after 4 secs
   241  func maintain_outgoing_priority_connection(endpoint string, sync_node bool) {
   242  	for {
   243  		select {
   244  		case <-Exit_Event:
   245  			return
   246  		case <-time.After(4 * time.Second):
   247  		}
   248  		connect_with_endpoint(endpoint, sync_node)
   249  	}
   250  }
   251  
   252  // this will maintain connection to 1 seed node randomly
   253  func maintain_seed_node_connection() {
   254  	for {
   255  
   256  		select {
   257  		case <-Exit_Event:
   258  			return
   259  		case <-time.After(2 * time.Second):
   260  		}
   261  		endpoint := ""
   262  		if globals.IsMainnet() { // choose mainnet seed node
   263  			r, _ := rand.Int(rand.Reader, big.NewInt(10240))
   264  			endpoint = config.Mainnet_seed_nodes[r.Int64()%int64(len(config.Mainnet_seed_nodes))]
   265  		} else { // choose testnet peer node
   266  			r, _ := rand.Int(rand.Reader, big.NewInt(10240))
   267  			endpoint = config.Testnet_seed_nodes[r.Int64()%int64(len(config.Testnet_seed_nodes))]
   268  		}
   269  		if endpoint != "" {
   270  			//connect_with_endpoint(endpoint, sync_node)
   271  			connect_with_endpoint(endpoint, true) // seed nodes always have sync mode
   272  		}
   273  	}
   274  }
   275  
   276  // keep building connections to network, we are talking outgoing connections
   277  func maintain_connection_to_peers() {
   278  
   279  	Min_Peers := int64(13) // we need to expose this to be modifieable at runtime without taking daemon offline
   280  	// check how many connections are active
   281  	if _, ok := globals.Arguments["--min-peers"]; ok && globals.Arguments["--min-peers"] != nil { // user specified a limit, use it if possible
   282  		i, err := strconv.ParseInt(globals.Arguments["--min-peers"].(string), 10, 64)
   283  		if err != nil {
   284  			logger.Warnf("Error Parsing --max-peers err %s", err)
   285  		} else {
   286  			if i <= 1 {
   287  				logger.Warnf("--min-peers should be positive and more than 1")
   288  			} else {
   289  				Min_Peers = i
   290  			}
   291  		}
   292  		logger.Infof("Min outgoing peers limit %d", Min_Peers)
   293  	}
   294  
   295  	for {
   296  		select {
   297  		case <-Exit_Event:
   298  			return
   299  		case <-time.After(1000 * time.Millisecond):
   300  		}
   301  
   302  		// check number of connections, if limit is reached, trigger new connections if we have peers
   303  		// if we have more do nothing
   304  		_, out := Peer_Direction_Count()
   305  		if out >= uint64(Min_Peers) { // we already have required number of peers, donot connect to more peers
   306  			continue
   307  		}
   308  
   309  		peer := find_peer_to_connect(1)
   310  		if peer != nil {
   311  			go connect_with_endpoint(peer.Address, false)
   312  		}
   313  	}
   314  }
   315  
   316  func P2P_Server_v2() {
   317  
   318  	default_address := "0.0.0.0:" + fmt.Sprintf("%d", config.Mainnet.P2P_Default_Port)
   319  	P2P_Port = config.Mainnet.P2P_Default_Port
   320  	if !globals.IsMainnet() {
   321  		default_address = "0.0.0.0:" + fmt.Sprintf("%d", config.Testnet.P2P_Default_Port)
   322  		P2P_Port = config.Testnet.P2P_Default_Port
   323  	}
   324  
   325  	if _, ok := globals.Arguments["--p2p-bind"]; ok && globals.Arguments["--p2p-bind"] != nil {
   326  		addr, err := net.ResolveTCPAddr("tcp", globals.Arguments["--p2p-bind"].(string))
   327  		if err != nil {
   328  			logger.Warnf("--p2p-bind address is invalid, err = %s", err)
   329  		} else {
   330  			if addr.Port == 0 {
   331  				logger.Infof("P2P server is disabled, No ports will be opened for P2P activity")
   332  				return
   333  			} else {
   334  				default_address = addr.String()
   335  				P2P_Port = addr.Port
   336  			}
   337  		}
   338  	}
   339  
   340  	logger.Infof("P2P  will listen on %s", default_address)
   341  	tlsconfig := &tls.Config{Certificates: []tls.Certificate{generate_random_tls_cert()}}
   342  	//l, err := tls.Listen("tcp", default_address, tlsconfig) // listen as TLS server
   343  
   344  	// listen to incoming tcp connections tls style
   345  	l, err := net.Listen("tcp", default_address)  // listen as simple TCP server
   346  	if err != nil {
   347  		logger.Fatalf("Could not listen on %s, err %s", default_address, err)
   348  	}
   349  	defer l.Close()
   350  
   351  	// p2p is shutting down, close the listening socket
   352  	go func() { <-Exit_Event; l.Close() }()
   353  
   354  	// A common pattern is to start a loop to continously accept connections
   355  	for {
   356  		conn, err := l.Accept() //accept connections using Listener.Accept()
   357  		if err != nil {
   358  			select {
   359  			case <-Exit_Event:
   360  				return
   361  			default:
   362  			}
   363  			logger.Warnf("Err while accepting incoming connection errr %s", err)
   364  			continue
   365  		}
   366  		raddr := conn.RemoteAddr().(*net.TCPAddr)
   367  
   368  		//if incoming IP is banned, disconnect now
   369  		if IsAddressInBanList(raddr.IP.String()) {
   370  			rlog.Tracef(1, "Incoming IP %s is banned, disconnecting now", raddr.IP.String())
   371  			conn.Close()
   372  		} else {
   373  
   374  			tcpc := conn.(*net.TCPConn)
   375  	// detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
   376  	// default on linux:  30 + 8 * 30
   377  	// default on osx:    30 + 8 * 75
   378  	tcpc.SetKeepAlive(true)
   379  	tcpc.SetKeepAlivePeriod(8 * time.Second)
   380  	tcpc.SetLinger(0) // discard any pending data
   381  
   382  	tlsconn := tls.Server(conn,tlsconfig)
   383  	go Handle_Connection(tlsconn, raddr, true, false) // handle connection in a different go routine
   384  	
   385  
   386  	//go Handle_Connection(conn, raddr, true, false) // handle connection in a different go routine
   387  		}
   388  	}
   389  }
   390  
   391  // shutdown the p2p component
   392  func P2P_Shutdown() {
   393  	close(Exit_Event) // send signal to all connections to exit
   394  	save_peer_list()  // save peer list
   395  	save_ban_list()   // save ban list
   396  
   397  	// TODO we  must wait for connections to kill themselves
   398  	time.Sleep(1 * time.Second)
   399  	logger.Infof("P2P Shutdown")
   400  	atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem
   401  
   402  }
   403  
   404  // generate default tls cert to encrypt everything
   405  // NOTE: this does NOT protect from individual active man-in-the-middle attacks
   406  func generate_random_tls_cert() tls.Certificate {
   407  
   408  	/* RSA can do only 500 exchange per second, we need to be faster
   409  	     * reference https://github.com/golang/go/issues/20058
   410  	    key, err := rsa.GenerateKey(rand.Reader, 512) // current using minimum size
   411  	if err != nil {
   412  	    log.Fatal("Private key cannot be created.", err.Error())
   413  	}
   414  
   415  	// Generate a pem block with the private key
   416  	keyPem := pem.EncodeToMemory(&pem.Block{
   417  	    Type:  "RSA PRIVATE KEY",
   418  	    Bytes: x509.MarshalPKCS1PrivateKey(key),
   419  	})
   420  	*/
   421  	// EC256 does roughly 20000 exchanges per second
   422  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   423  	b, err := x509.MarshalECPrivateKey(key)
   424  	if err != nil {
   425  		log.Fatalf("Unable to marshal ECDSA private key: %v", err)
   426  	}
   427  	// Generate a pem block with the private key
   428  	keyPem := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
   429  
   430  	tml := x509.Certificate{
   431  		SerialNumber: big.NewInt(int64(GetPeerID()) ^ int64(time.Now().UnixNano())),
   432  
   433  		// TODO do we need to add more parameters to make our certificate more authentic
   434  		// and thwart traffic identification as a mass scale
   435  		/*
   436  		   // you can add any attr that you need
   437  		   NotBefore:    time.Now(),
   438  		   NotAfter:     time.Now().AddDate(5, 0, 0),
   439  		   // you have to generate a different serial number each execution
   440  
   441  		   Subject: pkix.Name{
   442  		       CommonName:   "New Name",
   443  		       Organization: []string{"New Org."},
   444  		   },
   445  		   BasicConstraintsValid: true,   // even basic constraints are not required
   446  		*/
   447  	}
   448  	cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
   449  	if err != nil {
   450  		log.Fatal("Certificate cannot be created.", err.Error())
   451  	}
   452  
   453  	// Generate a pem block with the certificate
   454  	certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
   455  	tlsCert, err := tls.X509KeyPair(certPem, keyPem)
   456  	if err != nil {
   457  		log.Fatal("Cannot be loaded the certificate.", err.Error())
   458  	}
   459  	return tlsCert
   460  }