diff --git a/packages/enr/src/decoder.ts b/packages/enr/src/decoder.ts index c2a2370f2d..12f1dfc241 100644 --- a/packages/enr/src/decoder.ts +++ b/packages/enr/src/decoder.ts @@ -18,42 +18,67 @@ export class EnrDecoder { static fromRLP(encoded: Uint8Array): Promise { const decoded = RLP.decode(encoded).map(hexToBytes); - return EnrDecoder.fromValues(decoded); - } - - private static async fromValues(decoded: Uint8Array[]): Promise { - if (!Array.isArray(decoded)) { - throw new Error("Decoded ENR must be an array"); - } - if (decoded.length % 2 !== 0) { - throw new Error("Decoded ENR must have an even number of elements"); - } - const [signature, seq, ...kvs] = decoded; - 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" - ); - } - const obj: Record = {}; - for (let i = 0; i < kvs.length; i += 2) { - try { - obj[bytesToUtf8(kvs[i])] = kvs[i + 1]; - } catch (e) { - log("Failed to decode ENR key to UTF-8, skipping it", kvs[i], e); - } - } - // If seq is an empty array, translate as value 0 - const hexSeq = "0x" + (seq.length ? bytesToHex(seq) : "00"); - - const enr = await ENR.create(obj, BigInt(hexSeq), signature); - - const rlpEncodedBytes = hexToBytes(RLP.encode([seq, ...kvs])); - if (!enr.verify(rlpEncodedBytes, signature)) { - throw new Error("Unable to verify ENR signature"); - } - return enr; + return fromValues(decoded); + } +} + +async function fromValues(values: Uint8Array[]): Promise { + const { signature, seq, kvs } = checkValues(values); + + const obj: Record = {}; + for (let i = 0; i < kvs.length; i += 2) { + try { + obj[bytesToUtf8(kvs[i])] = kvs[i + 1]; + } catch (e) { + log("Failed to decode ENR key to UTF-8, skipping it", kvs[i], e); + } + } + const _seq = decodeSeq(seq); + + const enr = await ENR.create(obj, _seq, signature); + checkSignature(seq, kvs, enr, signature); + return enr; +} + +function decodeSeq(seq: Uint8Array): bigint { + // If seq is an empty array, translate as value 0 + if (!seq.length) return BigInt(0); + + return BigInt("0x" + bytesToHex(seq)); +} + +function checkValues(values: Uint8Array[]): { + signature: Uint8Array; + seq: Uint8Array; + kvs: Uint8Array[]; +} { + 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: Uint8Array, + kvs: Uint8Array[], + enr: ENR, + signature: Uint8Array +): void { + const rlpEncodedBytes = hexToBytes(RLP.encode([seq, ...kvs])); + if (!enr.verify(rlpEncodedBytes, signature)) { + throw new Error("Unable to verify ENR signature"); } }