diff --git a/eth/p2p/discoveryv5/encoding.nim b/eth/p2p/discoveryv5/encoding.nim index ef22522..2c907f3 100644 --- a/eth/p2p/discoveryv5/encoding.nim +++ b/eth/p2p/discoveryv5/encoding.nim @@ -17,7 +17,7 @@ import std/[tables, options, hashes, net], nimcrypto, stint, chronicles, bearssl, stew/[results, byteutils], metrics, ".."/../[rlp, keys], - "."/[messages, node, enr, hkdf, sessions] + "."/[messages, messages_encoding, node, enr, hkdf, sessions] from stew/objects import checkedEnumAssign @@ -367,50 +367,6 @@ proc decodeHeader*(id: NodeId, iv, maskedHeader: openArray[byte]): ok((StaticHeader(authdataSize: authdataSize, flag: flag, nonce: nonce), staticHeader & authdata)) -proc decodeMessage*(body: openArray[byte]): DecodeResult[Message] = - ## Decodes to the specific `Message` type. - if body.len < 1: - return err("No message data") - - var kind: MessageKind - if not checkedEnumAssign(kind, body[0]): - return err("Invalid message type") - - var message = Message(kind: kind) - var rlp = rlpFromBytes(body.toOpenArray(1, body.high)) - if rlp.enterList: - try: - message.reqId = rlp.read(RequestId) - except RlpError, ValueError: - return err("Invalid request-id") - - proc decode[T](rlp: var Rlp, v: var T) - {.nimcall, raises:[RlpError, ValueError, Defect].} = - for k, v in v.fieldPairs: - v = rlp.read(typeof(v)) - - try: - case kind - of unused: return err("Invalid message type") - of ping: rlp.decode(message.ping) - of pong: rlp.decode(message.pong) - of findNode: rlp.decode(message.findNode) - of nodes: rlp.decode(message.nodes) - of talkReq: rlp.decode(message.talkReq) - of talkResp: rlp.decode(message.talkResp) - of regTopic, ticket, regConfirmation, topicQuery: - # We just pass the empty type of this message without attempting to - # decode, so that the protocol knows what was received. - # But we ignore the message as per specification as "the content and - # semantics of this message are not final". - discard - except RlpError, ValueError: - return err("Invalid message encoding") - - ok(message) - else: - err("Invalid message encoding: no rlp list") - proc decodeMessagePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce, iv, header, ct: openArray[byte]): DecodeResult[Packet] = # We now know the exact size that the header should be @@ -607,17 +563,3 @@ proc init*(T: type RequestId, rng: var BrHmacDrbgContext): T = var reqId = RequestId(id: newSeq[byte](8)) # RequestId must be <= 8 bytes brHmacDrbgGenerate(rng, reqId.id) reqId - -proc numFields(T: typedesc): int = - for k, v in fieldPairs(default(T)): inc result - -proc encodeMessage*[T: SomeMessage](p: T, reqId: RequestId): seq[byte] = - result = newSeqOfCap[byte](64) - result.add(messageKind(T).ord) - - const sz = numFields(T) - var writer = initRlpList(sz + 1) - writer.append(reqId) - for k, v in fieldPairs(p): - writer.append(v) - result.add(writer.finish()) diff --git a/eth/p2p/discoveryv5/messages_encoding.nim b/eth/p2p/discoveryv5/messages_encoding.nim new file mode 100644 index 0000000..7d0de75 --- /dev/null +++ b/eth/p2p/discoveryv5/messages_encoding.nim @@ -0,0 +1,69 @@ +import + std/[net], + stint, bearssl, metrics, + ".."/../[rlp, keys], + "."/[messages, node, enr] + +from stew/objects import checkedEnumAssign + +type + DecodeResult*[T] = Result[T, cstring] + +proc numFields(T: typedesc): int = + for k, v in fieldPairs(default(T)): inc result + +proc encodeMessage*[T: SomeMessage](p: T, reqId: RequestId): seq[byte] = + result = newSeqOfCap[byte](64) + result.add(messageKind(T).ord) + + const sz = numFields(T) + var writer = initRlpList(sz + 1) + writer.append(reqId) + for k, v in fieldPairs(p): + writer.append(v) + result.add(writer.finish()) + +proc decodeMessage*(body: openArray[byte]): DecodeResult[Message] = + ## Decodes to the specific `Message` type. + if body.len < 1: + return err("No message data") + + var kind: MessageKind + if not checkedEnumAssign(kind, body[0]): + return err("Invalid message type") + + var message = Message(kind: kind) + var rlp = rlpFromBytes(body.toOpenArray(1, body.high)) + if rlp.enterList: + try: + message.reqId = rlp.read(RequestId) + except RlpError, ValueError: + return err("Invalid request-id") + + proc decode[T](rlp: var Rlp, v: var T) + {.nimcall, raises:[RlpError, ValueError, Defect].} = + for k, v in v.fieldPairs: + v = rlp.read(typeof(v)) + + try: + case kind + of unused: return err("Invalid message type") + of ping: rlp.decode(message.ping) + of pong: rlp.decode(message.pong) + of findNode: rlp.decode(message.findNode) + of nodes: rlp.decode(message.nodes) + of talkReq: rlp.decode(message.talkReq) + of talkResp: rlp.decode(message.talkResp) + of regTopic, ticket, regConfirmation, topicQuery: + # We just pass the empty type of this message without attempting to + # decode, so that the protocol knows what was received. + # But we ignore the message as per specification as "the content and + # semantics of this message are not final". + discard + except RlpError, ValueError: + return err("Invalid message encoding") + + ok(message) + else: + err("Invalid message encoding: no rlp list") + diff --git a/eth/p2p/discoveryv5/protocol.nim b/eth/p2p/discoveryv5/protocol.nim index 2582727..865aea6 100644 --- a/eth/p2p/discoveryv5/protocol.nim +++ b/eth/p2p/discoveryv5/protocol.nim @@ -78,7 +78,7 @@ import stew/shims/net as stewNet, json_serialization/std/net, stew/[endians2, results], chronicles, chronos, stint, bearssl, metrics, ".."/../[rlp, keys, async_utils], - "."/[messages, encoding, node, routing_table, enr, random2, sessions, ip_vote, nodes_verification] + "."/[messages, messages_encoding, encoding, node, routing_table, enr, random2, sessions, ip_vote, nodes_verification] import nimcrypto except toHex