You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
25 KiB
713 lines
25 KiB
"use strict"; |
|
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.bytes = exports.stringToBytes = exports.str = exports.bytesToString = exports.hex = exports.utf8 = exports.bech32m = exports.bech32 = exports.base58check = exports.createBase58check = exports.base58xmr = exports.base58xrp = exports.base58flickr = exports.base58 = exports.base64urlnopad = exports.base64url = exports.base64nopad = exports.base64 = exports.base32crockford = exports.base32hexnopad = exports.base32hex = exports.base32nopad = exports.base32 = exports.base16 = exports.utils = void 0; |
|
function isBytes(a) { |
|
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array'); |
|
} |
|
/** Asserts something is Uint8Array. */ |
|
function abytes(b, ...lengths) { |
|
if (!isBytes(b)) |
|
throw new Error('Uint8Array expected'); |
|
if (lengths.length > 0 && !lengths.includes(b.length)) |
|
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length); |
|
} |
|
function isArrayOf(isString, arr) { |
|
if (!Array.isArray(arr)) |
|
return false; |
|
if (arr.length === 0) |
|
return true; |
|
if (isString) { |
|
return arr.every((item) => typeof item === 'string'); |
|
} |
|
else { |
|
return arr.every((item) => Number.isSafeInteger(item)); |
|
} |
|
} |
|
// no abytes: seems to have 10% slowdown. Why?! |
|
function afn(input) { |
|
if (typeof input !== 'function') |
|
throw new Error('function expected'); |
|
return true; |
|
} |
|
function astr(label, input) { |
|
if (typeof input !== 'string') |
|
throw new Error(`${label}: string expected`); |
|
return true; |
|
} |
|
function anumber(n) { |
|
if (!Number.isSafeInteger(n)) |
|
throw new Error(`invalid integer: ${n}`); |
|
} |
|
function aArr(input) { |
|
if (!Array.isArray(input)) |
|
throw new Error('array expected'); |
|
} |
|
function astrArr(label, input) { |
|
if (!isArrayOf(true, input)) |
|
throw new Error(`${label}: array of strings expected`); |
|
} |
|
function anumArr(label, input) { |
|
if (!isArrayOf(false, input)) |
|
throw new Error(`${label}: array of numbers expected`); |
|
} |
|
/** |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function chain(...args) { |
|
const id = (a) => a; |
|
// Wrap call in closure so JIT can inline calls |
|
const wrap = (a, b) => (c) => a(b(c)); |
|
// Construct chain of args[-1].encode(args[-2].encode([...])) |
|
const encode = args.map((x) => x.encode).reduceRight(wrap, id); |
|
// Construct chain of args[0].decode(args[1].decode(...)) |
|
const decode = args.map((x) => x.decode).reduce(wrap, id); |
|
return { encode, decode }; |
|
} |
|
/** |
|
* Encodes integer radix representation to array of strings using alphabet and back. |
|
* Could also be array of strings. |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function alphabet(letters) { |
|
// mapping 1 to "b" |
|
const lettersA = typeof letters === 'string' ? letters.split('') : letters; |
|
const len = lettersA.length; |
|
astrArr('alphabet', lettersA); |
|
// mapping "b" to 1 |
|
const indexes = new Map(lettersA.map((l, i) => [l, i])); |
|
return { |
|
encode: (digits) => { |
|
aArr(digits); |
|
return digits.map((i) => { |
|
if (!Number.isSafeInteger(i) || i < 0 || i >= len) |
|
throw new Error(`alphabet.encode: digit index outside alphabet "${i}". Allowed: ${letters}`); |
|
return lettersA[i]; |
|
}); |
|
}, |
|
decode: (input) => { |
|
aArr(input); |
|
return input.map((letter) => { |
|
astr('alphabet.decode', letter); |
|
const i = indexes.get(letter); |
|
if (i === undefined) |
|
throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`); |
|
return i; |
|
}); |
|
}, |
|
}; |
|
} |
|
/** |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function join(separator = '') { |
|
astr('join', separator); |
|
return { |
|
encode: (from) => { |
|
astrArr('join.decode', from); |
|
return from.join(separator); |
|
}, |
|
decode: (to) => { |
|
astr('join.decode', to); |
|
return to.split(separator); |
|
}, |
|
}; |
|
} |
|
/** |
|
* Pad strings array so it has integer number of bits |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function padding(bits, chr = '=') { |
|
anumber(bits); |
|
astr('padding', chr); |
|
return { |
|
encode(data) { |
|
astrArr('padding.encode', data); |
|
while ((data.length * bits) % 8) |
|
data.push(chr); |
|
return data; |
|
}, |
|
decode(input) { |
|
astrArr('padding.decode', input); |
|
let end = input.length; |
|
if ((end * bits) % 8) |
|
throw new Error('padding: invalid, string should have whole number of bytes'); |
|
for (; end > 0 && input[end - 1] === chr; end--) { |
|
const last = end - 1; |
|
const byte = last * bits; |
|
if (byte % 8 === 0) |
|
throw new Error('padding: invalid, string has too much padding'); |
|
} |
|
return input.slice(0, end); |
|
}, |
|
}; |
|
} |
|
/** |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function normalize(fn) { |
|
afn(fn); |
|
return { encode: (from) => from, decode: (to) => fn(to) }; |
|
} |
|
/** |
|
* Slow: O(n^2) time complexity |
|
*/ |
|
function convertRadix(data, from, to) { |
|
// base 1 is impossible |
|
if (from < 2) |
|
throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`); |
|
if (to < 2) |
|
throw new Error(`convertRadix: invalid to=${to}, base cannot be less than 2`); |
|
aArr(data); |
|
if (!data.length) |
|
return []; |
|
let pos = 0; |
|
const res = []; |
|
const digits = Array.from(data, (d) => { |
|
anumber(d); |
|
if (d < 0 || d >= from) |
|
throw new Error(`invalid integer: ${d}`); |
|
return d; |
|
}); |
|
const dlen = digits.length; |
|
while (true) { |
|
let carry = 0; |
|
let done = true; |
|
for (let i = pos; i < dlen; i++) { |
|
const digit = digits[i]; |
|
const fromCarry = from * carry; |
|
const digitBase = fromCarry + digit; |
|
if (!Number.isSafeInteger(digitBase) || |
|
fromCarry / from !== carry || |
|
digitBase - digit !== fromCarry) { |
|
throw new Error('convertRadix: carry overflow'); |
|
} |
|
const div = digitBase / to; |
|
carry = digitBase % to; |
|
const rounded = Math.floor(div); |
|
digits[i] = rounded; |
|
if (!Number.isSafeInteger(rounded) || rounded * to + carry !== digitBase) |
|
throw new Error('convertRadix: carry overflow'); |
|
if (!done) |
|
continue; |
|
else if (!rounded) |
|
pos = i; |
|
else |
|
done = false; |
|
} |
|
res.push(carry); |
|
if (done) |
|
break; |
|
} |
|
for (let i = 0; i < data.length - 1 && data[i] === 0; i++) |
|
res.push(0); |
|
return res.reverse(); |
|
} |
|
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); |
|
const radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); |
|
const powers = /* @__PURE__ */ (() => { |
|
let res = []; |
|
for (let i = 0; i < 40; i++) |
|
res.push(2 ** i); |
|
return res; |
|
})(); |
|
/** |
|
* Implemented with numbers, because BigInt is 5x slower |
|
*/ |
|
function convertRadix2(data, from, to, padding) { |
|
aArr(data); |
|
if (from <= 0 || from > 32) |
|
throw new Error(`convertRadix2: wrong from=${from}`); |
|
if (to <= 0 || to > 32) |
|
throw new Error(`convertRadix2: wrong to=${to}`); |
|
if (radix2carry(from, to) > 32) { |
|
throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${radix2carry(from, to)}`); |
|
} |
|
let carry = 0; |
|
let pos = 0; // bitwise position in current element |
|
const max = powers[from]; |
|
const mask = powers[to] - 1; |
|
const res = []; |
|
for (const n of data) { |
|
anumber(n); |
|
if (n >= max) |
|
throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); |
|
carry = (carry << from) | n; |
|
if (pos + from > 32) |
|
throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`); |
|
pos += from; |
|
for (; pos >= to; pos -= to) |
|
res.push(((carry >> (pos - to)) & mask) >>> 0); |
|
const pow = powers[pos]; |
|
if (pow === undefined) |
|
throw new Error('invalid carry'); |
|
carry &= pow - 1; // clean carry, otherwise it will cause overflow |
|
} |
|
carry = (carry << (to - pos)) & mask; |
|
if (!padding && pos >= from) |
|
throw new Error('Excess padding'); |
|
if (!padding && carry > 0) |
|
throw new Error(`Non-zero padding: ${carry}`); |
|
if (padding && pos > 0) |
|
res.push(carry >>> 0); |
|
return res; |
|
} |
|
/** |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function radix(num) { |
|
anumber(num); |
|
const _256 = 2 ** 8; |
|
return { |
|
encode: (bytes) => { |
|
if (!isBytes(bytes)) |
|
throw new Error('radix.encode input should be Uint8Array'); |
|
return convertRadix(Array.from(bytes), _256, num); |
|
}, |
|
decode: (digits) => { |
|
anumArr('radix.decode', digits); |
|
return Uint8Array.from(convertRadix(digits, num, _256)); |
|
}, |
|
}; |
|
} |
|
/** |
|
* If both bases are power of same number (like `2**8 <-> 2**64`), |
|
* there is a linear algorithm. For now we have implementation for power-of-two bases only. |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function radix2(bits, revPadding = false) { |
|
anumber(bits); |
|
if (bits <= 0 || bits > 32) |
|
throw new Error('radix2: bits should be in (0..32]'); |
|
if (radix2carry(8, bits) > 32 || radix2carry(bits, 8) > 32) |
|
throw new Error('radix2: carry overflow'); |
|
return { |
|
encode: (bytes) => { |
|
if (!isBytes(bytes)) |
|
throw new Error('radix2.encode input should be Uint8Array'); |
|
return convertRadix2(Array.from(bytes), 8, bits, !revPadding); |
|
}, |
|
decode: (digits) => { |
|
anumArr('radix2.decode', digits); |
|
return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding)); |
|
}, |
|
}; |
|
} |
|
function unsafeWrapper(fn) { |
|
afn(fn); |
|
return function (...args) { |
|
try { |
|
return fn.apply(null, args); |
|
} |
|
catch (e) { } |
|
}; |
|
} |
|
function checksum(len, fn) { |
|
anumber(len); |
|
afn(fn); |
|
return { |
|
encode(data) { |
|
if (!isBytes(data)) |
|
throw new Error('checksum.encode: input should be Uint8Array'); |
|
const sum = fn(data).slice(0, len); |
|
const res = new Uint8Array(data.length + len); |
|
res.set(data); |
|
res.set(sum, data.length); |
|
return res; |
|
}, |
|
decode(data) { |
|
if (!isBytes(data)) |
|
throw new Error('checksum.decode: input should be Uint8Array'); |
|
const payload = data.slice(0, -len); |
|
const oldChecksum = data.slice(-len); |
|
const newChecksum = fn(payload).slice(0, len); |
|
for (let i = 0; i < len; i++) |
|
if (newChecksum[i] !== oldChecksum[i]) |
|
throw new Error('Invalid checksum'); |
|
return payload; |
|
}, |
|
}; |
|
} |
|
// prettier-ignore |
|
exports.utils = { |
|
alphabet, chain, checksum, convertRadix, convertRadix2, radix, radix2, join, padding, |
|
}; |
|
// RFC 4648 aka RFC 3548 |
|
// --------------------- |
|
/** |
|
* base16 encoding from RFC 4648. |
|
* @example |
|
* ```js |
|
* base16.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => '12AB' |
|
* ``` |
|
*/ |
|
exports.base16 = chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); |
|
/** |
|
* base32 encoding from RFC 4648. Has padding. |
|
* Use `base32nopad` for unpadded version. |
|
* Also check out `base32hex`, `base32hexnopad`, `base32crockford`. |
|
* @example |
|
* ```js |
|
* base32.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => 'CKVQ====' |
|
* base32.decode('CKVQ===='); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base32 = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); |
|
/** |
|
* base32 encoding from RFC 4648. No padding. |
|
* Use `base32` for padded version. |
|
* Also check out `base32hex`, `base32hexnopad`, `base32crockford`. |
|
* @example |
|
* ```js |
|
* base32nopad.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => 'CKVQ' |
|
* base32nopad.decode('CKVQ'); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base32nopad = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), join('')); |
|
/** |
|
* base32 encoding from RFC 4648. Padded. Compared to ordinary `base32`, slightly different alphabet. |
|
* Use `base32hexnopad` for unpadded version. |
|
* @example |
|
* ```js |
|
* base32hex.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => '2ALG====' |
|
* base32hex.decode('2ALG===='); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base32hex = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join('')); |
|
/** |
|
* base32 encoding from RFC 4648. No padding. Compared to ordinary `base32`, slightly different alphabet. |
|
* Use `base32hex` for padded version. |
|
* @example |
|
* ```js |
|
* base32hexnopad.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => '2ALG' |
|
* base32hexnopad.decode('2ALG'); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base32hexnopad = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), join('')); |
|
/** |
|
* base32 encoding from RFC 4648. Doug Crockford's version. |
|
* https://www.crockford.com/base32.html |
|
* @example |
|
* ```js |
|
* base32crockford.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => '2ANG' |
|
* base32crockford.decode('2ANG'); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base32crockford = chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize((s) => s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'))); |
|
// Built-in base64 conversion https://caniuse.com/mdn-javascript_builtins_uint8array_frombase64 |
|
// prettier-ignore |
|
const hasBase64Builtin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toBase64 === 'function' && |
|
typeof Uint8Array.fromBase64 === 'function')(); |
|
const decodeBase64Builtin = (s, isUrl) => { |
|
astr('base64', s); |
|
const re = isUrl ? /^[A-Za-z0-9=_-]+$/ : /^[A-Za-z0-9=+/]+$/; |
|
const alphabet = isUrl ? 'base64url' : 'base64'; |
|
if (s.length > 0 && !re.test(s)) |
|
throw new Error('invalid base64'); |
|
return Uint8Array.fromBase64(s, { alphabet, lastChunkHandling: 'strict' }); |
|
}; |
|
/** |
|
* base64 from RFC 4648. Padded. |
|
* Use `base64nopad` for unpadded version. |
|
* Also check out `base64url`, `base64urlnopad`. |
|
* Falls back to built-in function, when available. |
|
* @example |
|
* ```js |
|
* base64.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => 'Eqs=' |
|
* base64.decode('Eqs='); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
// prettier-ignore |
|
exports.base64 = hasBase64Builtin ? { |
|
encode(b) { abytes(b); return b.toBase64(); }, |
|
decode(s) { return decodeBase64Builtin(s, false); }, |
|
} : chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join('')); |
|
/** |
|
* base64 from RFC 4648. No padding. |
|
* Use `base64` for padded version. |
|
* @example |
|
* ```js |
|
* base64nopad.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => 'Eqs' |
|
* base64nopad.decode('Eqs'); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base64nopad = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), join('')); |
|
/** |
|
* base64 from RFC 4648, using URL-safe alphabet. Padded. |
|
* Use `base64urlnopad` for unpadded version. |
|
* Falls back to built-in function, when available. |
|
* @example |
|
* ```js |
|
* base64url.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => 'Eqs=' |
|
* base64url.decode('Eqs='); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
// prettier-ignore |
|
exports.base64url = hasBase64Builtin ? { |
|
encode(b) { abytes(b); return b.toBase64({ alphabet: 'base64url' }); }, |
|
decode(s) { return decodeBase64Builtin(s, true); }, |
|
} : chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); |
|
/** |
|
* base64 from RFC 4648, using URL-safe alphabet. No padding. |
|
* Use `base64url` for padded version. |
|
* @example |
|
* ```js |
|
* base64urlnopad.encode(Uint8Array.from([0x12, 0xab])); |
|
* // => 'Eqs' |
|
* base64urlnopad.decode('Eqs'); |
|
* // => Uint8Array.from([0x12, 0xab]) |
|
* ``` |
|
*/ |
|
exports.base64urlnopad = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), join('')); |
|
// base58 code |
|
// ----------- |
|
const genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => chain(radix(58), alphabet(abc), join('')); |
|
/** |
|
* base58: base64 without ambigous characters +, /, 0, O, I, l. |
|
* Quadratic (O(n^2)) - so, can't be used on large inputs. |
|
* @example |
|
* ```js |
|
* base58.decode('01abcdef'); |
|
* // => '3UhJW' |
|
* ``` |
|
*/ |
|
exports.base58 = genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); |
|
/** |
|
* base58: flickr version. Check out `base58`. |
|
*/ |
|
exports.base58flickr = genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'); |
|
/** |
|
* base58: XRP version. Check out `base58`. |
|
*/ |
|
exports.base58xrp = genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); |
|
// Data len (index) -> encoded block len |
|
const XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11]; |
|
/** |
|
* base58: XMR version. Check out `base58`. |
|
* Done in 8-byte blocks (which equals 11 chars in decoding). Last (non-full) block padded with '1' to size in XMR_BLOCK_LEN. |
|
* Block encoding significantly reduces quadratic complexity of base58. |
|
*/ |
|
exports.base58xmr = { |
|
encode(data) { |
|
let res = ''; |
|
for (let i = 0; i < data.length; i += 8) { |
|
const block = data.subarray(i, i + 8); |
|
res += exports.base58.encode(block).padStart(XMR_BLOCK_LEN[block.length], '1'); |
|
} |
|
return res; |
|
}, |
|
decode(str) { |
|
let res = []; |
|
for (let i = 0; i < str.length; i += 11) { |
|
const slice = str.slice(i, i + 11); |
|
const blockLen = XMR_BLOCK_LEN.indexOf(slice.length); |
|
const block = exports.base58.decode(slice); |
|
for (let j = 0; j < block.length - blockLen; j++) { |
|
if (block[j] !== 0) |
|
throw new Error('base58xmr: wrong padding'); |
|
} |
|
res = res.concat(Array.from(block.slice(block.length - blockLen))); |
|
} |
|
return Uint8Array.from(res); |
|
}, |
|
}; |
|
/** |
|
* Method, which creates base58check encoder. |
|
* Requires function, calculating sha256. |
|
*/ |
|
const createBase58check = (sha256) => chain(checksum(4, (data) => sha256(sha256(data))), exports.base58); |
|
exports.createBase58check = createBase58check; |
|
/** |
|
* Use `createBase58check` instead. |
|
* @deprecated |
|
*/ |
|
exports.base58check = exports.createBase58check; |
|
const BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); |
|
const POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; |
|
function bech32Polymod(pre) { |
|
const b = pre >> 25; |
|
let chk = (pre & 0x1ffffff) << 5; |
|
for (let i = 0; i < POLYMOD_GENERATORS.length; i++) { |
|
if (((b >> i) & 1) === 1) |
|
chk ^= POLYMOD_GENERATORS[i]; |
|
} |
|
return chk; |
|
} |
|
function bechChecksum(prefix, words, encodingConst = 1) { |
|
const len = prefix.length; |
|
let chk = 1; |
|
for (let i = 0; i < len; i++) { |
|
const c = prefix.charCodeAt(i); |
|
if (c < 33 || c > 126) |
|
throw new Error(`Invalid prefix (${prefix})`); |
|
chk = bech32Polymod(chk) ^ (c >> 5); |
|
} |
|
chk = bech32Polymod(chk); |
|
for (let i = 0; i < len; i++) |
|
chk = bech32Polymod(chk) ^ (prefix.charCodeAt(i) & 0x1f); |
|
for (let v of words) |
|
chk = bech32Polymod(chk) ^ v; |
|
for (let i = 0; i < 6; i++) |
|
chk = bech32Polymod(chk); |
|
chk ^= encodingConst; |
|
return BECH_ALPHABET.encode(convertRadix2([chk % powers[30]], 30, 5, false)); |
|
} |
|
/** |
|
* @__NO_SIDE_EFFECTS__ |
|
*/ |
|
function genBech32(encoding) { |
|
const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; |
|
const _words = radix2(5); |
|
const fromWords = _words.decode; |
|
const toWords = _words.encode; |
|
const fromWordsUnsafe = unsafeWrapper(fromWords); |
|
function encode(prefix, words, limit = 90) { |
|
astr('bech32.encode prefix', prefix); |
|
if (isBytes(words)) |
|
words = Array.from(words); |
|
anumArr('bech32.encode', words); |
|
const plen = prefix.length; |
|
if (plen === 0) |
|
throw new TypeError(`Invalid prefix length ${plen}`); |
|
const actualLength = plen + 7 + words.length; |
|
if (limit !== false && actualLength > limit) |
|
throw new TypeError(`Length ${actualLength} exceeds limit ${limit}`); |
|
const lowered = prefix.toLowerCase(); |
|
const sum = bechChecksum(lowered, words, ENCODING_CONST); |
|
return `${lowered}1${BECH_ALPHABET.encode(words)}${sum}`; |
|
} |
|
function decode(str, limit = 90) { |
|
astr('bech32.decode input', str); |
|
const slen = str.length; |
|
if (slen < 8 || (limit !== false && slen > limit)) |
|
throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit})`); |
|
// don't allow mixed case |
|
const lowered = str.toLowerCase(); |
|
if (str !== lowered && str !== str.toUpperCase()) |
|
throw new Error(`String must be lowercase or uppercase`); |
|
const sepIndex = lowered.lastIndexOf('1'); |
|
if (sepIndex === 0 || sepIndex === -1) |
|
throw new Error(`Letter "1" must be present between prefix and data only`); |
|
const prefix = lowered.slice(0, sepIndex); |
|
const data = lowered.slice(sepIndex + 1); |
|
if (data.length < 6) |
|
throw new Error('Data must be at least 6 characters long'); |
|
const words = BECH_ALPHABET.decode(data).slice(0, -6); |
|
const sum = bechChecksum(prefix, words, ENCODING_CONST); |
|
if (!data.endsWith(sum)) |
|
throw new Error(`Invalid checksum in ${str}: expected "${sum}"`); |
|
return { prefix, words }; |
|
} |
|
const decodeUnsafe = unsafeWrapper(decode); |
|
function decodeToBytes(str) { |
|
const { prefix, words } = decode(str, false); |
|
return { prefix, words, bytes: fromWords(words) }; |
|
} |
|
function encodeFromBytes(prefix, bytes) { |
|
return encode(prefix, toWords(bytes)); |
|
} |
|
return { |
|
encode, |
|
decode, |
|
encodeFromBytes, |
|
decodeToBytes, |
|
decodeUnsafe, |
|
fromWords, |
|
fromWordsUnsafe, |
|
toWords, |
|
}; |
|
} |
|
/** |
|
* bech32 from BIP 173. Operates on words. |
|
* For high-level, check out scure-btc-signer: |
|
* https://github.com/paulmillr/scure-btc-signer. |
|
*/ |
|
exports.bech32 = genBech32('bech32'); |
|
/** |
|
* bech32m from BIP 350. Operates on words. |
|
* It was to mitigate `bech32` weaknesses. |
|
* For high-level, check out scure-btc-signer: |
|
* https://github.com/paulmillr/scure-btc-signer. |
|
*/ |
|
exports.bech32m = genBech32('bech32m'); |
|
/** |
|
* UTF-8-to-byte decoder. Uses built-in TextDecoder / TextEncoder. |
|
* @example |
|
* ```js |
|
* const b = utf8.decode("hey"); // => new Uint8Array([ 104, 101, 121 ]) |
|
* const str = utf8.encode(b); // "hey" |
|
* ``` |
|
*/ |
|
exports.utf8 = { |
|
encode: (data) => new TextDecoder().decode(data), |
|
decode: (str) => new TextEncoder().encode(str), |
|
}; |
|
// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex |
|
// prettier-ignore |
|
const hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === 'function' && |
|
typeof Uint8Array.fromHex === 'function')(); |
|
// prettier-ignore |
|
const hexBuiltin = { |
|
encode(data) { abytes(data); return data.toHex(); }, |
|
decode(s) { astr('hex', s); return Uint8Array.fromHex(s); }, |
|
}; |
|
/** |
|
* hex string decoder. Uses built-in function, when available. |
|
* @example |
|
* ```js |
|
* const b = hex.decode("0102ff"); // => new Uint8Array([ 1, 2, 255 ]) |
|
* const str = hex.encode(b); // "0102ff" |
|
* ``` |
|
*/ |
|
exports.hex = hasHexBuiltin |
|
? hexBuiltin |
|
: chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => { |
|
if (typeof s !== 'string' || s.length % 2 !== 0) |
|
throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`); |
|
return s.toLowerCase(); |
|
})); |
|
// prettier-ignore |
|
const CODERS = { |
|
utf8: exports.utf8, hex: exports.hex, base16: exports.base16, base32: exports.base32, base64: exports.base64, base64url: exports.base64url, base58: exports.base58, base58xmr: exports.base58xmr |
|
}; |
|
const coderTypeError = 'Invalid encoding type. Available types: utf8, hex, base16, base32, base64, base64url, base58, base58xmr'; |
|
/** @deprecated */ |
|
const bytesToString = (type, bytes) => { |
|
if (typeof type !== 'string' || !CODERS.hasOwnProperty(type)) |
|
throw new TypeError(coderTypeError); |
|
if (!isBytes(bytes)) |
|
throw new TypeError('bytesToString() expects Uint8Array'); |
|
return CODERS[type].encode(bytes); |
|
}; |
|
exports.bytesToString = bytesToString; |
|
/** @deprecated */ |
|
exports.str = exports.bytesToString; // as in python, but for bytes only |
|
/** @deprecated */ |
|
const stringToBytes = (type, str) => { |
|
if (!CODERS.hasOwnProperty(type)) |
|
throw new TypeError(coderTypeError); |
|
if (typeof str !== 'string') |
|
throw new TypeError('stringToBytes() expects string'); |
|
return CODERS[type].decode(str); |
|
}; |
|
exports.stringToBytes = stringToBytes; |
|
/** @deprecated */ |
|
exports.bytes = exports.stringToBytes; |
|
//# sourceMappingURL=index.js.map
|