@waku/enr
Version:
ENR (EIP-778) for Waku
63 lines • 2.25 kB
JavaScript
import * as RLP from "@ethersproject/rlp";
import { Logger } from "@waku/utils";
import { bytesToHex, bytesToUtf8, hexToBytes } from "@waku/utils/bytes";
import { fromString } from "uint8arrays/from-string";
import { ENR } from "./enr.js";
const log = new Logger("enr:decoder");
export class EnrDecoder {
static fromString(encoded) {
if (!encoded.startsWith(ENR.RECORD_PREFIX)) {
throw new Error(`"string encoded ENR must start with '${ENR.RECORD_PREFIX}'`);
}
return EnrDecoder.fromRLP(fromString(encoded.slice(4), "base64url"));
}
static fromRLP(encoded) {
const decoded = RLP.decode(encoded).map(hexToBytes);
return fromValues(decoded);
}
}
async function fromValues(values) {
const { signature, seq, kvs } = checkValues(values);
const obj = {};
for (let i = 0; i < kvs.length; i += 2) {
try {
obj[bytesToUtf8(kvs[i])] = kvs[i + 1];
}
catch (e) {
log.error("Failed to decode ENR key to UTF-8, skipping it", kvs[i], e);
}
}
const _seq = decodeSeq(seq);
const enr = ENR.create(obj, _seq, signature);
checkSignature(seq, kvs, enr, signature);
return enr;
}
function decodeSeq(seq) {
// If seq is an empty array, translate as value 0
if (!seq.length)
return BigInt(0);
return BigInt("0x" + bytesToHex(seq));
}
function checkValues(values) {
if (!Array.isArray(values)) {
throw new Error("Decoded ENR must be an array");
}
if (values.length % 2 !== 0) {
throw new Error("Decoded ENR must have an even number of elements");
}
const [signature, seq, ...kvs] = values;
if (!signature || Array.isArray(signature)) {
throw new Error("Decoded ENR invalid signature: must be a byte array");
}
if (!seq || Array.isArray(seq)) {
throw new Error("Decoded ENR invalid sequence number: must be a byte array");
}
return { signature, seq, kvs };
}
function checkSignature(seq, kvs, enr, signature) {
const rlpEncodedBytes = hexToBytes(RLP.encode([seq, ...kvs]));
if (!enr.verify(rlpEncodedBytes, signature)) {
throw new Error("Unable to verify ENR signature");
}
}
//# sourceMappingURL=decoder.js.map