@exodus/bytes 1.0.0-rc.1 → 1.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -2
- package/array.js +1 -1
- package/assert.js +10 -6
- package/base32.js +40 -0
- package/base58.js +212 -0
- package/base58check.js +69 -0
- package/base64.js +133 -161
- package/bech32.js +254 -0
- package/encoding-lite.js +7 -0
- package/encoding.js +12 -0
- package/fallback/_utils.js +118 -0
- package/fallback/base32.js +233 -0
- package/fallback/base64.js +192 -0
- package/fallback/encoding.js +279 -0
- package/fallback/encoding.labels.js +46 -0
- package/fallback/encoding.util.js +34 -0
- package/fallback/hex.js +127 -0
- package/fallback/latin1.js +120 -0
- package/fallback/multi-byte.encodings.cjs +1 -0
- package/fallback/multi-byte.encodings.json +545 -0
- package/fallback/multi-byte.js +448 -0
- package/fallback/multi-byte.table.js +114 -0
- package/fallback/single-byte.encodings.js +45 -0
- package/fallback/single-byte.js +83 -0
- package/fallback/utf16.js +180 -0
- package/fallback/utf8.js +245 -0
- package/hex.js +12 -71
- package/hex.node.js +28 -0
- package/multi-byte.js +13 -0
- package/multi-byte.node.js +25 -0
- package/package.json +105 -14
- package/single-byte.js +55 -0
- package/single-byte.node.js +62 -0
- package/utf16.js +73 -0
- package/utf16.node.js +79 -0
- package/utf8.js +80 -0
- package/utf8.node.js +54 -0
- package/wif.js +42 -0
package/base64.js
CHANGED
|
@@ -1,205 +1,177 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { assertUint8, assertEmptyRest } from './assert.js'
|
|
2
|
+
import { typedView } from './array.js'
|
|
3
|
+
import { isHermes, skipWeb } from './fallback/_utils.js'
|
|
4
|
+
import { decodeLatin1, encodeLatin1 } from './fallback/latin1.js'
|
|
5
|
+
import * as js from './fallback/base64.js'
|
|
3
6
|
|
|
4
7
|
// See https://datatracker.ietf.org/doc/html/rfc4648
|
|
5
8
|
|
|
6
|
-
// base64: A-Za-z0-9+/ and =
|
|
7
|
-
// base64url: A-Za-z0-9_-
|
|
9
|
+
// base64: A-Za-z0-9+/ and = if padding not disabled
|
|
10
|
+
// base64url: A-Za-z0-9_- and = if padding enabled
|
|
8
11
|
|
|
9
|
-
const { Buffer, atob } = globalThis // Buffer is optional, only used when native
|
|
12
|
+
const { Buffer, atob, btoa } = globalThis // Buffer is optional, only used when native
|
|
10
13
|
const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
|
|
11
14
|
const { toBase64: web64 } = Uint8Array.prototype // Modern engines have this
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
const { E_CHAR, E_PADDING, E_LENGTH, E_LAST } = js
|
|
17
|
+
|
|
18
|
+
// faster only on Hermes (and a little in old Chrome), js path beats it on normal engines
|
|
19
|
+
const shouldUseBtoa = btoa && isHermes
|
|
20
|
+
const shouldUseAtob = atob && isHermes
|
|
21
|
+
|
|
22
|
+
// For native Buffer codepaths only
|
|
23
|
+
const isBuffer = (x) => x.constructor === Buffer && Buffer.isBuffer(x)
|
|
24
|
+
const toBuffer = (x) => (isBuffer(x) ? x : Buffer.from(x.buffer, x.byteOffset, x.byteLength))
|
|
25
|
+
|
|
26
|
+
function maybeUnpad(res, padding) {
|
|
27
|
+
if (padding) return res
|
|
28
|
+
const at = res.indexOf('=', res.length - 3)
|
|
29
|
+
return at === -1 ? res : res.slice(0, at)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function maybePad(res, padding) {
|
|
33
|
+
return padding && res.length % 4 !== 0 ? res + '='.repeat(4 - (res.length % 4)) : res
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const toUrl = (x) => x.replaceAll('+', '-').replaceAll('/', '_')
|
|
37
|
+
const haveWeb = (x) => !skipWeb && web64 && x.toBase64 === web64
|
|
38
|
+
|
|
39
|
+
export function toBase64(x, { padding = true } = {}) {
|
|
14
40
|
assertUint8(x)
|
|
15
|
-
if (
|
|
16
|
-
if (
|
|
17
|
-
if (
|
|
18
|
-
return
|
|
41
|
+
if (haveWeb(x)) return padding ? x.toBase64() : x.toBase64({ omitPadding: !padding }) // Modern, optionless is slightly faster
|
|
42
|
+
if (haveNativeBuffer) return maybeUnpad(toBuffer(x).base64Slice(0, x.byteLength), padding) // Older Node.js
|
|
43
|
+
if (shouldUseBtoa) return maybeUnpad(btoa(decodeLatin1(x)), padding)
|
|
44
|
+
return js.toBase64(x, false, padding) // Fallback
|
|
19
45
|
}
|
|
20
46
|
|
|
21
|
-
// NOTE: base64url omits padding
|
|
22
|
-
export function toBase64url(x) {
|
|
47
|
+
// NOTE: base64url omits padding by default
|
|
48
|
+
export function toBase64url(x, { padding = false } = {}) {
|
|
23
49
|
assertUint8(x)
|
|
24
|
-
if (
|
|
25
|
-
if (
|
|
26
|
-
if (
|
|
27
|
-
return
|
|
50
|
+
if (haveWeb(x)) return x.toBase64({ alphabet: 'base64url', omitPadding: !padding }) // Modern
|
|
51
|
+
if (haveNativeBuffer) return maybePad(toBuffer(x).base64urlSlice(0, x.byteLength), padding) // Older Node.js
|
|
52
|
+
if (shouldUseBtoa) return maybeUnpad(toUrl(btoa(decodeLatin1(x))), padding)
|
|
53
|
+
return js.toBase64(x, true, padding) // Fallback
|
|
28
54
|
}
|
|
29
55
|
|
|
30
56
|
// Unlike Buffer.from(), throws on invalid input (non-base64 symbols and incomplete chunks)
|
|
31
57
|
// Unlike Buffer.from() and Uint8Array.fromBase64(), does not allow spaces
|
|
32
58
|
// NOTE: Always operates in strict mode for last chunk
|
|
33
59
|
|
|
34
|
-
//
|
|
35
|
-
export function fromBase64(str,
|
|
36
|
-
if (typeof
|
|
60
|
+
// By default accepts both padded and non-padded variants, only strict base64
|
|
61
|
+
export function fromBase64(str, options) {
|
|
62
|
+
if (typeof options === 'string') options = { format: options } // Compat due to usage, TODO: remove
|
|
63
|
+
if (!options) return fromBase64common(str, false, 'both', 'uint8', null)
|
|
64
|
+
const { format = 'uint8', padding = 'both', ...rest } = options
|
|
65
|
+
return fromBase64common(str, false, padding, format, rest)
|
|
66
|
+
}
|
|
37
67
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (str
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
68
|
+
// By default accepts only non-padded strict base64url
|
|
69
|
+
export function fromBase64url(str, options) {
|
|
70
|
+
if (!options) return fromBase64common(str, true, false, 'uint8', null)
|
|
71
|
+
const { format = 'uint8', padding = false, ...rest } = options
|
|
72
|
+
return fromBase64common(str, true, padding, format, rest)
|
|
73
|
+
}
|
|
44
74
|
|
|
45
|
-
|
|
75
|
+
// By default accepts both padded and non-padded variants, base64 or base64url
|
|
76
|
+
export function fromBase64any(str, { format = 'uint8', padding = 'both', ...rest } = {}) {
|
|
77
|
+
const isBase64url = !str.includes('+') && !str.includes('/') // likely to fail fast, as most input is non-url, also double scan is faster than regex
|
|
78
|
+
return fromBase64common(str, isBase64url, padding, format, rest)
|
|
46
79
|
}
|
|
47
80
|
|
|
48
|
-
|
|
49
|
-
export function fromBase64url(str, format = 'uint8') {
|
|
81
|
+
function fromBase64common(str, isBase64url, padding, format, rest) {
|
|
50
82
|
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
83
|
+
if (rest !== null) assertEmptyRest(rest)
|
|
84
|
+
const auto = padding === 'both' ? str.endsWith('=') : undefined
|
|
85
|
+
// Older JSC supporting Uint8Array.fromBase64 lacks proper checks
|
|
86
|
+
if (padding === true || auto === true) {
|
|
87
|
+
if (str.length % 4 !== 0) throw new SyntaxError(E_PADDING) // JSC misses this
|
|
88
|
+
if (str[str.length - 3] === '=') throw new SyntaxError(E_PADDING) // no more than two = at the end
|
|
89
|
+
} else if (padding === false || auto === false) {
|
|
90
|
+
if (str.length % 4 === 1) throw new SyntaxError(E_LENGTH) // JSC misses this in fromBase64
|
|
91
|
+
if (padding === false && str.endsWith('=')) {
|
|
92
|
+
throw new SyntaxError('Did not expect padding in base64 input') // inclusion is checked separately
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
throw new TypeError('Invalid padding option')
|
|
96
|
+
}
|
|
51
97
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
98
|
+
return typedView(fromBase64impl(str, isBase64url, padding), format)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ASCII whitespace is U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE
|
|
102
|
+
const ASCII_WHITESPACE = /[\t\n\f\r ]/ // non-u for JSC perf
|
|
55
103
|
|
|
56
|
-
|
|
104
|
+
function noWhitespaceSeen(str, arr) {
|
|
105
|
+
const at = str.indexOf('=', str.length - 3)
|
|
106
|
+
const paddingLength = at >= 0 ? str.length - at : 0
|
|
107
|
+
const chars = str.length - paddingLength
|
|
108
|
+
const e = chars % 4 // extra chars past blocks of 4
|
|
109
|
+
const b = arr.length - ((chars - e) / 4) * 3 // remaining bytes not covered by full blocks of chars
|
|
110
|
+
return (e === 0 && b === 0) || (e === 2 && b === 1) || (e === 3 && b === 2)
|
|
57
111
|
}
|
|
58
112
|
|
|
59
|
-
let
|
|
60
|
-
if (Uint8Array.fromBase64) {
|
|
113
|
+
let fromBase64impl
|
|
114
|
+
if (!skipWeb && Uint8Array.fromBase64) {
|
|
61
115
|
// NOTICE: this is actually slower than our JS impl in older JavaScriptCore and (slightly) in SpiderMonkey, but faster on V8 and new JavaScriptCore
|
|
62
|
-
|
|
116
|
+
fromBase64impl = (str, isBase64url, padding) => {
|
|
63
117
|
const alphabet = isBase64url ? 'base64url' : 'base64'
|
|
64
|
-
assert(!/\s/u.test(str), `Invalid character in ${alphabet} input`) // all other chars are checked natively
|
|
65
|
-
const padded = str.length % 4 > 0 ? `${str}${'='.repeat(4 - (str.length % 4))}` : str
|
|
66
|
-
return Uint8Array.fromBase64(padded, { alphabet, lastChunkHandling: 'strict' })
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
69
|
-
fromBase64common = (str, isBase64url) => {
|
|
70
|
-
if (isBase64url) {
|
|
71
|
-
assert(!/[^0-9a-z_-]/iu.test(str), 'Invalid character in base64url input')
|
|
72
|
-
} else {
|
|
73
|
-
assert(!/[^0-9a-z=+/]/iu.test(str), 'Invalid character in base64 input')
|
|
74
|
-
}
|
|
75
118
|
|
|
76
119
|
let arr
|
|
77
|
-
if (
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
arr = new Uint8Array(length)
|
|
82
|
-
for (let i = 0; i < length; i++) arr[i] = raw.charCodeAt(i)
|
|
120
|
+
if (padding === true) {
|
|
121
|
+
// Padding is required from user, and we already checked that string length is divisible by 4
|
|
122
|
+
// Padding might still be wrong due to whitespace, but in that case native impl throws expected error
|
|
123
|
+
arr = Uint8Array.fromBase64(str, { alphabet, lastChunkHandling: 'strict' })
|
|
83
124
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
125
|
+
try {
|
|
126
|
+
const padded = str.length % 4 > 0 ? `${str}${'='.repeat(4 - (str.length % 4))}` : str
|
|
127
|
+
arr = Uint8Array.fromBase64(padded, { alphabet, lastChunkHandling: 'strict' })
|
|
128
|
+
} catch (err) {
|
|
129
|
+
// Normalize error: whitespace in input could have caused added padding to be invalid
|
|
130
|
+
// But reporting that as a padding error would be confusing
|
|
131
|
+
throw ASCII_WHITESPACE.test(str) ? new SyntaxError(E_CHAR) : err
|
|
88
132
|
}
|
|
89
|
-
|
|
90
|
-
arr = haveNativeBuffer ? Buffer.from(str, 'base64') : fromBase64js(str)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (arr.length % 3 !== 0) {
|
|
94
|
-
// Check last chunk to be strict if it was incomplete
|
|
95
|
-
const expected = toBase64(arr.subarray(-(arr.length % 3)))
|
|
96
|
-
const end = str.length % 4 === 0 ? str.slice(-4) : str.slice(-(str.length % 4)).padEnd(4, '=')
|
|
97
|
-
const actual = isBase64url ? end.replaceAll('-', '+').replaceAll('_', '/') : end
|
|
98
|
-
if (expected !== actual) throw new Error('Invalid last chunk')
|
|
99
133
|
}
|
|
100
134
|
|
|
135
|
+
// We don't allow whitespace in input, but that can be rechecked based on output length
|
|
136
|
+
// All other chars are checked natively
|
|
137
|
+
if (!noWhitespaceSeen(str, arr)) throw new SyntaxError(E_CHAR)
|
|
101
138
|
return arr
|
|
102
139
|
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// We construct output by concatenating chars, this seems to be fine enough on modern JS engines
|
|
112
|
-
function toBase64js(arr, alphabet, padding) {
|
|
113
|
-
assertUint8(arr)
|
|
114
|
-
const fullChunks = Math.floor(arr.length / 3)
|
|
115
|
-
const fullChunksBytes = fullChunks * 3
|
|
116
|
-
let o = ''
|
|
117
|
-
let i = 0
|
|
118
|
-
|
|
119
|
-
const pairs = alphabet === BASE64URL ? BASE64URL_PAIRS : BASE64_PAIRS
|
|
120
|
-
if (pairs.length === 0) {
|
|
121
|
-
for (let i = 0; i < 64; i++) {
|
|
122
|
-
for (let j = 0; j < 64; j++) pairs.push(`${alphabet[i]}${alphabet[j]}`)
|
|
123
|
-
}
|
|
140
|
+
} else if (haveNativeBuffer) {
|
|
141
|
+
fromBase64impl = (str, isBase64url, padding) => {
|
|
142
|
+
const arr = Buffer.from(str, 'base64')
|
|
143
|
+
// Rechecking by re-encoding is cheaper than regexes on Node.js
|
|
144
|
+
const got = isBase64url ? maybeUnpad(str, padding === false) : maybePad(str, padding !== true)
|
|
145
|
+
const valid = isBase64url ? arr.base64urlSlice(0, arr.length) : arr.base64Slice(0, arr.length)
|
|
146
|
+
if (got !== valid) throw new SyntaxError(E_PADDING)
|
|
147
|
+
return arr // fully checked
|
|
124
148
|
}
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
o += pairs[(a << 4) | (b >> 4)] + pairs[((b & 0x0f) << 8) | c]
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// If we have something left, process it with a full algo
|
|
136
|
-
let carry = 0
|
|
137
|
-
let shift = 2 // First byte needs to be shifted by 2 to get 6 bits
|
|
138
|
-
const length = arr.length
|
|
139
|
-
for (; i < length; i++) {
|
|
140
|
-
const x = arr[i]
|
|
141
|
-
o += alphabet[carry | (x >> shift)] // shift >= 2, so this fits
|
|
142
|
-
if (shift === 6) {
|
|
143
|
-
shift = 0
|
|
144
|
-
o += alphabet[x & 0x3f]
|
|
149
|
+
} else if (shouldUseAtob) {
|
|
150
|
+
// atob is faster than manual parsing on Hermes
|
|
151
|
+
fromBase64impl = (str, isBase64url, padding) => {
|
|
152
|
+
let arr
|
|
153
|
+
if (isBase64url) {
|
|
154
|
+
if (/[\t\n\f\r +/]/.test(str)) throw new SyntaxError(E_CHAR) // atob verifies other invalid input
|
|
155
|
+
str = str.replaceAll('-', '+').replaceAll('_', '/') // from url to normal
|
|
145
156
|
}
|
|
146
157
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (padding) o += ['', '==', '='][length - fullChunksBytes]
|
|
153
|
-
|
|
154
|
-
return o
|
|
155
|
-
}
|
|
158
|
+
try {
|
|
159
|
+
arr = encodeLatin1(atob(str))
|
|
160
|
+
} catch {
|
|
161
|
+
throw new SyntaxError(E_CHAR) // convert atob errors
|
|
162
|
+
}
|
|
156
163
|
|
|
157
|
-
|
|
158
|
-
let fromBase64jsMap
|
|
164
|
+
if (!isBase64url && !noWhitespaceSeen(str, arr)) throw new SyntaxError(E_CHAR) // base64url checks input above
|
|
159
165
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
map['_'.charCodeAt(0)] = map['/'.charCodeAt(0)] // for base64url
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let inputLength = str.length
|
|
170
|
-
while (str[inputLength - 1] === '=') inputLength--
|
|
171
|
-
|
|
172
|
-
const arr = new Uint8Array(Math.floor((inputLength * 3) / 4))
|
|
173
|
-
const tailLength = inputLength % 4
|
|
174
|
-
const mainLength = inputLength - tailLength // multiples of 4
|
|
175
|
-
|
|
176
|
-
let at = 0
|
|
177
|
-
let i = 0
|
|
178
|
-
let tmp
|
|
179
|
-
|
|
180
|
-
while (i < mainLength) {
|
|
181
|
-
tmp =
|
|
182
|
-
(map[str.charCodeAt(i)] << 18) |
|
|
183
|
-
(map[str.charCodeAt(i + 1)] << 12) |
|
|
184
|
-
(map[str.charCodeAt(i + 2)] << 6) |
|
|
185
|
-
map[str.charCodeAt(i + 3)]
|
|
186
|
-
arr[at++] = tmp >> 16
|
|
187
|
-
arr[at++] = (tmp >> 8) & 0xff
|
|
188
|
-
arr[at++] = tmp & 0xff
|
|
189
|
-
i += 4
|
|
190
|
-
}
|
|
166
|
+
if (arr.length % 3 !== 0) {
|
|
167
|
+
// Check last chunk to be strict if it was incomplete
|
|
168
|
+
const expected = toBase64(arr.subarray(-(arr.length % 3))) // str is normalized to non-url already
|
|
169
|
+
const end = str.length % 4 === 0 ? str.slice(-4) : str.slice(-(str.length % 4)).padEnd(4, '=')
|
|
170
|
+
if (expected !== end) throw new SyntaxError(E_LAST)
|
|
171
|
+
}
|
|
191
172
|
|
|
192
|
-
|
|
193
|
-
tmp =
|
|
194
|
-
(map[str.charCodeAt(i)] << 10) |
|
|
195
|
-
(map[str.charCodeAt(i + 1)] << 4) |
|
|
196
|
-
(map[str.charCodeAt(i + 2)] >> 2)
|
|
197
|
-
arr[at++] = (tmp >> 8) & 0xff
|
|
198
|
-
arr[at++] = tmp & 0xff
|
|
199
|
-
} else if (tailLength === 2) {
|
|
200
|
-
tmp = (map[str.charCodeAt(i)] << 2) | (map[str.charCodeAt(i + 1)] >> 4)
|
|
201
|
-
arr[at++] = tmp & 0xff
|
|
173
|
+
return arr
|
|
202
174
|
}
|
|
203
|
-
|
|
204
|
-
|
|
175
|
+
} else {
|
|
176
|
+
fromBase64impl = (str, isBase64url, padding) => js.fromBase64(str, isBase64url) // validated in js
|
|
205
177
|
}
|
package/bech32.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { nativeEncoder } from './fallback/_utils.js'
|
|
3
|
+
import { decodeAscii, encodeAscii, encodeLatin1 } from './fallback/latin1.js'
|
|
4
|
+
|
|
5
|
+
const alphabet = [...'qpzry9x8gf2tvdw0s3jn54khce6mua7l']
|
|
6
|
+
const BECH32 = 1
|
|
7
|
+
const BECH32M = 0x2b_c8_30_a3
|
|
8
|
+
|
|
9
|
+
const E_SIZE = 'Input length is out of range'
|
|
10
|
+
const E_PREFIX = 'Missing or invalid prefix'
|
|
11
|
+
const E_MIXED = 'Mixed-case string'
|
|
12
|
+
const E_PADDING = 'Padding is invalid'
|
|
13
|
+
const E_CHECKSUM = 'Invalid checksum'
|
|
14
|
+
const E_CHARACTER = 'Non-bech32 character'
|
|
15
|
+
const E_STRING = 'Input is not a string'
|
|
16
|
+
|
|
17
|
+
// nativeEncoder path uses encodeAscii which asserts ascii, otherwise we have 0-255 bytes from encodeLatin1
|
|
18
|
+
const c2x = new Int8Array(nativeEncoder ? 128 : 256).fill(-1)
|
|
19
|
+
const x2c = new Uint8Array(32)
|
|
20
|
+
for (let i = 0; i < alphabet.length; i++) {
|
|
21
|
+
const c = alphabet[i].charCodeAt(0)
|
|
22
|
+
c2x[c] = i
|
|
23
|
+
x2c[i] = c
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// checksum size is 30 bits, 0x3f_ff_ff_ff
|
|
27
|
+
// The good thing about the checksum is that it's linear over every bit
|
|
28
|
+
const poly0 = new Uint32Array(32) // just precache all possible ones, it's only 1 KiB
|
|
29
|
+
const p = (x) => ((x & 0x1_ff_ff_ff) << 5) ^ poly0[x >> 25]
|
|
30
|
+
for (let i = 0; i < 32; i++) {
|
|
31
|
+
poly0[i] =
|
|
32
|
+
(i & 0b0_0001 ? 0x3b_6a_57_b2 : 0) ^
|
|
33
|
+
(i & 0b0_0010 ? 0x26_50_8e_6d : 0) ^
|
|
34
|
+
(i & 0b0_0100 ? 0x1e_a1_19_fa : 0) ^
|
|
35
|
+
(i & 0b0_1000 ? 0x3d_42_33_dd : 0) ^
|
|
36
|
+
(i & 0b1_0000 ? 0x2a_14_62_b3 : 0)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 7 KiB more for faster p6/p8
|
|
40
|
+
const poly1 = new Uint32Array(32)
|
|
41
|
+
const poly2 = new Uint32Array(32)
|
|
42
|
+
const poly3 = new Uint32Array(32)
|
|
43
|
+
const poly4 = new Uint32Array(32)
|
|
44
|
+
const poly5 = new Uint32Array(32)
|
|
45
|
+
const poly6 = new Uint32Array(32)
|
|
46
|
+
const poly7 = new Uint32Array(32)
|
|
47
|
+
for (let i = 0; i < 32; i++) {
|
|
48
|
+
// poly0[i] === p(p(p(p(p(p(i))))))
|
|
49
|
+
poly1[i] = p(poly0[i]) // aka p(p(p(p(p(p(i << 5))))))
|
|
50
|
+
poly2[i] = p(poly1[i]) // aka p(p(p(p(p(p(i << 10))))))
|
|
51
|
+
poly3[i] = p(poly2[i]) // aka p(p(p(p(p(p(i << 15))))))
|
|
52
|
+
poly4[i] = p(poly3[i]) // aka p(p(p(p(p(p(i << 20))))))
|
|
53
|
+
poly5[i] = p(poly4[i]) // aka p(p(p(p(p(p(i << 25))))))
|
|
54
|
+
poly6[i] = p(poly5[i])
|
|
55
|
+
poly7[i] = p(poly6[i])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function p6(x) {
|
|
59
|
+
// Same as: return p(p(p(p(p(p(x))))))
|
|
60
|
+
const x0 = x & 0x1f
|
|
61
|
+
const x1 = (x >> 5) & 0x1f
|
|
62
|
+
const x2 = (x >> 10) & 0x1f
|
|
63
|
+
const x3 = (x >> 15) & 0x1f
|
|
64
|
+
const x4 = (x >> 20) & 0x1f
|
|
65
|
+
const x5 = (x >> 25) & 0x1f
|
|
66
|
+
return poly0[x0] ^ poly1[x1] ^ poly2[x2] ^ poly3[x3] ^ poly4[x4] ^ poly5[x5]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function p8(x) {
|
|
70
|
+
// Same as: return p(p(p(p(p(p(p(p(x))))))))
|
|
71
|
+
const x0 = x & 0x1f
|
|
72
|
+
const x1 = (x >> 5) & 0x1f
|
|
73
|
+
const x2 = (x >> 10) & 0x1f
|
|
74
|
+
const x3 = (x >> 15) & 0x1f
|
|
75
|
+
const x4 = (x >> 20) & 0x1f
|
|
76
|
+
const x5 = (x >> 25) & 0x1f
|
|
77
|
+
return poly2[x0] ^ poly3[x1] ^ poly4[x2] ^ poly5[x3] ^ poly6[x4] ^ poly7[x5]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// p(p(p(p(p(p(chk) ^ x0) ^ x1) ^ x2) ^ x3) ^ x4) ^ x5 === p6(chk) ^ merge(x0, x1, x2, x3, x4, x5)
|
|
81
|
+
const merge = (a, b, c, d, e, f) => f ^ (e << 5) ^ (d << 10) ^ (c << 15) ^ (b << 20) ^ (a << 25)
|
|
82
|
+
|
|
83
|
+
const prefixCache = new Map() // Cache 10 of them
|
|
84
|
+
|
|
85
|
+
function pPrefix(prefix) {
|
|
86
|
+
if (prefix === 'bc') return 0x2_31_80_43 // perf
|
|
87
|
+
const cached = prefixCache.get(prefix)
|
|
88
|
+
if (cached !== undefined) return cached
|
|
89
|
+
|
|
90
|
+
// bech32_hrp_expand(s): [ord(x) >> 5 for x in s] + [0] + [ord(x) & 31 for x in s]
|
|
91
|
+
// We can do this in a single scan due to linearity, but it's not very beneficial
|
|
92
|
+
let chk = 1 // it starts with one (see def bech32_polymod in BIP_0173)
|
|
93
|
+
const length = prefix.length
|
|
94
|
+
for (let i = 0; i < length; i++) {
|
|
95
|
+
const c = prefix.charCodeAt(i)
|
|
96
|
+
if (c < 33 || c > 126) throw new Error(E_PREFIX) // each character having a value in the range [33-126]
|
|
97
|
+
chk = p(chk) ^ (c >> 5)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
chk = p(chk) // <= for + [0]
|
|
101
|
+
for (let i = 0; i < length; i++) {
|
|
102
|
+
const c = prefix.charCodeAt(i)
|
|
103
|
+
chk = p(chk) ^ (c & 0x1f)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (prefixCache.size < 10) prefixCache.set(prefix, chk)
|
|
107
|
+
return chk
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function toBech32enc(prefix, bytes, limit, encoding) {
|
|
111
|
+
if (typeof prefix !== 'string' || !prefix) throw new TypeError(E_PREFIX)
|
|
112
|
+
if (typeof limit !== 'number') throw new TypeError(E_SIZE)
|
|
113
|
+
assertUint8(bytes)
|
|
114
|
+
const bytesLength = bytes.length
|
|
115
|
+
const wordsLength = Math.ceil((bytesLength * 8) / 5)
|
|
116
|
+
if (!(prefix.length + 7 + wordsLength <= limit)) throw new TypeError(E_SIZE)
|
|
117
|
+
prefix = prefix.toLowerCase()
|
|
118
|
+
const out = new Uint8Array(wordsLength + 6)
|
|
119
|
+
|
|
120
|
+
let chk = pPrefix(prefix)
|
|
121
|
+
let i = 0, j = 0 // prettier-ignore
|
|
122
|
+
|
|
123
|
+
// This loop is just an optimization of the next one
|
|
124
|
+
for (const length4 = bytesLength - 4; i < length4; i += 5, j += 8) {
|
|
125
|
+
const b0 = bytes[i], b1 = bytes[i + 1], b2 = bytes[i + 2], b3 = bytes[i + 3], b4 = bytes[i + 4] // prettier-ignore
|
|
126
|
+
const x0 = b0 >> 3
|
|
127
|
+
const x1 = ((b0 << 2) & 0x1f) | (b1 >> 6)
|
|
128
|
+
const x2 = (b1 >> 1) & 0x1f
|
|
129
|
+
const x3 = ((b1 << 4) & 0x1f) | (b2 >> 4)
|
|
130
|
+
const x4 = ((b2 << 1) & 0x1f) | (b3 >> 7)
|
|
131
|
+
const x5 = (b3 >> 2) & 0x1f
|
|
132
|
+
const x6 = ((b3 << 3) & 0x1f) | (b4 >> 5)
|
|
133
|
+
const x7 = b4 & 0x1f
|
|
134
|
+
chk = merge(x2, x3, x4, x5, x6, x7) ^ poly0[x1] ^ poly1[x0] ^ p8(chk)
|
|
135
|
+
out[j] = x2c[x0]
|
|
136
|
+
out[j + 1] = x2c[x1]
|
|
137
|
+
out[j + 2] = x2c[x2]
|
|
138
|
+
out[j + 3] = x2c[x3]
|
|
139
|
+
out[j + 4] = x2c[x4]
|
|
140
|
+
out[j + 5] = x2c[x5]
|
|
141
|
+
out[j + 6] = x2c[x6]
|
|
142
|
+
out[j + 7] = x2c[x7]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let value = 0, bits = 0 // prettier-ignore
|
|
146
|
+
for (; i < bytesLength; i++) {
|
|
147
|
+
value = ((value & 0xf) << 8) | bytes[i]
|
|
148
|
+
bits += 3
|
|
149
|
+
const x = (value >> bits) & 0x1f
|
|
150
|
+
chk = p(chk) ^ x
|
|
151
|
+
out[j++] = x2c[x]
|
|
152
|
+
if (bits >= 5) {
|
|
153
|
+
bits -= 5
|
|
154
|
+
const x = (value >> bits) & 0x1f
|
|
155
|
+
chk = p(chk) ^ x
|
|
156
|
+
out[j++] = x2c[x]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (bits > 0) {
|
|
161
|
+
const x = (value << (5 - bits)) & 0x1f
|
|
162
|
+
chk = p(chk) ^ x
|
|
163
|
+
out[j++] = x2c[x]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
chk = encoding ^ p6(chk)
|
|
167
|
+
out[j++] = x2c[(chk >> 25) & 0x1f]
|
|
168
|
+
out[j++] = x2c[(chk >> 20) & 0x1f]
|
|
169
|
+
out[j++] = x2c[(chk >> 15) & 0x1f]
|
|
170
|
+
out[j++] = x2c[(chk >> 10) & 0x1f]
|
|
171
|
+
out[j++] = x2c[(chk >> 5) & 0x1f]
|
|
172
|
+
out[j++] = x2c[(chk >> 0) & 0x1f]
|
|
173
|
+
|
|
174
|
+
return prefix + '1' + decodeAscii(out) // suboptimal in barebones, but actually ok in Hermes for not to care atm
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function assertDecodeArgs(str, limit) {
|
|
178
|
+
if (typeof str !== 'string') throw new TypeError(E_STRING)
|
|
179
|
+
if (typeof limit !== 'number' || str.length < 8 || !(str.length <= limit)) throw new Error(E_SIZE)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function fromBech32enc(str, limit, encoding) {
|
|
183
|
+
assertDecodeArgs(str, limit)
|
|
184
|
+
const lower = str.toLowerCase()
|
|
185
|
+
if (str !== lower) {
|
|
186
|
+
if (str !== str.toUpperCase()) throw new Error(E_MIXED)
|
|
187
|
+
str = lower
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const split = str.lastIndexOf('1')
|
|
191
|
+
if (split <= 0) throw new Error(E_PREFIX)
|
|
192
|
+
const prefix = str.slice(0, split)
|
|
193
|
+
const charsLength = str.length - split - 1
|
|
194
|
+
const wordsLength = charsLength - 6
|
|
195
|
+
if (wordsLength < 0) throw new Error(E_SIZE)
|
|
196
|
+
const bytesLength = (wordsLength * 5) >> 3
|
|
197
|
+
const slice = str.slice(split + 1)
|
|
198
|
+
const c = nativeEncoder ? encodeAscii(slice, E_CHARACTER) : encodeLatin1(slice) // suboptimal, but only affects non-Hermes barebones
|
|
199
|
+
const bytes = new Uint8Array(bytesLength)
|
|
200
|
+
|
|
201
|
+
let chk = pPrefix(prefix)
|
|
202
|
+
let i = 0, j = 0 // prettier-ignore
|
|
203
|
+
|
|
204
|
+
// This loop is just an optimization of the next one
|
|
205
|
+
for (const length7 = wordsLength - 7; i < length7; i += 8, j += 5) {
|
|
206
|
+
const c0 = c[i], c1 = c[i + 1], c2 = c[i + 2], c3 = c[i + 3], c4 = c[i + 4], c5 = c[i + 5], c6 = c[i + 6], c7 = c[i + 7] // prettier-ignore
|
|
207
|
+
const x0 = c2x[c0], x1 = c2x[c1], x2 = c2x[c2], x3 = c2x[c3], x4 = c2x[c4], x5 = c2x[c5], x6 = c2x[c6], x7 = c2x[c7] // prettier-ignore
|
|
208
|
+
if (x0 < 0 || x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || x5 < 0 || x6 < 0 || x7 < 0) throw new SyntaxError(E_CHARACTER) // prettier-ignore
|
|
209
|
+
chk = merge(x2, x3, x4, x5, x6, x7) ^ poly0[x1] ^ poly1[x0] ^ p8(chk)
|
|
210
|
+
bytes[j] = (x0 << 3) | (x1 >> 2)
|
|
211
|
+
bytes[j + 1] = (((x1 << 6) | (x2 << 1)) & 0xff) | (x3 >> 4)
|
|
212
|
+
bytes[j + 2] = ((x3 << 4) & 0xff) | (x4 >> 1)
|
|
213
|
+
bytes[j + 3] = ((((x4 << 5) | x5) << 2) & 0xff) | (x6 >> 3)
|
|
214
|
+
bytes[j + 4] = ((x6 << 5) & 0xff) | x7
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let value = 0, bits = 0 // prettier-ignore
|
|
218
|
+
for (; i < wordsLength; i++) {
|
|
219
|
+
const x = c2x[c[i]]
|
|
220
|
+
if (x < 0) throw new SyntaxError(E_CHARACTER)
|
|
221
|
+
chk = p(chk) ^ x
|
|
222
|
+
value = (value << 5) | x
|
|
223
|
+
bits += 5
|
|
224
|
+
if (bits >= 8) {
|
|
225
|
+
bits -= 8
|
|
226
|
+
bytes[j++] = (value >> bits) & 0xff
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (bits >= 5 || (value << (8 - bits)) & 0xff) throw new Error(E_PADDING)
|
|
231
|
+
|
|
232
|
+
// Checksum
|
|
233
|
+
{
|
|
234
|
+
const c0 = c[i], c1 = c[i + 1], c2 = c[i + 2], c3 = c[i + 3], c4 = c[i + 4], c5 = c[i + 5] // prettier-ignore
|
|
235
|
+
const x0 = c2x[c0], x1 = c2x[c1], x2 = c2x[c2], x3 = c2x[c3], x4 = c2x[c4], x5 = c2x[c5] // prettier-ignore
|
|
236
|
+
if (x0 < 0 || x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || x5 < 0) throw new SyntaxError(E_CHARACTER)
|
|
237
|
+
if ((merge(x0, x1, x2, x3, x4, x5) ^ p6(chk)) !== encoding) throw new Error(E_CHECKSUM)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { prefix, bytes }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// This is designed to be a very quick check, skipping all other validation
|
|
244
|
+
export function getPrefix(str, limit = 90) {
|
|
245
|
+
assertDecodeArgs(str, limit)
|
|
246
|
+
const split = str.lastIndexOf('1')
|
|
247
|
+
if (split <= 0) throw new Error(E_PREFIX)
|
|
248
|
+
return str.slice(0, split).toLowerCase()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const toBech32 = (prefix, bytes, limit = 90) => toBech32enc(prefix, bytes, limit, BECH32)
|
|
252
|
+
export const fromBech32 = (str, limit = 90) => fromBech32enc(str, limit, BECH32)
|
|
253
|
+
export const toBech32m = (prefix, bytes, limit = 90) => toBech32enc(prefix, bytes, limit, BECH32M)
|
|
254
|
+
export const fromBech32m = (str, limit = 90) => fromBech32enc(str, limit, BECH32M)
|
package/encoding-lite.js
ADDED
package/encoding.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createMultibyteDecoder } from '@exodus/bytes/multi-byte.js' // eslint-disable-line @exodus/import/no-unresolved
|
|
2
|
+
import { setMultibyteDecoder } from './fallback/encoding.js'
|
|
3
|
+
|
|
4
|
+
setMultibyteDecoder(createMultibyteDecoder)
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
TextDecoder,
|
|
8
|
+
TextEncoder,
|
|
9
|
+
normalizeEncoding,
|
|
10
|
+
getBOMEncoding,
|
|
11
|
+
legacyHookDecode,
|
|
12
|
+
} from './fallback/encoding.js'
|