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 }