mirror of
https://github.com/waku-org/nwaku.git
synced 2025-01-14 08:57:14 +00:00
feat(common): added the enr builder
This commit is contained in:
parent
622ec27fcd
commit
1995bbec6c
@ -5,4 +5,5 @@ import
|
||||
./common/test_envvar_serialization,
|
||||
./common/test_confutils_envvar,
|
||||
./common/test_protobuf_validation,
|
||||
./common/test_enr_builder,
|
||||
./common/test_sqlite_migrations
|
||||
|
133
tests/common/test_enr_builder.nim
Normal file
133
tests/common/test_enr_builder.nim
Normal file
@ -0,0 +1,133 @@
|
||||
{.used.}
|
||||
|
||||
import
|
||||
std/options ,
|
||||
stew/results,
|
||||
stew/shims/net,
|
||||
testutils/unittests
|
||||
import
|
||||
../../waku/common/enr,
|
||||
../v2/testlib/waku2
|
||||
|
||||
|
||||
suite "nim-eth ENR - builder":
|
||||
|
||||
test "Non-supported private key (ECDSA)":
|
||||
## Given
|
||||
let privateKey = generateEcdsaKey()
|
||||
|
||||
## Then
|
||||
expect Defect:
|
||||
discard EnrBuilder.init(privateKey)
|
||||
|
||||
test "Supported private key (Secp256k1)":
|
||||
let
|
||||
seqNum = 1u64
|
||||
privateKey = generateSecp256k1Key()
|
||||
|
||||
let expectedPubKey = privateKey.getPublicKey().get().getRawBytes().get()
|
||||
|
||||
## When
|
||||
var builder = EnrBuilder.init(privateKey, seqNum)
|
||||
let enrRes = builder.build()
|
||||
|
||||
## Then
|
||||
check enrRes.isOk()
|
||||
|
||||
let record = enrRes.tryGet().toTypedRecord().get()
|
||||
check:
|
||||
@(record.secp256k1.get()) == expectedPubKey
|
||||
|
||||
|
||||
suite "nim-eth ENR - builder ext: IP address and TCP/UDP ports":
|
||||
|
||||
test "EIP-778 test vector":
|
||||
## Given
|
||||
# Test vector from EIP-778
|
||||
# See: https://eips.ethereum.org/EIPS/eip-778#test-vectors
|
||||
let expectedEnr = "-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04j" &
|
||||
"RzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJ" &
|
||||
"c2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0x" &
|
||||
"OIN1ZHCCdl8"
|
||||
|
||||
let
|
||||
seqNum = 1u64
|
||||
privateKey = ethSecp256k1Key("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
||||
enrIpAddr = ValidIpAddress.init("127.0.0.1")
|
||||
enrUdpPort = Port(30303)
|
||||
|
||||
## When
|
||||
var builder = EnrBuilder.init(privateKey, seqNum)
|
||||
builder.withIpAddressAndPorts(ipAddr=some(enrIpAddr), udpPort=some(enrUdpPort))
|
||||
|
||||
let enrRes = builder.build()
|
||||
|
||||
## Then
|
||||
check enrRes.isOk()
|
||||
|
||||
let record = enrRes.tryGet().toBase64()
|
||||
check:
|
||||
record == expectedEnr
|
||||
|
||||
test "IPv4 and TCP port":
|
||||
let
|
||||
seqNum = 1u64
|
||||
privateKey = generateSecp256k1Key()
|
||||
|
||||
enrIpAddr = ValidIpAddress.init("127.0.0.1")
|
||||
enrTcpPort = Port(30301)
|
||||
|
||||
let expectedPubKey = privateKey.getPublicKey().get().getRawBytes().get()
|
||||
|
||||
## When
|
||||
var builder = EnrBuilder.init(privateKey, seqNum)
|
||||
builder.withIpAddressAndPorts(
|
||||
ipAddr=some(enrIpAddr),
|
||||
tcpPort=some(enrTcpPort),
|
||||
)
|
||||
|
||||
let enrRes = builder.build()
|
||||
|
||||
## Then
|
||||
check enrRes.isOk()
|
||||
|
||||
let record = enrRes.tryGet().toTypedRecord().get()
|
||||
check:
|
||||
@(record.secp256k1.get()) == expectedPubKey
|
||||
record.ip == some(enrIpAddr.address_v4)
|
||||
record.tcp == some(enrTcpPort.int)
|
||||
record.udp == none(int)
|
||||
record.ip6 == none(array[0..15, byte])
|
||||
|
||||
test "IPv6 and UDP port":
|
||||
let
|
||||
seqNum = 1u64
|
||||
privateKey = generateSecp256k1Key()
|
||||
|
||||
enrIpAddr = ValidIpAddress.init("::1")
|
||||
enrUdpPort = Port(30301)
|
||||
|
||||
let expectedPubKey = privateKey.getPublicKey().get().getRawBytes().get()
|
||||
|
||||
## When
|
||||
var builder = EnrBuilder.init(privateKey, seqNum)
|
||||
builder.withIpAddressAndPorts(
|
||||
ipAddr=some(enrIpAddr),
|
||||
udpPort=some(enrUdpPort),
|
||||
)
|
||||
|
||||
let enrRes = builder.build()
|
||||
|
||||
## Then
|
||||
check enrRes.isOk()
|
||||
|
||||
let record = enrRes.tryGet().toTypedRecord().get()
|
||||
check:
|
||||
@(record.secp256k1.get()) == expectedPubKey
|
||||
record.ip == none(array[0..3, byte])
|
||||
record.tcp == none(int)
|
||||
record.udp == none(int)
|
||||
record.ip6 == some(enrIpAddr.address_v6)
|
||||
record.tcp6 == none(int)
|
||||
record.udp6 == some(enrUdpPort.int)
|
@ -2,7 +2,9 @@ import
|
||||
std/options,
|
||||
stew/byteutils,
|
||||
libp2p/switch,
|
||||
libp2p/builders
|
||||
libp2p/builders,
|
||||
libp2p/crypto/crypto as libp2p_keys,
|
||||
eth/keys as eth_keys
|
||||
import
|
||||
../../../waku/v2/protocol/waku_message,
|
||||
./common
|
||||
@ -12,17 +14,20 @@ export switch
|
||||
|
||||
# Switch
|
||||
|
||||
proc generateEcdsaKey*(): PrivateKey =
|
||||
PrivateKey.random(ECDSA, rng[]).get()
|
||||
proc generateEcdsaKey*(): libp2p_keys.PrivateKey =
|
||||
libp2p_keys.PrivateKey.random(ECDSA, rng[]).get()
|
||||
|
||||
proc generateEcdsaKeyPair*(): KeyPair =
|
||||
KeyPair.random(ECDSA, rng[]).get()
|
||||
proc generateEcdsaKeyPair*(): libp2p_keys.KeyPair =
|
||||
libp2p_keys.KeyPair.random(ECDSA, rng[]).get()
|
||||
|
||||
proc generateSecp256k1Key*(): PrivateKey =
|
||||
PrivateKey.random(Secp256k1, rng[]).get()
|
||||
proc generateSecp256k1Key*(): libp2p_keys.PrivateKey =
|
||||
libp2p_keys.PrivateKey.random(Secp256k1, rng[]).get()
|
||||
|
||||
proc ethSecp256k1Key*(hex: string): eth_keys.PrivateKey =
|
||||
eth_keys.PrivateKey.fromHex(hex).get()
|
||||
|
||||
|
||||
proc newTestSwitch*(key=none(PrivateKey), address=none(MultiAddress)): Switch =
|
||||
proc newTestSwitch*(key=none(libp2p_keys.PrivateKey), address=none(MultiAddress)): Switch =
|
||||
let peerKey = key.get(generateSecp256k1Key())
|
||||
let peerAddr = address.get(MultiAddress.init("/ip4/127.0.0.1/tcp/0").get())
|
||||
return newStandardSwitch(some(peerKey), addrs=peerAddr)
|
||||
|
107
waku/common/enr.nim
Normal file
107
waku/common/enr.nim
Normal file
@ -0,0 +1,107 @@
|
||||
## An extension wrapper around nim-eth's ENR module
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
|
||||
import
|
||||
std/options,
|
||||
stew/results,
|
||||
stew/shims/net,
|
||||
eth/keys as eth_keys,
|
||||
eth/p2p/discoveryv5/enr,
|
||||
libp2p/crypto/crypto as libp2p_crypto
|
||||
|
||||
export enr
|
||||
|
||||
|
||||
## Builder
|
||||
|
||||
type EnrBuilder* = object
|
||||
seqNumber: uint64
|
||||
privateKey: eth_keys.PrivateKey
|
||||
fields: seq[FieldPair]
|
||||
|
||||
|
||||
proc init*(T: type EnrBuilder, key: eth_keys.PrivateKey, seqNum: uint64 = 1): EnrBuilder =
|
||||
EnrBuilder(
|
||||
seqNumber: seqNum,
|
||||
privateKey: key,
|
||||
fields: newSeq[FieldPair]()
|
||||
)
|
||||
|
||||
proc init*(T: type EnrBuilder, key: libp2p_crypto.PrivateKey, seqNum: uint64 = 1): EnrBuilder =
|
||||
# TODO: Inconvenient runtime assertion. Move this assertion to compile time
|
||||
if key.scheme != PKScheme.Secp256k1:
|
||||
raise newException(Defect, "invalid private key scheme")
|
||||
|
||||
let
|
||||
bytes = key.getRawBytes().expect("Private key is valid")
|
||||
privateKey = eth_keys.PrivateKey.fromRaw(bytes).expect("Raw private key is of valid length")
|
||||
|
||||
EnrBuilder.init(key=privateKey, seqNum=seqNum)
|
||||
|
||||
proc addFieldPair*(builder: var EnrBuilder, pair: FieldPair) =
|
||||
builder.fields.add(pair)
|
||||
|
||||
proc addFieldPair*[V](builder: var EnrBuilder, key: string, value: V) =
|
||||
builder.addFieldPair(toFieldPair(key, value))
|
||||
|
||||
proc build*(builder: EnrBuilder): EnrResult[enr.Record] =
|
||||
# Note that nim-eth's `Record.init` does not deduplicate the field pairs.
|
||||
# See: https://github.com/status-im/nim-eth/blob/4b22fcd/eth/p2p/discoveryv5/enr.nim#L143-L144
|
||||
enr.Record.init(
|
||||
seqNum = builder.seqNumber,
|
||||
pk = builder.privateKey,
|
||||
ip = none(ValidIpAddress),
|
||||
tcpPort = none(Port),
|
||||
udpPort = none(Port),
|
||||
extraFields = builder.fields
|
||||
)
|
||||
|
||||
|
||||
## Builder extension: IP address and TCP/UDP ports
|
||||
|
||||
proc addAddressAndPorts(builder: var EnrBuilder, ip: ValidIpAddress, tcpPort, udpPort: Option[Port]) =
|
||||
# Based on: https://github.com/status-im/nim-eth/blob/4b22fcd/eth/p2p/discoveryv5/enr.nim#L166
|
||||
let isV6 = ip.family == IPv6
|
||||
|
||||
let ipField = if isV6: toFieldPair("ip6", ip.address_v6)
|
||||
else: toFieldPair("ip", ip.address_v4)
|
||||
builder.addFieldPair(ipField)
|
||||
|
||||
if tcpPort.isSome():
|
||||
let
|
||||
tcpPortFieldKey = if isV6: "tcp6" else: "tcp"
|
||||
tcpPortFieldValue = tcpPort.get()
|
||||
builder.addFieldPair(tcpPortFieldKey, tcpPortFieldValue.uint16)
|
||||
|
||||
if udpPort.isSome():
|
||||
let
|
||||
udpPortFieldKey = if isV6: "udp6" else: "udp"
|
||||
udpPortFieldValue = udpPort.get()
|
||||
builder.addFieldPair(udpPortFieldKey, udpPortFieldValue.uint16)
|
||||
|
||||
proc addPorts(builder: var EnrBuilder, tcp, udp: Option[Port]) =
|
||||
# Based on: https://github.com/status-im/nim-eth/blob/4b22fcd/eth/p2p/discoveryv5/enr.nim#L166
|
||||
|
||||
if tcp.isSome():
|
||||
let tcpPort = tcp.get()
|
||||
builder.addFieldPair("tcp", tcpPort.uint16)
|
||||
|
||||
if udp.isSome():
|
||||
let udpPort = udp.get()
|
||||
builder.addFieldPair("udp", udpPort.uint16)
|
||||
|
||||
|
||||
proc withIpAddressAndPorts*(builder: var EnrBuilder,
|
||||
ipAddr = none(ValidIpAddress),
|
||||
tcpPort = none(Port),
|
||||
udpPort = none(Port)) =
|
||||
if ipAddr.isSome():
|
||||
addAddressAndPorts(builder, ipAddr.get(), tcpPort, udpPort)
|
||||
else:
|
||||
addPorts(builder, tcpPort, udpPort)
|
||||
|
@ -66,6 +66,7 @@ func toCapabilities*(bitfield: CapabilitiesBitfield): seq[Capabilities] =
|
||||
toSeq(Capabilities.low..Capabilities.high).filterIt(supportsCapability(bitfield, it))
|
||||
|
||||
|
||||
## TODO: Turn into an EnrBuilder extension
|
||||
func toFieldPair*(caps: CapabilitiesBitfield): FieldPair =
|
||||
toFieldPair(CapabilitiesEnrField, @[caps.uint8])
|
||||
|
||||
@ -190,7 +191,8 @@ func init*(T: type enr.Record,
|
||||
enrTcpPort = none(Port),
|
||||
enrUdpPort = none(Port),
|
||||
wakuFlags = none(CapabilitiesBitfield),
|
||||
multiaddrs: seq[MultiAddress] = @[]): T =
|
||||
multiaddrs: seq[MultiAddress] = @[]): T {.
|
||||
deprecated: "Use Waku commons EnrBuilder instead" .} =
|
||||
|
||||
assert privateKey.scheme == PKScheme.Secp256k1
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user