mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-10 19:17:16 +00:00
Minimal implementation of Portal ping payload extensions spec (#3010)
* Minimal implementation of Portal ping payload extensions spec * Remove serialization layer for CustomPayload (spec change)
This commit is contained in:
parent
c4bc2a4eea
commit
28f9b792d9
@ -1,5 +1,5 @@
|
||||
# fluffy
|
||||
# Copyright (c) 2022-2024 Status Research & Development GmbH
|
||||
# Copyright (c) 2022-2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
@ -14,7 +14,7 @@ import
|
||||
eth/p2p/discoveryv5/[protocol, enr],
|
||||
beacon_chain/spec/forks,
|
||||
beacon_chain/gossip_processing/light_client_processor,
|
||||
../wire/[portal_protocol, portal_stream, portal_protocol_config],
|
||||
../wire/[portal_protocol, portal_stream, portal_protocol_config, ping_extensions],
|
||||
"."/[beacon_content, beacon_db, beacon_validation, beacon_chain_historical_summaries]
|
||||
|
||||
export beacon_content, beacon_db
|
||||
@ -22,6 +22,8 @@ export beacon_content, beacon_db
|
||||
logScope:
|
||||
topics = "portal_beacon"
|
||||
|
||||
const pingExtensionCapabilities = {CapabilitiesType, BasicRadiusType}
|
||||
|
||||
type BeaconNetwork* = ref object
|
||||
portalProtocol*: PortalProtocol
|
||||
beaconDb*: BeaconDb
|
||||
@ -213,6 +215,7 @@ proc new*(
|
||||
stream,
|
||||
bootstrapRecords,
|
||||
config = portalConfig,
|
||||
pingExtensionCapabilities = pingExtensionCapabilities,
|
||||
)
|
||||
|
||||
let beaconBlockRoot =
|
||||
|
@ -17,7 +17,7 @@ import
|
||||
../../common/common_types,
|
||||
../../database/content_db,
|
||||
../../network_metadata,
|
||||
../wire/[portal_protocol, portal_stream, portal_protocol_config],
|
||||
../wire/[portal_protocol, portal_stream, portal_protocol_config, ping_extensions],
|
||||
"."/[history_content, history_validation, history_type_conversions],
|
||||
../beacon/beacon_chain_historical_roots,
|
||||
./content/content_deprecated
|
||||
@ -30,6 +30,8 @@ logScope:
|
||||
|
||||
export blocks_rlp
|
||||
|
||||
const pingExtensionCapabilities = {CapabilitiesType, HistoryRadiusType}
|
||||
|
||||
type
|
||||
HistoryNetwork* = ref object
|
||||
portalProtocol*: PortalProtocol
|
||||
@ -352,6 +354,7 @@ proc new*(
|
||||
stream,
|
||||
bootstrapRecords,
|
||||
config = portalConfig,
|
||||
pingExtensionCapabilities = pingExtensionCapabilities,
|
||||
)
|
||||
|
||||
HistoryNetwork(
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Fluffy
|
||||
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
||||
# Copyright (c) 2021-2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
@ -16,7 +16,7 @@ import
|
||||
eth/p2p/discoveryv5/[protocol, enr],
|
||||
../../database/content_db,
|
||||
../history/history_network,
|
||||
../wire/[portal_protocol, portal_stream, portal_protocol_config],
|
||||
../wire/[portal_protocol, portal_stream, portal_protocol_config, ping_extensions],
|
||||
./state_content,
|
||||
./state_validation,
|
||||
./state_gossip
|
||||
@ -31,6 +31,8 @@ declareCounter state_network_offers_success,
|
||||
declareCounter state_network_offers_failed,
|
||||
"Portal state network offers which failed validation", labels = ["protocol_id"]
|
||||
|
||||
const pingExtensionCapabilities = {CapabilitiesType, BasicRadiusType}
|
||||
|
||||
type StateNetwork* = ref object
|
||||
portalProtocol*: PortalProtocol
|
||||
contentQueue*: AsyncQueue[(Opt[NodeId], ContentKeysList, seq[seq[byte]])]
|
||||
@ -69,6 +71,7 @@ proc new*(
|
||||
s,
|
||||
bootstrapRecords,
|
||||
config = portalConfig,
|
||||
pingExtensionCapabilities = pingExtensionCapabilities,
|
||||
)
|
||||
|
||||
StateNetwork(
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Nimbus - Portal Network- Message types
|
||||
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
||||
# Copyright (c) 2021-2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
@ -29,10 +29,6 @@ type
|
||||
ContentKeysList* = List[ContentKeyByteList, contentKeysLimit]
|
||||
ContentKeysBitList* = BitList[contentKeysLimit]
|
||||
|
||||
# TODO: should become part of the specific networks, considering it is custom.
|
||||
CustomPayload* = object
|
||||
dataRadius*: UInt256
|
||||
|
||||
MessageKind* = enum
|
||||
ping = 0x00
|
||||
pong = 0x01
|
||||
@ -50,11 +46,13 @@ type
|
||||
|
||||
PingMessage* = object
|
||||
enrSeq*: uint64
|
||||
customPayload*: ByteList[2048]
|
||||
payload_type*: uint16
|
||||
payload*: ByteList[1100]
|
||||
|
||||
PongMessage* = object
|
||||
enrSeq*: uint64
|
||||
customPayload*: ByteList[2048]
|
||||
payload_type*: uint16
|
||||
payload*: ByteList[1100]
|
||||
|
||||
FindNodesMessage* = object
|
||||
distances*: List[uint16, 256]
|
||||
|
63
fluffy/network/wire/ping_extensions.nim
Normal file
63
fluffy/network/wire/ping_extensions.nim
Normal file
@ -0,0 +1,63 @@
|
||||
# Nimbus
|
||||
# Copyright (c) 2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import ssz_serialization
|
||||
|
||||
const
|
||||
# Extension types
|
||||
CapabilitiesType* = 0'u16
|
||||
BasicRadiusType* = 1'u16
|
||||
HistoryRadiusType* = 2'u16
|
||||
ErrorType* = 65535'u16
|
||||
|
||||
# Limits
|
||||
MAX_CLIENT_INFO_BYTE_LENGTH* = 200
|
||||
MAX_CAPABILITIES_LENGTH* = 400
|
||||
MAX_ERROR_BYTE_LENGTH* = 300
|
||||
|
||||
# Different ping extension payloads, TODO: could be moved to each their own file?
|
||||
type
|
||||
CapabilitiesPayload* = object
|
||||
client_info*: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH]
|
||||
data_radius*: UInt256
|
||||
capabilities*: List[uint16, MAX_CAPABILITIES_LENGTH]
|
||||
|
||||
BasicRadiusPayload* = object
|
||||
data_radius*: UInt256
|
||||
|
||||
HistoryRadiusPayload* = object
|
||||
data_radius*: UInt256
|
||||
ephemeral_header_count*: uint16
|
||||
|
||||
ErrorPayload* = object
|
||||
error_code*: uint16
|
||||
message*: ByteList[MAX_ERROR_BYTE_LENGTH]
|
||||
|
||||
CustomPayload* =
|
||||
CapabilitiesPayload | BasicRadiusPayload | HistoryRadiusPayload | ErrorPayload
|
||||
|
||||
ErrorCode* = enum
|
||||
ExtensionNotSupported = 0
|
||||
RequestedDataNotFound = 1
|
||||
FailedToDecodePayload = 2
|
||||
SystemError = 3
|
||||
|
||||
func encodePayload*(payload: CustomPayload): ByteList[1100] =
|
||||
ByteList[1100].init(SSZ.encode(payload))
|
||||
|
||||
func encodeErrorPayload*(code: ErrorCode): (uint16, ByteList[1100]) =
|
||||
(
|
||||
ErrorType,
|
||||
encodePayload(
|
||||
ErrorPayload(
|
||||
error_code: uint16(ord(code)),
|
||||
message: ByteList[MAX_ERROR_BYTE_LENGTH].init(@[]),
|
||||
)
|
||||
),
|
||||
)
|
@ -24,7 +24,7 @@ import
|
||||
minilru,
|
||||
eth/rlp,
|
||||
eth/p2p/discoveryv5/[protocol, node, enr, routing_table, random2, nodes_verification],
|
||||
"."/[portal_stream, portal_protocol_config],
|
||||
"."/[portal_stream, portal_protocol_config, ping_extensions],
|
||||
./messages
|
||||
|
||||
from std/times import epochTime # For system timestamp in traceContentLookup
|
||||
@ -186,6 +186,7 @@ type
|
||||
offerWorkers: seq[Future[void]]
|
||||
pingTimings: Table[NodeId, chronos.Moment]
|
||||
config*: PortalProtocolConfig
|
||||
pingExtensionCapabilities*: set[uint16]
|
||||
|
||||
PortalResult*[T] = Result[T, string]
|
||||
|
||||
@ -334,26 +335,68 @@ func truncateEnrs(
|
||||
|
||||
enrs
|
||||
|
||||
proc handlePingExtension(
|
||||
p: PortalProtocol,
|
||||
payloadType: uint16,
|
||||
encodedPayload: ByteList[1100],
|
||||
srcId: NodeId,
|
||||
): (uint16, ByteList[1100]) =
|
||||
if payloadType notin p.pingExtensionCapabilities:
|
||||
return encodeErrorPayload(ErrorCode.ExtensionNotSupported)
|
||||
|
||||
case payloadType
|
||||
of CapabilitiesType:
|
||||
let payload = decodeSsz(encodedPayload.asSeq(), CapabilitiesPayload).valueOr:
|
||||
return encodeErrorPayload(ErrorCode.FailedToDecodePayload)
|
||||
|
||||
p.radiusCache.put(srcId, payload.data_radius)
|
||||
|
||||
(
|
||||
payloadType,
|
||||
encodePayload(
|
||||
CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH].init(@[]),
|
||||
data_radius: p.dataRadius(),
|
||||
capabilities: List[uint16, MAX_CAPABILITIES_LENGTH].init(
|
||||
p.pingExtensionCapabilities.toSeq()
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
of BasicRadiusType:
|
||||
let payload = decodeSsz(encodedPayload.asSeq(), BasicRadiusPayload).valueOr:
|
||||
return encodeErrorPayload(ErrorCode.FailedToDecodePayload)
|
||||
|
||||
p.radiusCache.put(srcId, payload.data_radius)
|
||||
|
||||
(payloadType, encodePayload(HistoryRadiusPayload(data_radius: p.dataRadius())))
|
||||
of HistoryRadiusType:
|
||||
let payload = decodeSsz(encodedPayload.asSeq(), HistoryRadiusPayload).valueOr:
|
||||
return encodeErrorPayload(ErrorCode.FailedToDecodePayload)
|
||||
|
||||
p.radiusCache.put(srcId, payload.data_radius)
|
||||
|
||||
(
|
||||
payloadType,
|
||||
encodePayload(
|
||||
HistoryRadiusPayload(data_radius: p.dataRadius(), ephemeral_header_count: 0)
|
||||
),
|
||||
)
|
||||
else:
|
||||
encodeErrorPayload(ErrorCode.ExtensionNotSupported)
|
||||
|
||||
proc handlePing(p: PortalProtocol, ping: PingMessage, srcId: NodeId): seq[byte] =
|
||||
# TODO: This should become custom per Portal Network
|
||||
# TODO: Need to think about the effect of malicious actor sending lots of
|
||||
# pings from different nodes to clear the LRU.
|
||||
let customPayloadDecoded =
|
||||
try:
|
||||
SSZ.decode(ping.customPayload.asSeq(), CustomPayload)
|
||||
except SerializationError:
|
||||
# invalid custom payload, send empty back
|
||||
return @[]
|
||||
p.radiusCache.put(srcId, customPayloadDecoded.dataRadius)
|
||||
let (payloadType, payload) =
|
||||
handlePingExtension(p, ping.payload_type, ping.payload, srcId)
|
||||
|
||||
let customPayload = CustomPayload(dataRadius: p.dataRadius())
|
||||
let p = PongMessage(
|
||||
enrSeq: p.localNode.record.seqNum,
|
||||
customPayload: ByteList[2048](SSZ.encode(customPayload)),
|
||||
encodeMessage(
|
||||
PongMessage(
|
||||
enrSeq: p.localNode.record.seqNum, payload_type: payloadType, payload: payload
|
||||
)
|
||||
)
|
||||
|
||||
encodeMessage(p)
|
||||
|
||||
proc handleFindNodes(p: PortalProtocol, fn: FindNodesMessage): seq[byte] =
|
||||
if fn.distances.len == 0:
|
||||
let enrs = List[ByteList[2048], 32](@[])
|
||||
@ -573,6 +616,7 @@ proc new*(
|
||||
bootstrapRecords: openArray[Record] = [],
|
||||
distanceCalculator: DistanceCalculator = XorDistanceCalculator,
|
||||
config: PortalProtocolConfig = defaultPortalProtocolConfig,
|
||||
pingExtensionCapabilities: set[uint16] = {CapabilitiesType},
|
||||
): T =
|
||||
let proto = PortalProtocol(
|
||||
protocolHandler: messageHandler,
|
||||
@ -595,6 +639,7 @@ proc new*(
|
||||
offerQueue: newAsyncQueue[OfferRequest](config.maxConcurrentOffers),
|
||||
pingTimings: Table[NodeId, chronos.Moment](),
|
||||
config: config,
|
||||
pingExtensionCapabilities: pingExtensionCapabilities,
|
||||
)
|
||||
|
||||
proto.baseProtocol.registerTalkProtocol(@(proto.protocolId), proto).expect(
|
||||
@ -657,10 +702,19 @@ proc reqResponse[Request: SomeMessage, Response: SomeMessage](
|
||||
proc pingImpl*(
|
||||
p: PortalProtocol, dst: Node
|
||||
): Future[PortalResult[PongMessage]] {.async: (raises: [CancelledError]).} =
|
||||
let customPayload = CustomPayload(dataRadius: p.dataRadius())
|
||||
let pingPayload = encodePayload(
|
||||
CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH].init(@[]),
|
||||
data_radius: p.dataRadius(),
|
||||
capabilities:
|
||||
List[uint16, MAX_CAPABILITIES_LENGTH].init(p.pingExtensionCapabilities.toSeq()),
|
||||
)
|
||||
)
|
||||
|
||||
let ping = PingMessage(
|
||||
enrSeq: p.localNode.record.seqNum,
|
||||
customPayload: ByteList[2048](SSZ.encode(customPayload)),
|
||||
payload_type: CapabilitiesType,
|
||||
payload: pingPayload,
|
||||
)
|
||||
|
||||
return await reqResponse[PingMessage, PongMessage](p, dst, ping)
|
||||
@ -701,7 +755,9 @@ proc recordsFromBytes(rawRecords: List[ByteList[2048], 32]): PortalResult[seq[Re
|
||||
|
||||
proc ping*(
|
||||
p: PortalProtocol, dst: Node
|
||||
): Future[PortalResult[PongMessage]] {.async: (raises: [CancelledError]).} =
|
||||
): Future[PortalResult[(uint64, CapabilitiesPayload)]] {.
|
||||
async: (raises: [CancelledError])
|
||||
.} =
|
||||
let pongResponse = await p.pingImpl(dst)
|
||||
|
||||
if pongResponse.isOk():
|
||||
@ -709,17 +765,20 @@ proc ping*(
|
||||
p.pingTimings[dst.id] = now(chronos.Moment)
|
||||
|
||||
let pong = pongResponse.get()
|
||||
# TODO: This should become custom per Portal Network
|
||||
let customPayloadDecoded =
|
||||
try:
|
||||
SSZ.decode(pong.customPayload.asSeq(), CustomPayload)
|
||||
except SerializationError:
|
||||
# invalid custom payload
|
||||
return err("Pong message contains invalid custom payload")
|
||||
|
||||
p.radiusCache.put(dst.id, customPayloadDecoded.dataRadius)
|
||||
# Note: currently only decoding as capabilities payload as this is the only
|
||||
# one that we support sending.
|
||||
if pong.payload_type != CapabilitiesType:
|
||||
return err("Pong message contains invalid or error payload")
|
||||
|
||||
return pongResponse
|
||||
let payload = decodeSsz(pong.payload.asSeq(), CapabilitiesPayload).valueOr:
|
||||
return err("Pong message contains invalid CapabilitiesPayload")
|
||||
|
||||
p.radiusCache.put(dst.id, payload.data_radius)
|
||||
|
||||
ok((pong.enrSeq, payload))
|
||||
else:
|
||||
err(pongResponse.error)
|
||||
|
||||
proc findNodes*(
|
||||
p: PortalProtocol, dst: Node, distances: seq[uint16]
|
||||
@ -1691,8 +1750,8 @@ proc revalidateNode*(p: PortalProtocol, n: Node) {.async: (raises: [CancelledErr
|
||||
let pong = await p.ping(n)
|
||||
|
||||
if pong.isOk():
|
||||
let res = pong.get()
|
||||
if res.enrSeq > n.record.seqNum:
|
||||
let (enrSeq, _) = pong.get()
|
||||
if enrSeq > n.record.seqNum:
|
||||
# Request new ENR
|
||||
let nodesMessage = await p.findNodes(n, @[0'u16])
|
||||
if nodesMessage.isOk():
|
||||
|
@ -1,5 +1,5 @@
|
||||
# fluffy
|
||||
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
||||
# Copyright (c) 2021-2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
@ -83,16 +83,8 @@ proc installPortalCommonApiHandlers*(
|
||||
if pong.isErr():
|
||||
raise newException(ValueError, $pong.error)
|
||||
else:
|
||||
let
|
||||
p = pong.get()
|
||||
# Note: the SSZ.decode cannot fail here as it has already been verified
|
||||
# in the ping call.
|
||||
decodedPayload =
|
||||
try:
|
||||
SSZ.decode(p.customPayload.asSeq(), CustomPayload)
|
||||
except MalformedSszError, SszSizeMismatchError:
|
||||
raiseAssert("Already verified")
|
||||
return (p.enrSeq, decodedPayload.dataRadius)
|
||||
let (enrSeq, pongPayload) = pong.get()
|
||||
return (enrSeq, pongPayload.data_radius)
|
||||
|
||||
rpcServer.rpc("portal_" & networkStr & "FindNodes") do(
|
||||
enr: Record, distances: seq[uint16]
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Nimbus
|
||||
# Copyright (c) 2024 Status Research & Development GmbH
|
||||
# Copyright (c) 2025 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
@ -7,4 +7,7 @@
|
||||
|
||||
{.warning[UnusedImport]: off.}
|
||||
|
||||
import ./test_portal_wire_encoding, ./test_portal_wire_protocol
|
||||
import
|
||||
./test_portal_wire_encoding,
|
||||
./test_portal_wire_protocol,
|
||||
./test_ping_extensions_encoding
|
||||
|
@ -0,0 +1,205 @@
|
||||
# Nimbus
|
||||
# Copyright (c) 2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.used.}
|
||||
|
||||
import
|
||||
unittest2,
|
||||
stint,
|
||||
stew/byteutils,
|
||||
results,
|
||||
../../network/wire/[messages, ping_extensions]
|
||||
|
||||
suite "Portal Wire Ping Extension Encodings - Type 0x00":
|
||||
test "SSZ encoded Ping request - with client info":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
client_info = "trin/v0.1.1-b61fdc5c/linux-x86_64/rustc1.81.0"
|
||||
capabilities = @[uint16 0, 1, 65535]
|
||||
|
||||
payload = CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH](client_info.toBytes()),
|
||||
data_radius: data_radius,
|
||||
capabilities: List[uint16, MAX_CAPABILITIES_LENGTH].init(capabilities),
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
ping = PingMessage(
|
||||
enrSeq: enr_seq, payload_type: CapabilitiesType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(ping)
|
||||
check encoded.to0xHex ==
|
||||
"0x00010000000000000000000e00000028000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff550000007472696e2f76302e312e312d62363166646335632f6c696e75782d7838365f36342f7275737463312e38312e3000000100ffff"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
let message = decoded.value()
|
||||
check:
|
||||
message.kind == MessageKind.ping
|
||||
message.ping.enrSeq == enr_seq
|
||||
message.ping.payload_type == CapabilitiesType
|
||||
message.ping.payload == customPayload
|
||||
|
||||
let decodedPayload = decodeSsz(message.ping.payload.asSeq(), CapabilitiesPayload)
|
||||
check:
|
||||
decodedPayload.isOk()
|
||||
decodedPayload.value().client_info.asSeq() == client_info.toBytes()
|
||||
decodedPayload.value().data_radius == data_radius
|
||||
decodedPayload.value().capabilities.asSeq() == capabilities
|
||||
|
||||
test "SSZ encoded Ping request - with empty client info":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
client_info = ""
|
||||
capabilities = @[uint16 0, 1, 65535]
|
||||
|
||||
payload = CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH](client_info.toBytes()),
|
||||
data_radius: data_radius,
|
||||
capabilities: List[uint16, MAX_CAPABILITIES_LENGTH].init(capabilities),
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
ping = PingMessage(
|
||||
enrSeq: enr_seq, payload_type: CapabilitiesType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(ping)
|
||||
check encoded.to0xHex ==
|
||||
"0x00010000000000000000000e00000028000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2800000000000100ffff"
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
test "SSZ encoded Pong response - with client info":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
client_info = "trin/v0.1.1-b61fdc5c/linux-x86_64/rustc1.81.0"
|
||||
capabilities = @[uint16 0, 1, 65535]
|
||||
|
||||
payload = CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH](client_info.toBytes()),
|
||||
data_radius: data_radius,
|
||||
capabilities: List[uint16, MAX_CAPABILITIES_LENGTH].init(capabilities),
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
pong = PongMessage(
|
||||
enrSeq: enr_seq, payload_type: CapabilitiesType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(pong)
|
||||
check encoded.to0xHex ==
|
||||
"0x01010000000000000000000e00000028000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff550000007472696e2f76302e312e312d62363166646335632f6c696e75782d7838365f36342f7275737463312e38312e3000000100ffff"
|
||||
|
||||
test "SSZ encoded Pong response - with empty client info":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
client_info = ""
|
||||
capabilities = @[uint16 0, 1, 65535]
|
||||
|
||||
payload = CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH](client_info.toBytes()),
|
||||
data_radius: data_radius,
|
||||
capabilities: List[uint16, MAX_CAPABILITIES_LENGTH].init(capabilities),
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
pong = PongMessage(
|
||||
enrSeq: enr_seq, payload_type: CapabilitiesType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(pong)
|
||||
check encoded.to0xHex ==
|
||||
"0x01010000000000000000000e00000028000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2800000000000100ffff"
|
||||
|
||||
suite "Portal Wire Ping Extension Encodings - Type 0x01":
|
||||
test "SSZ encoded Ping request":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
|
||||
payload = BasicRadiusPayload(data_radius: data_radius)
|
||||
customPayload = encodePayload(payload)
|
||||
ping = PingMessage(
|
||||
enrSeq: enr_seq, payload_type: BasicRadiusType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(ping)
|
||||
check encoded.to0xHex ==
|
||||
"0x00010000000000000001000e000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
|
||||
test "SSZ encoded Pong response":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
|
||||
payload = BasicRadiusPayload(data_radius: data_radius)
|
||||
customPayload = encodePayload(payload)
|
||||
pong = PongMessage(
|
||||
enrSeq: enr_seq, payload_type: BasicRadiusType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(pong)
|
||||
check encoded.to0xHex ==
|
||||
"0x01010000000000000001000e000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
|
||||
suite "Portal Wire Ping Extension Encodings - Type 0x02":
|
||||
test "SSZ encoded Ping request":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
ephemeral_header_count = 4242'u16
|
||||
|
||||
payload = HistoryRadiusPayload(
|
||||
data_radius: data_radius, ephemeral_header_count: ephemeral_header_count
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
ping = PingMessage(
|
||||
enrSeq: enr_seq, payload_type: HistoryRadiusType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(ping)
|
||||
check encoded.to0xHex ==
|
||||
"0x00010000000000000002000e000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9210"
|
||||
|
||||
test "SSZ encoded Pong response":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
data_radius = UInt256.high() - 1 # Full radius - 1
|
||||
ephemeral_header_count = 4242'u16
|
||||
|
||||
payload = HistoryRadiusPayload(
|
||||
data_radius: data_radius, ephemeral_header_count: ephemeral_header_count
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
pong = PongMessage(
|
||||
enrSeq: enr_seq, payload_type: HistoryRadiusType, payload: customPayload
|
||||
)
|
||||
|
||||
let encoded = encodeMessage(pong)
|
||||
check encoded.to0xHex ==
|
||||
"0x01010000000000000002000e000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9210"
|
||||
|
||||
suite "Portal Wire Ping Extension Encodings - Type 0x03":
|
||||
test "SSZ encoded Pong response":
|
||||
let
|
||||
enr_seq = 1'u64
|
||||
error_code = 2'u16
|
||||
message = "hello world"
|
||||
|
||||
payload = ErrorPayload(
|
||||
error_code: error_code,
|
||||
message: ByteList[MAX_ERROR_BYTE_LENGTH].init(message.toBytes()),
|
||||
)
|
||||
customPayload = encodePayload(payload)
|
||||
pong =
|
||||
PongMessage(enrSeq: enr_seq, payload_type: ErrorType, payload: customPayload)
|
||||
|
||||
let encoded = encodeMessage(pong)
|
||||
check encoded.to0xHex ==
|
||||
"0x010100000000000000ffff0e00000002000600000068656c6c6f20776f726c64"
|
@ -1,5 +1,5 @@
|
||||
# Fluffy
|
||||
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
||||
# Nimbus
|
||||
# Copyright (c) 2021-2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
@ -21,15 +21,13 @@ import
|
||||
suite "Portal Wire Protocol Message Encodings":
|
||||
test "Ping Request":
|
||||
let
|
||||
dataRadius = UInt256.high() - 1 # Full radius - 1
|
||||
enrSeq = 1'u64
|
||||
# Can be any custom payload, testing with just dataRadius here.
|
||||
customPayload = ByteList[2048](SSZ.encode(CustomPayload(dataRadius: dataRadius)))
|
||||
p = PingMessage(enrSeq: enrSeq, customPayload: customPayload)
|
||||
# Can be any custom payload, testing with meaningless string of bytes.
|
||||
customPayload = ByteList[1100].init(@[byte 0x01, 0x02, 0x03, 0x04])
|
||||
p = PingMessage(enrSeq: enrSeq, payload_type: 42'u16, payload: customPayload)
|
||||
|
||||
let encoded = encodeMessage(p)
|
||||
check encoded.toHex ==
|
||||
"0001000000000000000c000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
check encoded.toHex == "0001000000000000002a000e00000001020304"
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
@ -37,19 +35,18 @@ suite "Portal Wire Protocol Message Encodings":
|
||||
check:
|
||||
message.kind == ping
|
||||
message.ping.enrSeq == enrSeq
|
||||
message.ping.customPayload == customPayload
|
||||
message.ping.payload_type == 42'u16
|
||||
message.ping.payload == customPayload
|
||||
|
||||
test "Pong Response":
|
||||
let
|
||||
dataRadius = UInt256.high() div 2.stuint(256) # Radius of half the UInt256
|
||||
enrSeq = 1'u64
|
||||
# Can be any custom payload, testing with just dataRadius here.
|
||||
customPayload = ByteList[2048](SSZ.encode(CustomPayload(dataRadius: dataRadius)))
|
||||
p = PongMessage(enrSeq: enrSeq, customPayload: customPayload)
|
||||
# Can be any custom payload, testing with meaningless string of bytes.
|
||||
customPayload = ByteList[1100].init(@[byte 0x01, 0x02, 0x03, 0x04])
|
||||
p = PongMessage(enrSeq: enrSeq, payload_type: 42'u16, payload: customPayload)
|
||||
|
||||
let encoded = encodeMessage(p)
|
||||
check encoded.toHex ==
|
||||
"0101000000000000000c000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"
|
||||
check encoded.toHex == "0101000000000000002a000e00000001020304"
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
@ -57,7 +54,8 @@ suite "Portal Wire Protocol Message Encodings":
|
||||
check:
|
||||
message.kind == pong
|
||||
message.pong.enrSeq == enrSeq
|
||||
message.pong.customPayload == customPayload
|
||||
message.pong.payload_type == 42'u16
|
||||
message.pong.payload == customPayload
|
||||
|
||||
test "FindNodes Request":
|
||||
let
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Fluffy
|
||||
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
||||
# Copyright (c) 2021-2025 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
@ -16,7 +16,8 @@ import
|
||||
eth/p2p/discoveryv5/routing_table,
|
||||
nimcrypto/[hash, sha2],
|
||||
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||
../../network/wire/[portal_protocol, portal_stream, portal_protocol_config],
|
||||
../../network/wire/
|
||||
[portal_protocol, portal_stream, portal_protocol_config, ping_extensions],
|
||||
../../database/content_db,
|
||||
../test_helpers
|
||||
|
||||
@ -77,13 +78,20 @@ procSuite "Portal Wire Protocol Tests":
|
||||
|
||||
let pong = await proto1.ping(proto2.localNode)
|
||||
|
||||
let customPayload =
|
||||
ByteList[2048](SSZ.encode(CustomPayload(dataRadius: UInt256.high())))
|
||||
let customPayload = CapabilitiesPayload(
|
||||
client_info: ByteList[MAX_CLIENT_INFO_BYTE_LENGTH].init(@[]),
|
||||
data_radius: UInt256.high(),
|
||||
capabilities: List[uint16, MAX_CAPABILITIES_LENGTH].init(
|
||||
proto1.pingExtensionCapabilities.toSeq()
|
||||
),
|
||||
)
|
||||
|
||||
check pong.isOk()
|
||||
|
||||
let (enrSeq, payload) = pong.value()
|
||||
check:
|
||||
pong.isOk()
|
||||
pong.get().enrSeq == 1'u64
|
||||
pong.get().customPayload == customPayload
|
||||
enrSeq == 1'u64
|
||||
payload == customPayload
|
||||
|
||||
await proto1.stopPortalProtocol()
|
||||
await proto2.stopPortalProtocol()
|
||||
|
Loading…
x
Reference in New Issue
Block a user