From f274104865794f7f24db4244d591c39ad16f6688 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Wed, 17 Aug 2022 23:03:27 -0400 Subject: [PATCH] Applied lastest changes from ens-normalize (#42, #2376, #2754). --- packages/hash/src.ts/ens-normalize/decoder.ts | 28 ++-- packages/hash/src.ts/ens-normalize/include.ts | 2 +- packages/hash/src.ts/ens-normalize/lib.ts | 137 +++++++++++------- packages/hash/src.ts/namehash.ts | 22 --- 4 files changed, 100 insertions(+), 89 deletions(-) diff --git a/packages/hash/src.ts/ens-normalize/decoder.ts b/packages/hash/src.ts/ens-normalize/decoder.ts index 825e2b61dc..bc6d8c3ad2 100644 --- a/packages/hash/src.ts/ens-normalize/decoder.ts +++ b/packages/hash/src.ts/ens-normalize/decoder.ts @@ -216,10 +216,10 @@ export function read_zero_terminated_array(next: NextFunc): Array { return v; } -function read_transposed(n: number, w: number, next: NextFunc, lookup?: NextFunc): Array> { +function read_transposed(n: number, w: number, next: NextFunc): Array> { let m = Array(n).fill(undefined).map(() => []); for (let i = 0; i < w; i++) { - read_deltas(n, next).forEach((x, j) => m[j].push(lookup ? lookup(x) : x)); + read_deltas(n, next).forEach((x, j) => m[j].push(x)); } return m; } @@ -254,7 +254,7 @@ export type Branch = { export type Node = { branches: Array; - valid: boolean; + valid: number; fe0f: boolean; save: boolean; check: boolean; @@ -266,18 +266,18 @@ export function read_emoji_trie(next: NextFunc): Node { function read(): Node { let branches = []; while (true) { - let keys = read_member_array(next); + let keys = read_member_array(next, sorted); if (keys.length == 0) break; - branches.push({set: new Set(keys.map(i => sorted[i])), node: read()}); + branches.push({set: new Set(keys), node: read()}); } - branches.sort((a, b) => b.set.size - a.set.size); - let flag = next(); - return { - branches, - valid: (flag & 1) != 0, - fe0f: (flag & 2) != 0, - save: (flag & 4) != 0, - check: (flag & 8) != 0, - }; + branches.sort((a, b) => b.set.size - a.set.size); // sort by likelihood + let temp = next(); + let valid = temp % 3; + temp = (temp / 3)|0; + let fe0f = !!(temp & 1); + temp >>= 1; + let save = temp == 1; + let check = temp == 2; + return {branches, valid, fe0f, save, check}; } } diff --git a/packages/hash/src.ts/ens-normalize/include.ts b/packages/hash/src.ts/ens-normalize/include.ts index 38a6565e12..5eb56ca6b3 100644 --- a/packages/hash/src.ts/ens-normalize/include.ts +++ b/packages/hash/src.ts/ens-normalize/include.ts @@ -32,6 +32,6 @@ import { decode } from "@ethersproject/base64"; import {read_compressed_payload} from './decoder.js'; export function getData(): () => number { - return read_compressed_payload(decode('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')); + return read_compressed_payload(decode('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')); } diff --git a/packages/hash/src.ts/ens-normalize/lib.ts b/packages/hash/src.ts/ens-normalize/lib.ts index 20fd7ad087..a2007bce60 100644 --- a/packages/hash/src.ts/ens-normalize/lib.ts +++ b/packages/hash/src.ts/ens-normalize/lib.ts @@ -34,8 +34,6 @@ const r = getData(); import {read_member_array, read_mapped_map, read_emoji_trie} from './decoder.js'; -import type { Node } from "./decoder.js"; - // @TODO: This should be lazily loaded const VALID = new Set(read_member_array(r)); @@ -44,64 +42,99 @@ const MAPPED = read_mapped_map(r); const EMOJI_ROOT = read_emoji_trie(r); //const NFC_CHECK = new Set(read_member_array(r, Array.from(VALID.values()).sort((a, b) => a - b))); -function nfc(s: string): string { - return s.normalize('NFC'); +//const STOP = 0x2E; +const HYPHEN = 0x2D; +const UNDERSCORE = 0x5F; + +function explode_cp(name: string): Array { + return toUtf8CodePoints(name); } function filter_fe0f(cps: Array): Array { return cps.filter(cp => cp != 0xFE0F); } -export function ens_normalize(name: string, beautify = false): string { - const input = toUtf8CodePoints(name).reverse(); // flip for pop - const output = []; - while (input.length) { - const emoji = consume_emoji_reversed(input, EMOJI_ROOT); - if (emoji) { - output.push(...(beautify ? emoji : filter_fe0f(emoji))); - continue; - } - const cp = input.pop(); - if (VALID.has(cp)) { - output.push(cp); - continue; - } - if (IGNORED.has(cp)) { - continue; - } - let cps = MAPPED[cp]; - if (cps) { - output.push(...cps); - continue; - } - throw new Error(`Disallowed codepoint: 0x${cp.toString(16).toUpperCase()}`); - } - return nfc(String.fromCodePoint(...output)); +export function ens_normalize_post_check(name: string): string { + for (let label of name.split('.')) { + let cps = explode_cp(label); + try { + for (let i = cps.lastIndexOf(UNDERSCORE) - 1; i >= 0; i--) { + if (cps[i] !== UNDERSCORE) { + throw new Error(`underscore only allowed at start`); + } + } + if (cps.length >= 4 && cps.every(cp => cp < 0x80) && cps[2] === HYPHEN && cps[3] === HYPHEN) { + throw new Error(`invalid label extension`); + } + } catch (err) { + throw new Error(`Invalid label "${label}": ${err.message}`); + } + } + return name; +} + +export function ens_normalize(name: string): string { + return ens_normalize_post_check(normalize(name, filter_fe0f)); } +function normalize(name: string, emoji_filter: (a: Array) => Array): string { + let input = explode_cp(name).reverse(); // flip for pop + let output = []; + while (input.length) { + let emoji = consume_emoji_reversed(input); + if (emoji) { + output.push(...emoji_filter(emoji)); + continue; + } + let cp = input.pop(); + if (VALID.has(cp)) { + output.push(cp); + continue; + } + if (IGNORED.has(cp)) { + continue; + } + let cps = MAPPED[cp]; + if (cps) { + output.push(...cps); + continue; + } + throw new Error(`Disallowed codepoint: 0x${cp.toString(16).toUpperCase()}`); + } + return ens_normalize_post_check(nfc(String.fromCodePoint(...output))); +} -function consume_emoji_reversed(cps: Array, node: Node, eaten?: Array) { - let emoji; - const stack = []; - let pos = cps.length; - if (eaten) { eaten.length = 0; } // clear input buffer (if needed) - while (pos) { - const cp = cps[--pos]; - const branch = node.branches.find(x => x.set.has(cp)); - if (branch == null) { break; } - node = branch.node; - if (!node) { break; } - stack.push(cp); - if (node.fe0f) { - stack.push(0xFE0F); - if (pos > 0 && cps[pos - 1] == 0xFE0F) { pos--; } - } - if (node.valid) { // this is a valid emoji (so far) - emoji = stack.slice(); // copy stack - if (eaten) { eaten.push(...cps.slice(pos).reverse()); } // copy input (if needed) - cps.length = pos; // truncate - } - } - return emoji; +function nfc(s: string): string { + return s.normalize('NFC'); } +function consume_emoji_reversed(cps: Array, eaten?: Array) { + let node = EMOJI_ROOT; + let emoji; + let saved; + let stack = []; + let pos = cps.length; + if (eaten) eaten.length = 0; // clear input buffer (if needed) + while (pos) { + let cp = cps[--pos]; + node = node.branches.find(x => x.set.has(cp))?.node; + if (!node) break; + if (node.save) { // remember + saved = cp; + } else if (node.check) { // check exclusion + if (cp === saved) break; + } + stack.push(cp); + if (node.fe0f) { + stack.push(0xFE0F); + if (pos > 0 && cps[pos - 1] == 0xFE0F) pos--; // consume optional FE0F + } + if (node.valid) { // this is a valid emoji (so far) + emoji = stack.slice(); // copy stack + if (node.valid == 2) emoji.splice(1, 1); // delete FE0F at position 1 (RGI ZWJ don't follow spec!) + if (eaten) eaten.push(...cps.slice(pos).reverse()); // copy input (if needed) + cps.length = pos; // truncate + } + } + return emoji; +} diff --git a/packages/hash/src.ts/namehash.ts b/packages/hash/src.ts/namehash.ts index 953ff99041..5e38be59ab 100644 --- a/packages/hash/src.ts/namehash.ts +++ b/packages/hash/src.ts/namehash.ts @@ -13,28 +13,6 @@ Zeros.fill(0); function checkComponent(comp: Uint8Array): Uint8Array { if (comp.length === 0) { throw new Error("invalid ENS name; empty component"); } - let nonUnder = false; - let allAscii = true; - for (let i = 0; i < comp.length; i++) { - const c = comp[i]; - - // An underscore (i.e. "_"); only allows at the beginning - if (c === 0x5f) { - if (nonUnder) { throw new Error("invalid ENS name; non-prefix underscore"); } - } else { - // Non-ASCII byte - if (c & 0x80) { allAscii = false; } - - // Non-underscore found - nonUnder = true; - } - } - - // Prevent punycode-looking components - if (allAscii && comp[2] === 0x2d && comp[3] === 0x2d) { - throw new Error("invalid ENS name; punycode conflict"); - } - return comp; }