diff --git a/apps/wakunode2/wakunode2.nim b/apps/wakunode2/wakunode2.nim index 381e105da..122df5761 100644 --- a/apps/wakunode2/wakunode2.nim +++ b/apps/wakunode2/wakunode2.nim @@ -269,10 +269,12 @@ proc initNode(conf: WakuNodeConf, else: @[] - wakuFlags = initWakuFlags(conf.lightpush, - conf.filter, - conf.store, - conf.relay) + wakuFlags = CapabilitiesBitfield.init( + lightpush = conf.lightpush, + filter = conf.filter, + store = conf.store, + relay = conf.relay + ) var node: WakuNode diff --git a/examples/v2/publisher.nim b/examples/v2/publisher.nim index 48bae9128..11880ad83 100644 --- a/examples/v2/publisher.nim +++ b/examples/v2/publisher.nim @@ -36,7 +36,7 @@ proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} = nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] ip = ValidIpAddress.init("0.0.0.0") node = WakuNode.new(nodeKey, ip, Port(wakuPort)) - flags = initWakuFlags(lightpush = false, filter = false, store = false, relay = true) + flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) # assumes behind a firewall, so not care about being discoverable node.wakuDiscv5 = WakuDiscoveryV5.new( diff --git a/examples/v2/subscriber.nim b/examples/v2/subscriber.nim index 6c1a2556c..654fa36f9 100644 --- a/examples/v2/subscriber.nim +++ b/examples/v2/subscriber.nim @@ -32,7 +32,7 @@ proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} = nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] ip = ValidIpAddress.init("0.0.0.0") node = WakuNode.new(nodeKey, ip, Port(wakuPort)) - flags = initWakuFlags(lightpush = false, filter = false, store = false, relay = true) + flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) # assumes behind a firewall, so not care about being discoverable node.wakuDiscv5 = WakuDiscoveryV5.new( diff --git a/tests/v2/test_waku_discv5.nim b/tests/v2/test_waku_discv5.nim index ab30f178f..dbc73e22d 100644 --- a/tests/v2/test_waku_discv5.nim +++ b/tests/v2/test_waku_discv5.nim @@ -38,10 +38,12 @@ procSuite "Waku Discovery v5": nodeUdpPort3 = Port(9004) node3 = WakuNode.new(nodeKey3, bindIp, nodeTcpPort3) - flags = initWakuFlags(lightpush = false, - filter = false, - store = false, - relay = true) + flags = CapabilitiesBitfield.init( + lightpush = false, + filter = false, + store = false, + relay = true + ) # E2E relay test paramaters pubSubTopic = "/waku/2/default-waku/proto" @@ -128,10 +130,12 @@ procSuite "Waku Discovery v5": extIp = ValidIpAddress.init("127.0.0.1") expectedMultiAddr = MultiAddress.init("/ip4/200.200.200.200/tcp/9000/wss").tryGet() - flags = initWakuFlags(lightpush = false, - filter = false, - store = false, - relay = true) + flags = CapabilitiesBitfield.init( + lightpush = false, + filter = false, + store = false, + relay = true + ) nodeTcpPort1 = Port(9010) nodeUdpPort1 = Port(9012) diff --git a/tests/v2/test_waku_enr.nim b/tests/v2/test_waku_enr.nim index 147c06b95..f89d2be36 100644 --- a/tests/v2/test_waku_enr.nim +++ b/tests/v2/test_waku_enr.nim @@ -1,14 +1,113 @@ {.used.} import - std/[options, sequtils], - stew/byteutils, + std/options, + stew/[results, byteutils], testutils/unittests import ../../waku/v2/protocol/waku_enr, ./testlib/waku2 -suite "Waku ENR": + +suite "Waku ENR - Capabilities bitfield": + test "check capabilities support": + ## Given + let bitfield: CapabilitiesBitfield = 0b0000_1101u8 # Lightpush, Filter, Relay + + ## Then + check: + bitfield.supportsCapability(Capabilities.Relay) + not bitfield.supportsCapability(Capabilities.Store) + bitfield.supportsCapability(Capabilities.Filter) + bitfield.supportsCapability(Capabilities.Lightpush) + + test "bitfield to capabilities list": + ## Given + let bitfield = CapabilitiesBitfield.init( + relay = true, + store = false, + lightpush = true, + filter = true + ) + + ## When + let caps = bitfield.toCapabilities() + + ## Then + check: + caps == @[Capabilities.Relay, Capabilities.Filter, Capabilities.Lightpush] + + test "encode and extract capabilities from record": + ## Given + let enrkey = generatesecp256k1key() + let caps = CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Store) + + let record = Record.init(1, enrkey, wakuFlags=some(caps)) + + ## When + let bitfieldRes = record.getCapabilitiesField() + + ## Then + check bitfieldRes.isOk() + + let bitfield = bitfieldRes.tryGet() + check: + bitfield.toCapabilities() == @[Capabilities.Relay, Capabilities.Store] + + test "cannot extract capabilities from record": + ## Given + let enrkey = generatesecp256k1key() + let record = Record.init(1, enrkey, wakuFlags=none(CapabilitiesBitfield)) + + ## When + let bitfieldRes = record.getCapabilitiesField() + + ## Then + check bitfieldRes.isErr() + + let err = bitfieldRes.tryError() + check: + err == "Key not found in ENR" + + test "check capabilities on a waku node record": + ## Given + let wakuRecord = "-Hy4QC73_E3B_FkZhsOakaD4pHe-U--UoGASdG9N0F3SFFUDY_jdQbud8" & + "EXVyrlOZ5pZ7VYFBDPMRCENwy87Lh74dFIBgmlkgnY0iXNlY3AyNTZrMaECvNt1jIWbWGp" & + "AWWdlLGYm1E1OjlkQk3ONoxDC5sfw8oOFd2FrdTID" + + ## When + var record: Record + require waku_enr.fromBase64(record, wakuRecord) + + ## Then + check: + record.supportsCapability(Relay) == true + record.supportsCapability(Store) == true + record.supportsCapability(Filter) == false + record.supportsCapability(Lightpush) == false + record.getCapabilities() == @[Capabilities.Relay, Capabilities.Store] + + test "check capabilities on a non-waku node record": + ## Given + # non waku enr, i.e. Ethereum one + let nonWakuEnr = "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2G" & + "xb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNl" & + "Y3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA" + + ## When + var nonWakuEnrRecord: Record + require waku_enr.fromURI(nonWakuEnrRecord, nonWakuEnr) + + ## Then + check: + nonWakuEnrRecord.getCapabilities() == [] + nonWakuEnrRecord.supportsCapability(Relay) == false + nonWakuEnrRecord.supportsCapability(Store) == false + nonWakuEnrRecord.supportsCapability(Filter) == false + nonWakuEnrRecord.supportsCapability(Lightpush) == false + + +suite "Waku ENR - Multiaddresses": test "Parse multiaddr field": let @@ -45,14 +144,13 @@ suite "Waku ENR": enrIp = ValidIpAddress.init("127.0.0.1") enrTcpPort, enrUdpPort = Port(61101) enrKey = generateSecp256k1Key() - wakuFlags = initWakuFlags(false, true, false, true) multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[], MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss")[]] let - record = enr.Record.init(enrKey, some(enrIp), + record = enr.Record.init(1, enrKey, some(enrIp), some(enrTcpPort), some(enrUdpPort), - some(wakuFlags), + none(CapabilitiesBitfield), multiaddrs) typedRecord = record.toTypedRecord.get() @@ -64,11 +162,8 @@ suite "Waku ENR": Port(typedRecord.udp.get()) == enrUdpPort # Check Waku ENR fields - let - decodedFlags = record.get(WAKU_ENR_FIELD, seq[byte])[] - decodedAddrs = record.get(MULTIADDR_ENR_FIELD, seq[byte])[].toMultiAddresses() + let decodedAddrs = record.get(MultiaddrEnrField, seq[byte]).tryGet().toMultiAddresses() check: - decodedFlags == @[wakuFlags.byte] decodedAddrs.contains(MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[]) decodedAddrs.contains(MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss")[]) @@ -81,9 +176,9 @@ suite "Waku ENR": multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr31iDQpSN5Qa882BCjjwgrD")[]] let - record = enr.Record.init(enrKey, some(enrIp), + record = enr.Record.init(1, enrKey, some(enrIp), some(enrTcpPort), some(enrUdpPort), - none(WakuEnrBitfield), + none(CapabilitiesBitfield), multiaddrs) # Check Waku ENR fields @@ -126,92 +221,3 @@ suite "Waku ENR": for knownMultiaddr in knownMultiaddrs: check decodedAddrs.contains(knownMultiaddr) - test "Supports specific capabilities encoded in the ENR": - let - enrIp = ValidIpAddress.init("127.0.0.1") - enrTcpPort, enrUdpPort = Port(60000) - enrKey = generateSecp256k1Key() - multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[]] - - # TODO: Refactor enr.Record.init, provide enums as inputs enr.Record.init(capabilites=[Store,Filter]) - # TODO: safer than a util function and directly using the bits - # test all flag combinations 2^4 = 16 (b0000-b1111) - records = toSeq(0b0000_0000'u8..0b0000_1111'u8) - .mapIt(enr.Record.init(enrKey, - some(enrIp), - some(enrTcpPort), - some(enrUdpPort), - some(uint8(it)), - multiaddrs)) - - # same order:  lightpush | filter| store | relay - expectedCapabilities = @[[false, false, false, false], - [false, false, false, true], - [false, false, true, false], - [false, false, true, true], - [false, true, false, false], - [false, true, false, true], - [false, true, true, false], - [false, true, true, true], - [true, false, false, false], - [true, false, false, true], - [true, false, true, false], - [true, false, true, true], - [true, true, false, false], - [true, true, false, true], - [true, true, true, false], - [true, true, true, true]] - - for i, record in records: - for j, capability in @[Lightpush, Filter, Store, Relay]: - check expectedCapabilities[i][j] == record.supportsCapability(capability) - - test "Get all supported capabilities encoded in the ENR": - let - enrIp = ValidIpAddress.init("127.0.0.1") - enrTcpPort, enrUdpPort = Port(60000) - enrKey = generateSecp256k1Key() - multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[]] - - records = @[0b0000_0000'u8, - 0b0000_1111'u8, - 0b0000_1001'u8, - 0b0000_1110'u8, - 0b0000_1000'u8,] - .mapIt(enr.Record.init(enrKey, - some(enrIp), - some(enrTcpPort), - some(enrUdpPort), - some(uint8(it)), - multiaddrs)) - - # expected capabilities, ordered LSB to MSB - expectedCapabilities: seq[seq[Capabilities]] = @[ - #[0b0000_0000]# @[], - #[0b0000_1111]# @[Relay, Store, Filter, Lightpush], - #[0b0000_1001]# @[Relay, Lightpush], - #[0b0000_1110]# @[Store, Filter, Lightpush], - #[0b0000_1000]# @[Lightpush]] - - for i, actualExpetedTuple in zip(records, expectedCapabilities): - check actualExpetedTuple[0].getCapabilities() == actualExpetedTuple[1] - - test "Get supported capabilities of a non waku node": - - # non waku enr, i.e. Ethereum one - let nonWakuEnr = "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2G"& - "xb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNl"& - "Y3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA" - - var nonWakuEnrRecord: Record - - check: - nonWakuEnrRecord.fromURI(nonWakuEnr) - - # check that it doesn't support any capability and it doesnt't break - check: - nonWakuEnrRecord.getCapabilities() == [] - nonWakuEnrRecord.supportsCapability(Relay) == false - nonWakuEnrRecord.supportsCapability(Store) == false - nonWakuEnrRecord.supportsCapability(Filter) == false - nonWakuEnrRecord.supportsCapability(Lightpush) == false diff --git a/tests/v2/test_waku_peer_exchange.nim b/tests/v2/test_waku_peer_exchange.nim index baebfb9f8..96e80880f 100644 --- a/tests/v2/test_waku_peer_exchange.nim +++ b/tests/v2/test_waku_peer_exchange.nim @@ -89,10 +89,12 @@ procSuite "Waku Peer Exchange": node3 = WakuNode.new(nodeKey3, bindIp, nodeTcpPort3) # todo: px flag - flags = initWakuFlags(lightpush = false, - filter = false, - store = false, - relay = true) + flags = CapabilitiesBitfield.init( + lightpush = false, + filter = false, + store = false, + relay = true + ) # Mount discv5 node1.wakuDiscv5 = WakuDiscoveryV5.new( diff --git a/tools/networkmonitor/networkmonitor.nim b/tools/networkmonitor/networkmonitor.nim index 99910c84b..f00235a13 100644 --- a/tools/networkmonitor/networkmonitor.nim +++ b/tools/networkmonitor/networkmonitor.nim @@ -224,7 +224,7 @@ proc initAndStartNode(conf: NetworkMonitorConf): Result[WakuNode, string] = nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] nodeTcpPort = Port(60000) nodeUdpPort = Port(9000) - flags = initWakuFlags(lightpush = false, filter = false, store = false, relay = true) + flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) try: let diff --git a/waku/v2/node/waku_node.nim b/waku/v2/node/waku_node.nim index d9c6ff620..91d2aeb1b 100644 --- a/waku/v2/node/waku_node.nim +++ b/waku/v2/node/waku_node.nim @@ -135,7 +135,7 @@ type NetConfig* = object enrIp*: Option[ValidIpAddress] enrPort*: Option[Port] discv5UdpPort*: Option[Port] - wakuFlags*: Option[WakuEnrBitfield] + wakuFlags*: Option[CapabilitiesBitfield] bindIp*: ValidIpAddress bindPort*: Port @@ -151,7 +151,7 @@ proc init*( wssEnabled: bool = false, dns4DomainName = none(string), discv5UdpPort = none(Port), - wakuFlags = none(WakuEnrBitfield) + wakuFlags = none(CapabilitiesBitfield) ): T {.raises: [LPError]} = ## Initialize addresses let @@ -230,7 +230,7 @@ proc getEnr*(netConfig: NetConfig, if wakuDiscV5.isSome(): return wakuDiscV5.get().protocol.getRecord() - return enr.Record.init(nodekey, + return enr.Record.init(1, nodekey, netConfig.enrIp, netConfig.enrPort, netConfig.discv5UdpPort, @@ -275,7 +275,7 @@ proc new*(T: type WakuNode, wssEnabled: bool = false, secureKey: string = "", secureCert: string = "", - wakuFlags = none(WakuEnrBitfield), + wakuFlags = none(CapabilitiesBitfield), nameResolver: NameResolver = nil, sendSignedPeerRecord = false, dns4DomainName = none(string), diff --git a/waku/v2/protocol/waku_discv5.nim b/waku/v2/protocol/waku_discv5.nim index de3fcfaf8..da1b1f349 100644 --- a/waku/v2/protocol/waku_discv5.nim +++ b/waku/v2/protocol/waku_discv5.nim @@ -72,10 +72,10 @@ proc addBootstrapNode*(bootstrapAddr: string, bootstrapAddr, reason = enrRes.error proc isWakuNode(node: Node): bool = - let wakuField = node.record.tryGet(WAKU_ENR_FIELD, uint8) + let wakuField = node.record.tryGet(CapabilitiesEnrField, uint8) if wakuField.isSome(): - return wakuField.get().WakuEnrBitfield != 0x00 # True if any flag set to true + return wakuField.get() != 0x00 # True if any flag set to true return false @@ -116,14 +116,14 @@ proc new*(T: type WakuDiscoveryV5, bootstrapEnrs = newSeq[enr.Record](), enrAutoUpdate = false, privateKey: keys.PrivateKey, - flags: WakuEnrBitfield, + flags: CapabilitiesBitfield, multiaddrs = newSeq[MultiAddress](), rng: ref HmacDrbgContext, discv5Config: protocol.DiscoveryConfig = protocol.defaultDiscoveryConfig): T = ## TODO: consider loading from a configurable bootstrap file ## We always add the waku field as specified - var enrInitFields = @[(WAKU_ENR_FIELD, @[flags.byte])] + var enrInitFields = @[(CapabilitiesEnrField, @[flags.byte])] ## Add multiaddresses to ENR if multiaddrs.len > 0: @@ -151,7 +151,7 @@ proc new*(T: type WakuDiscoveryV5, bootstrapNodes: seq[string], enrAutoUpdate = false, privateKey: keys.PrivateKey, - flags: WakuEnrBitfield, + flags: CapabilitiesBitfield, multiaddrs = newSeq[MultiAddress](), rng: ref HmacDrbgContext, discv5Config: protocol.DiscoveryConfig = protocol.defaultDiscoveryConfig): T = diff --git a/waku/v2/protocol/waku_enr.nim b/waku/v2/protocol/waku_enr.nim index e603a92b6..67e3b90c0 100644 --- a/waku/v2/protocol/waku_enr.nim +++ b/waku/v2/protocol/waku_enr.nim @@ -19,14 +19,17 @@ import export enr, crypto, multiaddress, net const - MULTIADDR_ENR_FIELD* = "multiaddrs" - WAKU_ENR_FIELD* = "waku2" + MultiaddrEnrField* = "multiaddrs" + CapabilitiesEnrField* = "waku2" + + +## Capabilities type ## 8-bit flag field to indicate Waku capabilities. ## Only the 4 LSBs are currently defined according ## to RFC31 (https://rfc.vac.dev/spec/31/). - WakuEnrBitfield* = uint8 + CapabilitiesBitfield* = distinct uint8 ## See: https://rfc.vac.dev/spec/31/#waku2-enr-key ## each enum numbers maps to a bit (where 0 is the LSB) @@ -34,7 +37,59 @@ type Relay = 0, Store = 1, Filter = 2, - Lightpush = 3, + Lightpush = 3 + + +func init*(T: type CapabilitiesBitfield, lightpush, filter, store, relay: bool): T = + ## Creates an waku2 ENR flag bit field according to RFC 31 (https://rfc.vac.dev/spec/31/) + var bitfield: uint8 + if relay: bitfield.setBit(0) + if store: bitfield.setBit(1) + if filter: bitfield.setBit(2) + if lightpush: bitfield.setBit(3) + CapabilitiesBitfield(bitfield) + +func init*(T: type CapabilitiesBitfield, caps: varargs[Capabilities]): T = + ## Creates an waku2 ENR flag bit field according to RFC 31 (https://rfc.vac.dev/spec/31/) + var bitfield: uint8 + for cap in caps: + bitfield.setBit(ord(cap)) + CapabilitiesBitfield(bitfield) + +converter toCapabilitiesBitfield*(field: uint8): CapabilitiesBitfield = + CapabilitiesBitfield(field) + +proc supportsCapability*(bitfield: CapabilitiesBitfield, cap: Capabilities): bool = + testBit(bitfield.uint8, ord(cap)) + +func toCapabilities*(bitfield: CapabilitiesBitfield): seq[Capabilities] = + toSeq(Capabilities.low..Capabilities.high).filterIt(supportsCapability(bitfield, it)) + + +func toFieldPair*(caps: CapabilitiesBitfield): FieldPair = + toFieldPair(CapabilitiesEnrField, @[caps.uint8]) + +proc getCapabilitiesField*(r: Record): EnrResult[CapabilitiesBitfield] = + let field = ?r.get(CapabilitiesEnrField, seq[uint8]) + ok(CapabilitiesBitfield(field[0])) + + +proc supportsCapability*(r: Record, cap: Capabilities): bool = + let bitfield = getCapabilitiesField(r) + if bitfield.isErr(): + return false + + bitfield.value.supportsCapability(cap) + +proc getCapabilities*(r: Record): seq[Capabilities] = + let bitfield = getCapabilitiesField(r) + if bitfield.isErr(): + return @[] + + bitfield.value.toCapabilities() + + +## Multiaddress func getRawField*(multiaddrs: seq[MultiAddress]): seq[byte] = var fieldRaw: seq[byte] @@ -93,28 +148,6 @@ func readBytes(rawBytes: seq[byte], numBytes: int, pos: var int = 0): Result[seq return ok(slicedSeq) -################ -# Public utils # -################ - -func initWakuFlags*(lightpush, filter, store, relay: bool): WakuEnrBitfield = - ## Creates an waku2 ENR flag bit field according to RFC 31 (https://rfc.vac.dev/spec/31/) - var v = 0b0000_0000'u8 - if lightpush: v.setBit(3) - if filter: v.setBit(2) - if store: v.setBit(1) - if relay: v.setBit(0) - - # TODO: With the changes in this PR, this can be refactored? Using the enum? - # Perhaps refactor to: - # WaKuEnr.enr.Record.init(..., capabilities=[Store, Lightpush]) - # WaKuEnr.enr.Record.init(..., capabilities=[Store, Lightpush, Relay, Filter]) - - # Safer also since we dont inject WakuEnrBitfield, and we let this package - # handle the bits according to the capabilities - - return v.WakuEnrBitfield - func toMultiAddresses*(multiaddrsField: seq[byte]): seq[MultiAddress] = ## Parses a `multiaddrs` ENR field according to ## https://rfc.vac.dev/spec/31/ @@ -147,11 +180,16 @@ func toMultiAddresses*(multiaddrsField: seq[byte]): seq[MultiAddress] = return multiaddrs + +## ENR + func init*(T: type enr.Record, + seqNum: uint64, privateKey: crypto.PrivateKey, - enrIp: Option[ValidIpAddress], - enrTcpPort, enrUdpPort: Option[Port], - wakuFlags = none(WakuEnrBitfield), + enrIp = none(ValidIpAddress), + enrTcpPort = none(Port), + enrUdpPort = none(Port), + wakuFlags = none(CapabilitiesBitfield), multiaddrs: seq[MultiAddress] = @[]): T = assert privateKey.scheme == PKScheme.Secp256k1 @@ -160,27 +198,22 @@ func init*(T: type enr.Record, var wakuEnrFields: seq[FieldPair] # `waku2` field - if wakuFlags.isSome: - wakuEnrFields.add(toFieldPair(WAKU_ENR_FIELD, @[wakuFlags.get().byte])) + if wakuFlags.isSome(): + wakuEnrFields.add(toFieldPair(wakuFlags.get())) # `multiaddrs` field if multiaddrs.len > 0: - wakuEnrFields.add(multiaddrs.stripPeerIds().toFieldPair) + wakuEnrFields.add(multiaddrs.stripPeerIds().toFieldPair()) let rawPk = privateKey.getRawBytes().expect("Private key is valid") pk = keys.PrivateKey.fromRaw(rawPk).expect("Raw private key is of valid length") - enr = enr.Record.init(1, pk, - enrIp, enrTcpPort, enrUdpPort, - wakuEnrFields).expect("Record within size limits") - return enr - -proc supportsCapability*(r: Record, capability: Capabilities): bool = - let enrCapabilities = r.get(WAKU_ENR_FIELD, seq[byte]) - if enrCapabilities.isOk(): - return testBit(enrCapabilities.get()[0], capability.ord) - return false - -proc getCapabilities*(r: Record): seq[Capabilities] = - return toSeq(Capabilities.low..Capabilities.high).filterIt(r.supportsCapability(it)) + enr.Record.init( + seqNum=seqNum, + pk=pk, + ip=enrIp, + tcpPort=enrTcpPort, + udpPort=enrUdpPort, + extraFields=wakuEnrFields + ).expect("Record within size limits")