diff --git a/go.mod b/go.mod index 907e72d3..a6c6bd69 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/ethereum/go-ethereum v1.10.16 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.0 github.com/gorilla/rpc v1.2.0 github.com/ipfs/go-ds-sql v0.3.0 github.com/ipfs/go-log v1.0.5 diff --git a/library/api.go b/library/api.go index 38aba320..95444836 100644 --- a/library/api.go +++ b/library/api.go @@ -66,7 +66,7 @@ type WakuConfig struct { NodeKey *string `json:"nodeKey,omitempty"` KeepAliveInterval *int `json:"keepAliveInterval,omitempty"` EnableRelay *bool `json:"relay"` - MinPeersToPublish *int `json:"minPeersToPublish` + MinPeersToPublish *int `json:"minPeersToPublish"` } var DefaultHost = "0.0.0.0" diff --git a/library/response.go b/library/response.go index d3d27ecd..1d538cb9 100644 --- a/library/response.go +++ b/library/response.go @@ -5,10 +5,6 @@ import ( "encoding/json" ) -var errToCodeMap = map[error]int{ - //transactions.ErrInvalidTxSender: codeErrInvalidTxSender, -} - type JSONResponse struct { Error *string `json:"error,omitempty"` Result interface{} `json:"result"` diff --git a/waku/v2/discv5/discover.go b/waku/v2/discv5/discover.go index 62fd64f8..18334f30 100644 --- a/waku/v2/discv5/discover.go +++ b/waku/v2/discv5/discover.go @@ -3,10 +3,12 @@ package discv5 import ( "context" "crypto/ecdsa" + "errors" "fmt" "math" "math/rand" "net" + "strconv" "sync" "time" @@ -16,6 +18,7 @@ import ( "github.com/libp2p/go-libp2p-core/discovery" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/peer" + ma "github.com/multiformats/go-multiaddr" "github.com/status-im/go-discover/discover" "github.com/status-im/go-waku/waku/v2/utils" "go.uber.org/zap" @@ -57,7 +60,6 @@ type discV5Parameters struct { autoUpdate bool bootnodes []*enode.Node udpPort int - tcpPort int advertiseAddr *net.IP } @@ -93,7 +95,7 @@ func DefaultOptions() []DiscoveryV5Option { } } -func NewDiscoveryV5(host host.Host, ipAddr net.IP, tcpPort int, priv *ecdsa.PrivateKey, wakuFlags utils.WakuEnrBitfield, log *zap.SugaredLogger, opts ...DiscoveryV5Option) (*DiscoveryV5, error) { +func NewDiscoveryV5(host host.Host, addresses []ma.Multiaddr, priv *ecdsa.PrivateKey, wakuFlags utils.WakuEnrBitfield, log *zap.SugaredLogger, opts ...DiscoveryV5Option) (*DiscoveryV5, error) { params := new(discV5Parameters) optList := DefaultOptions() optList = append(optList, opts...) @@ -103,9 +105,12 @@ func NewDiscoveryV5(host host.Host, ipAddr net.IP, tcpPort int, priv *ecdsa.Priv logger := log.Named("discv5") - params.tcpPort = tcpPort + ipAddr, err := selectIPAddr(addresses) + if err != nil { + return nil, err + } - localnode, err := newLocalnode(priv, ipAddr, params.udpPort, tcpPort, wakuFlags, params.advertiseAddr, logger) + localnode, err := newLocalnode(priv, ipAddr, params.udpPort, wakuFlags, params.advertiseAddr, logger) if err != nil { return nil, err } @@ -144,16 +149,16 @@ func NewDiscoveryV5(host host.Host, ipAddr net.IP, tcpPort int, priv *ecdsa.Priv }, nil } -func newLocalnode(priv *ecdsa.PrivateKey, ipAddr net.IP, udpPort int, tcpPort int, wakuFlags utils.WakuEnrBitfield, advertiseAddr *net.IP, log *zap.SugaredLogger) (*enode.LocalNode, error) { +func newLocalnode(priv *ecdsa.PrivateKey, ipAddr *net.TCPAddr, udpPort int, wakuFlags utils.WakuEnrBitfield, advertiseAddr *net.IP, log *zap.SugaredLogger) (*enode.LocalNode, error) { db, err := enode.OpenDB("") if err != nil { return nil, err } localnode := enode.NewLocalNode(db, priv) - localnode.SetFallbackIP(net.IP{127, 0, 0, 1}) localnode.SetFallbackUDP(udpPort) localnode.Set(enr.WithEntry(utils.WakuENRField, wakuFlags)) - localnode.Set(enr.IP(ipAddr)) + localnode.SetFallbackIP(net.IP{127, 0, 0, 1}) + localnode.SetStaticIP(ipAddr.IP) if udpPort > 0 && udpPort <= math.MaxUint16 { localnode.Set(enr.UDP(uint16(udpPort))) // lgtm [go/incorrect-integer-conversion] @@ -161,10 +166,10 @@ func newLocalnode(priv *ecdsa.PrivateKey, ipAddr net.IP, udpPort int, tcpPort in log.Error("could not set udpPort ", udpPort) } - if tcpPort > 0 && tcpPort <= math.MaxUint16 { - localnode.Set(enr.TCP(uint16(tcpPort))) // lgtm [go/incorrect-integer-conversion] + if ipAddr.Port > 0 && ipAddr.Port <= math.MaxUint16 { + localnode.Set(enr.TCP(uint16(ipAddr.Port))) // lgtm [go/incorrect-integer-conversion] } else { - log.Error("could not set tcpPort ", tcpPort) + log.Error("could not set tcpPort ", ipAddr.Port) } if advertiseAddr != nil { @@ -200,7 +205,7 @@ func (d *DiscoveryV5) listen() error { d.listener = listener - d.log.Info(fmt.Sprintf("Started Discovery V5 at %s:%d, advertising IP: %s:%d", d.udpAddr.IP, d.udpAddr.Port, d.localnode.Node().IP(), d.params.tcpPort)) + d.log.Info(fmt.Sprintf("Started Discovery V5 at %s:%d, advertising IP: %s:%d", d.udpAddr.IP, d.udpAddr.Port, d.localnode.Node().IP(), d.localnode.Node().TCP())) d.log.Info("Discovery V5 ", d.localnode.Node()) return nil @@ -236,30 +241,6 @@ func (d *DiscoveryV5) Stop() { d.wg.Wait() } -// IsPrivate reports whether ip is a private address, according to -// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses). -// Copied/Adapted from https://go-review.googlesource.com/c/go/+/272668/11/src/net/ip.go -// Copyright (c) The Go Authors. All rights reserved. -// @TODO: once Go 1.17 is released in Q42021, remove this function as it will become part of the language -func IsPrivate(ip net.IP) bool { - if ip4 := ip.To4(); ip4 != nil { - // Following RFC 4193, Section 3. Local IPv6 Unicast Addresses which says: - // The Internet Assigned Numbers Authority (IANA) has reserved the - // following three blocks of the IPv4 address space for private internets: - // 10.0.0.0 - 10.255.255.255 (10/8 prefix) - // 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) - // 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) - return ip4[0] == 10 || - (ip4[0] == 172 && ip4[1]&0xf0 == 16) || - (ip4[0] == 192 && ip4[1] == 168) - } - // Following RFC 4193, Section 3. Private Address Space which says: - // The Internet Assigned Numbers Authority (IANA) has reserved the - // following block of the IPv6 address space for local internets: - // FC00:: - FDFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF (FC00::/7 prefix) - return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc -} - func (d *DiscoveryV5) UpdateAddr(addr net.IP) error { if !d.params.autoUpdate { return nil @@ -273,12 +254,12 @@ func (d *DiscoveryV5) UpdateAddr(addr net.IP) error { } // TODO: improve this logic to determine if an address should be replaced or not - if !(d.localnode.Node().IP().IsLoopback() && IsPrivate(addr)) && !(IsPrivate(d.localnode.Node().IP()) && !addr.IsLoopback() && !IsPrivate(addr)) { + if !((d.localnode.Node().IP().IsLoopback() && isPrivate(&net.TCPAddr{IP: addr})) || + (isPrivate(&net.TCPAddr{IP: d.localnode.Node().IP()}) && isExternal(&net.TCPAddr{IP: addr}))) { return nil } - d.localnode.Set(enr.IP(addr)) - + d.localnode.SetStaticIP(addr) d.log.Info(fmt.Sprintf("Updated Discovery V5 node IP: %s", d.localnode.Node().IP())) d.log.Info("Discovery V5 ", d.localnode.Node()) @@ -479,3 +460,82 @@ func (d *DiscoveryV5) FindPeers(ctx context.Context, topic string, opts ...disco return chPeer, err } + +// IsPrivate reports whether ip is a private address, according to +// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses). +// Copied/Adapted from https://go-review.googlesource.com/c/go/+/272668/11/src/net/ip.go +// Copyright (c) The Go Authors. All rights reserved. +// @TODO: once Go 1.17 is released in Q42021, remove this function as it will become part of the language +func isPrivate(ip *net.TCPAddr) bool { + if ip4 := ip.IP.To4(); ip4 != nil { + // Following RFC 4193, Section 3. Local IPv6 Unicast Addresses which says: + // The Internet Assigned Numbers Authority (IANA) has reserved the + // following three blocks of the IPv4 address space for private internets: + // 10.0.0.0 - 10.255.255.255 (10/8 prefix) + // 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) + // 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) + return ip4[0] == 10 || + (ip4[0] == 172 && ip4[1]&0xf0 == 16) || + (ip4[0] == 192 && ip4[1] == 168) + } + // Following RFC 4193, Section 3. Private Address Space which says: + // The Internet Assigned Numbers Authority (IANA) has reserved the + // following block of the IPv6 address space for local internets: + // FC00:: - FDFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF (FC00::/7 prefix) + return len(ip.IP) == net.IPv6len && ip.IP[0]&0xfe == 0xfc +} + +func isExternal(ip *net.TCPAddr) bool { + return !isPrivate(ip) && !ip.IP.IsLoopback() && !ip.IP.IsUnspecified() +} + +func isLoopback(ip *net.TCPAddr) bool { + return ip.IP.IsLoopback() +} +func filter(ss []*net.TCPAddr, fn func(*net.TCPAddr) bool) (ret []*net.TCPAddr) { + for _, s := range ss { + if fn(s) { + ret = append(ret, s) + } + } + return +} + +func selectIPAddr(addresses []ma.Multiaddr) (*net.TCPAddr, error) { + var ipAddrs []*net.TCPAddr + for _, addr := range addresses { + ipStr, err := addr.ValueForProtocol(ma.P_IP4) + if err != nil { + continue + } + portStr, err := addr.ValueForProtocol(ma.P_TCP) + if err != nil { + continue + } + port, err := strconv.Atoi(portStr) + if err != nil { + continue + } + ipAddrs = append(ipAddrs, &net.TCPAddr{ + IP: net.ParseIP(ipStr), + Port: port, + }) + } + + externalIPs := filter(ipAddrs, isExternal) + if len(externalIPs) > 0 { + return externalIPs[0], nil + } + + privateIPs := filter(ipAddrs, isPrivate) + if len(privateIPs) > 0 { + return privateIPs[0], nil + } + + loopback := filter(ipAddrs, isLoopback) + if len(loopback) > 0 { + return loopback[0], nil + } + + return nil, errors.New("could not obtain ip address") +} diff --git a/waku/v2/discv5/discover_test.go b/waku/v2/discv5/discover_test.go index aba9e2df..31ee301e 100644 --- a/waku/v2/discv5/discover_test.go +++ b/waku/v2/discv5/discover_test.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ecdsa" "fmt" - "net" "testing" "time" @@ -45,22 +44,22 @@ func createHost(t *testing.T) (host.Host, int, *ecdsa.PrivateKey) { func TestDiscV5(t *testing.T) { // Host1 <-> Host2 <-> Host3 - host1, tcpPort1, prvKey1 := createHost(t) + host1, _, prvKey1 := createHost(t) udpPort1, err := tests.FindFreePort(t, "127.0.0.1", 3) require.NoError(t, err) - d1, err := NewDiscoveryV5(host1, net.IPv4(127, 0, 0, 1), tcpPort1, prvKey1, utils.NewWakuEnrBitfield(true, true, true, true), tests.Logger(), WithUDPPort(udpPort1)) + d1, err := NewDiscoveryV5(host1, host1.Addrs(), prvKey1, utils.NewWakuEnrBitfield(true, true, true, true), tests.Logger(), WithUDPPort(udpPort1)) require.NoError(t, err) - host2, tcpPort2, prvKey2 := createHost(t) + host2, _, prvKey2 := createHost(t) udpPort2, err := tests.FindFreePort(t, "127.0.0.1", 3) require.NoError(t, err) - d2, err := NewDiscoveryV5(host2, net.IPv4(127, 0, 0, 1), tcpPort2, prvKey2, utils.NewWakuEnrBitfield(true, true, true, true), tests.Logger(), WithUDPPort(udpPort2), WithBootnodes([]*enode.Node{d1.localnode.Node()})) + d2, err := NewDiscoveryV5(host2, host2.Addrs(), prvKey2, utils.NewWakuEnrBitfield(true, true, true, true), tests.Logger(), WithUDPPort(udpPort2), WithBootnodes([]*enode.Node{d1.localnode.Node()})) require.NoError(t, err) - host3, tcpPort3, prvKey3 := createHost(t) + host3, _, prvKey3 := createHost(t) udpPort3, err := tests.FindFreePort(t, "127.0.0.1", 3) require.NoError(t, err) - d3, err := NewDiscoveryV5(host3, net.IPv4(127, 0, 0, 1), tcpPort3, prvKey3, utils.NewWakuEnrBitfield(true, true, true, true), tests.Logger(), WithUDPPort(udpPort3), WithBootnodes([]*enode.Node{d2.localnode.Node()})) + d3, err := NewDiscoveryV5(host3, host3.Addrs(), prvKey3, utils.NewWakuEnrBitfield(true, true, true, true), tests.Logger(), WithUDPPort(udpPort3), WithBootnodes([]*enode.Node{d2.localnode.Node()})) require.NoError(t, err) defer d1.Stop() diff --git a/waku/v2/node/wakunode2.go b/waku/v2/node/wakunode2.go index 9bdd9bf5..7b61f5b5 100644 --- a/waku/v2/node/wakunode2.go +++ b/waku/v2/node/wakunode2.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net" - "strconv" "sync" "time" @@ -197,6 +196,7 @@ func (w *WakuNode) onAddrChange() { continue } ip := net.ParseIP(ipStr) + if !ip.IsLoopback() && !ip.IsUnspecified() { if w.opts.enableDiscV5 { err := w.discoveryV5.UpdateAddr(ip) @@ -458,24 +458,8 @@ func (w *WakuNode) mountDiscV5() error { discV5Options = append(discV5Options, discv5.WithAdvertiseAddr(*w.opts.advertiseAddr)) } - addr := w.ListenAddresses()[0] - - ipStr, err := addr.ValueForProtocol(ma.P_IP4) - if err != nil { - return err - } - - portStr, err := addr.ValueForProtocol(ma.P_TCP) - if err != nil { - return err - } - - port, err := strconv.Atoi(portStr) - if err != nil { - return err - } - - w.discoveryV5, err = discv5.NewDiscoveryV5(w.Host(), net.ParseIP(ipStr), port, w.opts.privKey, w.wakuFlag, w.log, discV5Options...) + var err error + w.discoveryV5, err = discv5.NewDiscoveryV5(w.Host(), w.ListenAddresses(), w.opts.privKey, w.wakuFlag, w.log, discV5Options...) return err }