@exodus/bytes 1.0.0-rc.7 → 1.0.0-rc.8
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 +17 -1
- package/base58.js +3 -3
- package/base58check.js +2 -1
- package/base64.js +30 -29
- package/bech32.js +254 -0
- package/fallback/_utils.js +26 -1
- package/fallback/base32.js +2 -2
- package/fallback/hex.js +2 -2
- package/fallback/latin1.js +9 -3
- package/hex.js +6 -4
- package/package.json +15 -5
- package/utf8.js +6 -5
- package/utf8.node.js +22 -4
- package/wif.js +42 -0
package/README.md
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
# `@exodus/bytes`
|
|
2
2
|
|
|
3
|
-
`Uint8Array` conversion to and from `base64`, `base32`, `hex
|
|
3
|
+
`Uint8Array` conversion to and from `base64`, `base32`, `base58`, `hex`, and `utf8`
|
|
4
4
|
|
|
5
5
|
[Fast](./Performance.md)
|
|
6
6
|
|
|
7
7
|
Performs proper input validation
|
|
8
8
|
|
|
9
|
+
## API
|
|
10
|
+
|
|
11
|
+
### `@exodus/bytes/hex.js`
|
|
12
|
+
|
|
13
|
+
### `@exodus/bytes/base64.js`
|
|
14
|
+
|
|
15
|
+
### `@exodus/bytes/base32.js`
|
|
16
|
+
|
|
17
|
+
### `@exodus/bytes/hex.js`
|
|
18
|
+
|
|
19
|
+
### `@exodus/bytes/base58.js`
|
|
20
|
+
|
|
21
|
+
### `@exodus/bytes/base58check.js`
|
|
22
|
+
|
|
23
|
+
### `@exodus/bytes/wif.js`
|
|
24
|
+
|
|
9
25
|
## License
|
|
10
26
|
|
|
11
27
|
[MIT](./LICENSE)
|
package/base58.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { typedView } from './array.js'
|
|
2
2
|
import { assertUint8 } from './assert.js'
|
|
3
|
-
import { nativeDecoder, nativeEncoder } from './fallback/_utils.js'
|
|
3
|
+
import { nativeDecoder, nativeEncoder, isHermes } from './fallback/_utils.js'
|
|
4
4
|
import { encodeAscii, decodeAscii } from './fallback/latin1.js'
|
|
5
5
|
|
|
6
6
|
const alphabet = [...'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz']
|
|
@@ -20,7 +20,7 @@ let fromMap
|
|
|
20
20
|
|
|
21
21
|
const E_CHAR = 'Invalid character in base58 input'
|
|
22
22
|
|
|
23
|
-
const shouldUseBigIntFrom =
|
|
23
|
+
const shouldUseBigIntFrom = isHermes // faster only on Hermes, numbers path beats it on normal engines
|
|
24
24
|
|
|
25
25
|
export function toBase58(arr) {
|
|
26
26
|
assertUint8(arr)
|
|
@@ -134,7 +134,7 @@ export function fromBase58(str, format = 'uint8') {
|
|
|
134
134
|
for (let i = 0; i < 58; i++) fromMap[alphabet[i].charCodeAt(0)] = i
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const size = zeros + (((length - zeros) * 3
|
|
137
|
+
const size = zeros + (((length - zeros + 1) * 3) >> 2) // 3/4 rounded up, larger than ~0.73 coef to fit everything
|
|
138
138
|
const res = new Uint8Array(size)
|
|
139
139
|
let at = size // where is the first significant byte written
|
|
140
140
|
|
package/base58check.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { typedView } from './array.js'
|
|
2
2
|
import { assertUint8 } from './assert.js'
|
|
3
3
|
import { toBase58, fromBase58 } from './base58.js'
|
|
4
|
-
import { hashSync } from '@exodus/crypto/hash'
|
|
4
|
+
import { hashSync } from '@exodus/crypto/hash' // eslint-disable-line @exodus/import/no-deprecated
|
|
5
5
|
|
|
6
6
|
// Note: while API is async, we use hashSync for now until we improve webcrypto perf for hash256
|
|
7
7
|
// Inputs to base58 are typically very small, and that makes a difference
|
|
@@ -56,6 +56,7 @@ export const makeBase58check = (hashAlgo, hashAlgoSync) => {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
59
60
|
const hash256sync = (x) => hashSync('sha256', hashSync('sha256', x, 'uint8'), 'uint8')
|
|
60
61
|
const hash256 = hash256sync // See note at the top
|
|
61
62
|
const {
|
package/base64.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assertUint8, assertEmptyRest } from './assert.js'
|
|
2
2
|
import { typedView } from './array.js'
|
|
3
|
+
import { isHermes, skipWeb } from './fallback/_utils.js'
|
|
3
4
|
import { decodeLatin1, encodeLatin1 } from './fallback/latin1.js'
|
|
4
5
|
import * as js from './fallback/base64.js'
|
|
5
6
|
|
|
@@ -15,8 +16,8 @@ const { toBase64: web64 } = Uint8Array.prototype // Modern engines have this
|
|
|
15
16
|
const { E_CHAR, E_PADDING, E_LENGTH, E_LAST } = js
|
|
16
17
|
|
|
17
18
|
// faster only on Hermes (and a little in old Chrome), js path beats it on normal engines
|
|
18
|
-
const shouldUseBtoa = btoa &&
|
|
19
|
-
const shouldUseAtob = atob &&
|
|
19
|
+
const shouldUseBtoa = btoa && isHermes
|
|
20
|
+
const shouldUseAtob = atob && isHermes
|
|
20
21
|
|
|
21
22
|
// For native Buffer codepaths only
|
|
22
23
|
const isBuffer = (x) => x.constructor === Buffer && Buffer.isBuffer(x)
|
|
@@ -33,8 +34,7 @@ function maybePad(res, padding) {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
const toUrl = (x) => x.replaceAll('+', '-').replaceAll('/', '_')
|
|
36
|
-
const
|
|
37
|
-
const haveWeb = (x) => web64 && x.toBase64 === web64
|
|
37
|
+
const haveWeb = (x) => !skipWeb && web64 && x.toBase64 === web64
|
|
38
38
|
|
|
39
39
|
export function toBase64(x, { padding = true } = {}) {
|
|
40
40
|
assertUint8(x)
|
|
@@ -111,7 +111,7 @@ function noWhitespaceSeen(str, arr) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
let fromBase64impl
|
|
114
|
-
if (Uint8Array.fromBase64) {
|
|
114
|
+
if (!skipWeb && Uint8Array.fromBase64) {
|
|
115
115
|
// NOTICE: this is actually slower than our JS impl in older JavaScriptCore and (slightly) in SpiderMonkey, but faster on V8 and new JavaScriptCore
|
|
116
116
|
fromBase64impl = (str, isBase64url, padding) => {
|
|
117
117
|
const alphabet = isBase64url ? 'base64url' : 'base64'
|
|
@@ -137,40 +137,41 @@ if (Uint8Array.fromBase64) {
|
|
|
137
137
|
if (!noWhitespaceSeen(str, arr)) throw new SyntaxError(E_CHAR)
|
|
138
138
|
return arr
|
|
139
139
|
}
|
|
140
|
-
} else {
|
|
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
|
|
148
|
+
}
|
|
149
|
+
} else if (shouldUseAtob) {
|
|
150
|
+
// atob is faster than manual parsing on Hermes
|
|
141
151
|
fromBase64impl = (str, isBase64url, padding) => {
|
|
142
152
|
let arr
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (r !== arr.toString(isBase64url ? 'base64url' : 'base64')) throw new SyntaxError(E_PADDING)
|
|
148
|
-
} else if (shouldUseAtob) {
|
|
149
|
-
// atob is faster than manual parsing on Hermes
|
|
150
|
-
if (isBase64url) {
|
|
151
|
-
if (/[\t\n\f\r +/]/.test(str)) throw new SyntaxError(E_CHAR) // atob verifies other invalid input
|
|
152
|
-
str = fromUrl(str)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
arr = encodeLatin1(atob(str))
|
|
157
|
-
} catch {
|
|
158
|
-
throw new SyntaxError(E_CHAR) // convert atob errors
|
|
159
|
-
}
|
|
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
|
|
156
|
+
}
|
|
160
157
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
try {
|
|
159
|
+
arr = encodeLatin1(atob(str))
|
|
160
|
+
} catch {
|
|
161
|
+
throw new SyntaxError(E_CHAR) // convert atob errors
|
|
164
162
|
}
|
|
165
163
|
|
|
164
|
+
if (!isBase64url && !noWhitespaceSeen(str, arr)) throw new SyntaxError(E_CHAR) // base64url checks input above
|
|
165
|
+
|
|
166
166
|
if (arr.length % 3 !== 0) {
|
|
167
167
|
// Check last chunk to be strict if it was incomplete
|
|
168
|
-
const expected = toBase64(arr.subarray(-(arr.length % 3)))
|
|
168
|
+
const expected = toBase64(arr.subarray(-(arr.length % 3))) // str is normalized to non-url already
|
|
169
169
|
const end = str.length % 4 === 0 ? str.slice(-4) : str.slice(-(str.length % 4)).padEnd(4, '=')
|
|
170
|
-
|
|
171
|
-
if (expected !== actual) throw new SyntaxError(E_LAST)
|
|
170
|
+
if (expected !== end) throw new SyntaxError(E_LAST)
|
|
172
171
|
}
|
|
173
172
|
|
|
174
173
|
return arr
|
|
175
174
|
}
|
|
175
|
+
} else {
|
|
176
|
+
fromBase64impl = (str, isBase64url, padding) => js.fromBase64(str, isBase64url) // validated in js
|
|
176
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/fallback/_utils.js
CHANGED
|
@@ -4,6 +4,7 @@ const isNative = (x) => x && (haveNativeBuffer || `${x}`.includes('[native code]
|
|
|
4
4
|
const nativeEncoder = isNative(TextEncoder) ? new TextEncoder() : null
|
|
5
5
|
const nativeDecoder = isNative(TextDecoder) ? new TextDecoder('utf8', { ignoreBOM: true }) : null
|
|
6
6
|
const nativeBuffer = haveNativeBuffer ? Buffer : null
|
|
7
|
+
const isHermes = Boolean(globalThis.HermesInternal)
|
|
7
8
|
|
|
8
9
|
// Actually windows-1252, compatible with ascii and latin1 decoding
|
|
9
10
|
// Beware that on non-latin1, i.e. on windows-1252, this is broken in ~all Node.js versions released
|
|
@@ -12,4 +13,28 @@ const nativeDecoderLatin1 = isNative(TextDecoder)
|
|
|
12
13
|
? new TextDecoder('latin1', { ignoreBOM: true })
|
|
13
14
|
: null
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
// Block Firefox < 146 specifically from using native hex/base64, as it's very slow there
|
|
17
|
+
// Refs: https://bugzilla.mozilla.org/show_bug.cgi?id=1994067 (and linked issues), fixed in 146
|
|
18
|
+
// Before that, all versions of Firefox >= 133 are slow
|
|
19
|
+
// TODO: this could be removed when < 146 usage diminishes (note ESR)
|
|
20
|
+
// We do not worry about false-negatives here but worry about false-positives!
|
|
21
|
+
function shouldSkipBuiltins() {
|
|
22
|
+
const g = globalThis
|
|
23
|
+
// First, attempt to exclude as many things as we can using trivial checks, just in case, and to not hit ua
|
|
24
|
+
if (haveNativeBuffer || isHermes || !g.window || g.chrome || !g.navigator) return false
|
|
25
|
+
try {
|
|
26
|
+
// This was fixed specifically in Firefox 146. Other engines except Hermes (already returned) get this right
|
|
27
|
+
new WeakSet().add(Symbol()) // eslint-disable-line symbol-description
|
|
28
|
+
return false
|
|
29
|
+
} catch {
|
|
30
|
+
// In catch and not after in case if something too smart optimizes out code in try. False-negative is acceptable in that case
|
|
31
|
+
if (!('onmozfullscreenerror' in g)) return false // Firefox has it (might remove in the future, but we don't care)
|
|
32
|
+
return /firefox/i.test(g.navigator.userAgent || '') // as simple as we can
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false // eslint-disable-line no-unreachable
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const skipWeb = shouldSkipBuiltins()
|
|
39
|
+
|
|
40
|
+
export { nativeEncoder, nativeDecoder, nativeDecoderLatin1, nativeBuffer, isHermes, skipWeb }
|
package/fallback/base32.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertUint8 } from '../assert.js'
|
|
2
|
-
import { nativeEncoder, nativeDecoder } from './_utils.js'
|
|
2
|
+
import { nativeEncoder, nativeDecoder, isHermes } from './_utils.js'
|
|
3
3
|
import { encodeAscii, decodeAscii } from './latin1.js'
|
|
4
4
|
|
|
5
5
|
// See https://datatracker.ietf.org/doc/html/rfc4648
|
|
@@ -14,7 +14,7 @@ export const E_PADDING = 'Invalid base32 padding'
|
|
|
14
14
|
export const E_LENGTH = 'Invalid base32 length'
|
|
15
15
|
export const E_LAST = 'Invalid last chunk'
|
|
16
16
|
|
|
17
|
-
const useTemplates =
|
|
17
|
+
const useTemplates = isHermes // Faster on Hermes and JSC, but we use it only on Hermes
|
|
18
18
|
|
|
19
19
|
// We construct output by concatenating chars, this seems to be fine enough on modern JS engines
|
|
20
20
|
export function toBase32(arr, isBase32Hex, padding) {
|
package/fallback/hex.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertUint8 } from '../assert.js'
|
|
2
|
-
import { nativeDecoder, nativeEncoder } from './_utils.js'
|
|
2
|
+
import { nativeDecoder, nativeEncoder, isHermes } from './_utils.js'
|
|
3
3
|
import { encodeAscii, decodeAscii } from './latin1.js'
|
|
4
4
|
|
|
5
5
|
let hexArray // array of 256 bytes converted to two-char hex strings
|
|
@@ -61,7 +61,7 @@ function toHexPartTemplates(a, start, end) {
|
|
|
61
61
|
|
|
62
62
|
// Using templates is significantly faster in Hermes and JSC
|
|
63
63
|
// It's harder to detect JSC and not important anyway as it has native impl, so we detect only Hermes
|
|
64
|
-
const toHexPart =
|
|
64
|
+
const toHexPart = isHermes ? toHexPartTemplates : toHexPartAddition
|
|
65
65
|
|
|
66
66
|
export function toHex(arr) {
|
|
67
67
|
assertUint8(arr)
|
package/fallback/latin1.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
nativeEncoder,
|
|
3
|
+
nativeDecoder,
|
|
4
|
+
nativeDecoderLatin1,
|
|
5
|
+
nativeBuffer,
|
|
6
|
+
isHermes,
|
|
7
|
+
} from './_utils.js'
|
|
2
8
|
|
|
3
9
|
// See http://stackoverflow.com/a/22747272/680742, which says that lowest limit is in Chrome, with 0xffff args
|
|
4
10
|
// On Hermes, actual max is 0x20_000 minus current stack depth, 1/16 of that should be safe
|
|
@@ -67,7 +73,7 @@ export const decodeAscii = nativeBuffer
|
|
|
67
73
|
|
|
68
74
|
/* eslint-disable @exodus/mutable/no-param-reassign-prop-only */
|
|
69
75
|
|
|
70
|
-
export const encodeCharcodes =
|
|
76
|
+
export const encodeCharcodes = isHermes
|
|
71
77
|
? (str, arr) => {
|
|
72
78
|
const length = str.length
|
|
73
79
|
if (length > 64) {
|
|
@@ -91,7 +97,7 @@ export const encodeCharcodes = globalThis.HermesInternal
|
|
|
91
97
|
export const encodeLatin1 = (str) => encodeCharcodes(str, new Uint8Array(str.length))
|
|
92
98
|
|
|
93
99
|
// Expects nativeEncoder to be present
|
|
94
|
-
export const encodeAscii =
|
|
100
|
+
export const encodeAscii = isHermes
|
|
95
101
|
? (str, ERR) => {
|
|
96
102
|
// Much faster in Hermes
|
|
97
103
|
const codes = new Uint8Array(str.length + 4) // overshoot by a full utf8 char
|
package/hex.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assertUint8 } from './assert.js'
|
|
2
2
|
import { typedView } from './array.js'
|
|
3
|
+
import { skipWeb } from './fallback/_utils.js'
|
|
3
4
|
import * as js from './fallback/hex.js'
|
|
4
5
|
|
|
5
6
|
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
@@ -7,11 +8,12 @@ const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
|
7
8
|
export function toHex(arr) {
|
|
8
9
|
assertUint8(arr)
|
|
9
10
|
if (arr.length === 0) return ''
|
|
10
|
-
if (webHex && arr.toHex === webHex) return arr.toHex()
|
|
11
|
+
if (!skipWeb && webHex && arr.toHex === webHex) return arr.toHex()
|
|
11
12
|
return js.toHex(arr)
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
// Unlike Buffer.from(), throws on invalid input
|
|
15
|
-
export const fromHex =
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
export const fromHex =
|
|
17
|
+
!skipWeb && Uint8Array.fromHex
|
|
18
|
+
? (str, format = 'uint8') => typedView(Uint8Array.fromHex(str), format)
|
|
19
|
+
: (str, format = 'uint8') => typedView(js.fromHex(str), format)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bytes",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.8",
|
|
4
4
|
"description": "Various operations on Uint8Array data",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -50,10 +50,12 @@
|
|
|
50
50
|
"/base58.js",
|
|
51
51
|
"/base58check.js",
|
|
52
52
|
"/base64.js",
|
|
53
|
+
"/bech32.js",
|
|
53
54
|
"/hex.node.js",
|
|
54
55
|
"/hex.js",
|
|
55
56
|
"/utf8.node.js",
|
|
56
|
-
"/utf8.js"
|
|
57
|
+
"/utf8.js",
|
|
58
|
+
"/wif.js"
|
|
57
59
|
],
|
|
58
60
|
"exports": {
|
|
59
61
|
"./array.js": "./array.js",
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"./base58.js": "./base58.js",
|
|
62
64
|
"./base58check.js": "./base58check.js",
|
|
63
65
|
"./base64.js": "./base64.js",
|
|
66
|
+
"./bech32.js": "./bech32.js",
|
|
64
67
|
"./hex.js": {
|
|
65
68
|
"node": "./hex.node.js",
|
|
66
69
|
"default": "./hex.js"
|
|
@@ -72,7 +75,8 @@
|
|
|
72
75
|
"./utf8.js": {
|
|
73
76
|
"node": "./utf8.node.js",
|
|
74
77
|
"default": "./utf8.js"
|
|
75
|
-
}
|
|
78
|
+
},
|
|
79
|
+
"./wif.js": "./wif.js"
|
|
76
80
|
},
|
|
77
81
|
"peerDependencies": {
|
|
78
82
|
"@exodus/crypto": "^1.0.0-rc.4"
|
|
@@ -84,7 +88,7 @@
|
|
|
84
88
|
},
|
|
85
89
|
"devDependencies": {
|
|
86
90
|
"@ethersproject/strings": "^5.8.0",
|
|
87
|
-
"@exodus/crypto": "1.0.0-rc.
|
|
91
|
+
"@exodus/crypto": "^1.0.0-rc.30",
|
|
88
92
|
"@exodus/eslint-config": "^5.24.0",
|
|
89
93
|
"@exodus/prettier": "^1.0.0",
|
|
90
94
|
"@exodus/test": "^1.0.0-rc.108",
|
|
@@ -96,11 +100,14 @@
|
|
|
96
100
|
"base-x": "^5.0.1",
|
|
97
101
|
"base32.js": "^0.1.0",
|
|
98
102
|
"base64-js": "^1.5.1",
|
|
103
|
+
"bech32": "^2.0.0",
|
|
99
104
|
"bs58": "^6.0.0",
|
|
100
105
|
"bs58check": "^4.0.0",
|
|
101
106
|
"bstring": "^0.3.9",
|
|
102
107
|
"buffer": "^6.0.3",
|
|
108
|
+
"decode-utf8": "^1.0.1",
|
|
103
109
|
"electron": "36.5.0",
|
|
110
|
+
"encode-utf8": "^2.0.0",
|
|
104
111
|
"eslint": "^8.44.0",
|
|
105
112
|
"fast-base64-decode": "^2.0.0",
|
|
106
113
|
"fast-base64-encode": "^1.0.0",
|
|
@@ -109,7 +116,10 @@
|
|
|
109
116
|
"iconv-lite": "^0.7.0",
|
|
110
117
|
"jsvu": "^3.0.0",
|
|
111
118
|
"text-encoding": "^0.7.0",
|
|
112
|
-
"typescript": "^5.9.3"
|
|
119
|
+
"typescript": "^5.9.3",
|
|
120
|
+
"uint8array-tools": "^0.0.9",
|
|
121
|
+
"utf8": "^3.0.0",
|
|
122
|
+
"wif": "^5.0.0"
|
|
113
123
|
},
|
|
114
124
|
"prettier": "@exodus/prettier",
|
|
115
125
|
"packageManager": "pnpm@10.12.1+sha256.889bac470ec93ccc3764488a19d6ba8f9c648ad5e50a9a6e4be3768a5de387a3"
|
package/utf8.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { assertUint8 } from './assert.js'
|
|
2
2
|
import { typedView } from './array.js'
|
|
3
|
-
import
|
|
3
|
+
import { isHermes } from './fallback/_utils.js'
|
|
4
4
|
import { asciiPrefix, decodeLatin1 } from './fallback/latin1.js'
|
|
5
|
+
import * as js from './fallback/utf8.js'
|
|
5
6
|
|
|
6
7
|
const { Buffer, TextEncoder, TextDecoder, decodeURIComponent, escape } = globalThis // Buffer is optional
|
|
7
8
|
const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
|
|
@@ -16,7 +17,7 @@ const { isWellFormed } = String.prototype
|
|
|
16
17
|
|
|
17
18
|
const { E_STRICT, E_STRICT_UNICODE } = js
|
|
18
19
|
|
|
19
|
-
const shouldUseEscapePath =
|
|
20
|
+
const shouldUseEscapePath = isHermes // faster only on Hermes, js path beats it on normal engines
|
|
20
21
|
|
|
21
22
|
function deLoose(str, loose, res) {
|
|
22
23
|
if (loose || str.length === res.length) return res // length is equal only for ascii, which is automatically fine
|
|
@@ -46,16 +47,16 @@ function deLoose(str, loose, res) {
|
|
|
46
47
|
|
|
47
48
|
function encode(str, loose = false) {
|
|
48
49
|
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
49
|
-
if (
|
|
50
|
-
if (nativeEncoder) return deLoose(str, loose, nativeEncoder.encode(str))
|
|
50
|
+
if (str.length === 0) return new Uint8Array() // faster than Uint8Array.of
|
|
51
|
+
if (nativeEncoder) return deLoose(str, loose, nativeEncoder.encode(str))
|
|
51
52
|
// No reason to use unescape + encodeURIComponent: it's slower than JS on normal engines, and modern Hermes already has TextEncoder
|
|
52
53
|
return js.encode(str, loose)
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
function decode(arr, loose = false) {
|
|
56
57
|
assertUint8(arr)
|
|
58
|
+
if (arr.byteLength === 0) return ''
|
|
57
59
|
if (haveDecoder) return loose ? decoderLoose.decode(arr) : decoderFatal.decode(arr) // Node.js and browsers
|
|
58
|
-
// No reason to use native Buffer: it's not faster than TextDecoder, needs rechecks in non-loose mode, and Node.js has TextDecoder
|
|
59
60
|
|
|
60
61
|
// Fast path for ASCII prefix, this is faster than all alternatives below
|
|
61
62
|
const prefix = decodeLatin1(arr, 0, asciiPrefix(arr))
|
package/utf8.node.js
CHANGED
|
@@ -11,16 +11,34 @@ const { isWellFormed } = String.prototype
|
|
|
11
11
|
|
|
12
12
|
function encode(str, loose = false) {
|
|
13
13
|
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
|
|
14
|
+
const strLength = str.length
|
|
15
|
+
if (strLength === 0) return new Uint8Array() // faster than Uint8Array.of
|
|
16
|
+
let res
|
|
17
|
+
if (strLength > 0x4_00) {
|
|
18
|
+
// Faster for large strings
|
|
19
|
+
const byteLength = Buffer.byteLength(str)
|
|
20
|
+
res = Buffer.allocUnsafe(byteLength)
|
|
21
|
+
const ascii = byteLength === strLength
|
|
22
|
+
const written = ascii ? res.latin1Write(str) : res.utf8Write(str)
|
|
23
|
+
if (written !== byteLength) throw new Error('Failed to write all bytes') // safeguard just in case
|
|
24
|
+
if (ascii || loose) return res // no further checks needed
|
|
25
|
+
} else {
|
|
26
|
+
res = Buffer.from(str)
|
|
27
|
+
if (res.length === strLength || loose) return res
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!isWellFormed.call(str)) throw new TypeError(E_STRICT_UNICODE)
|
|
31
|
+
return res
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
function decode(arr, loose = false) {
|
|
20
35
|
assertUint8(arr)
|
|
21
|
-
|
|
36
|
+
const byteLength = arr.byteLength
|
|
37
|
+
if (byteLength === 0) return ''
|
|
38
|
+
if (byteLength > 0x6_00 && isAscii(arr)) {
|
|
22
39
|
// On non-ascii strings, this loses ~10% * [relative position of the first non-ascii byte] (up to 10% total)
|
|
23
40
|
// On ascii strings, this wins 1.5x on loose = false and 1.3x on loose = true
|
|
41
|
+
// Only makes sense for large enough strings
|
|
24
42
|
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).latin1Slice(0, arr.byteLength) // .latin1Slice is faster than .asciiSlice
|
|
25
43
|
}
|
|
26
44
|
|
package/wif.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { toBase58checkSync, fromBase58checkSync } from './base58check.js'
|
|
2
|
+
import { assertUint8 } from './assert.js'
|
|
3
|
+
|
|
4
|
+
// Mostly matches npmjs.com/wif, but with extra checks + using our base58check
|
|
5
|
+
// Also no inconsistent behavior on Buffer/Uint8Array input
|
|
6
|
+
|
|
7
|
+
function from(arr, expectedVersion) {
|
|
8
|
+
assertUint8(arr)
|
|
9
|
+
const version = arr[0]
|
|
10
|
+
if (expectedVersion !== undefined && version !== expectedVersion) {
|
|
11
|
+
throw new Error('Invalid network version')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Makes a copy, regardless of input being a Buffer or a Uint8Array (unlike .slice)
|
|
15
|
+
const privateKey = Uint8Array.from(arr.subarray(1, 33))
|
|
16
|
+
if (arr.length === 33) return { version, privateKey, compressed: false }
|
|
17
|
+
if (arr.length !== 34) throw new Error('Invalid WIF length')
|
|
18
|
+
if (arr[33] !== 1) throw new Error('Invalid compression flag')
|
|
19
|
+
return { version, privateKey, compressed: true }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function to({ version: v, privateKey, compressed }) {
|
|
23
|
+
if (!Number.isSafeInteger(v) || v < 0 || v > 0xff) throw new Error('Missing or invalid version')
|
|
24
|
+
assertUint8(privateKey, { length: 32, name: 'privateKey' })
|
|
25
|
+
if (privateKey.length !== 32) throw new TypeError('Invalid privateKey length')
|
|
26
|
+
const out = new Uint8Array(compressed ? 34 : 33)
|
|
27
|
+
out[0] = v
|
|
28
|
+
out.set(privateKey, 1)
|
|
29
|
+
if (compressed) out[33] = 1
|
|
30
|
+
return out
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Async performance is worse here, so expose the same internal methods as sync for now
|
|
34
|
+
// ./base58check is sync internally anyway for now, so doesn't matter until that is changed
|
|
35
|
+
|
|
36
|
+
export const fromWifStringSync = (string, version) => from(fromBase58checkSync(string), version)
|
|
37
|
+
// export const fromWifString = async (string, version) => from(await fromBase58check(string), version)
|
|
38
|
+
export const fromWifString = async (string, version) => from(fromBase58checkSync(string), version)
|
|
39
|
+
|
|
40
|
+
export const toWifStringSync = (wif) => toBase58checkSync(to(wif))
|
|
41
|
+
// export const toWifString = async (wif) => toBase58check(to(wif))
|
|
42
|
+
export const toWifString = async (wif) => toBase58checkSync(to(wif))
|