UNPKG

@apocentre/bc-ur

Version:

A JS implementation of the Uniform Resources (UR) specification from Blockchain Commons

115 lines 5.96 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const assert_1 = __importDefault(require("assert")); const utils_1 = require("./utils"); const bytewords = 'ableacidalsoapexaquaarchatomauntawayaxisbackbaldbarnbeltbetabiasbluebodybragbrewbulbbuzzcalmcashcatschefcityclawcodecolacookcostcruxcurlcuspcyandarkdatadaysdelidicedietdoordowndrawdropdrumdulldutyeacheasyechoedgeepicevenexamexiteyesfactfairfernfigsfilmfishfizzflapflewfluxfoxyfreefrogfuelfundgalagamegeargemsgiftgirlglowgoodgraygrimgurugushgyrohalfhanghardhawkheathelphighhillholyhopehornhutsicedideaidleinchinkyintoirisironitemjadejazzjoinjoltjowljudojugsjumpjunkjurykeepkenokeptkeyskickkilnkingkitekiwiknoblamblavalazyleaflegsliarlimplionlistlogoloudloveluaulucklungmainmanymathmazememomenumeowmildmintmissmonknailnavyneednewsnextnoonnotenumbobeyoboeomitonyxopenovalowlspaidpartpeckplaypluspoempoolposepuffpumapurrquadquizraceramprealredorichroadrockroofrubyruinrunsrustsafesagascarsetssilkskewslotsoapsolosongstubsurfswantacotasktaxitenttiedtimetinytoiltombtoystriptunatwinuglyundouniturgeuservastveryvetovialvibeviewvisavoidvowswallwandwarmwaspwavewaxywebswhatwhenwhizwolfworkyankyawnyellyogayurtzapszerozestzinczonezoom'; let bytewordsLookUpTable = []; const BYTEWORDS_NUM = 256; const BYTEWORD_LENGTH = 4; const MINIMAL_BYTEWORD_LENGTH = 2; var STYLES; (function (STYLES) { STYLES["STANDARD"] = "standard"; STYLES["URI"] = "uri"; STYLES["MINIMAL"] = "minimal"; })(STYLES || (STYLES = {})); const getWord = (index) => { return bytewords.slice(index * BYTEWORD_LENGTH, (index * BYTEWORD_LENGTH) + BYTEWORD_LENGTH); }; const getMinimalWord = (index) => { const byteword = getWord(index); return `${byteword[0]}${byteword[BYTEWORD_LENGTH - 1]}`; }; const addCRC = (string) => { const crc = utils_1.getCRCHex(Buffer.from(string, 'hex')); return `${string}${crc}`; }; const encodeWithSeparator = (word, separator) => { const crcAppendedWord = addCRC(word); const crcWordBuff = Buffer.from(crcAppendedWord, 'hex'); const result = crcWordBuff.reduce((result, w) => ([...result, getWord(w)]), []); return result.join(separator); }; const encodeMinimal = (word) => { const crcAppendedWord = addCRC(word); const crcWordBuff = Buffer.from(crcAppendedWord, 'hex'); const result = crcWordBuff.reduce((result, w) => result + getMinimalWord(w), ''); return result; }; const decodeWord = (word, wordLength) => { assert_1.default(word.length === wordLength, 'Invalid Bytewords: word.length does not match wordLength provided'); const dim = 26; // Since the first and last letters of each Byteword are unique, // we can use them as indexes into a two-dimensional lookup table. // This table is generated lazily. if (bytewordsLookUpTable.length === 0) { const array_len = dim * dim; bytewordsLookUpTable = [...new Array(array_len)].map(() => -1); for (let i = 0; i < BYTEWORDS_NUM; i++) { const byteword = getWord(i); let x = byteword[0].charCodeAt(0) - 'a'.charCodeAt(0); let y = byteword[3].charCodeAt(0) - 'a'.charCodeAt(0); let offset = y * dim + x; bytewordsLookUpTable[offset] = i; } } // If the coordinates generated by the first and last letters are out of bounds, // or the lookup table contains -1 at the coordinates, then the word is not valid. let x = (word[0]).toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0); let y = (word[wordLength == 4 ? 3 : 1]).toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0); assert_1.default(0 <= x && x < dim && 0 <= y && y < dim, 'Invalid Bytewords: invalid word'); let offset = y * dim + x; let value = bytewordsLookUpTable[offset]; assert_1.default(value !== -1, 'Invalid Bytewords: value not in lookup table'); // If we're decoding a full four-letter word, verify that the two middle letters are correct. if (wordLength == BYTEWORD_LENGTH) { const byteword = getWord(value); let c1 = word[1].toLowerCase(); let c2 = word[2].toLowerCase(); assert_1.default(c1 === byteword[1] && c2 === byteword[2], 'Invalid Bytewords: invalid middle letters of word'); } // Successful decode. return Buffer.from([value]).toString('hex'); }; const _decode = (string, separator, wordLength) => { const words = wordLength == BYTEWORD_LENGTH ? string.split(separator) : utils_1.partition(string, 2); const decodedString = words.map((word) => decodeWord(word, wordLength)).join(''); assert_1.default(decodedString.length >= 5, 'Invalid Bytewords: invalid decoded string length'); const [body, bodyChecksum] = utils_1.split(Buffer.from(decodedString, 'hex'), 4); const checksum = utils_1.getCRCHex(body); // convert to hex assert_1.default(checksum === bodyChecksum.toString('hex'), 'Invalid Checksum'); return body.toString('hex'); }; const decode = (string, style = STYLES.MINIMAL) => { switch (style) { case STYLES.STANDARD: return _decode(string, ' ', BYTEWORD_LENGTH); case STYLES.URI: return _decode(string, '-', BYTEWORD_LENGTH); case STYLES.MINIMAL: return _decode(string, '', MINIMAL_BYTEWORD_LENGTH); default: throw new Error(`Invalid style ${style}`); } }; const encode = (string, style = STYLES.MINIMAL) => { switch (style) { case STYLES.STANDARD: return encodeWithSeparator(string, ' '); case STYLES.URI: return encodeWithSeparator(string, '-'); case STYLES.MINIMAL: return encodeMinimal(string); default: throw new Error(`Invalid style ${style}`); } }; exports.default = { decode, encode, STYLES }; //# sourceMappingURL=bytewords.js.map