From 976f7104429263ba15be33415f3576ea022a260c Mon Sep 17 00:00:00 2001 From: JKincorperated Date: Mon, 17 Jun 2024 14:48:39 +0100 Subject: [PATCH] Add ENR 7636 read support --- packages/enr/src/enr.ts | 57 ++++++++++++++++++++++++++++++ packages/enr/test/unit/enr.test.ts | 40 +++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/packages/enr/src/enr.ts b/packages/enr/src/enr.ts index 9ace062c..c1538834 100644 --- a/packages/enr/src/enr.ts +++ b/packages/enr/src/enr.ts @@ -30,6 +30,12 @@ export type SignableENRData = { privateKey: Uint8Array; }; +export type ClientInfo = { + name: string; + version: string; + opt: string | undefined; +}; + export function id(kvs: ReadonlyMap): IDScheme { const idBuf = kvs.get("id"); if (!idBuf) throw new Error("id not found"); @@ -191,6 +197,31 @@ export function portToBuf(port: number): Uint8Array { return buf; } +export function getClientInfoValue(kvs: ReadonlyMap, key: string): ClientInfo | undefined { + const rawClientInfo = kvs.get(key); + if (!rawClientInfo) { return undefined } + let notRawClientInfo = rawClientInfo! as unknown as Array; + + if (notRawClientInfo.length != 2 && notRawClientInfo.length != 3) { return undefined; } + + let softwareName, softwareVersion, opt; + + [softwareName, softwareVersion, opt] = notRawClientInfo; + + softwareName = new TextDecoder("ascii").decode(softwareName); + softwareVersion = new TextDecoder("ascii").decode(softwareVersion); + + if (opt) { + opt = new TextDecoder("ascii").decode(opt); + } + + return { + name: softwareName, + version: softwareVersion, + opt: opt, + }; +} + // Classes export abstract class BaseENR { @@ -292,6 +323,10 @@ export abstract class BaseENR { } } + get clientInfo(): ClientInfo | undefined { + return getClientInfoValue(this.kvs, "client"); + } + // Serialization methods abstract encodeToValues(): (ENRKey | ENRValue | number)[]; @@ -534,6 +569,28 @@ export class SignableENR extends BaseENR { this.set("udp6", portToBuf(port)); } } + + get clientInfo(): ClientInfo | undefined { + return getClientInfoValue(this.kvs, "client"); + } + set clientInfo(info: ClientInfo | undefined) { + throw new Error("Not implemented"); + // if (info && info.opt) { + // this.set("client", [ + // new TextEncoder().encode(info.name), + // new TextEncoder().encode(info.version), + // new TextEncoder().encode(info.opt), + // ]); + // } else if (info) { + // this.set("client", [ + // new TextEncoder().encode(info.name), + // new TextEncoder().encode(info.version) + // ]); + // } else { + // this.delete("client"); + // } + } + setLocationMultiaddr(multiaddr: Multiaddr): void { const protoNames = multiaddr.protoNames(); if (protoNames.length !== 2 && protoNames[1] !== "udp" && protoNames[1] !== "tcp") { diff --git a/packages/enr/test/unit/enr.test.ts b/packages/enr/test/unit/enr.test.ts index 7c2d5ec2..3d7e4034 100644 --- a/packages/enr/test/unit/enr.test.ts +++ b/packages/enr/test/unit/enr.test.ts @@ -252,3 +252,43 @@ describe("ENR fuzzing testcases", () => { } }); }); + +describe("ENR EIP-7636 Support", () => { + const text = "enr:-MO4QBn4OF-y-dqULg4WOIlc8gQAt-arldNFe0_YQ4HNX28jDtg41xjDyKfCXGfZaPN97I-MCfogeK91TyqmWTpb0_AChmNsaWVudNqKTmV0aGVybWluZIYxLjkuNTOHN2ZjYjU2N4JpZIJ2NIJpcIR_AAABg2lwNpAAAAAAAAAAAAAAAAAAAAABiXNlY3AyNTZrMaECn-TTdCwfZP4XgJyq8Lxoj-SgEoIFgDLVBEUqQk4HnAqDdWRwgiMshHVkcDaCIyw" + it("should properly round trip decode and encode", () => { + expect(ENR.decodeTxt(text).encodeTxt()).to.equal(text); + }); + it("should be able to read", () => { + let data = ENR.decodeTxt(text).clientInfo; + expect(data).to.not.be.undefined; + expect(data!.name == "Nethermind") + expect(data!.opt == "7fcb567") + expect(data!.version == "1.9.53") + }); + // it("should properly round trip encode then decode. Incl. OPT", async () => { + // const peerId = await createSecp256k1PeerId(); + // const enr = SignableENR.createFromPeerId(peerId); + // enr.clientInfo = { + // name: "Example", + // version: "1234", + // opt: "1234", + // }; + // let round = ENR.decodeTxt(enr.encodeTxt()).clientInfo; + // expect(round?.opt).to.equal("1234"); + // expect(round?.name).to.equal("Example"); + // expect(round?.version).to.equal("1234"); + // }); + // it("should properly round trip encode then decode. Excl. OPT", async () => { + // const peerId = await createSecp256k1PeerId(); + // const enr = SignableENR.createFromPeerId(peerId); + // enr.clientInfo = { + // name: "Example", + // version: "1234", + // opt: undefined + // }; + // let round = ENR.decodeTxt(enr.encodeTxt()); + // expect(round.clientInfo?.opt).to.be.undefined; + // expect(round.clientInfo?.name).to.equal("Example"); + // expect(round.clientInfo?.version).to.equal("1234"); + // }); +}) \ No newline at end of file