diff --git a/waku/v2/discv5/discover.go b/waku/v2/discv5/discover.go index d4a7d28b..fefa19b2 100644 --- a/waku/v2/discv5/discover.go +++ b/waku/v2/discv5/discover.go @@ -61,11 +61,6 @@ type discV5Parameters struct { advertiseAddr *net.IP } -const WakuENRField = "waku2" - -// WakuEnrBitfield is a8-bit flag field to indicate Waku capabilities. Only the 4 LSBs are currently defined according to RFC31 (https://rfc.vac.dev/spec/31/). -type WakuEnrBitfield = uint8 - type DiscoveryV5Option func(*discV5Parameters) func WithAutoUpdate(autoUpdate bool) DiscoveryV5Option { @@ -98,29 +93,7 @@ func DefaultOptions() []DiscoveryV5Option { } } -func NewWakuEnrBitfield(lightpush, filter, store, relay bool) WakuEnrBitfield { - var v uint8 = 0 - - if lightpush { - v |= (1 << 3) - } - - if filter { - v |= (1 << 2) - } - - if store { - v |= (1 << 1) - } - - if relay { - v |= (1 << 0) - } - - return v -} - -func NewDiscoveryV5(host host.Host, ipAddr net.IP, tcpPort int, priv *ecdsa.PrivateKey, wakuFlags WakuEnrBitfield, opts ...DiscoveryV5Option) (*DiscoveryV5, error) { +func NewDiscoveryV5(host host.Host, ipAddr net.IP, tcpPort int, priv *ecdsa.PrivateKey, wakuFlags utils.WakuEnrBitfield, opts ...DiscoveryV5Option) (*DiscoveryV5, error) { params := new(discV5Parameters) optList := DefaultOptions() optList = append(optList, opts...) @@ -161,7 +134,7 @@ 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 WakuEnrBitfield, advertiseAddr *net.IP) (*enode.LocalNode, error) { +func newLocalnode(priv *ecdsa.PrivateKey, ipAddr net.IP, udpPort int, tcpPort int, wakuFlags utils.WakuEnrBitfield, advertiseAddr *net.IP) (*enode.LocalNode, error) { db, err := enode.OpenDB("") if err != nil { return nil, err @@ -169,7 +142,7 @@ func newLocalnode(priv *ecdsa.PrivateKey, ipAddr net.IP, udpPort int, tcpPort in localnode := enode.NewLocalNode(db, priv) localnode.SetFallbackIP(net.IP{127, 0, 0, 1}) localnode.SetFallbackUDP(udpPort) - localnode.Set(enr.WithEntry(WakuENRField, wakuFlags)) + localnode.Set(enr.WithEntry(utils.WakuENRField, wakuFlags)) localnode.Set(enr.IP(ipAddr)) if udpPort > 0 && udpPort <= math.MaxUint16 { @@ -303,8 +276,8 @@ func (d *DiscoveryV5) UpdateAddr(addr net.IP) error { } func isWakuNode(node *enode.Node) bool { - enrField := new(WakuEnrBitfield) - if err := node.Record().Load(enr.WithEntry(WakuENRField, &enrField)); err != nil { + enrField := new(utils.WakuEnrBitfield) + if err := node.Record().Load(enr.WithEntry(utils.WakuENRField, &enrField)); err != nil { if !enr.IsNotFound(err) { log.Error("could not retrieve port for enr ", node) } diff --git a/waku/v2/discv5/discover_test.go b/waku/v2/discv5/discover_test.go index 70a72902..5b1ff636 100644 --- a/waku/v2/discv5/discover_test.go +++ b/waku/v2/discv5/discover_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/multiformats/go-multiaddr" "github.com/status-im/go-waku/tests" + "github.com/status-im/go-waku/waku/v2/utils" "github.com/stretchr/testify/require" "github.com/libp2p/go-libp2p" @@ -48,19 +49,19 @@ func TestDiscV5(t *testing.T) { host1, tcpPort1, 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, NewWakuEnrBitfield(true, true, true, true), WithUDPPort(udpPort1)) + d1, err := NewDiscoveryV5(host1, net.IPv4(127, 0, 0, 1), tcpPort1, prvKey1, utils.NewWakuEnrBitfield(true, true, true, true), WithUDPPort(udpPort1)) require.NoError(t, err) host2, tcpPort2, 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, NewWakuEnrBitfield(true, true, true, true), WithUDPPort(udpPort2), WithBootnodes([]*enode.Node{d1.localnode.Node()})) + d2, err := NewDiscoveryV5(host2, net.IPv4(127, 0, 0, 1), tcpPort2, prvKey2, utils.NewWakuEnrBitfield(true, true, true, true), WithUDPPort(udpPort2), WithBootnodes([]*enode.Node{d1.localnode.Node()})) require.NoError(t, err) host3, tcpPort3, 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, NewWakuEnrBitfield(true, true, true, true), WithUDPPort(udpPort3), WithBootnodes([]*enode.Node{d2.localnode.Node()})) + d3, err := NewDiscoveryV5(host3, net.IPv4(127, 0, 0, 1), tcpPort3, prvKey3, utils.NewWakuEnrBitfield(true, true, true, true), 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 c8fb6df7..8b9e274f 100644 --- a/waku/v2/node/wakunode2.go +++ b/waku/v2/node/wakunode2.go @@ -55,6 +55,7 @@ type WakuNode struct { lightPush *lightpush.WakuLightPush rendezvous *rendezvous.RendezvousService store *store.WakuStore + wakuFlag utils.WakuEnrBitfield addrChan chan ma.Multiaddr @@ -132,6 +133,7 @@ func New(ctx context.Context, opts ...WakuNodeOption) (*WakuNode, error) { w.wg = &sync.WaitGroup{} w.addrChan = make(chan ma.Multiaddr, 1024) w.keepAliveFails = make(map[peer.ID]int) + w.wakuFlag = utils.NewWakuEnrBitfield(w.opts.enableLightPush, w.opts.enableFilter, w.opts.enableStore, w.opts.enableRelay) if w.protocolEventSub, err = host.EventBus().Subscribe(new(event.EvtPeerProtocolsUpdated)); err != nil { return nil, err @@ -190,7 +192,7 @@ func (w *WakuNode) logAddress(addr ma.Multiaddr) { // TODO: make this optional depending on DNS Disc being enabled if w.opts.privKey != nil { - enr, ip, err := utils.GetENRandIP(addr, w.opts.privKey) + enr, ip, err := utils.GetENRandIP(addr, w.wakuFlag, w.opts.privKey) if err != nil { log.Error("could not obtain ENR record from multiaddress", err) } else { @@ -388,8 +390,6 @@ func (w *WakuNode) mountRelay(opts ...pubsub.Option) error { } func (w *WakuNode) mountDiscV5() error { - wakuFlag := discv5.NewWakuEnrBitfield(w.opts.enableLightPush, w.opts.enableFilter, w.opts.enableStore, w.opts.enableRelay) - discV5Options := []discv5.DiscoveryV5Option{ discv5.WithBootnodes(w.opts.discV5bootnodes), discv5.WithUDPPort(w.opts.udpPort), @@ -413,7 +413,7 @@ func (w *WakuNode) mountDiscV5() error { return err } - discoveryV5, err := discv5.NewDiscoveryV5(w.Host(), net.ParseIP(ipStr), port, w.opts.privKey, wakuFlag, discV5Options...) + discoveryV5, err := discv5.NewDiscoveryV5(w.Host(), net.ParseIP(ipStr), port, w.opts.privKey, w.wakuFlag, discV5Options...) if err != nil { return err } diff --git a/waku/v2/utils/enr.go b/waku/v2/utils/enr.go new file mode 100644 index 00000000..eef4396e --- /dev/null +++ b/waku/v2/utils/enr.go @@ -0,0 +1,101 @@ +package utils + +import ( + "crypto/ecdsa" + "fmt" + "math" + "net" + "strconv" + + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/libp2p/go-libp2p-core/peer" + ma "github.com/multiformats/go-multiaddr" +) + +const WakuENRField = "waku2" + +// WakuEnrBitfield is a8-bit flag field to indicate Waku capabilities. Only the 4 LSBs are currently defined according to RFC31 (https://rfc.vac.dev/spec/31/). +type WakuEnrBitfield = uint8 + +func NewWakuEnrBitfield(lightpush, filter, store, relay bool) WakuEnrBitfield { + var v uint8 = 0 + + if lightpush { + v |= (1 << 3) + } + + if filter { + v |= (1 << 2) + } + + if store { + v |= (1 << 1) + } + + if relay { + v |= (1 << 0) + } + + return v +} + +func GetENRandIP(addr ma.Multiaddr, wakuFlags WakuEnrBitfield, privK *ecdsa.PrivateKey) (*enode.Node, *net.TCPAddr, error) { + ip, err := addr.ValueForProtocol(ma.P_IP4) + if err != nil { + return nil, nil, err + } + + portStr, err := addr.ValueForProtocol(ma.P_TCP) + if err != nil { + return nil, nil, err + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return nil, nil, err + } + + tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", ip, port)) + if err != nil { + return nil, nil, err + } + + r := &enr.Record{} + + if port > 0 && port <= math.MaxUint16 { + r.Set(enr.TCP(uint16(port))) // lgtm [go/incorrect-integer-conversion] + } else { + return nil, nil, fmt.Errorf("could not set port %d", port) + } + + r.Set(enr.IP(net.ParseIP(ip))) + r.Set(enr.WithEntry(WakuENRField, wakuFlags)) + + err = enode.SignV4(r, privK) + if err != nil { + return nil, nil, err + } + + node, err := enode.New(enode.ValidSchemes, r) + + return node, tcpAddr, err +} + +func EnodeToMultiAddr(node *enode.Node) (ma.Multiaddr, error) { + peerID, err := peer.IDFromPublicKey(&ECDSAPublicKey{node.Pubkey()}) + if err != nil { + return nil, err + } + + return ma.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", node.IP(), node.TCP(), peerID)) +} + +func EnodeToPeerInfo(node *enode.Node) (*peer.AddrInfo, error) { + address, err := EnodeToMultiAddr(node) + if err != nil { + return nil, err + } + + return peer.AddrInfoFromP2pAddr(address) +} diff --git a/waku/v2/utils/enr_test.go b/waku/v2/utils/enr_test.go new file mode 100644 index 00000000..d53e590e --- /dev/null +++ b/waku/v2/utils/enr_test.go @@ -0,0 +1,47 @@ +package utils + +import ( + "fmt" + "net" + "testing" + + gcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/enode" + crypto "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-core/peer" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/stretchr/testify/require" +) + +func TestEnodeToMultiAddr(t *testing.T) { + enr := "enr:-IS4QAmC_o1PMi5DbR4Bh4oHVyQunZblg4bTaottPtBodAhJZvxVlWW-4rXITPNg4mwJ8cW__D9FBDc9N4mdhyMqB-EBgmlkgnY0gmlwhIbRi9KJc2VjcDI1NmsxoQOevTdO6jvv3fRruxguKR-3Ge4bcFsLeAIWEDjrfaigNoN0Y3CCdl8" + + parsedNode := enode.MustParse(enr) + expectedMultiAddr := "/ip4/134.209.139.210/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ" + actualMultiAddr, err := EnodeToMultiAddr(parsedNode) + require.NoError(t, err) + require.Equal(t, expectedMultiAddr, actualMultiAddr.String()) +} + +func TestGetENRandIP(t *testing.T) { + key, _ := gcrypto.GenerateKey() + privKey := crypto.PrivKey((*crypto.Secp256k1PrivateKey)(key)) + id, _ := peer.IDFromPublicKey(privKey.GetPublic()) + + hostAddr := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 9999} + hostMultiAddr, _ := manet.FromNetAddr(hostAddr) + hostInfo, _ := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s", id.Pretty())) + ogMultiaddress := hostMultiAddr.Encapsulate(hostInfo) + + wakuFlag := NewWakuEnrBitfield(true, true, true, true) + + node, resTCPAddr, err := GetENRandIP(ogMultiaddress, wakuFlag, key) + require.NoError(t, err) + require.Equal(t, hostAddr, resTCPAddr) + + parsedNode := enode.MustParse(node.String()) + resMultiaddress, err := EnodeToMultiAddr(parsedNode) + require.NoError(t, err) + require.Equal(t, ogMultiaddress.String(), resMultiaddress.String()) +} diff --git a/waku/v2/utils/peer.go b/waku/v2/utils/peer.go index 3700f5b1..66acbb64 100644 --- a/waku/v2/utils/peer.go +++ b/waku/v2/utils/peer.go @@ -2,20 +2,11 @@ package utils import ( "context" - "crypto/ecdsa" "errors" - "fmt" - "math" "math/rand" - "net" - "strconv" "sync" "time" - ma "github.com/multiformats/go-multiaddr" - - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/peer" @@ -122,62 +113,3 @@ func SelectPeerWithLowestRTT(ctx context.Context, host host.Host, protocolId str return nil, ErrNoPeersAvailable } } - -func EnodeToMultiAddr(node *enode.Node) (ma.Multiaddr, error) { - peerID, err := peer.IDFromPublicKey(&ECDSAPublicKey{node.Pubkey()}) - if err != nil { - return nil, err - } - - return ma.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", node.IP(), node.TCP(), peerID)) -} - -func EnodeToPeerInfo(node *enode.Node) (*peer.AddrInfo, error) { - address, err := EnodeToMultiAddr(node) - if err != nil { - return nil, err - } - - return peer.AddrInfoFromP2pAddr(address) -} - -func GetENRandIP(addr ma.Multiaddr, privK *ecdsa.PrivateKey) (*enode.Node, *net.TCPAddr, error) { - ip, err := addr.ValueForProtocol(ma.P_IP4) - if err != nil { - return nil, nil, err - } - - portStr, err := addr.ValueForProtocol(ma.P_TCP) - if err != nil { - return nil, nil, err - } - - port, err := strconv.Atoi(portStr) - if err != nil { - return nil, nil, err - } - - tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", ip, port)) - if err != nil { - return nil, nil, err - } - - r := &enr.Record{} - - if port > 0 && port <= math.MaxUint16 { - r.Set(enr.TCP(uint16(port))) // lgtm [go/incorrect-integer-conversion] - } else { - return nil, nil, fmt.Errorf("could not set port %d", port) - } - - r.Set(enr.IP(net.ParseIP(ip))) - - err = enode.SignV4(r, privK) - if err != nil { - return nil, nil, err - } - - node, err := enode.New(enode.ValidSchemes, r) - - return node, tcpAddr, err -} diff --git a/waku/v2/utils/peer_test.go b/waku/v2/utils/peer_test.go index db0f5835..f32ae92b 100644 --- a/waku/v2/utils/peer_test.go +++ b/waku/v2/utils/peer_test.go @@ -3,18 +3,10 @@ package utils import ( "context" "crypto/rand" - "fmt" - "net" "testing" "time" - gcrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/p2p/enode" - crypto "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peerstore" - ma "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr/net" "github.com/status-im/go-waku/tests" "github.com/stretchr/testify/require" ) @@ -87,33 +79,3 @@ func TestSelectPeerWithLowestRTT(t *testing.T) { _, err = SelectPeerWithLowestRTT(ctx, h1, proto) require.NoError(t, err) } - -func TestEnodeToMultiAddr(t *testing.T) { - enr := "enr:-IS4QAmC_o1PMi5DbR4Bh4oHVyQunZblg4bTaottPtBodAhJZvxVlWW-4rXITPNg4mwJ8cW__D9FBDc9N4mdhyMqB-EBgmlkgnY0gmlwhIbRi9KJc2VjcDI1NmsxoQOevTdO6jvv3fRruxguKR-3Ge4bcFsLeAIWEDjrfaigNoN0Y3CCdl8" - - parsedNode := enode.MustParse(enr) - expectedMultiAddr := "/ip4/134.209.139.210/tcp/30303/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ" - actualMultiAddr, err := EnodeToMultiAddr(parsedNode) - require.NoError(t, err) - require.Equal(t, expectedMultiAddr, actualMultiAddr.String()) -} - -func TestGetENRandIP(t *testing.T) { - key, _ := gcrypto.GenerateKey() - privKey := crypto.PrivKey((*crypto.Secp256k1PrivateKey)(key)) - id, _ := peer.IDFromPublicKey(privKey.GetPublic()) - - hostAddr := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 9999} - hostMultiAddr, _ := manet.FromNetAddr(hostAddr) - hostInfo, _ := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s", id.Pretty())) - ogMultiaddress := hostMultiAddr.Encapsulate(hostInfo) - - node, resTCPAddr, err := GetENRandIP(ogMultiaddress, key) - require.NoError(t, err) - require.Equal(t, hostAddr, resTCPAddr) - - parsedNode := enode.MustParse(node.String()) - resMultiaddress, err := EnodeToMultiAddr(parsedNode) - require.NoError(t, err) - require.Equal(t, ogMultiaddress.String(), resMultiaddress.String()) -}