@fitzdoud/valun 0.0.0-stage → 0.3.1

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/src/basis.js ADDED
@@ -0,0 +1,243 @@
1
+ // Basis-compatible Schnorr signatures over secp256k1, in plain JavaScript with BigInt (no dependencies).
2
+ //
3
+ // A port of the Python client's basis.py, which mirrors basis_core::schnorr_sign / SchnorrVerifier at Basis 3df7ed93:
4
+ // signature = a (33-byte compressed point k*G) || z (32 bytes, big-endian)
5
+ // e = blake2b256(a || message || public_key), read as a big-endian integer
6
+ // z = k + e*s mod n, retried until e < 2^255 and 0 < z < 2^255 (ErgoScript reads both as signed integers),
7
+ // verified as z*G == a + e*X.
8
+ // Note message (80 bytes): debt_key(32) || total(8 BE) || timestamp(8 BE) || tracker_nft_id(32),
9
+ // debt_key = blake2b256(issuer_pk || recipient_pk [|| token_id]).
10
+ //
11
+ // A client-side signer, so a participant's key never leaves its machine. Not constant-time (BigInt arithmetic): fine
12
+ // for a local signer in an experiment, not for an exposed signing service.
13
+
14
+ import {blake2b256} from './blake2b.js';
15
+ import {bigIntToBytes, bytesToBigInt, concatBytes, equalBytes, hexToBytes, toBytes} from './bytes.js';
16
+ import {SignatureError} from './errors.js';
17
+ import {randomBytes as defaultRandomBytes} from './random.js';
18
+
19
+ export const P = 2n ** 256n - 2n ** 32n - 977n;
20
+ export const N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n;
21
+ export const G = Object.freeze([
22
+ 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798n,
23
+ 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8n,
24
+ ]);
25
+ export const HALF = 1n << 255n;
26
+
27
+ export {blake2b256, SignatureError};
28
+
29
+ const mod = (a, m = P) => {
30
+ const r = a % m;
31
+ return r < 0n ? r + m : r;
32
+ };
33
+
34
+ function powmod(b, e, m) {
35
+ let r = 1n;
36
+ b = mod(b, m);
37
+ while (e > 0n) {
38
+ if (e & 1n) r = (r * b) % m;
39
+ b = (b * b) % m;
40
+ e >>= 1n;
41
+ }
42
+ return r;
43
+ }
44
+
45
+ function invert(a, m = P) {
46
+ let [r0, r1] = [mod(a, m), m], [s0, s1] = [1n, 0n];
47
+ while (r1 !== 0n) {
48
+ const q = r0 / r1;
49
+ [r0, r1] = [r1, r0 - q * r1];
50
+ [s0, s1] = [s1, s0 - q * s1];
51
+ }
52
+ if (r0 !== 1n) throw new SignatureError('not invertible');
53
+ return mod(s0, m);
54
+ }
55
+
56
+ // ---- secp256k1 arithmetic (Jacobian coordinates; null is the point at infinity) ----
57
+ function double(j) {
58
+ if (j === null) return null;
59
+ const [x, y, z] = j;
60
+ if (y === 0n) return null;
61
+ const yy = (y * y) % P;
62
+ const s = (4n * x * yy) % P;
63
+ const m = (3n * x * x) % P;
64
+ const x3 = mod(m * m - 2n * s);
65
+ const y3 = mod(m * (s - x3) - 8n * yy * yy);
66
+ return [x3, y3, (2n * y * z) % P];
67
+ }
68
+
69
+ function add(j1, j2) {
70
+ if (j1 === null) return j2;
71
+ if (j2 === null) return j1;
72
+ const [x1, y1, z1] = j1, [x2, y2, z2] = j2;
73
+ const z1z1 = (z1 * z1) % P, z2z2 = (z2 * z2) % P;
74
+ const u1 = (x1 * z2z2) % P, u2 = (x2 * z1z1) % P;
75
+ const s1 = (y1 * z2 * z2z2) % P, s2 = (y2 * z1 * z1z1) % P;
76
+ if (u1 === u2) return s1 === s2 ? double(j1) : null;
77
+ const h = mod(u2 - u1), r = mod(s2 - s1);
78
+ const h2 = (h * h) % P;
79
+ const h3 = (h * h2) % P, u1h2 = (u1 * h2) % P;
80
+ const x3 = mod(r * r - h3 - 2n * u1h2);
81
+ const y3 = mod(r * (u1h2 - x3) - s1 * h3);
82
+ return [x3, y3, (h * z1 * z2) % P];
83
+ }
84
+
85
+ function affine(j) {
86
+ if (j === null || j[2] % P === 0n) return null;
87
+ const zi = invert(j[2]);
88
+ const zi2 = (zi * zi) % P;
89
+ return [(j[0] * zi2) % P, (j[1] * zi2 * zi) % P];
90
+ }
91
+
92
+ function mul(k, pt) {
93
+ let acc = null, addend = [pt[0], pt[1], 1n];
94
+ while (k > 0n) {
95
+ if (k & 1n) acc = add(acc, addend);
96
+ addend = double(addend);
97
+ k >>= 1n;
98
+ }
99
+ return acc; // Jacobian
100
+ }
101
+
102
+ function compress(pt) {
103
+ return concatBytes(Uint8Array.of(2 + Number(pt[1] & 1n)), bigIntToBytes(pt[0], 32));
104
+ }
105
+
106
+ function decompress(b) {
107
+ if (b.length !== 33 || (b[0] !== 2 && b[0] !== 3)) throw new SignatureError('not a compressed secp256k1 point');
108
+ const x = bytesToBigInt(b.subarray(1));
109
+ if (x >= P) throw new SignatureError('point x out of range');
110
+ const y2 = mod(x * x * x + 7n);
111
+ let y = powmod(y2, (P + 1n) / 4n, P);
112
+ if ((y * y) % P !== y2) throw new SignatureError('point is not on the curve');
113
+ if ((y & 1n) !== BigInt(b[0] & 1)) y = P - y;
114
+ return [x, y];
115
+ }
116
+
117
+ // ---- keys ----
118
+ function scalarFrom(secret) {
119
+ const sk = toBytes(secret, 'secret key');
120
+ const s = sk.length === 32 ? bytesToBigInt(sk) : 0n;
121
+ if (!(s > 0n && s < N)) throw new SignatureError('secret key out of range');
122
+ return s;
123
+ }
124
+
125
+ /** A uniformly random scalar in [1, n-1] from 32-byte draws (rejection sampling; a draw is rejected with p ~ 2^-128). */
126
+ function randomScalar(randomBytes) {
127
+ for (;;) {
128
+ const b = randomBytes(32);
129
+ if (!(b instanceof Uint8Array) || b.length !== 32) throw new TypeError('randomBytes(32) must return 32 bytes');
130
+ const k = bytesToBigInt(b);
131
+ if (k > 0n && k < N) return k;
132
+ }
133
+ }
134
+
135
+ /** A new secret key: 32 bytes, uniform in [1, n-1], from the platform CSPRNG (or the `randomBytes` given). */
136
+ export function generateSecret(randomBytes = defaultRandomBytes) {
137
+ return bigIntToBytes(randomScalar(randomBytes), 32);
138
+ }
139
+
140
+ /** The 33-byte compressed public key of a 32-byte secret (Uint8Array or hex). */
141
+ export function publicKey(secret) {
142
+ return compress(affine(mul(scalarFrom(secret), G)));
143
+ }
144
+
145
+ /** Throws SignatureError unless `pk` is a valid compressed secp256k1 point. */
146
+ export function validatePublicKey(pk) {
147
+ decompress(toBytes(pk, 'public key'));
148
+ }
149
+
150
+ // ---- Schnorr (Basis) ----
151
+ function challenge(a, message, pk) {
152
+ return blake2b256(a, message, pk);
153
+ }
154
+
155
+ /**
156
+ * Sign `message` (Uint8Array: the exact bytes, e.g. UTF-8 of a statement or the 80-byte note message) with `secret`.
157
+ * `pk`, if given, must be the secret's public key. `options.randomBytes` replaces the nonce source (tests only).
158
+ * Returns the 65-byte signature a || z.
159
+ */
160
+ export function sign(message, secret, pk = undefined, options = {}) {
161
+ if (!(message instanceof Uint8Array)) throw new TypeError('message must be a Uint8Array (the exact bytes to sign)');
162
+ const s = scalarFrom(secret);
163
+ const own = compress(affine(mul(s, G)));
164
+ const X = pk === undefined || pk === null ? own : toBytes(pk, 'public key');
165
+ if (!equalBytes(X, own)) throw new SignatureError('secret does not match public key');
166
+ const rnd = options.randomBytes || defaultRandomBytes;
167
+ for (;;) {
168
+ const k = randomScalar(rnd);
169
+ const a = compress(affine(mul(k, G)));
170
+ const eBytes = challenge(a, message, X);
171
+ const e = bytesToBigInt(eBytes);
172
+ if (eBytes[0] >= 0x80 || e >= N) continue; // the contract reads e as a signed integer
173
+ const z = (k + e * s) % N;
174
+ if (z === 0n || z >= HALF) continue; // same retry rule as basis_core / SigUtils
175
+ const sig = concatBytes(a, bigIntToBytes(z, 32));
176
+ if (!verify(sig, message, X)) continue; // never emit a signature Basis would reject
177
+ return sig;
178
+ }
179
+ }
180
+
181
+ /** True iff `sig` (65 bytes) is a valid Basis Schnorr signature of `message` by public key `pk`. Never throws. */
182
+ export function verify(sig, message, pk) {
183
+ try {
184
+ sig = toBytes(sig, 'signature');
185
+ pk = toBytes(pk, 'public key');
186
+ if (!(message instanceof Uint8Array)) return false;
187
+ if (sig.length !== 65 || (sig[0] !== 2 && sig[0] !== 3)) return false;
188
+ const a = sig.subarray(0, 33), zBytes = sig.subarray(33);
189
+ if (zBytes.every(x => x === 0) || (zBytes[0] & 0x80)) return false;
190
+ const xPt = decompress(pk);
191
+ const aPt = decompress(a);
192
+ const eBytes = challenge(a, message, pk);
193
+ const e = bytesToBigInt(eBytes);
194
+ if ((eBytes[0] & 0x80) || e >= N) return false;
195
+ const z = bytesToBigInt(zBytes);
196
+ if (!(z > 0n && z < N)) return false;
197
+ const left = affine(mul(z, G));
198
+ const right = affine(add([aPt[0], aPt[1], 1n], mul(e, xPt)));
199
+ return left !== null && right !== null && left[0] === right[0] && left[1] === right[1];
200
+ } catch {
201
+ return false;
202
+ }
203
+ }
204
+
205
+ /** verify() over hex inputs: false (never an exception) for anything malformed. */
206
+ export function verifyHex(sigHex, message, pkHex) {
207
+ if (typeof sigHex !== 'string' || typeof pkHex !== 'string') return false;
208
+ if (!/^[0-9a-f]{130}$/.test(sigHex) || !/^0[23][0-9a-f]{64}$/.test(pkHex)) return false;
209
+ return verify(hexToBytes(sigHex), message, hexToBytes(pkHex));
210
+ }
211
+
212
+ // ---- Basis note messages ----
213
+ const TOKEN_DENOMINATION = /^token:([0-9a-f]{64})$/;
214
+
215
+ /** Basis debt key: blake2b256(issuer || recipient) for "erg", blake2b256(issuer || recipient || token_id) for "token:<id>". */
216
+ export function debtKey(issuerPk, recipientPk, denomination = 'erg') {
217
+ const i = toBytes(issuerPk, 'issuer public key'), r = toBytes(recipientPk, 'recipient public key');
218
+ if (i.length !== 33 || r.length !== 33) throw new SignatureError('public keys must be 33 bytes');
219
+ if (denomination === 'erg') return blake2b256(i, r);
220
+ const m = typeof denomination === 'string' ? TOKEN_DENOMINATION.exec(denomination) : null;
221
+ if (m) return blake2b256(i, r, hexToBytes(m[1]));
222
+ throw new SignatureError(`unsupported denomination ${JSON.stringify(denomination)}`);
223
+ }
224
+
225
+ function u64(x, what) {
226
+ let v;
227
+ if (typeof x === 'bigint') v = x;
228
+ else if (typeof x === 'number' && Number.isSafeInteger(x)) v = BigInt(x);
229
+ else if (typeof x === 'string' && /^(0|[1-9][0-9]*)$/.test(x)) v = BigInt(x);
230
+ else throw new SignatureError(`bad note fields: ${what} must be an integer`);
231
+ if (v < 0n || v >= 1n << 64n) throw new SignatureError(`bad note fields: ${what} out of u64 range`);
232
+ return bigIntToBytes(v, 8);
233
+ }
234
+
235
+ /**
236
+ * The 80-byte Basis note message. `total` and `timestamp` (ms) are integers (number, bigint or decimal string) in
237
+ * [0, 2^64); `trackerNftId` is 32 bytes (or 64 hex).
238
+ */
239
+ export function noteMessage(issuerPk, recipientPk, total, timestamp, trackerNftId, denomination = 'erg') {
240
+ const nft = toBytes(trackerNftId, 'tracker_nft_id');
241
+ if (nft.length !== 32) throw new SignatureError('bad note fields: tracker_nft_id must be 32 bytes');
242
+ return concatBytes(debtKey(issuerPk, recipientPk, denomination), u64(total, 'total'), u64(timestamp, 'timestamp'), nft);
243
+ }
package/src/blake2b.js ADDED
@@ -0,0 +1,137 @@
1
+ // BLAKE2b (RFC 7693) in plain JavaScript: digest length 1..64 bytes set in the parameter block, optional key (0..64
2
+ // bytes). 64-bit words are held as (low, high) pairs of 32-bit integers in Uint32Arrays, as in the RFC's reference
3
+ // structure. BLAKE2b-256 here is BLAKE2b with digest length 32, which is NOT the first 32 bytes of BLAKE2b-512.
4
+
5
+ // Initialisation vector, as (low, high) 32-bit halves of the eight 64-bit words.
6
+ const IV = new Uint32Array([
7
+ 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,
8
+ 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19,
9
+ ]);
10
+
11
+ const SIGMA = [
12
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
13
+ [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
14
+ [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
15
+ [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
16
+ [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
17
+ [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
18
+ [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
19
+ [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
20
+ [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
21
+ [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
22
+ ];
23
+
24
+ // v[a] += v[b] (64-bit, word indices are pair offsets)
25
+ function add64(v, a, b) {
26
+ const lo = v[a] + v[b];
27
+ v[a + 1] = v[a + 1] + v[b + 1] + (lo >= 0x100000000 ? 1 : 0);
28
+ v[a] = lo;
29
+ }
30
+
31
+ // v[a] += (lo, hi)
32
+ function add64c(v, a, lo, hi) {
33
+ const l = v[a] + lo;
34
+ v[a + 1] = v[a + 1] + hi + (l >= 0x100000000 ? 1 : 0);
35
+ v[a] = l;
36
+ }
37
+
38
+ function mix(v, m, a, b, c, d, x, y) {
39
+ add64(v, a, b);
40
+ add64c(v, a, m[x], m[x + 1]);
41
+ // v[d] = (v[d] ^ v[a]) >>> 32
42
+ let lo = v[d] ^ v[a], hi = v[d + 1] ^ v[a + 1];
43
+ v[d] = hi; v[d + 1] = lo;
44
+ add64(v, c, d);
45
+ // v[b] = (v[b] ^ v[c]) >>> 24
46
+ lo = v[b] ^ v[c]; hi = v[b + 1] ^ v[c + 1];
47
+ v[b] = (lo >>> 24) | (hi << 8); v[b + 1] = (hi >>> 24) | (lo << 8);
48
+ add64(v, a, b);
49
+ add64c(v, a, m[y], m[y + 1]);
50
+ // v[d] = (v[d] ^ v[a]) >>> 16
51
+ lo = v[d] ^ v[a]; hi = v[d + 1] ^ v[a + 1];
52
+ v[d] = (lo >>> 16) | (hi << 16); v[d + 1] = (hi >>> 16) | (lo << 16);
53
+ add64(v, c, d);
54
+ // v[b] = (v[b] ^ v[c]) >>> 63 (= rotate left by 1)
55
+ lo = v[b] ^ v[c]; hi = v[b + 1] ^ v[c + 1];
56
+ v[b] = (hi >>> 31) | (lo << 1); v[b + 1] = (lo >>> 31) | (hi << 1);
57
+ }
58
+
59
+ function compress(ctx, last) {
60
+ const {h, v, m, b} = ctx;
61
+ for (let i = 0; i < 16; i++) { v[i] = h[i]; v[i + 16] = IV[i]; }
62
+ // t is a byte counter < 2^53 (the 128-bit counter's high 64 bits stay 0)
63
+ v[24] ^= ctx.t >>> 0;
64
+ v[25] ^= Math.floor(ctx.t / 0x100000000);
65
+ if (last) { v[28] = ~v[28]; v[29] = ~v[29]; }
66
+ for (let i = 0; i < 32; i++) m[i] = b[4 * i] | (b[4 * i + 1] << 8) | (b[4 * i + 2] << 16) | (b[4 * i + 3] << 24);
67
+ for (let r = 0; r < 12; r++) {
68
+ const s = SIGMA[r % 10];
69
+ mix(v, m, 0, 8, 16, 24, s[0] * 2, s[1] * 2);
70
+ mix(v, m, 2, 10, 18, 26, s[2] * 2, s[3] * 2);
71
+ mix(v, m, 4, 12, 20, 28, s[4] * 2, s[5] * 2);
72
+ mix(v, m, 6, 14, 22, 30, s[6] * 2, s[7] * 2);
73
+ mix(v, m, 0, 10, 20, 30, s[8] * 2, s[9] * 2);
74
+ mix(v, m, 2, 12, 22, 24, s[10] * 2, s[11] * 2);
75
+ mix(v, m, 4, 14, 16, 26, s[12] * 2, s[13] * 2);
76
+ mix(v, m, 6, 8, 18, 28, s[14] * 2, s[15] * 2);
77
+ }
78
+ for (let i = 0; i < 16; i++) h[i] ^= v[i] ^ v[i + 16];
79
+ }
80
+
81
+ /** Incremental BLAKE2b. `outLen` 1..64, optional `key` (Uint8Array, 0..64 bytes). */
82
+ export class Blake2b {
83
+ constructor(outLen = 64, key = undefined) {
84
+ const keyLen = key ? key.length : 0;
85
+ if (!Number.isInteger(outLen) || outLen < 1 || outLen > 64) throw new RangeError('BLAKE2b digest length must be 1..64');
86
+ if (keyLen > 64) throw new RangeError('BLAKE2b key must be at most 64 bytes');
87
+ this.outLen = outLen;
88
+ this.h = new Uint32Array(IV);
89
+ this.h[0] ^= 0x01010000 ^ (keyLen << 8) ^ outLen; // parameter block: digest length, key length, fanout 1, depth 1
90
+ this.v = new Uint32Array(32);
91
+ this.m = new Uint32Array(32);
92
+ this.b = new Uint8Array(128);
93
+ this.c = 0;
94
+ this.t = 0;
95
+ this.done = false;
96
+ if (keyLen > 0) {
97
+ this.update(key);
98
+ this.c = 128; // the key block is always a full (zero-padded) block
99
+ }
100
+ }
101
+
102
+ update(data) {
103
+ if (this.done) throw new Error('BLAKE2b: update after digest');
104
+ for (let i = 0; i < data.length; i++) {
105
+ if (this.c === 128) {
106
+ this.t += 128;
107
+ compress(this, false);
108
+ this.c = 0;
109
+ }
110
+ this.b[this.c++] = data[i];
111
+ }
112
+ return this;
113
+ }
114
+
115
+ digest() {
116
+ if (this.done) throw new Error('BLAKE2b: digest called twice');
117
+ this.done = true;
118
+ this.t += this.c;
119
+ this.b.fill(0, this.c);
120
+ compress(this, true);
121
+ const out = new Uint8Array(this.outLen);
122
+ for (let i = 0; i < this.outLen; i++) out[i] = this.h[i >> 2] >>> (8 * (i & 3));
123
+ return out;
124
+ }
125
+ }
126
+
127
+ /** One-shot BLAKE2b of `data` with digest length `outLen` (default 64) and optional key. */
128
+ export function blake2b(data, outLen = 64, key = undefined) {
129
+ return new Blake2b(outLen, key).update(data).digest();
130
+ }
131
+
132
+ /** BLAKE2b-256 (digest length 32, unkeyed) over the concatenation of the byte arrays given. */
133
+ export function blake2b256(...parts) {
134
+ const h = new Blake2b(32);
135
+ for (const p of parts) h.update(p);
136
+ return h.digest();
137
+ }
package/src/bytes.js ADDED
@@ -0,0 +1,60 @@
1
+ // Byte helpers shared by every module. No dependencies; works in Node, Deno, Bun and browsers.
2
+
3
+ const HEX = Array.from({length: 256}, (_, i) => i.toString(16).padStart(2, '0'));
4
+ const ENCODER = new TextEncoder();
5
+
6
+ /** Lowercase hex of a byte array. */
7
+ export function bytesToHex(bytes) {
8
+ let s = '';
9
+ for (let i = 0; i < bytes.length; i++) s += HEX[bytes[i]];
10
+ return s;
11
+ }
12
+
13
+ /** Bytes of an even-length hex string (upper or lower case, nothing else). Throws TypeError otherwise. */
14
+ export function hexToBytes(hex) {
15
+ if (typeof hex !== 'string' || hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) {
16
+ throw new TypeError('expected an even-length hex string');
17
+ }
18
+ const out = new Uint8Array(hex.length / 2);
19
+ for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.substr(i * 2, 2), 16);
20
+ return out;
21
+ }
22
+
23
+ /** UTF-8 bytes of a string (a lone surrogate becomes U+FFFD, exactly as Node's Buffer does on the server). */
24
+ export function utf8(text) {
25
+ return ENCODER.encode(text);
26
+ }
27
+
28
+ export function concatBytes(...parts) {
29
+ let n = 0;
30
+ for (const p of parts) n += p.length;
31
+ const out = new Uint8Array(n);
32
+ let o = 0;
33
+ for (const p of parts) { out.set(p, o); o += p.length; }
34
+ return out;
35
+ }
36
+
37
+ export function equalBytes(a, b) {
38
+ if (a.length !== b.length) return false;
39
+ let d = 0;
40
+ for (let i = 0; i < a.length; i++) d |= a[i] ^ b[i];
41
+ return d === 0;
42
+ }
43
+
44
+ /** Big-endian unsigned integer of a byte array. */
45
+ export function bytesToBigInt(bytes) {
46
+ return bytes.length === 0 ? 0n : BigInt('0x' + bytesToHex(bytes));
47
+ }
48
+
49
+ /** `len` bytes, big-endian, of a non-negative BigInt that fits. */
50
+ export function bigIntToBytes(x, len) {
51
+ if (typeof x !== 'bigint' || x < 0n || x >= 1n << BigInt(8 * len)) throw new RangeError(`value does not fit in ${len} bytes`);
52
+ return hexToBytes(x.toString(16).padStart(len * 2, '0'));
53
+ }
54
+
55
+ /** Accepts a Uint8Array or a hex string and returns bytes (hex strings are decoded). */
56
+ export function toBytes(x, what = 'value') {
57
+ if (x instanceof Uint8Array) return x;
58
+ if (typeof x === 'string') return hexToBytes(x);
59
+ throw new TypeError(`${what} must be a Uint8Array or a hex string`);
60
+ }