mirror of https://github.com/status-im/js-waku.git
chore!: extract encoder code
Separation of concerns by moving encoding logic in new class.
This commit is contained in:
parent
130c49b636
commit
22ffcf571a
|
@ -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")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import { equals } from "uint8arrays/equals";
|
||||||
import { ERR_INVALID_ID } from "./constants.js";
|
import { ERR_INVALID_ID } from "./constants.js";
|
||||||
import { EnrCreator } from "./creator.js";
|
import { EnrCreator } from "./creator.js";
|
||||||
import { EnrDecoder } from "./decoder.js";
|
import { EnrDecoder } from "./decoder.js";
|
||||||
|
import { EnrEncoder } from "./encoder.js";
|
||||||
import { ENR } from "./enr.js";
|
import { ENR } from "./enr.js";
|
||||||
import { getPrivateKeyFromPeerId } from "./peer_id.js";
|
import { getPrivateKeyFromPeerId } from "./peer_id.js";
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ describe("ENR", function () {
|
||||||
lightPush: false,
|
lightPush: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const enr2 = await EnrDecoder.fromString(txt);
|
const enr2 = await EnrDecoder.fromString(txt);
|
||||||
|
|
||||||
if (!enr.signature) throw "enr.signature is undefined";
|
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.setLocationMultiaddr(multiaddr("/ip4/18.223.219.100/udp/9000"));
|
||||||
|
|
||||||
enr.set("id", new Uint8Array([0]));
|
enr.set("id", new Uint8Array([0]));
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
|
|
||||||
await EnrDecoder.fromString(txt);
|
await EnrDecoder.fromString(txt);
|
||||||
assert.fail("Expect error here");
|
assert.fail("Expect error here");
|
||||||
|
@ -199,7 +200,7 @@ describe("ENR", function () {
|
||||||
record = await EnrCreator.fromPublicKey(secp.getPublicKey(privateKey));
|
record = await EnrCreator.fromPublicKey(secp.getPublicKey(privateKey));
|
||||||
record.setLocationMultiaddr(multiaddr("/ip4/127.0.0.1/udp/30303"));
|
record.setLocationMultiaddr(multiaddr("/ip4/127.0.0.1/udp/30303"));
|
||||||
record.seq = seq;
|
record.seq = seq;
|
||||||
await record.encodeTxt(privateKey);
|
await EnrEncoder.toString(record, privateKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly compute the node id", () => {
|
it("should properly compute the node id", () => {
|
||||||
|
@ -209,7 +210,7 @@ describe("ENR", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should encode/decode to RLP encoding", async 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);
|
const decoded = await EnrDecoder.fromRLP(encoded);
|
||||||
|
|
||||||
record.forEach((value, key) => {
|
record.forEach((value, key) => {
|
||||||
|
@ -403,7 +404,7 @@ describe("ENR", function () {
|
||||||
it("should set field with all protocols disabled", async () => {
|
it("should set field with all protocols disabled", async () => {
|
||||||
enr.waku2 = waku2Protocols;
|
enr.waku2 = waku2Protocols;
|
||||||
|
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
||||||
|
|
||||||
expect(decoded.relay).to.equal(false);
|
expect(decoded.relay).to.equal(false);
|
||||||
|
@ -419,7 +420,7 @@ describe("ENR", function () {
|
||||||
waku2Protocols.lightPush = true;
|
waku2Protocols.lightPush = true;
|
||||||
|
|
||||||
enr.waku2 = waku2Protocols;
|
enr.waku2 = waku2Protocols;
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
||||||
|
|
||||||
expect(decoded.relay).to.equal(true);
|
expect(decoded.relay).to.equal(true);
|
||||||
|
@ -432,7 +433,7 @@ describe("ENR", function () {
|
||||||
waku2Protocols.relay = true;
|
waku2Protocols.relay = true;
|
||||||
|
|
||||||
enr.waku2 = waku2Protocols;
|
enr.waku2 = waku2Protocols;
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
||||||
|
|
||||||
expect(decoded.relay).to.equal(true);
|
expect(decoded.relay).to.equal(true);
|
||||||
|
@ -445,7 +446,7 @@ describe("ENR", function () {
|
||||||
waku2Protocols.store = true;
|
waku2Protocols.store = true;
|
||||||
|
|
||||||
enr.waku2 = waku2Protocols;
|
enr.waku2 = waku2Protocols;
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
||||||
|
|
||||||
expect(decoded.relay).to.equal(false);
|
expect(decoded.relay).to.equal(false);
|
||||||
|
@ -458,7 +459,7 @@ describe("ENR", function () {
|
||||||
waku2Protocols.filter = true;
|
waku2Protocols.filter = true;
|
||||||
|
|
||||||
enr.waku2 = waku2Protocols;
|
enr.waku2 = waku2Protocols;
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
||||||
|
|
||||||
expect(decoded.relay).to.equal(false);
|
expect(decoded.relay).to.equal(false);
|
||||||
|
@ -471,7 +472,7 @@ describe("ENR", function () {
|
||||||
waku2Protocols.lightPush = true;
|
waku2Protocols.lightPush = true;
|
||||||
|
|
||||||
enr.waku2 = waku2Protocols;
|
enr.waku2 = waku2Protocols;
|
||||||
const txt = await enr.encodeTxt(privateKey);
|
const txt = await EnrEncoder.toString(enr, privateKey);
|
||||||
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
const decoded = (await EnrDecoder.fromString(txt)).waku2!;
|
||||||
|
|
||||||
expect(decoded.relay).to.equal(false);
|
expect(decoded.relay).to.equal(false);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as RLP from "@ethersproject/rlp";
|
|
||||||
import type { PeerId } from "@libp2p/interface-peer-id";
|
import type { PeerId } from "@libp2p/interface-peer-id";
|
||||||
import type { Multiaddr } from "@multiformats/multiaddr";
|
import type { Multiaddr } from "@multiformats/multiaddr";
|
||||||
import {
|
import {
|
||||||
|
@ -13,15 +12,10 @@ import type {
|
||||||
SequenceNumber,
|
SequenceNumber,
|
||||||
Waku2,
|
Waku2,
|
||||||
} from "@waku/interfaces";
|
} from "@waku/interfaces";
|
||||||
import { bytesToUtf8, hexToBytes, utf8ToBytes } from "@waku/utils";
|
import { bytesToUtf8 } from "@waku/utils";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
import { toString } from "uint8arrays/to-string";
|
|
||||||
|
|
||||||
import {
|
import { ERR_INVALID_ID } from "./constants.js";
|
||||||
ERR_INVALID_ID,
|
|
||||||
ERR_NO_SIGNATURE,
|
|
||||||
MAX_RECORD_SIZE,
|
|
||||||
} from "./constants.js";
|
|
||||||
import { keccak256, verifySignature } from "./crypto.js";
|
import { keccak256, verifySignature } from "./crypto.js";
|
||||||
import { multiaddrFromFields } from "./multiaddr_from_fields.js";
|
import { multiaddrFromFields } from "./multiaddr_from_fields.js";
|
||||||
import { decodeMultiaddrs, encodeMultiaddrs } from "./multiaddrs_codec.js";
|
import { decodeMultiaddrs, encodeMultiaddrs } from "./multiaddrs_codec.js";
|
||||||
|
@ -380,43 +374,4 @@ export class ENR extends Map<ENRKey, ENRValue> implements IEnr {
|
||||||
}
|
}
|
||||||
return this.signature;
|
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")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,5 @@ export interface IEnr extends Map<ENRKey, ENRValue> {
|
||||||
multiaddrs?: Multiaddr[];
|
multiaddrs?: Multiaddr[];
|
||||||
waku2?: Waku2;
|
waku2?: Waku2;
|
||||||
|
|
||||||
encode(privateKey?: Uint8Array): Promise<Uint8Array>;
|
|
||||||
getFullMultiaddrs(): Multiaddr[];
|
getFullMultiaddrs(): Multiaddr[];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue