From faf5cad9d45c3c73f25b187b2ab10c2ff7a7c886 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Thu, 7 Apr 2022 14:02:12 -0500 Subject: [PATCH] protobuf encoding for all message types --- README.md | 11 +- libp2pdht/dht/providers_encoding.nim | 1 - .../private/eth/p2p/discoveryv5/encoding.nim | 3 +- .../private/eth/p2p/discoveryv5/messages.nim | 4 +- .../eth/p2p/discoveryv5/messages_encoding.nim | 498 ++++++++++++++---- .../private/eth/p2p/discoveryv5/protocol.nim | 6 +- libp2pdht/private/eth/p2p/discoveryv5/spr.nim | 25 +- .../private/eth/p2p/discoveryv5/transport.nim | 2 +- tests/discv5/test_discoveryv5_encoding.nim | 52 +- tests/testAll.nim | 2 +- 10 files changed, 432 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index f768387..ef875cf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # A DHT implementation for Dagger -![GitHub CI](https://github.com/status-im/nim-libp2p-dht/actions/workflows/ci.yml/badge.svg) ![codecov](https://codecov.io/gh/status-im/nim-libp2p-dht/branch/main/graph/badge.svg?token=tlmMJgU4l7)](https://codecov.io/gh/status-im/nim-libp2p-dht) +[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)](#stability) +[![CI (GitHub Actions)](https://github.com/status-im/nim-libp2p-dht/workflows/CI/badge.svg?branch=main)](https://github.com/status-im/nim-libp2p-dht/actions?query=workflow%3ACI+branch%3Amain) +[![codecov](https://codecov.io/gh/status-im/nim-libp2p-dht/branch/main/graph/badge.svg?token=tlmMJgU4l7)](https://codecov.io/gh/status-im/nim-libp2p-dht) This DHT implementation is aiming to provide a DHT for Dagger with the following properties * flexible transport usage with @@ -12,5 +16,6 @@ This DHT implementation is aiming to provide a DHT for Dagger with the following * roughly follow the libp2p-dht specifications from https://github.com/libp2p/specs/tree/master/kad-dht * provide compatibility mode with the above specs -Current implementation is based on nim-eth's Discovery v5 implementation. Base files were copied -from nim-eth@779d767b024175a51cf74c79ec7513301ebe2f46 \ No newline at end of file +Current implementation is based on nim-eth's Discovery v5 implementation. + +Base files were copied from [`status-im/nim-eth@779d767b024175a51cf74c79ec7513301ebe2f46`](https://github.com/status-im/nim-eth/commit/779d767b024175a51cf74c79ec7513301ebe2f46) diff --git a/libp2pdht/dht/providers_encoding.nim b/libp2pdht/dht/providers_encoding.nim index 045c382..0acd066 100644 --- a/libp2pdht/dht/providers_encoding.nim +++ b/libp2pdht/dht/providers_encoding.nim @@ -121,4 +121,3 @@ proc encode*(msg: ProvidersMessage): seq[byte] = pb.finish() pb.buffer - diff --git a/libp2pdht/private/eth/p2p/discoveryv5/encoding.nim b/libp2pdht/private/eth/p2p/discoveryv5/encoding.nim index 265aaeb..8cf2fed 100644 --- a/libp2pdht/private/eth/p2p/discoveryv5/encoding.nim +++ b/libp2pdht/private/eth/p2p/discoveryv5/encoding.nim @@ -15,7 +15,6 @@ import std/[hashes, net, options, sugar, tables], - eth/rlp, bearssl, chronicles, stew/[results, byteutils], @@ -519,7 +518,7 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce, ("Invalid bytes for SignedPeerRecord: " & $e).cstring ) record = some(decoded) - except RlpError, ValueError: + except ValueError: return err("Invalid encoded SPR") var pubkey: PublicKey diff --git a/libp2pdht/private/eth/p2p/discoveryv5/messages.nim b/libp2pdht/private/eth/p2p/discoveryv5/messages.nim index 9e3c52f..50114e8 100644 --- a/libp2pdht/private/eth/p2p/discoveryv5/messages.nim +++ b/libp2pdht/private/eth/p2p/discoveryv5/messages.nim @@ -7,14 +7,14 @@ # ## Discovery v5 Protocol Messages as specified at ## https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire.md#protocol-messages -## These messages get RLP encoded. +## These messages get protobuf encoded, while in the spec they get RLP encoded. ## {.push raises: [Defect].} import std/[hashes, net], - eth/[keys], + bearssl, ./spr, ./node, ../../../../dht/providers_messages diff --git a/libp2pdht/private/eth/p2p/discoveryv5/messages_encoding.nim b/libp2pdht/private/eth/p2p/discoveryv5/messages_encoding.nim index 7938661..74ee990 100644 --- a/libp2pdht/private/eth/p2p/discoveryv5/messages_encoding.nim +++ b/libp2pdht/private/eth/p2p/discoveryv5/messages_encoding.nim @@ -10,8 +10,6 @@ import std/net, - stew/arrayops, - eth/[rlp], chronicles, libp2p/routing_record, libp2p/signed_envelope, @@ -23,83 +21,312 @@ from stew/objects import checkedEnumAssign type DecodeResult*[T] = Result[T, cstring] -proc read*(rlp: var Rlp, T: type RequestId): T - {.raises: [ValueError, RlpError, Defect].} = - mixin read - var reqId: RequestId - reqId.id = rlp.toBytes() - if reqId.id.len > 8: - raise newException(ValueError, "RequestId is > 8 bytes") - rlp.skipElem() + Distances = seq[uint16] - reqId + EncodedMessage = seq[byte] -proc append*(writer: var RlpWriter, value: RequestId) = - writer.append(value.id) + IPv4 = array[0..3, uint8] -proc read*(rlp: var Rlp, T: type IpAddress): T - {.raises: [RlpError, Defect].} = - let ipBytes = rlp.toBytes() - rlp.skipElem() + IPv6 = array[0..15, uint8] - if ipBytes.len == 4: - var ip: array[4, byte] - discard copyFrom(ip, ipBytes) - IpAddress(family: IPv4, address_v4: ip) - elif ipBytes.len == 16: - var ip: array[16, byte] - discard copyFrom(ip, ipBytes) - IpAddress(family: IPv6, address_v6: ip) + Port = uint16 + +proc getField*(pb: ProtoBuffer, field: int, + reqId: var RequestId): ProtoResult[bool] {.inline.} = + ## Read ``RequestId`` from ProtoBuf's message and validate it + var buffer: seq[byte] + let res = ? pb.getField(field, buffer) + if not(res): + ok(false) + elif buffer.len > 8: + ok(false) # RequestId must not be more than 8 bytes else: - raise newException(RlpTypeMismatch, - "Amount of bytes for IP address is different from 4 or 16") + reqId = RequestId(id: buffer) + ok(true) + +proc write*(pb: var ProtoBuffer, field: int, reqId: RequestId) = + ## Write RequestId value ``reqId`` to object ``pb`` using ProtoBuf's encoding. + let encoded = reqId.id + write(pb, field, encoded) + +proc decode*( + T: typedesc[PingMessage], + buffer: openArray[byte]): Result[PingMessage, ProtoError] = + + let pb = initProtoBuffer(buffer) + var msg = PingMessage() + + ? pb.getRequiredField(1, msg.sprSeq) + + ok(msg) + +proc encode*(msg: PingMessage): seq[byte] = + var pb = initProtoBuffer() + + pb.write(1, msg.sprSeq) + + pb.finish() + pb.buffer + +proc getField*(pb: ProtoBuffer, field: int, + ipv4: var IPv4): ProtoResult[bool] {.inline.} = + ## Read ``IPv4`` from ProtoBuf's message and validate it + var buffer: seq[byte] + let res = ? pb.getField(field, buffer) + if not(res): + ok(false) + else: + for i in 0..