2022-07-21 08:58:17 +00:00
|
|
|
# Copyright (c) 2019-2022 Status Research & Development GmbH
|
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2021-03-29 13:26:34 +00:00
|
|
|
{.used.}
|
|
|
|
|
2020-02-11 16:25:31 +00:00
|
|
|
import
|
2021-12-11 18:12:55 +00:00
|
|
|
std/[options, sequtils],
|
|
|
|
unittest2,
|
2022-07-21 08:58:17 +00:00
|
|
|
stew/shims/net,
|
2021-04-06 11:33:24 +00:00
|
|
|
../../eth/p2p/discoveryv5/enr, ../../eth/[keys, rlp]
|
2019-12-10 18:34:57 +00:00
|
|
|
|
2020-07-07 08:56:26 +00:00
|
|
|
let rng = newRng()
|
|
|
|
|
2019-12-10 18:34:57 +00:00
|
|
|
suite "ENR":
|
|
|
|
test "Serialization":
|
2020-04-04 16:44:01 +00:00
|
|
|
var pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
2020-04-29 22:11:03 +00:00
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7, 8]})[]
|
2021-10-19 12:13:09 +00:00
|
|
|
check($r == """(123, id: "v4", ip: 5.6.7.8, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, udp: 1234)""")
|
2019-12-10 18:34:57 +00:00
|
|
|
let uri = r.toURI()
|
|
|
|
var r2: Record
|
|
|
|
let sigValid = r2.fromURI(uri)
|
2020-07-10 23:28:53 +00:00
|
|
|
check(sigValid)
|
|
|
|
check($r2 == $r)
|
|
|
|
check(r2.raw == r.raw)
|
|
|
|
|
|
|
|
test "RLP serialisation":
|
|
|
|
var pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7, 8]})[]
|
2021-10-19 12:13:09 +00:00
|
|
|
check($r == """(123, id: "v4", ip: 5.6.7.8, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, udp: 1234)""")
|
2020-07-10 23:28:53 +00:00
|
|
|
let encoded = rlp.encode(r)
|
|
|
|
let decoded = rlp.decode(encoded, enr.Record)
|
|
|
|
check($decoded == $r)
|
|
|
|
check(decoded.raw == r.raw)
|
|
|
|
|
|
|
|
test "RLP deserialisation without data":
|
|
|
|
expect ValueError:
|
|
|
|
let decoded = rlp.decode([], enr.Record)
|
|
|
|
|
|
|
|
var r: Record
|
|
|
|
check not fromBytes(r, [])
|
|
|
|
|
2021-10-19 07:26:14 +00:00
|
|
|
test "Base64 deserialsation without data":
|
2020-07-10 23:28:53 +00:00
|
|
|
var r: Record
|
|
|
|
let sigValid = r.fromURI("enr:")
|
|
|
|
check(not sigValid)
|
2019-12-10 18:34:57 +00:00
|
|
|
|
|
|
|
test "Parsing":
|
|
|
|
var r: Record
|
|
|
|
let sigValid = r.fromBase64("-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8")
|
2020-07-10 23:28:53 +00:00
|
|
|
check(sigValid)
|
2021-10-19 12:13:09 +00:00
|
|
|
check($r == """(1, id: "v4", ip: 127.0.0.1, secp256k1: 0x03CA634CAE0D49ACB401D8A4C6B6FE8C55B70D115BF400769CC1400F3258CD3138, udp: 30303)""")
|
2019-12-10 18:34:57 +00:00
|
|
|
|
|
|
|
test "Bad base64":
|
|
|
|
var r: Record
|
|
|
|
let sigValid = r.fromURI("enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnhMHcBFZntXNFrdv*jX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8")
|
2020-07-10 23:28:53 +00:00
|
|
|
check(not sigValid)
|
2019-12-10 18:34:57 +00:00
|
|
|
|
|
|
|
test "Bad rlp":
|
|
|
|
var r: Record
|
|
|
|
let sigValid = r.fromBase64("-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOOnrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8")
|
2020-07-10 23:28:53 +00:00
|
|
|
check(not sigValid)
|
2020-02-11 16:25:31 +00:00
|
|
|
|
|
|
|
test "Create from ENode address":
|
|
|
|
let
|
2020-07-07 15:19:15 +00:00
|
|
|
keypair = KeyPair.random(rng[])
|
2020-06-09 09:09:35 +00:00
|
|
|
ip = ValidIpAddress.init("10.20.30.40")
|
2021-01-26 13:11:22 +00:00
|
|
|
port = some(Port(9000))
|
2020-07-07 15:19:15 +00:00
|
|
|
enr = Record.init(
|
2021-01-26 13:11:22 +00:00
|
|
|
100, keypair.seckey, some(ip), port, port,@[])[]
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr = get enr.toTypedRecord()
|
2020-02-11 16:25:31 +00:00
|
|
|
|
|
|
|
check:
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr.secp256k1.isSome()
|
2020-07-07 15:19:15 +00:00
|
|
|
typedEnr.secp256k1.get == keypair.pubkey.toRawCompressed()
|
2020-02-11 16:25:31 +00:00
|
|
|
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr.ip.isSome()
|
|
|
|
typedEnr.ip.get() == [byte 10, 20, 30, 40]
|
2020-02-11 16:25:31 +00:00
|
|
|
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr.tcp.isSome()
|
|
|
|
typedEnr.tcp.get() == 9000
|
2020-02-11 16:25:31 +00:00
|
|
|
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr.udp.isSome()
|
|
|
|
typedEnr.udp.get() == 9000
|
2020-02-11 16:25:31 +00:00
|
|
|
|
2020-03-20 15:38:46 +00:00
|
|
|
test "ENR without address":
|
|
|
|
let
|
2020-07-07 15:19:15 +00:00
|
|
|
keypair = KeyPair.random(rng[])
|
2021-09-29 16:50:23 +00:00
|
|
|
port = none(Port)
|
2020-07-07 15:19:15 +00:00
|
|
|
enr = Record.init(
|
2021-01-26 13:11:22 +00:00
|
|
|
100, keypair.seckey, none(ValidIpAddress), port, port)[]
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr = get enr.toTypedRecord()
|
|
|
|
|
|
|
|
check:
|
|
|
|
typedEnr.secp256k1.isSome()
|
2020-07-07 15:19:15 +00:00
|
|
|
typedEnr.secp256k1.get() == keypair.pubkey.toRawCompressed()
|
2020-03-20 15:38:46 +00:00
|
|
|
|
|
|
|
typedEnr.ip.isNone()
|
2021-01-26 13:11:22 +00:00
|
|
|
typedEnr.tcp.isNone()
|
|
|
|
typedEnr.udp.isNone()
|
2020-03-30 11:21:32 +00:00
|
|
|
|
2020-03-20 15:38:46 +00:00
|
|
|
typedEnr.ip6.isNone()
|
|
|
|
typedEnr.tcp6.isNone()
|
|
|
|
typedEnr.udp6.isNone()
|
2020-07-07 15:19:15 +00:00
|
|
|
|
|
|
|
test "ENR init size too big":
|
|
|
|
let pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
block: # This gives ENR of 300 bytes encoded
|
|
|
|
let r = initRecord(1, pk, {"maxvalue": repeat(byte 2, 169),})
|
|
|
|
check r.isOk()
|
|
|
|
|
|
|
|
block: # This gives ENR of 301 bytes encoded
|
|
|
|
let r = initRecord(1, pk, {"maxplus1": repeat(byte 2, 170),})
|
|
|
|
check r.isErr()
|
|
|
|
|
2020-07-07 21:39:32 +00:00
|
|
|
test "ENR update":
|
2020-07-07 15:19:15 +00:00
|
|
|
let
|
|
|
|
pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
newField = toFieldPair("test", 123'u)
|
2021-01-26 13:11:22 +00:00
|
|
|
var r = Record.init(1, pk, none(ValidIpAddress), none(Port), none(Port))[]
|
2020-07-07 15:19:15 +00:00
|
|
|
|
|
|
|
block: # Insert new k:v pair, update of seqNum should occur.
|
2020-07-08 12:23:43 +00:00
|
|
|
let updated = r.update(pk, [newField])
|
|
|
|
check updated.isOk()
|
2020-07-07 15:19:15 +00:00
|
|
|
check:
|
2021-12-21 14:09:32 +00:00
|
|
|
r.get("test", uint).get() == 123
|
2020-07-07 15:19:15 +00:00
|
|
|
r.seqNum == 2
|
|
|
|
|
|
|
|
block: # Insert same k:v pair, no update of seqNum should occur.
|
2020-07-08 12:23:43 +00:00
|
|
|
let updated = r.update(pk, [newField])
|
|
|
|
check updated.isOk()
|
2020-07-07 15:19:15 +00:00
|
|
|
check:
|
2021-12-21 14:09:32 +00:00
|
|
|
r.get("test", uint).get() == 123
|
2020-07-07 15:19:15 +00:00
|
|
|
r.seqNum == 2
|
|
|
|
|
|
|
|
block: # Insert k:v pair with changed value, update of seqNum should occur.
|
|
|
|
let updatedField = toFieldPair("test", 1234'u)
|
2020-07-08 12:23:43 +00:00
|
|
|
let updated = r.update(pk, [updatedField])
|
|
|
|
check updated.isOk()
|
2020-07-07 15:19:15 +00:00
|
|
|
check:
|
2021-12-21 14:09:32 +00:00
|
|
|
r.get("test", uint).get() == 1234
|
2020-07-07 15:19:15 +00:00
|
|
|
r.seqNum == 3
|
|
|
|
|
2020-07-07 21:39:32 +00:00
|
|
|
test "ENR update sorted":
|
2020-07-07 15:19:15 +00:00
|
|
|
let pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
var r = initRecord(123, pk, {"abc": 1234'u,
|
|
|
|
"z": [byte 0],
|
|
|
|
"123": "abc",
|
|
|
|
"a12": 1'u})[]
|
2020-07-17 09:14:08 +00:00
|
|
|
check $r == """(123, 123: "abc", a12: 1, abc: 1234, id: "v4", secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, z: 0x00)"""
|
2020-07-07 15:19:15 +00:00
|
|
|
|
|
|
|
let newField = toFieldPair("test", 123'u)
|
2020-07-07 20:48:26 +00:00
|
|
|
let newField2 = toFieldPair("zzz", 123'u)
|
2020-07-08 12:23:43 +00:00
|
|
|
let updated = r.update(pk, [newField, newField2])
|
|
|
|
check updated.isOk()
|
2020-07-17 09:14:08 +00:00
|
|
|
check $r == """(124, 123: "abc", a12: 1, abc: 1234, id: "v4", secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, test: 123, z: 0x00, zzz: 123)"""
|
2020-07-07 15:19:15 +00:00
|
|
|
|
2020-07-07 21:39:32 +00:00
|
|
|
test "ENR update size too big":
|
2020-07-07 15:19:15 +00:00
|
|
|
let pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
|
|
|
|
var r = initRecord(1, pk, {"maxvalue": repeat(byte 2, 169),})
|
|
|
|
check r.isOk()
|
|
|
|
|
|
|
|
let newField = toFieldPair("test", 123'u)
|
2020-07-08 12:23:43 +00:00
|
|
|
let updated = r[].update(pk, [newField])
|
|
|
|
check updated.isErr()
|
2020-07-07 15:19:15 +00:00
|
|
|
|
2020-07-07 21:39:32 +00:00
|
|
|
test "ENR update invalid key":
|
2020-07-07 15:19:15 +00:00
|
|
|
let pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
|
|
|
|
var r = initRecord(1, pk, {"abc": 1'u,})
|
|
|
|
check r.isOk()
|
|
|
|
|
|
|
|
let
|
|
|
|
wrongPk = PrivateKey.random(rng[])
|
|
|
|
newField = toFieldPair("test", 123'u)
|
2020-07-08 12:23:43 +00:00
|
|
|
updated = r[].update(wrongPk, [newField])
|
|
|
|
check updated.isErr()
|
2020-07-07 21:39:32 +00:00
|
|
|
|
|
|
|
test "ENR update address":
|
|
|
|
let
|
|
|
|
pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
2021-01-26 13:11:22 +00:00
|
|
|
var r = Record.init(1, pk, none(ValidIpAddress),
|
|
|
|
some(Port(9000)), some(Port(9000)))[]
|
2020-07-07 21:39:32 +00:00
|
|
|
|
|
|
|
block:
|
2021-01-26 13:11:22 +00:00
|
|
|
let updated = r.update(pk, none(ValidIpAddress),
|
|
|
|
some(Port(9000)), some(Port(9000)))
|
2020-07-08 12:23:43 +00:00
|
|
|
check updated.isOk()
|
2020-07-07 21:39:32 +00:00
|
|
|
check:
|
2021-01-26 13:11:22 +00:00
|
|
|
r.tryGet("ip", uint).isNone()
|
2021-09-29 16:50:23 +00:00
|
|
|
r.tryGet("tcp", uint).isSome()
|
|
|
|
r.tryGet("udp", uint).isSome()
|
2020-07-07 21:39:32 +00:00
|
|
|
r.seqNum == 1
|
|
|
|
|
|
|
|
block:
|
2021-01-26 13:11:22 +00:00
|
|
|
let updated = r.update(pk, none(ValidIpAddress),
|
|
|
|
some(Port(9001)), some(Port(9002)))
|
2020-07-08 12:23:43 +00:00
|
|
|
check updated.isOk()
|
2020-07-07 21:39:32 +00:00
|
|
|
check:
|
2021-01-26 13:11:22 +00:00
|
|
|
r.tryGet("ip", uint).isNone()
|
2021-09-29 16:50:23 +00:00
|
|
|
r.tryGet("tcp", uint).isSome()
|
|
|
|
r.tryGet("udp", uint).isSome()
|
|
|
|
r.seqNum == 2
|
2021-01-26 13:11:22 +00:00
|
|
|
|
|
|
|
block:
|
|
|
|
let updated = r.update(pk, some(ValidIpAddress.init("10.20.30.40")),
|
|
|
|
some(Port(9000)), some(Port(9000)))
|
|
|
|
check updated.isOk()
|
|
|
|
|
|
|
|
let typedEnr = r.toTypedRecord().get()
|
|
|
|
|
|
|
|
check:
|
|
|
|
typedEnr.ip.isSome()
|
|
|
|
typedEnr.ip.get() == [byte 10, 20, 30, 40]
|
|
|
|
|
|
|
|
typedEnr.tcp.isSome()
|
|
|
|
typedEnr.tcp.get() == 9000
|
|
|
|
|
|
|
|
typedEnr.udp.isSome()
|
|
|
|
typedEnr.udp.get() == 9000
|
|
|
|
|
2021-09-29 16:50:23 +00:00
|
|
|
r.seqNum == 3
|
2021-01-26 13:11:22 +00:00
|
|
|
|
|
|
|
block:
|
|
|
|
let updated = r.update(pk, some(ValidIpAddress.init("10.20.30.40")),
|
|
|
|
some(Port(9001)), some(Port(9001)))
|
|
|
|
check updated.isOk()
|
|
|
|
|
|
|
|
let typedEnr = r.toTypedRecord().get()
|
|
|
|
|
|
|
|
check:
|
|
|
|
typedEnr.ip.isSome()
|
|
|
|
typedEnr.ip.get() == [byte 10, 20, 30, 40]
|
|
|
|
|
|
|
|
typedEnr.tcp.isSome()
|
|
|
|
typedEnr.tcp.get() == 9001
|
|
|
|
|
|
|
|
typedEnr.udp.isSome()
|
|
|
|
typedEnr.udp.get() == 9001
|
|
|
|
|
2021-09-29 16:50:23 +00:00
|
|
|
r.seqNum == 4
|
2021-10-19 07:26:14 +00:00
|
|
|
|
|
|
|
test "ENR with RLP list value":
|
|
|
|
type
|
|
|
|
RlpTestList = object
|
|
|
|
number: uint16
|
|
|
|
data: seq[byte]
|
|
|
|
text: string
|
|
|
|
|
|
|
|
let rlpList =
|
|
|
|
RlpTestList(number: 72, data: @[byte 0x0, 0x1, 0x2], text: "Hi there")
|
|
|
|
|
|
|
|
let pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7, 8],
|
|
|
|
"some_list": rlpList})[]
|
|
|
|
|
2021-10-19 12:13:09 +00:00
|
|
|
check($r == """(123, id: "v4", ip: 5.6.7.8, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, some_list: (Raw RLP list) 0xCE4883000102884869207468657265, udp: 1234)""")
|
2021-10-19 07:26:14 +00:00
|
|
|
|
|
|
|
let encoded = rlp.encode(r)
|
|
|
|
let decoded = rlp.decode(encoded, enr.Record)
|
|
|
|
check($decoded == $r)
|
|
|
|
check(decoded.raw == r.raw)
|
2021-10-19 12:13:09 +00:00
|
|
|
|
|
|
|
test "ENR IP addresses ":
|
|
|
|
let pk = PrivateKey.fromHex(
|
|
|
|
"5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[]
|
|
|
|
block: # valid ipv4
|
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7, 8]})[]
|
|
|
|
|
|
|
|
check($r == """(123, id: "v4", ip: 5.6.7.8, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, udp: 1234)""")
|
|
|
|
|
|
|
|
let encoded = rlp.encode(r)
|
|
|
|
let decoded = rlp.decode(encoded, enr.Record)
|
|
|
|
check($decoded == $r)
|
|
|
|
check(decoded.raw == r.raw)
|
|
|
|
|
|
|
|
block: # invalid ipv4
|
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7]})[]
|
|
|
|
|
|
|
|
check($r == """(123, id: "v4", ip: (Invalid) 0x050607, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, udp: 1234)""")
|
|
|
|
|
|
|
|
let encoded = rlp.encode(r)
|
|
|
|
let decoded = rlp.decode(encoded, enr.Record)
|
|
|
|
check($decoded == $r)
|
|
|
|
check(decoded.raw == r.raw)
|
|
|
|
|
|
|
|
block: # valid ipv4 + ipv6
|
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7, 8],
|
|
|
|
"ip6": [byte 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6]})[]
|
|
|
|
|
|
|
|
check($r == """(123, id: "v4", ip: 5.6.7.8, ip6: 102::102:304:506, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, udp: 1234)""")
|
|
|
|
|
|
|
|
let encoded = rlp.encode(r)
|
|
|
|
let decoded = rlp.decode(encoded, enr.Record)
|
|
|
|
check($decoded == $r)
|
|
|
|
check(decoded.raw == r.raw)
|
|
|
|
|
|
|
|
block: # invalid ipv4 + ipv6
|
|
|
|
var r = initRecord(123, pk, {"udp": 1234'u, "ip": [byte 5, 6, 7, 8, 9],
|
|
|
|
"ip6": [byte 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5]})[]
|
|
|
|
|
|
|
|
check($r == """(123, id: "v4", ip: (Invalid) 0x0506070809, ip6: (Invalid) 0x010200000000000000000102030405, secp256k1: 0x02E51EFA66628CE09F689BC2B82F165A75A9DDECBB6A804BE15AC3FDF41F3B34E7, udp: 1234)""")
|
|
|
|
|
|
|
|
let encoded = rlp.encode(r)
|
|
|
|
let decoded = rlp.decode(encoded, enr.Record)
|
|
|
|
check($decoded == $r)
|
|
|
|
check(decoded.raw == r.raw)
|