mirror of https://github.com/status-im/go-waku.git
164 lines
4.2 KiB
Go
164 lines
4.2 KiB
Go
package enr
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
"github.com/multiformats/go-multiaddr"
|
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
|
)
|
|
|
|
var ErrNoPortAvailable = errors.New("port not available")
|
|
|
|
// WakuENRField is the name of the ENR field that contains information about which protocols are supported by the node
|
|
const WakuENRField = "waku2"
|
|
|
|
// MultiaddrENRField is the name of the ENR field that will contain multiaddresses that cannot be described using the
|
|
// already available ENR fields (i.e. in the case of websocket connections)
|
|
const MultiaddrENRField = "multiaddrs"
|
|
|
|
const ShardingIndicesListEnrField = "rs"
|
|
|
|
const ShardingBitVectorEnrField = "rsv"
|
|
|
|
// 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
|
|
|
|
// NewWakuEnrBitfield creates a WakuEnrBitField whose value will depend on which protocols are enabled in the node
|
|
func NewWakuEnrBitfield(lightpush, filter, store, relay bool) WakuEnrBitfield {
|
|
var v uint8
|
|
|
|
if lightpush {
|
|
v |= (1 << 3)
|
|
}
|
|
|
|
if filter {
|
|
v |= (1 << 2)
|
|
}
|
|
|
|
if store {
|
|
v |= (1 << 1)
|
|
}
|
|
|
|
if relay {
|
|
v |= (1 << 0)
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// EnodeToMultiaddress converts an enode into a multiaddress
|
|
func enodeToMultiAddr(node *enode.Node) (multiaddr.Multiaddr, error) {
|
|
pubKey := utils.EcdsaPubKeyToSecp256k1PublicKey(node.Pubkey())
|
|
peerID, err := peer.IDFromPublicKey(pubKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ipType := "ip4"
|
|
portNumber := node.TCP()
|
|
|
|
if portNumber == 0 {
|
|
return nil, ErrNoPortAvailable
|
|
}
|
|
|
|
if utils.IsIPv6(node.IP().String()) {
|
|
ipType = "ip6"
|
|
var port enr.TCP6
|
|
if err := node.Record().Load(&port); err != nil {
|
|
return nil, err
|
|
}
|
|
portNumber = int(port)
|
|
}
|
|
|
|
return multiaddr.NewMultiaddr(fmt.Sprintf("/%s/%s/tcp/%d/p2p/%s", ipType, node.IP(), portNumber, peerID))
|
|
}
|
|
|
|
// Multiaddress is used to extract all the multiaddresses that are part of a ENR record
|
|
func Multiaddress(node *enode.Node) (peer.ID, []multiaddr.Multiaddr, error) {
|
|
pubKey := utils.EcdsaPubKeyToSecp256k1PublicKey(node.Pubkey())
|
|
peerID, err := peer.IDFromPublicKey(pubKey)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
var result []multiaddr.Multiaddr
|
|
|
|
addr, err := enodeToMultiAddr(node)
|
|
if err != nil {
|
|
if !errors.Is(err, ErrNoPortAvailable) {
|
|
return "", nil, err
|
|
}
|
|
} else {
|
|
result = append(result, addr)
|
|
}
|
|
|
|
var multiaddrRaw []byte
|
|
if err := node.Record().Load(enr.WithEntry(MultiaddrENRField, &multiaddrRaw)); err != nil {
|
|
if !enr.IsNotFound(err) {
|
|
return "", nil, err
|
|
}
|
|
// No multiaddr entry on enr
|
|
return peerID, result, nil
|
|
}
|
|
|
|
if len(multiaddrRaw) < 2 {
|
|
// There was no error loading the multiaddr field, but its length is incorrect
|
|
return peerID, result, nil
|
|
}
|
|
|
|
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 := multiaddr.NewMultiaddrBytes(maRaw)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("invalid multiaddress field length")
|
|
}
|
|
|
|
hostInfoStr := fmt.Sprintf("/p2p/%s", peerID.String())
|
|
_, pID := peer.SplitAddr(addr)
|
|
if pID != "" && pID != peerID {
|
|
// Addresses in the ENR that contain a p2p component are circuit relay addr
|
|
hostInfoStr = "/p2p-circuit" + hostInfoStr
|
|
}
|
|
|
|
hostInfo, err := multiaddr.NewMultiaddr(hostInfoStr)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
result = append(result, addr.Encapsulate(hostInfo))
|
|
|
|
offset += 2 + int(maSize)
|
|
if offset >= len(multiaddrRaw) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return peerID, result, nil
|
|
}
|
|
|
|
// EnodeToPeerInfo extracts the peer ID and multiaddresses defined in an ENR
|
|
func EnodeToPeerInfo(node *enode.Node) (*peer.AddrInfo, error) {
|
|
_, addresses, err := Multiaddress(node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := peer.AddrInfosFromP2pAddrs(addresses...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(res) == 0 {
|
|
return nil, errors.New("could not retrieve peer addresses from enr")
|
|
}
|
|
return &res[0], nil
|
|
}
|