chore!: extract encoder code

Separation of concerns by moving encoding logic in new class.
This commit is contained in:
fryorcraken.eth 2023-03-03 14:00:54 +11:00
parent 130c49b636
commit 22ffcf571a
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
4 changed files with 63 additions and 58 deletions

View File

@ -0,0 +1,50 @@
import * as RLP from "@ethersproject/rlp";
import type { ENRKey, ENRValue } from "@waku/interfaces";
import { hexToBytes, utf8ToBytes } from "@waku/utils";
import { toString } from "uint8arrays/to-string";
import { ERR_NO_SIGNATURE, MAX_RECORD_SIZE } from "./constants.js";
import { ENR } from "./enr.js";
export class EnrEncoder {
static async toValues(
enr: ENR,
privateKey?: Uint8Array
): Promise<(ENRKey | ENRValue | number[])[]> {
// sort keys and flatten into [k, v, k, v, ...]
const content: Array<ENRKey | ENRValue | number[]> = Array.from(enr.keys())
.sort((a, b) => a.localeCompare(b))
.map((k) => [k, enr.get(k)] as [ENRKey, ENRValue])
.map(([k, v]) => [utf8ToBytes(k), v])
.flat();
content.unshift(new Uint8Array([Number(enr.seq)]));
if (privateKey) {
content.unshift(
await enr.sign(hexToBytes(RLP.encode(content)), privateKey)
);
} else {
if (!enr.signature) {
throw new Error(ERR_NO_SIGNATURE);
}
content.unshift(enr.signature);
}
return content;
}
static async toBytes(enr: ENR, privateKey?: Uint8Array): Promise<Uint8Array> {
const encoded = hexToBytes(
RLP.encode(await EnrEncoder.toValues(enr, privateKey))
);
if (encoded.length >= MAX_RECORD_SIZE) {
throw new Error("ENR must be less than 300 bytes");
}
return encoded;
}
static async toString(enr: ENR, privateKey?: Uint8Array): Promise<string> {
return (
ENR.RECORD_PREFIX +
toString(await EnrEncoder.toBytes(enr, privateKey), "base64url")
);
}
}

View File

@ -9,6 +9,7 @@ import { equals } from "uint8arrays/equals";
import { ERR_INVALID_ID } from "./constants.js";
import { EnrCreator } from "./creator.js";
import { EnrDecoder } from "./decoder.js";
import { EnrEncoder } from "./encoder.js";
import { ENR } from "./enr.js";
import { getPrivateKeyFromPeerId } from "./peer_id.js";
@ -34,7 +35,7 @@ describe("ENR", function () {
lightPush: false,
};
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const enr2 = await EnrDecoder.fromString(txt);
if (!enr.signature) throw "enr.signature is undefined";
@ -113,7 +114,7 @@ describe("ENR", function () {
enr.setLocationMultiaddr(multiaddr("/ip4/18.223.219.100/udp/9000"));
enr.set("id", new Uint8Array([0]));
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
await EnrDecoder.fromString(txt);
assert.fail("Expect error here");
@ -199,7 +200,7 @@ describe("ENR", function () {
record = await EnrCreator.fromPublicKey(secp.getPublicKey(privateKey));
record.setLocationMultiaddr(multiaddr("/ip4/127.0.0.1/udp/30303"));
record.seq = seq;
await record.encodeTxt(privateKey);
await EnrEncoder.toString(record, privateKey);
});
it("should properly compute the node id", () => {
@ -209,7 +210,7 @@ describe("ENR", function () {
});
it("should encode/decode to RLP encoding", async function () {
const encoded = await record.encode(privateKey);
const encoded = await EnrEncoder.toBytes(record, privateKey);
const decoded = await EnrDecoder.fromRLP(encoded);
record.forEach((value, key) => {
@ -403,7 +404,7 @@ describe("ENR", function () {
it("should set field with all protocols disabled", async () => {
enr.waku2 = waku2Protocols;
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
expect(decoded.relay).to.equal(false);
@ -419,7 +420,7 @@ describe("ENR", function () {
waku2Protocols.lightPush = true;
enr.waku2 = waku2Protocols;
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
expect(decoded.relay).to.equal(true);
@ -432,7 +433,7 @@ describe("ENR", function () {
waku2Protocols.relay = true;
enr.waku2 = waku2Protocols;
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
expect(decoded.relay).to.equal(true);
@ -445,7 +446,7 @@ describe("ENR", function () {
waku2Protocols.store = true;
enr.waku2 = waku2Protocols;
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
expect(decoded.relay).to.equal(false);
@ -458,7 +459,7 @@ describe("ENR", function () {
waku2Protocols.filter = true;
enr.waku2 = waku2Protocols;
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
expect(decoded.relay).to.equal(false);
@ -471,7 +472,7 @@ describe("ENR", function () {
waku2Protocols.lightPush = true;
enr.waku2 = waku2Protocols;
const txt = await enr.encodeTxt(privateKey);
const txt = await EnrEncoder.toString(enr, privateKey);
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
expect(decoded.relay).to.equal(false);

View File

@ -1,4 +1,3 @@
import * as RLP from "@ethersproject/rlp";
import type { PeerId } from "@libp2p/interface-peer-id";
import type { Multiaddr } from "@multiformats/multiaddr";
import {
@ -13,15 +12,10 @@ import type {
SequenceNumber,
Waku2,
} from "@waku/interfaces";
import { bytesToUtf8, hexToBytes, utf8ToBytes } from "@waku/utils";
import { bytesToUtf8 } from "@waku/utils";
import debug from "debug";
import { toString } from "uint8arrays/to-string";
import {
ERR_INVALID_ID,
ERR_NO_SIGNATURE,
MAX_RECORD_SIZE,
} from "./constants.js";
import { ERR_INVALID_ID } from "./constants.js";
import { keccak256, verifySignature } from "./crypto.js";
import { multiaddrFromFields } from "./multiaddr_from_fields.js";
import { decodeMultiaddrs, encodeMultiaddrs } from "./multiaddrs_codec.js";
@ -380,43 +374,4 @@ export class ENR extends Map<ENRKey, ENRValue> implements IEnr {
}
return this.signature;
}
async encodeToValues(
privateKey?: Uint8Array
): Promise<(ENRKey | ENRValue | number[])[]> {
// sort keys and flatten into [k, v, k, v, ...]
const content: Array<ENRKey | ENRValue | number[]> = Array.from(this.keys())
.sort((a, b) => a.localeCompare(b))
.map((k) => [k, this.get(k)] as [ENRKey, ENRValue])
.map(([k, v]) => [utf8ToBytes(k), v])
.flat();
content.unshift(new Uint8Array([Number(this.seq)]));
if (privateKey) {
content.unshift(
await this.sign(hexToBytes(RLP.encode(content)), privateKey)
);
} else {
if (!this.signature) {
throw new Error(ERR_NO_SIGNATURE);
}
content.unshift(this.signature);
}
return content;
}
async encode(privateKey?: Uint8Array): Promise<Uint8Array> {
const encoded = hexToBytes(
RLP.encode(await this.encodeToValues(privateKey))
);
if (encoded.length >= MAX_RECORD_SIZE) {
throw new Error("ENR must be less than 300 bytes");
}
return encoded;
}
async encodeTxt(privateKey?: Uint8Array): Promise<string> {
return (
ENR.RECORD_PREFIX + toString(await this.encode(privateKey), "base64url")
);
}
}

View File

@ -33,6 +33,5 @@ export interface IEnr extends Map<ENRKey, ENRValue> {
multiaddrs?: Multiaddr[];
waku2?: Waku2;
encode(privateKey?: Uint8Array): Promise<Uint8Array>;
getFullMultiaddrs(): Multiaddr[];
}