2019-08-21 06:08:30 +00:00
|
|
|
package p2p
|
|
|
|
|
|
|
|
import (
|
2019-09-23 17:24:16 +00:00
|
|
|
"context"
|
2019-08-21 06:08:30 +00:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
2019-08-22 15:23:16 +00:00
|
|
|
iaddr "github.com/ipfs/go-ipfs-addr"
|
2019-09-23 17:24:16 +00:00
|
|
|
core "github.com/libp2p/go-libp2p-core"
|
2019-08-21 06:08:30 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2019-08-22 15:23:16 +00:00
|
|
|
"github.com/pkg/errors"
|
2019-08-21 06:08:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Listener defines the discovery V5 network interface that is used
|
|
|
|
// to communicate with other peers.
|
|
|
|
type Listener interface {
|
2019-09-06 19:20:20 +00:00
|
|
|
Self() *enode.Node
|
2019-08-21 06:08:30 +00:00
|
|
|
Close()
|
2019-09-06 19:20:20 +00:00
|
|
|
Lookup(enode.ID) []*enode.Node
|
|
|
|
ReadRandomNodes([]*enode.Node) int
|
|
|
|
Resolve(*enode.Node) *enode.Node
|
|
|
|
LookupRandom() []*enode.Node
|
|
|
|
Ping(*enode.Node) error
|
|
|
|
RequestENR(*enode.Node) (*enode.Node, error)
|
2019-08-21 06:08:30 +00:00
|
|
|
}
|
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
func createListener(ipAddr net.IP, privKey *ecdsa.PrivateKey, cfg *Config) *discover.UDPv5 {
|
2019-08-21 06:08:30 +00:00
|
|
|
udpAddr := &net.UDPAddr{
|
|
|
|
IP: ipAddr,
|
2019-09-12 04:52:27 +00:00
|
|
|
Port: int(cfg.UDPPort),
|
2019-08-21 06:08:30 +00:00
|
|
|
}
|
|
|
|
conn, err := net.ListenUDP("udp4", udpAddr)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2019-09-12 04:52:27 +00:00
|
|
|
localNode, err := createLocalNode(privKey, ipAddr, int(cfg.UDPPort), int(cfg.TCPPort))
|
2019-09-06 19:20:20 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2019-10-04 03:22:17 +00:00
|
|
|
if cfg.HostAddress != "" {
|
|
|
|
hostIP := net.ParseIP(cfg.HostAddress)
|
|
|
|
if hostIP.To4() == nil {
|
|
|
|
log.Errorf("Invalid host address given: %s", hostIP.String())
|
|
|
|
} else {
|
|
|
|
localNode.SetFallbackIP(hostIP)
|
|
|
|
}
|
|
|
|
}
|
2019-09-06 19:20:20 +00:00
|
|
|
dv5Cfg := discover.Config{
|
|
|
|
PrivateKey: privKey,
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
dv5Cfg.Bootnodes = []*enode.Node{}
|
|
|
|
for _, addr := range cfg.Discv5BootStrapAddr {
|
|
|
|
bootNode, err := enode.Parse(enode.ValidSchemes, addr)
|
2019-09-06 19:20:20 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
dv5Cfg.Bootnodes = append(dv5Cfg.Bootnodes, bootNode)
|
2019-09-06 19:20:20 +00:00
|
|
|
}
|
2019-08-21 06:08:30 +00:00
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
network, err := discover.ListenV5(conn, localNode, dv5Cfg)
|
2019-08-21 06:08:30 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
return network
|
|
|
|
}
|
|
|
|
|
2019-09-12 04:52:27 +00:00
|
|
|
func createLocalNode(privKey *ecdsa.PrivateKey, ipAddr net.IP, udpPort int, tcpPort int) (*enode.LocalNode, error) {
|
2019-09-06 19:20:20 +00:00
|
|
|
db, err := enode.OpenDB("")
|
2019-08-21 06:08:30 +00:00
|
|
|
if err != nil {
|
2019-09-06 19:20:20 +00:00
|
|
|
return nil, errors.Wrap(err, "could not open node's peer database")
|
2019-08-21 06:08:30 +00:00
|
|
|
}
|
2019-09-06 19:20:20 +00:00
|
|
|
localNode := enode.NewLocalNode(db, privKey)
|
|
|
|
ipEntry := enr.IP(ipAddr)
|
2019-09-12 04:52:27 +00:00
|
|
|
udpEntry := enr.UDP(udpPort)
|
|
|
|
tcpEntry := enr.TCP(tcpPort)
|
2019-09-06 19:20:20 +00:00
|
|
|
localNode.Set(ipEntry)
|
|
|
|
localNode.Set(udpEntry)
|
2019-09-12 04:52:27 +00:00
|
|
|
localNode.Set(tcpEntry)
|
2019-12-10 05:35:16 +00:00
|
|
|
localNode.SetFallbackIP(ipAddr)
|
|
|
|
localNode.SetFallbackUDP(udpPort)
|
2019-09-06 19:20:20 +00:00
|
|
|
|
|
|
|
return localNode, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func startDiscoveryV5(addr net.IP, privKey *ecdsa.PrivateKey, cfg *Config) (*discover.UDPv5, error) {
|
|
|
|
listener := createListener(addr, privKey, cfg)
|
2019-08-21 06:08:30 +00:00
|
|
|
node := listener.Self()
|
2019-10-01 20:05:17 +00:00
|
|
|
log.WithField("nodeID", node.ID()).Info("Started discovery v5")
|
2019-08-21 06:08:30 +00:00
|
|
|
return listener, nil
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:24:16 +00:00
|
|
|
// startDHTDiscovery supports discovery via DHT.
|
|
|
|
func startDHTDiscovery(host core.Host, bootstrapAddr string) error {
|
|
|
|
multiAddr, err := multiAddrFromString(bootstrapAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
peerInfo, err := peer.AddrInfoFromP2pAddr(multiAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = host.Connect(context.Background(), *peerInfo)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseBootStrapAddrs(addrs []string) (discv5Nodes []string, kadDHTNodes []string) {
|
|
|
|
for _, addr := range addrs {
|
2019-09-27 12:05:16 +00:00
|
|
|
if addr == "" {
|
|
|
|
// Ignore empty entries
|
|
|
|
continue
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
_, err := enode.Parse(enode.ValidSchemes, addr)
|
|
|
|
if err == nil {
|
|
|
|
discv5Nodes = append(discv5Nodes, addr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
_, err = multiAddrFromString(addr)
|
|
|
|
if err == nil {
|
|
|
|
kadDHTNodes = append(kadDHTNodes, addr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
log.Errorf("Invalid bootstrap address of %s provided", addr)
|
|
|
|
}
|
2019-09-27 12:05:16 +00:00
|
|
|
if len(discv5Nodes) == 0 && len(kadDHTNodes) == 0 {
|
|
|
|
log.Warn("No bootstrap addresses supplied")
|
|
|
|
}
|
2019-09-23 17:24:16 +00:00
|
|
|
return discv5Nodes, kadDHTNodes
|
|
|
|
}
|
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
func convertToMultiAddr(nodes []*enode.Node) []ma.Multiaddr {
|
2019-08-21 06:08:30 +00:00
|
|
|
var multiAddrs []ma.Multiaddr
|
|
|
|
for _, node := range nodes {
|
2019-10-04 08:05:52 +00:00
|
|
|
// ignore nodes with no ip address stored
|
|
|
|
if node.IP() == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-09-01 22:29:58 +00:00
|
|
|
multiAddr, err := convertToSingleMultiAddr(node)
|
2019-08-21 06:08:30 +00:00
|
|
|
if err != nil {
|
2019-09-01 22:29:58 +00:00
|
|
|
log.WithError(err).Error("Could not convert to multiAddr")
|
2019-08-21 06:08:30 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
multiAddrs = append(multiAddrs, multiAddr)
|
|
|
|
}
|
|
|
|
return multiAddrs
|
|
|
|
}
|
2019-08-22 15:23:16 +00:00
|
|
|
|
2019-09-06 19:20:20 +00:00
|
|
|
func convertToSingleMultiAddr(node *enode.Node) (ma.Multiaddr, error) {
|
|
|
|
ip4 := node.IP().To4()
|
2019-09-01 22:29:58 +00:00
|
|
|
if ip4 == nil {
|
2019-09-23 04:47:11 +00:00
|
|
|
return nil, errors.Errorf("node doesn't have an ip4 address, it's stated IP is %s", node.IP().String())
|
2019-09-01 22:29:58 +00:00
|
|
|
}
|
2019-09-06 19:20:20 +00:00
|
|
|
pubkey := node.Pubkey()
|
2019-09-01 22:29:58 +00:00
|
|
|
assertedKey := convertToInterfacePubkey(pubkey)
|
|
|
|
id, err := peer.IDFromPublicKey(assertedKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not get peer id")
|
|
|
|
}
|
2019-09-12 04:52:27 +00:00
|
|
|
multiAddrString := fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", ip4.String(), node.TCP(), id)
|
2019-09-01 22:29:58 +00:00
|
|
|
multiAddr, err := ma.NewMultiaddr(multiAddrString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not get multiaddr")
|
|
|
|
}
|
|
|
|
return multiAddr, nil
|
|
|
|
}
|
|
|
|
|
2019-08-22 15:23:16 +00:00
|
|
|
func manyMultiAddrsFromString(addrs []string) ([]ma.Multiaddr, error) {
|
|
|
|
var allAddrs []ma.Multiaddr
|
|
|
|
for _, stringAddr := range addrs {
|
|
|
|
addr, err := multiAddrFromString(stringAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "Could not get multiaddr from string")
|
|
|
|
}
|
|
|
|
allAddrs = append(allAddrs, addr)
|
|
|
|
}
|
|
|
|
return allAddrs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func multiAddrFromString(address string) (ma.Multiaddr, error) {
|
|
|
|
addr, err := iaddr.ParseString(address)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return addr.Multiaddr(), nil
|
|
|
|
}
|