2024-06-28 10:34:57 +00:00
|
|
|
|
{.push raises: [].}
|
2023-04-17 13:21:20 +00:00
|
|
|
|
|
2024-10-24 12:31:04 +00:00
|
|
|
|
import
|
|
|
|
|
std/[options, bitops, sequtils, net, tables], results, eth/keys, libp2p/crypto/crypto
|
|
|
|
|
import ../common/enr, ../waku_core/codecs
|
2023-04-17 13:21:20 +00:00
|
|
|
|
|
2024-03-15 23:08:47 +00:00
|
|
|
|
const CapabilitiesEnrField* = "waku2"
|
2023-04-17 13:21:20 +00:00
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
## 8-bit flag field to indicate Waku node capabilities.
|
|
|
|
|
## Only the 4 LSBs are currently defined according
|
|
|
|
|
## to RFC31 (https://rfc.vac.dev/spec/31/).
|
|
|
|
|
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)
|
2024-03-15 23:08:47 +00:00
|
|
|
|
Capabilities* {.pure.} = enum
|
|
|
|
|
Relay = 0
|
|
|
|
|
Store = 1
|
|
|
|
|
Filter = 2
|
2023-04-17 13:21:20 +00:00
|
|
|
|
Lightpush = 3
|
2024-08-13 11:27:34 +00:00
|
|
|
|
Sync = 4
|
2023-04-17 13:21:20 +00:00
|
|
|
|
|
2024-10-24 12:31:04 +00:00
|
|
|
|
const capabilityToCodec = {
|
|
|
|
|
Capabilities.Relay: WakuRelayCodec,
|
|
|
|
|
Capabilities.Store: WakuStoreCodec,
|
|
|
|
|
Capabilities.Filter: WakuFilterSubscribeCodec,
|
|
|
|
|
Capabilities.Lightpush: WakuLightPushCodec,
|
|
|
|
|
Capabilities.Sync: WakuSyncCodec,
|
|
|
|
|
}.toTable
|
|
|
|
|
|
2024-08-13 11:27:34 +00:00
|
|
|
|
func init*(
|
|
|
|
|
T: type CapabilitiesBitfield, lightpush, filter, store, relay, sync: bool = false
|
|
|
|
|
): T =
|
2023-04-17 13:21:20 +00:00
|
|
|
|
## Creates an waku2 ENR flag bit field according to RFC 31 (https://rfc.vac.dev/spec/31/)
|
|
|
|
|
var bitfield: uint8
|
2024-03-15 23:08:47 +00:00
|
|
|
|
if relay:
|
|
|
|
|
bitfield.setBit(0)
|
|
|
|
|
if store:
|
|
|
|
|
bitfield.setBit(1)
|
|
|
|
|
if filter:
|
|
|
|
|
bitfield.setBit(2)
|
|
|
|
|
if lightpush:
|
|
|
|
|
bitfield.setBit(3)
|
2024-08-13 11:27:34 +00:00
|
|
|
|
if sync:
|
|
|
|
|
bitfield.setBit(4)
|
2023-04-17 13:21:20 +00:00
|
|
|
|
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] =
|
2024-03-15 23:08:47 +00:00
|
|
|
|
toSeq(Capabilities.low .. Capabilities.high).filterIt(
|
|
|
|
|
supportsCapability(bitfield, it)
|
|
|
|
|
)
|
2023-04-17 13:21:20 +00:00
|
|
|
|
|
|
|
|
|
# ENR builder extension
|
|
|
|
|
|
|
|
|
|
proc withWakuCapabilities*(builder: var EnrBuilder, caps: CapabilitiesBitfield) =
|
|
|
|
|
builder.addFieldPair(CapabilitiesEnrField, @[caps.uint8])
|
|
|
|
|
|
|
|
|
|
proc withWakuCapabilities*(builder: var EnrBuilder, caps: varargs[Capabilities]) =
|
|
|
|
|
withWakuCapabilities(builder, CapabilitiesBitfield.init(caps))
|
|
|
|
|
|
|
|
|
|
proc withWakuCapabilities*(builder: var EnrBuilder, caps: openArray[Capabilities]) =
|
|
|
|
|
withWakuCapabilities(builder, CapabilitiesBitfield.init(@caps))
|
|
|
|
|
|
|
|
|
|
# ENR record accessors (e.g., Record, TypedRecord, etc.)
|
|
|
|
|
|
|
|
|
|
func waku2*(record: TypedRecord): Option[CapabilitiesBitfield] =
|
|
|
|
|
let field = record.tryGet(CapabilitiesEnrField, seq[uint8])
|
|
|
|
|
if field.isNone():
|
|
|
|
|
return none(CapabilitiesBitfield)
|
|
|
|
|
|
2023-08-08 14:22:53 +00:00
|
|
|
|
if field.get().len != 1:
|
|
|
|
|
return none(CapabilitiesBitfield)
|
|
|
|
|
|
2023-04-17 13:21:20 +00:00
|
|
|
|
some(CapabilitiesBitfield(field.get()[0]))
|
|
|
|
|
|
|
|
|
|
proc supportsCapability*(r: Record, cap: Capabilities): bool =
|
|
|
|
|
let recordRes = r.toTyped()
|
|
|
|
|
if recordRes.isErr():
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
let bitfieldOpt = recordRes.value.waku2
|
|
|
|
|
if bitfieldOpt.isNone():
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
let bitfield = bitfieldOpt.get()
|
|
|
|
|
bitfield.supportsCapability(cap)
|
|
|
|
|
|
|
|
|
|
proc getCapabilities*(r: Record): seq[Capabilities] =
|
|
|
|
|
let recordRes = r.toTyped()
|
|
|
|
|
if recordRes.isErr():
|
|
|
|
|
return @[]
|
|
|
|
|
|
|
|
|
|
let bitfieldOpt = recordRes.value.waku2
|
|
|
|
|
if bitfieldOpt.isNone():
|
|
|
|
|
return @[]
|
|
|
|
|
|
|
|
|
|
let bitfield = bitfieldOpt.get()
|
|
|
|
|
bitfield.toCapabilities()
|
2024-10-24 12:31:04 +00:00
|
|
|
|
|
|
|
|
|
proc getCapabilitiesCodecs*(r: Record): seq[string] {.raises: [ValueError].} =
|
|
|
|
|
let capabilities = r.getCapabilities()
|
|
|
|
|
return capabilities.mapIt(capabilityToCodec[it])
|