ufsecp 3.3.0

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 ADDED
@@ -0,0 +1,161 @@
1
+ # ultrafast-secp256k1
2
+
3
+ High-performance Node.js native addon for secp256k1 elliptic curve cryptography, powered by [UltrafastSecp256k1](https://github.com/shrec/UltrafastSecp256k1).
4
+
5
+ ## Features
6
+
7
+ - **ECDSA** -- sign, verify, recover, DER serialization (RFC 6979)
8
+ - **Schnorr** -- BIP-340 sign/verify
9
+ - **ECDH** -- compressed, x-only, raw shared secret
10
+ - **BIP-32** -- HD key derivation
11
+ - **Taproot** -- output key tweaking (BIP-341)
12
+ - **Addresses** -- P2PKH, P2WPKH, P2TR
13
+ - **WIF** -- encode/decode
14
+ - **Hashing** -- SHA-256 (hardware-accelerated), HASH160, tagged hash
15
+ - **Key tweaking** -- negate, add, multiply
16
+ - **Ethereum** -- Keccak-256, EIP-55 addresses, EIP-155 sign, ecrecover
17
+ - **BIP-39** -- mnemonic generation, validation, seed derivation
18
+ - **Multi-coin wallet** -- 7-coin address dispatch (BTC/LTC/DOGE/DASH/ETH/BCH/TRX)
19
+ - **Batch verification** -- ECDSA + Schnorr batch verify with invalid identification
20
+ - **MuSig2** -- BIP-327 multi-signatures (key agg, nonce gen, partial sign, aggregate)
21
+ - **FROST** -- threshold signatures (keygen, sign, aggregate, verify)
22
+ - **Adaptor signatures** -- Schnorr + ECDSA adaptor pre-sign, adapt, extract
23
+ - **Pedersen commitments** -- commit, verify, sum balance, switch commitments
24
+ - **ZK proofs** -- knowledge proof, DLEQ proof, Bulletproof range proof
25
+ - **Multi-scalar multiplication** -- Shamir's trick, MSM
26
+ - **Pubkey arithmetic** -- add, negate, combine N keys
27
+ - **SHA-512** -- full SHA-512 hash
28
+ - **Message signing** -- BIP-137 Bitcoin message sign/verify
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ npm install ultrafast-secp256k1
34
+ ```
35
+
36
+ Requires a C++ compiler and `node-gyp` (the native addon is built on install).
37
+
38
+ ## Quick Start
39
+
40
+ ```js
41
+ const { Secp256k1 } = require('ultrafast-secp256k1');
42
+ const crypto = require('crypto');
43
+
44
+ const secp = new Secp256k1();
45
+
46
+ // Generate a random private key
47
+ const privkey = crypto.randomBytes(32);
48
+
49
+ // Derive compressed public key (33 bytes)
50
+ const pubkey = secp.ecPubkeyCreate(privkey);
51
+ console.log('pubkey:', pubkey.toString('hex'));
52
+ ```
53
+
54
+ ## ECDSA Sign & Verify
55
+
56
+ ```js
57
+ const msgHash = secp.sha256(Buffer.from('hello world'));
58
+
59
+ // Sign (RFC 6979 deterministic nonce, low-S)
60
+ const sig = secp.ecdsaSign(msgHash, privkey);
61
+
62
+ // Verify
63
+ const valid = secp.ecdsaVerify(msgHash, sig, pubkey);
64
+ console.log('ECDSA valid:', valid); // true
65
+
66
+ // DER-encode for transmission
67
+ const der = secp.ecdsaSerializeDer(sig);
68
+ ```
69
+
70
+ ## Schnorr (BIP-340)
71
+
72
+ ```js
73
+ const xOnlyPub = secp.schnorrPubkey(privkey);
74
+ const auxRand = crypto.randomBytes(32);
75
+ const msg = secp.sha256(Buffer.from('schnorr message'));
76
+
77
+ const schnorrSig = secp.schnorrSign(msg, privkey, auxRand);
78
+ const ok = secp.schnorrVerify(msg, schnorrSig, xOnlyPub);
79
+ console.log('Schnorr valid:', ok); // true
80
+ ```
81
+
82
+ ## ECDH
83
+
84
+ ```js
85
+ const otherPriv = crypto.randomBytes(32);
86
+ const otherPub = secp.ecPubkeyCreate(otherPriv);
87
+
88
+ const shared = secp.ecdh(privkey, otherPub); // SHA-256 of compressed point
89
+ const xonly = secp.ecdhXonly(privkey, otherPub); // SHA-256 of x-coordinate
90
+ const raw = secp.ecdhRaw(privkey, otherPub); // raw 32-byte x-coordinate
91
+ ```
92
+
93
+ ## Bitcoin Addresses
94
+
95
+ ```js
96
+ const { NETWORK_MAINNET, NETWORK_TESTNET } = require('ultrafast-secp256k1');
97
+
98
+ const p2pkh = secp.addressP2PKH(pubkey, NETWORK_MAINNET); // 1...
99
+ const p2wpkh = secp.addressP2WPKH(pubkey, NETWORK_MAINNET); // bc1q...
100
+ const p2tr = secp.addressP2TR(xOnlyPub, NETWORK_MAINNET); // bc1p...
101
+ ```
102
+
103
+ ## BIP-32 HD Derivation
104
+
105
+ ```js
106
+ const seed = crypto.randomBytes(64);
107
+ const master = secp.bip32MasterKey(seed);
108
+ const child = secp.bip32DerivePath(master, "m/44'/0'/0'/0/0");
109
+ const childPriv = secp.bip32GetPrivkey(child);
110
+ const childPub = secp.bip32GetPubkey(child);
111
+ ```
112
+
113
+ ## WIF
114
+
115
+ ```js
116
+ const wif = secp.wifEncode(privkey, true, NETWORK_MAINNET);
117
+ const { privkey: decoded, compressed, network } = secp.wifDecode(wif);
118
+ ```
119
+
120
+ ## Taproot
121
+
122
+ ```js
123
+ const { outputKeyX, parity } = secp.taprootOutputKey(xOnlyPub);
124
+ const tweakedPriv = secp.taprootTweakPrivkey(privkey);
125
+ ```
126
+
127
+ ## Architecture Note
128
+
129
+ Built on hand-optimized C/C++ with platform-specific acceleration (AVX2, SHA-NI, BMI2 on x86; NEON on ARM). The C ABI layer uses the **fast** (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
130
+
131
+ | Operation | x86-64 | ARM64 | RISC-V |
132
+ |-----------|--------|-------|--------|
133
+ | ECDSA Sign | 8 us | 30 us | -- |
134
+ | kG (generator mul) | 5 us | 14 us | 33 us |
135
+ | kP (arbitrary mul) | 25 us | 131 us | 154 us |
136
+
137
+ ## Performance Tuning
138
+
139
+ When building the native addon from source, you can tune scalar multiplication (k*P) performance via the GLV window width:
140
+
141
+ ```bash
142
+ cmake -S . -B build -DSECP256K1_GLV_WINDOW_WIDTH=6
143
+ ```
144
+
145
+ | Window | Default On | Tradeoff |
146
+ |--------|-----------|----------|
147
+ | w=4 | ESP32, WASM | Smaller tables, more point additions |
148
+ | w=5 | x86-64, ARM64, RISC-V | Balanced (default) |
149
+ | w=6 | -- | Larger tables, fewer additions |
150
+
151
+ See [docs/PERFORMANCE_GUIDE.md](../../docs/PERFORMANCE_GUIDE.md) for detailed benchmarks and per-platform tuning advice.
152
+
153
+ ## License
154
+
155
+ MIT
156
+
157
+ ## Links
158
+
159
+ - [GitHub](https://github.com/shrec/UltrafastSecp256k1)
160
+ - [Benchmarks](https://github.com/shrec/UltrafastSecp256k1/blob/main/docs/BENCHMARKS.md)
161
+ - [Changelog](https://github.com/shrec/UltrafastSecp256k1/blob/main/CHANGELOG.md)
package/lib/ufsecp.js ADDED
@@ -0,0 +1,468 @@
1
+ /**
2
+ * UltrafastSecp256k1 — Node.js FFI binding (ufsecp stable C ABI v1).
3
+ *
4
+ * High-performance secp256k1 elliptic curve cryptography with dual-layer
5
+ * constant-time architecture. Context-based API.
6
+ *
7
+ * Usage:
8
+ * const { Ufsecp } = require('ufsecp');
9
+ * const ctx = new Ufsecp();
10
+ * const pub = ctx.pubkeyCreate(Buffer.alloc(32, 0).fill(1, 31));
11
+ * ctx.destroy();
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const ffi = require('ffi-napi');
17
+ const ref = require('ref-napi');
18
+ const path = require('path');
19
+ const os = require('os');
20
+
21
+ const voidPtr = ref.refType(ref.types.void);
22
+ const voidPtrPtr = ref.refType(voidPtr);
23
+ const uint8Ptr = ref.refType(ref.types.uint8);
24
+ const int32Ptr = ref.refType(ref.types.int32);
25
+ const sizeTPtr = ref.refType(ref.types.size_t);
26
+
27
+ // ── Error codes ────────────────────────────────────────────────────────
28
+
29
+ const UFSECP_OK = 0;
30
+ const UFSECP_ERR_NULL_ARG = 1;
31
+ const UFSECP_ERR_BAD_KEY = 2;
32
+ const UFSECP_ERR_BAD_PUBKEY = 3;
33
+ const UFSECP_ERR_BAD_SIG = 4;
34
+ const UFSECP_ERR_BAD_INPUT = 5;
35
+ const UFSECP_ERR_VERIFY_FAIL = 6;
36
+ const UFSECP_ERR_ARITH = 7;
37
+ const UFSECP_ERR_SELFTEST = 8;
38
+ const UFSECP_ERR_INTERNAL = 9;
39
+ const UFSECP_ERR_BUF_SMALL = 10;
40
+
41
+ const ERROR_NAMES = {
42
+ [UFSECP_ERR_NULL_ARG]: 'null argument',
43
+ [UFSECP_ERR_BAD_KEY]: 'invalid private key',
44
+ [UFSECP_ERR_BAD_PUBKEY]: 'invalid public key',
45
+ [UFSECP_ERR_BAD_SIG]: 'invalid signature',
46
+ [UFSECP_ERR_BAD_INPUT]: 'bad input',
47
+ [UFSECP_ERR_VERIFY_FAIL]: 'verification failed',
48
+ [UFSECP_ERR_ARITH]: 'arithmetic error',
49
+ [UFSECP_ERR_SELFTEST]: 'selftest failed',
50
+ [UFSECP_ERR_INTERNAL]: 'internal error',
51
+ [UFSECP_ERR_BUF_SMALL]: 'buffer too small',
52
+ };
53
+
54
+ class UfsecpError extends Error {
55
+ constructor(op, code) {
56
+ super(`ufsecp ${op} failed: ${ERROR_NAMES[code] || `unknown (${code})`}`);
57
+ this.name = 'UfsecpError';
58
+ this.code = code;
59
+ this.operation = op;
60
+ }
61
+ }
62
+
63
+ // ── Library resolution ─────────────────────────────────────────────────
64
+
65
+ function findLibrary() {
66
+ const plat = os.platform();
67
+ const name = plat === 'win32' ? 'ufsecp.dll'
68
+ : plat === 'darwin' ? 'libufsecp.dylib'
69
+ : 'libufsecp.so';
70
+
71
+ // 1. UFSECP_LIB env var
72
+ const env = process.env.UFSECP_LIB;
73
+ if (env) return env;
74
+
75
+ // 2. Next to this file
76
+ const here = path.join(__dirname, name);
77
+ try { require('fs').accessSync(here); return here; } catch {}
78
+
79
+ // 3. prebuilds/<platform>-<arch>/
80
+ const prebuilt = path.join(__dirname, '..', 'prebuilds',
81
+ `${plat}-${os.arch()}`, name);
82
+ try { require('fs').accessSync(prebuilt); return prebuilt; } catch {}
83
+
84
+ // 4. System default
85
+ return name;
86
+ }
87
+
88
+ // ── FFI binding ────────────────────────────────────────────────────────
89
+
90
+ const LIB_SPEC = {
91
+ // Context
92
+ ufsecp_ctx_create: ['int', [voidPtrPtr]],
93
+ ufsecp_ctx_destroy: ['void', [voidPtr]],
94
+ ufsecp_ctx_clone: ['int', [voidPtr, voidPtrPtr]],
95
+ // Version
96
+ ufsecp_version: ['uint32', []],
97
+ ufsecp_abi_version: ['uint32', []],
98
+ ufsecp_version_string:['string', []],
99
+ ufsecp_error_str: ['string', ['int']],
100
+ ufsecp_last_error: ['int', [voidPtr]],
101
+ ufsecp_last_error_msg:['string', [voidPtr]],
102
+ // Key ops
103
+ ufsecp_pubkey_create: ['int', [voidPtr, uint8Ptr, uint8Ptr]],
104
+ ufsecp_pubkey_create_uncompressed:['int', [voidPtr, uint8Ptr, uint8Ptr]],
105
+ ufsecp_pubkey_parse: ['int', [voidPtr, uint8Ptr, 'size_t', uint8Ptr]],
106
+ ufsecp_pubkey_xonly: ['int', [voidPtr, uint8Ptr, uint8Ptr]],
107
+ ufsecp_seckey_verify: ['int', [voidPtr, uint8Ptr]],
108
+ ufsecp_seckey_negate: ['int', [voidPtr, uint8Ptr]],
109
+ ufsecp_seckey_tweak_add: ['int', [voidPtr, uint8Ptr, uint8Ptr]],
110
+ ufsecp_seckey_tweak_mul: ['int', [voidPtr, uint8Ptr, uint8Ptr]],
111
+ // ECDSA
112
+ ufsecp_ecdsa_sign: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
113
+ ufsecp_ecdsa_verify: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
114
+ ufsecp_ecdsa_sig_to_der: ['int', [voidPtr, uint8Ptr, uint8Ptr, sizeTPtr]],
115
+ ufsecp_ecdsa_sig_from_der: ['int', [voidPtr, uint8Ptr, 'size_t', uint8Ptr]],
116
+ // Recovery
117
+ ufsecp_ecdsa_sign_recoverable: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr, int32Ptr]],
118
+ ufsecp_ecdsa_recover: ['int', [voidPtr, uint8Ptr, uint8Ptr, 'int', uint8Ptr]],
119
+ // Schnorr
120
+ ufsecp_schnorr_sign: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr, uint8Ptr]],
121
+ ufsecp_schnorr_verify: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
122
+ // ECDH
123
+ ufsecp_ecdh: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
124
+ ufsecp_ecdh_xonly: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
125
+ ufsecp_ecdh_raw: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
126
+ // Hashing
127
+ ufsecp_sha256: ['int', [uint8Ptr, 'size_t', uint8Ptr]],
128
+ ufsecp_hash160: ['int', [uint8Ptr, 'size_t', uint8Ptr]],
129
+ ufsecp_tagged_hash: ['int', ['string', uint8Ptr, 'size_t', uint8Ptr]],
130
+ // Addresses
131
+ ufsecp_addr_p2pkh: ['int', [voidPtr, uint8Ptr, 'int', uint8Ptr, sizeTPtr]],
132
+ ufsecp_addr_p2wpkh: ['int', [voidPtr, uint8Ptr, 'int', uint8Ptr, sizeTPtr]],
133
+ ufsecp_addr_p2tr: ['int', [voidPtr, uint8Ptr, 'int', uint8Ptr, sizeTPtr]],
134
+ // WIF
135
+ ufsecp_wif_encode: ['int', [voidPtr, uint8Ptr, 'int', 'int', uint8Ptr, sizeTPtr]],
136
+ ufsecp_wif_decode: ['int', [voidPtr, 'string', uint8Ptr, int32Ptr, int32Ptr]],
137
+ // BIP-32
138
+ ufsecp_bip32_master: ['int', [voidPtr, uint8Ptr, 'size_t', uint8Ptr]],
139
+ ufsecp_bip32_derive: ['int', [voidPtr, uint8Ptr, 'uint32', uint8Ptr]],
140
+ ufsecp_bip32_derive_path: ['int', [voidPtr, uint8Ptr, 'string', uint8Ptr]],
141
+ ufsecp_bip32_privkey: ['int', [voidPtr, uint8Ptr, uint8Ptr]],
142
+ ufsecp_bip32_pubkey: ['int', [voidPtr, uint8Ptr, uint8Ptr]],
143
+ // Taproot
144
+ ufsecp_taproot_output_key: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr, int32Ptr]],
145
+ ufsecp_taproot_tweak_seckey: ['int', [voidPtr, uint8Ptr, uint8Ptr, uint8Ptr]],
146
+ ufsecp_taproot_verify: ['int', [voidPtr, uint8Ptr, 'int', uint8Ptr, uint8Ptr, 'size_t']],
147
+ };
148
+
149
+ // ── Constants ──────────────────────────────────────────────────────────
150
+
151
+ const NET_MAINNET = 0;
152
+ const NET_TESTNET = 1;
153
+
154
+ // ── Ufsecp class ───────────────────────────────────────────────────────
155
+
156
+ class Ufsecp {
157
+ /**
158
+ * @param {string} [libPath] Path to the ufsecp shared library.
159
+ */
160
+ constructor(libPath) {
161
+ this._lib = ffi.Library(libPath || findLibrary(), LIB_SPEC);
162
+ const pp = ref.alloc(voidPtr);
163
+ const rc = this._lib.ufsecp_ctx_create(pp);
164
+ if (rc !== UFSECP_OK) throw new UfsecpError('ctx_create', rc);
165
+ this._ctx = pp.deref();
166
+ this._destroyed = false;
167
+ }
168
+
169
+ /** Explicitly destroy the context. Safe to call multiple times. */
170
+ destroy() {
171
+ if (!this._destroyed && this._ctx) {
172
+ this._lib.ufsecp_ctx_destroy(this._ctx);
173
+ this._ctx = null;
174
+ this._destroyed = true;
175
+ }
176
+ }
177
+
178
+ // ── Version ──────────────────────────────────────────────────────────
179
+
180
+ version() { return this._lib.ufsecp_version(); }
181
+ abiVersion() { return this._lib.ufsecp_abi_version(); }
182
+ versionString() { return this._lib.ufsecp_version_string(); }
183
+ lastError() { this._alive(); return this._lib.ufsecp_last_error(this._ctx); }
184
+ lastErrorMsg() { this._alive(); return this._lib.ufsecp_last_error_msg(this._ctx); }
185
+
186
+ // ── Key operations ────────────────────────────────────────────────────
187
+
188
+ /** Compressed public key (33 bytes). */
189
+ pubkeyCreate(privkey) {
190
+ _chk(privkey, 32, 'privkey'); this._alive();
191
+ const out = Buffer.alloc(33);
192
+ this._throw(this._lib.ufsecp_pubkey_create(this._ctx, privkey, out), 'pubkey_create');
193
+ return out;
194
+ }
195
+
196
+ /** Uncompressed public key (65 bytes). */
197
+ pubkeyCreateUncompressed(privkey) {
198
+ _chk(privkey, 32, 'privkey'); this._alive();
199
+ const out = Buffer.alloc(65);
200
+ this._throw(this._lib.ufsecp_pubkey_create_uncompressed(this._ctx, privkey, out), 'pubkey_create_uncompressed');
201
+ return out;
202
+ }
203
+
204
+ /** Parse compressed/uncompressed → compressed 33 bytes. */
205
+ pubkeyParse(pubkey) {
206
+ this._alive();
207
+ const out = Buffer.alloc(33);
208
+ this._throw(this._lib.ufsecp_pubkey_parse(this._ctx, pubkey, pubkey.length, out), 'pubkey_parse');
209
+ return out;
210
+ }
211
+
212
+ /** X-only (32 bytes, BIP-340) from private key. */
213
+ pubkeyXonly(privkey) {
214
+ _chk(privkey, 32, 'privkey'); this._alive();
215
+ const out = Buffer.alloc(32);
216
+ this._throw(this._lib.ufsecp_pubkey_xonly(this._ctx, privkey, out), 'pubkey_xonly');
217
+ return out;
218
+ }
219
+
220
+ seckeyVerify(privkey) { _chk(privkey, 32, 'privkey'); this._alive(); return this._lib.ufsecp_seckey_verify(this._ctx, privkey) === UFSECP_OK; }
221
+
222
+ seckeyNegate(privkey) {
223
+ _chk(privkey, 32, 'privkey'); this._alive();
224
+ const buf = Buffer.from(privkey);
225
+ this._throw(this._lib.ufsecp_seckey_negate(this._ctx, buf), 'seckey_negate');
226
+ return buf;
227
+ }
228
+
229
+ seckeyTweakAdd(privkey, tweak) {
230
+ _chk(privkey, 32, 'privkey'); _chk(tweak, 32, 'tweak'); this._alive();
231
+ const buf = Buffer.from(privkey);
232
+ this._throw(this._lib.ufsecp_seckey_tweak_add(this._ctx, buf, tweak), 'seckey_tweak_add');
233
+ return buf;
234
+ }
235
+
236
+ seckeyTweakMul(privkey, tweak) {
237
+ _chk(privkey, 32, 'privkey'); _chk(tweak, 32, 'tweak'); this._alive();
238
+ const buf = Buffer.from(privkey);
239
+ this._throw(this._lib.ufsecp_seckey_tweak_mul(this._ctx, buf, tweak), 'seckey_tweak_mul');
240
+ return buf;
241
+ }
242
+
243
+ // ── ECDSA ─────────────────────────────────────────────────────────────
244
+
245
+ ecdsaSign(msgHash, privkey) {
246
+ _chk(msgHash, 32, 'msgHash'); _chk(privkey, 32, 'privkey'); this._alive();
247
+ const sig = Buffer.alloc(64);
248
+ this._throw(this._lib.ufsecp_ecdsa_sign(this._ctx, msgHash, privkey, sig), 'ecdsa_sign');
249
+ return sig;
250
+ }
251
+
252
+ ecdsaVerify(msgHash, sig, pubkey) {
253
+ _chk(msgHash, 32, 'msgHash'); _chk(sig, 64, 'sig'); _chk(pubkey, 33, 'pubkey'); this._alive();
254
+ return this._lib.ufsecp_ecdsa_verify(this._ctx, msgHash, sig, pubkey) === UFSECP_OK;
255
+ }
256
+
257
+ ecdsaSigToDer(sig) {
258
+ _chk(sig, 64, 'sig'); this._alive();
259
+ const der = Buffer.alloc(72);
260
+ const len = ref.alloc('size_t', 72);
261
+ this._throw(this._lib.ufsecp_ecdsa_sig_to_der(this._ctx, sig, der, len), 'ecdsa_sig_to_der');
262
+ return der.subarray(0, len.deref());
263
+ }
264
+
265
+ ecdsaSigFromDer(der) {
266
+ this._alive();
267
+ const sig = Buffer.alloc(64);
268
+ this._throw(this._lib.ufsecp_ecdsa_sig_from_der(this._ctx, der, der.length, sig), 'ecdsa_sig_from_der');
269
+ return sig;
270
+ }
271
+
272
+ // ── Recovery ──────────────────────────────────────────────────────────
273
+
274
+ ecdsaSignRecoverable(msgHash, privkey) {
275
+ _chk(msgHash, 32, 'msgHash'); _chk(privkey, 32, 'privkey'); this._alive();
276
+ const sig = Buffer.alloc(64);
277
+ const recid = ref.alloc('int');
278
+ this._throw(this._lib.ufsecp_ecdsa_sign_recoverable(this._ctx, msgHash, privkey, sig, recid), 'ecdsa_sign_recoverable');
279
+ return { signature: sig, recoveryId: recid.deref() };
280
+ }
281
+
282
+ ecdsaRecover(msgHash, sig, recid) {
283
+ _chk(msgHash, 32, 'msgHash'); _chk(sig, 64, 'sig'); this._alive();
284
+ const pub = Buffer.alloc(33);
285
+ this._throw(this._lib.ufsecp_ecdsa_recover(this._ctx, msgHash, sig, recid, pub), 'ecdsa_recover');
286
+ return pub;
287
+ }
288
+
289
+ // ── Schnorr ───────────────────────────────────────────────────────────
290
+
291
+ schnorrSign(msg, privkey, auxRand) {
292
+ _chk(msg, 32, 'msg'); _chk(privkey, 32, 'privkey'); _chk(auxRand, 32, 'auxRand'); this._alive();
293
+ const sig = Buffer.alloc(64);
294
+ this._throw(this._lib.ufsecp_schnorr_sign(this._ctx, msg, privkey, auxRand, sig), 'schnorr_sign');
295
+ return sig;
296
+ }
297
+
298
+ schnorrVerify(msg, sig, pubkeyX) {
299
+ _chk(msg, 32, 'msg'); _chk(sig, 64, 'sig'); _chk(pubkeyX, 32, 'pubkeyX'); this._alive();
300
+ return this._lib.ufsecp_schnorr_verify(this._ctx, msg, sig, pubkeyX) === UFSECP_OK;
301
+ }
302
+
303
+ // ── ECDH ──────────────────────────────────────────────────────────────
304
+
305
+ ecdh(privkey, pubkey) {
306
+ _chk(privkey, 32, 'privkey'); _chk(pubkey, 33, 'pubkey'); this._alive();
307
+ const out = Buffer.alloc(32);
308
+ this._throw(this._lib.ufsecp_ecdh(this._ctx, privkey, pubkey, out), 'ecdh');
309
+ return out;
310
+ }
311
+
312
+ ecdhXonly(privkey, pubkey) {
313
+ _chk(privkey, 32, 'privkey'); _chk(pubkey, 33, 'pubkey'); this._alive();
314
+ const out = Buffer.alloc(32);
315
+ this._throw(this._lib.ufsecp_ecdh_xonly(this._ctx, privkey, pubkey, out), 'ecdh_xonly');
316
+ return out;
317
+ }
318
+
319
+ ecdhRaw(privkey, pubkey) {
320
+ _chk(privkey, 32, 'privkey'); _chk(pubkey, 33, 'pubkey'); this._alive();
321
+ const out = Buffer.alloc(32);
322
+ this._throw(this._lib.ufsecp_ecdh_raw(this._ctx, privkey, pubkey, out), 'ecdh_raw');
323
+ return out;
324
+ }
325
+
326
+ // ── Hashing ───────────────────────────────────────────────────────────
327
+
328
+ sha256(data) {
329
+ const out = Buffer.alloc(32);
330
+ this._throw(this._lib.ufsecp_sha256(data, data.length, out), 'sha256');
331
+ return out;
332
+ }
333
+
334
+ hash160(data) {
335
+ const out = Buffer.alloc(20);
336
+ this._throw(this._lib.ufsecp_hash160(data, data.length, out), 'hash160');
337
+ return out;
338
+ }
339
+
340
+ taggedHash(tag, data) {
341
+ const out = Buffer.alloc(32);
342
+ this._throw(this._lib.ufsecp_tagged_hash(tag, data, data.length, out), 'tagged_hash');
343
+ return out;
344
+ }
345
+
346
+ // ── Addresses ─────────────────────────────────────────────────────────
347
+
348
+ addrP2PKH(pubkey, network = NET_MAINNET) {
349
+ _chk(pubkey, 33, 'pubkey'); return this._getAddr('ufsecp_addr_p2pkh', pubkey, network);
350
+ }
351
+
352
+ addrP2WPKH(pubkey, network = NET_MAINNET) {
353
+ _chk(pubkey, 33, 'pubkey'); return this._getAddr('ufsecp_addr_p2wpkh', pubkey, network);
354
+ }
355
+
356
+ addrP2TR(xonlyKey, network = NET_MAINNET) {
357
+ _chk(xonlyKey, 32, 'xonlyKey'); return this._getAddr('ufsecp_addr_p2tr', xonlyKey, network);
358
+ }
359
+
360
+ // ── WIF ───────────────────────────────────────────────────────────────
361
+
362
+ wifEncode(privkey, compressed = true, network = NET_MAINNET) {
363
+ _chk(privkey, 32, 'privkey'); this._alive();
364
+ const buf = Buffer.alloc(128);
365
+ const len = ref.alloc('size_t', 128);
366
+ this._throw(this._lib.ufsecp_wif_encode(this._ctx, privkey, compressed ? 1 : 0, network, buf, len), 'wif_encode');
367
+ return buf.toString('utf8', 0, len.deref());
368
+ }
369
+
370
+ wifDecode(wif) {
371
+ this._alive();
372
+ const key = Buffer.alloc(32);
373
+ const comp = ref.alloc('int');
374
+ const net = ref.alloc('int');
375
+ this._throw(this._lib.ufsecp_wif_decode(this._ctx, wif, key, comp, net), 'wif_decode');
376
+ return { privkey: key, compressed: comp.deref() === 1, network: net.deref() };
377
+ }
378
+
379
+ // ── BIP-32 ────────────────────────────────────────────────────────────
380
+
381
+ bip32Master(seed) {
382
+ this._alive();
383
+ if (seed.length < 16 || seed.length > 64) throw new RangeError('Seed must be 16-64 bytes');
384
+ const key = Buffer.alloc(82);
385
+ this._throw(this._lib.ufsecp_bip32_master(this._ctx, seed, seed.length, key), 'bip32_master');
386
+ return key;
387
+ }
388
+
389
+ bip32Derive(parent, index) {
390
+ _chk(parent, 82, 'parent'); this._alive();
391
+ const child = Buffer.alloc(82);
392
+ this._throw(this._lib.ufsecp_bip32_derive(this._ctx, parent, index >>> 0, child), 'bip32_derive');
393
+ return child;
394
+ }
395
+
396
+ bip32DerivePath(master, path) {
397
+ _chk(master, 82, 'master'); this._alive();
398
+ const key = Buffer.alloc(82);
399
+ this._throw(this._lib.ufsecp_bip32_derive_path(this._ctx, master, path, key), 'bip32_derive_path');
400
+ return key;
401
+ }
402
+
403
+ bip32Privkey(key) {
404
+ _chk(key, 82, 'key'); this._alive();
405
+ const priv = Buffer.alloc(32);
406
+ this._throw(this._lib.ufsecp_bip32_privkey(this._ctx, key, priv), 'bip32_privkey');
407
+ return priv;
408
+ }
409
+
410
+ bip32Pubkey(key) {
411
+ _chk(key, 82, 'key'); this._alive();
412
+ const pub = Buffer.alloc(33);
413
+ this._throw(this._lib.ufsecp_bip32_pubkey(this._ctx, key, pub), 'bip32_pubkey');
414
+ return pub;
415
+ }
416
+
417
+ // ── Taproot ───────────────────────────────────────────────────────────
418
+
419
+ taprootOutputKey(internalKeyX, merkleRoot = null) {
420
+ _chk(internalKeyX, 32, 'internalKeyX'); this._alive();
421
+ const out = Buffer.alloc(32);
422
+ const parity = ref.alloc('int');
423
+ this._throw(this._lib.ufsecp_taproot_output_key(this._ctx, internalKeyX, merkleRoot, out, parity), 'taproot_output_key');
424
+ return { outputKeyX: out, parity: parity.deref() };
425
+ }
426
+
427
+ taprootTweakSeckey(privkey, merkleRoot = null) {
428
+ _chk(privkey, 32, 'privkey'); this._alive();
429
+ const out = Buffer.alloc(32);
430
+ this._throw(this._lib.ufsecp_taproot_tweak_seckey(this._ctx, privkey, merkleRoot, out), 'taproot_tweak_seckey');
431
+ return out;
432
+ }
433
+
434
+ taprootVerify(outputKeyX, parity, internalKeyX, merkleRoot = null) {
435
+ _chk(outputKeyX, 32, 'outputKeyX'); _chk(internalKeyX, 32, 'internalKeyX'); this._alive();
436
+ const mrLen = merkleRoot ? merkleRoot.length : 0;
437
+ return this._lib.ufsecp_taproot_verify(this._ctx, outputKeyX, parity, internalKeyX, merkleRoot, mrLen) === UFSECP_OK;
438
+ }
439
+
440
+ // ── Internal ──────────────────────────────────────────────────────────
441
+
442
+ _alive() {
443
+ if (this._destroyed) throw new Error('UfsecpContext already destroyed');
444
+ }
445
+
446
+ _throw(rc, op) {
447
+ if (rc !== UFSECP_OK) throw new UfsecpError(op, rc);
448
+ }
449
+
450
+ _getAddr(fnName, key, network) {
451
+ this._alive();
452
+ const buf = Buffer.alloc(128);
453
+ const len = ref.alloc('size_t', 128);
454
+ this._throw(this._lib[fnName](this._ctx, key, network, buf, len), 'address');
455
+ return buf.toString('utf8', 0, len.deref());
456
+ }
457
+ }
458
+
459
+ function _chk(buf, expected, name) {
460
+ if (!Buffer.isBuffer(buf) && !(buf instanceof Uint8Array)) {
461
+ throw new TypeError(`${name} must be a Buffer`);
462
+ }
463
+ if (buf.length !== expected) {
464
+ throw new RangeError(`${name} must be ${expected} bytes, got ${buf.length}`);
465
+ }
466
+ }
467
+
468
+ module.exports = { Ufsecp, UfsecpError, NET_MAINNET, NET_TESTNET };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "ufsecp",
3
+ "version": "3.3.0",
4
+ "description": "Node.js bindings for UltrafastSecp256k1 — high-performance secp256k1 ECC (ufsecp C ABI v1)",
5
+ "main": "lib/ufsecp.js",
6
+ "files": ["lib/ufsecp.js", "prebuilds/", "README.md"],
7
+ "scripts": {
8
+ "test": "node test/test.js"
9
+ },
10
+ "dependencies": {
11
+ "ffi-napi": "^4.0.3",
12
+ "ref-napi": "^3.0.3"
13
+ },
14
+ "keywords": [
15
+ "secp256k1", "ecdsa", "schnorr", "bitcoin", "cryptography",
16
+ "elliptic-curve", "taproot", "bip32", "ecdh", "ufsecp"
17
+ ],
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/shrec/UltrafastSecp256k1"
22
+ },
23
+ "engines": {
24
+ "node": ">=16.0.0"
25
+ },
26
+ "homepage": "https://github.com/shrec/UltrafastSecp256k1"
27
+ }
Binary file
Binary file