diff --git a/waku/v2/utils/enr.go b/waku/v2/utils/enr.go index eef4396e..2c8d64f6 100644 --- a/waku/v2/utils/enr.go +++ b/waku/v2/utils/enr.go @@ -2,6 +2,8 @@ package utils import ( "crypto/ecdsa" + "encoding/binary" + "errors" "fmt" "math" "net" @@ -14,6 +16,7 @@ import ( ) const WakuENRField = "waku2" +const MultiaddrENRField = "multiaddrs" // 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 @@ -69,6 +72,44 @@ func GetENRandIP(addr ma.Multiaddr, wakuFlags WakuEnrBitfield, privK *ecdsa.Priv return nil, nil, fmt.Errorf("could not set port %d", port) } + var multiaddrItems []ma.Multiaddr + + // 31/WAKU2-ENR + + _, err = addr.ValueForProtocol(ma.P_WS) + if err == nil { + multiaddrItems = append(multiaddrItems, addr) + } + + _, err = addr.ValueForProtocol(ma.P_WSS) + if err == nil { + multiaddrItems = append(multiaddrItems, addr) + } + + p2p, err := addr.ValueForProtocol(ma.P_P2P) + if err != nil { + return nil, nil, err + } + + p2pAddr, err := ma.NewMultiaddr("/p2p/" + p2p) + if err != nil { + return nil, nil, fmt.Errorf("Could not create p2p addr: %w", err) + } + + var fieldRaw []byte + for _, ma := range multiaddrItems { + maRaw := ma.Decapsulate(p2pAddr).Bytes() + maSize := make([]byte, 2) + binary.BigEndian.PutUint16(maSize, uint16(len(maRaw))) + + fieldRaw = append(fieldRaw, maSize...) + fieldRaw = append(fieldRaw, maRaw...) + } + + if len(fieldRaw) != 0 { + r.Set(enr.WithEntry(MultiaddrENRField, fieldRaw)) + } + r.Set(enr.IP(net.ParseIP(ip))) r.Set(enr.WithEntry(WakuENRField, wakuFlags)) @@ -91,6 +132,53 @@ func EnodeToMultiAddr(node *enode.Node) (ma.Multiaddr, error) { return ma.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", node.IP(), node.TCP(), peerID)) } +func Multiaddress(node *enode.Node) ([]ma.Multiaddr, error) { + peerID, err := peer.IDFromPublicKey(&ECDSAPublicKey{node.Pubkey()}) + if err != nil { + return nil, err + } + + var multiaddrRaw []byte + if err := node.Record().Load(enr.WithEntry(MultiaddrENRField, &multiaddrRaw)); err != nil { + if !enr.IsNotFound(err) { + log.Error("could not retrieve multiaddress field for node ", node) + } + return nil, err + } + + if len(multiaddrRaw) < 2 { + return nil, errors.New("invalid multiaddress field length") + } + + hostInfo, err := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s", peerID.Pretty())) + if err != nil { + return nil, err + } + + var result []ma.Multiaddr + offset := 0 + for { + maSize := binary.BigEndian.Uint16(multiaddrRaw[offset : offset+2]) + if len(multiaddrRaw) < offset+2+int(maSize) { + return nil, errors.New("invalid multiaddress field length") + } + maRaw := multiaddrRaw[offset+2 : offset+2+int(maSize)] + addr, err := ma.NewMultiaddrBytes(maRaw) + if err != nil { + return nil, fmt.Errorf("invalid multiaddress field length") + } + + result = append(result, addr.Encapsulate(hostInfo)) + + offset += 2 + int(maSize) + if offset >= len(multiaddrRaw) { + break + } + } + + return result, nil +} + func EnodeToPeerInfo(node *enode.Node) (*peer.AddrInfo, error) { address, err := EnodeToMultiAddr(node) if err != nil { diff --git a/waku/v2/utils/enr_test.go b/waku/v2/utils/enr_test.go index d53e590e..bc8c5541 100644 --- a/waku/v2/utils/enr_test.go +++ b/waku/v2/utils/enr_test.go @@ -45,3 +45,19 @@ func TestGetENRandIP(t *testing.T) { require.NoError(t, err) require.Equal(t, ogMultiaddress.String(), resMultiaddress.String()) } + +func TestMultiaddr(t *testing.T) { + key, _ := gcrypto.GenerateKey() + privKey := crypto.PrivKey((*crypto.Secp256k1PrivateKey)(key)) + id, _ := peer.IDFromPublicKey(privKey.GetPublic()) + ogMultiaddress, _ := ma.NewMultiaddr("/ip4/10.0.0.241/tcp/60001/ws/p2p/" + id.Pretty()) + wakuFlag := NewWakuEnrBitfield(true, true, true, true) + + node, _, err := GetENRandIP(ogMultiaddress, wakuFlag, key) + require.NoError(t, err) + + multiaddresses, err := Multiaddress(node) + require.NoError(t, err) + require.Len(t, multiaddresses, 1) + require.True(t, ogMultiaddress.Equal(multiaddresses[0])) +}