react-native-ufsecp 3.10.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/android/src/main/java/com/ultrafast/ufsecp/UfsecpModule.java +207 -0
- package/android/src/main/java/com/ultrafastsecp256k1/UltrafastSecp256k1Module.java +385 -0
- package/android/src/main/java/com/ultrafastsecp256k1/UltrafastSecp256k1Package.java +28 -0
- package/ios/RNUfsecp.m +253 -0
- package/ios/UltrafastSecp256k1.h +4 -0
- package/ios/UltrafastSecp256k1.m +483 -0
- package/lib/ufsecp.js +176 -0
- package/package.json +24 -0
- package/react-native-ufsecp.podspec +17 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
#import "UltrafastSecp256k1.h"
|
|
2
|
+
#import <React/RCTUtils.h>
|
|
3
|
+
#include "ultrafast_secp256k1.h"
|
|
4
|
+
|
|
5
|
+
@implementation UltrafastSecp256k1
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_MODULE()
|
|
8
|
+
|
|
9
|
+
+ (BOOL)requiresMainQueueSetup { return NO; }
|
|
10
|
+
|
|
11
|
+
// ── Hex helpers ──────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
static NSData *hexToData(NSString *hex) {
|
|
14
|
+
NSMutableData *data = [NSMutableData dataWithCapacity:hex.length / 2];
|
|
15
|
+
unsigned char byte;
|
|
16
|
+
char buf[3] = {0};
|
|
17
|
+
for (NSUInteger i = 0; i < hex.length; i += 2) {
|
|
18
|
+
buf[0] = [hex characterAtIndex:i];
|
|
19
|
+
buf[1] = [hex characterAtIndex:i + 1];
|
|
20
|
+
byte = strtol(buf, NULL, 16);
|
|
21
|
+
[data appendBytes:&byte length:1];
|
|
22
|
+
}
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static NSString *dataToHex(const uint8_t *bytes, size_t length) {
|
|
27
|
+
NSMutableString *hex = [NSMutableString stringWithCapacity:length * 2];
|
|
28
|
+
for (size_t i = 0; i < length; i++) {
|
|
29
|
+
[hex appendFormat:@"%02x", bytes[i]];
|
|
30
|
+
}
|
|
31
|
+
return hex;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── Init ─────────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
- (instancetype)init {
|
|
37
|
+
self = [super init];
|
|
38
|
+
if (self) { secp256k1_init(); }
|
|
39
|
+
return self;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(version) {
|
|
43
|
+
return [NSString stringWithUTF8String:secp256k1_version()];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Key Operations ───────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
RCT_EXPORT_METHOD(ecPubkeyCreate:(NSString *)privkeyHex
|
|
49
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
50
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
51
|
+
NSData *pk = hexToData(privkeyHex);
|
|
52
|
+
uint8_t out[33];
|
|
53
|
+
if (secp256k1_ec_pubkey_create(pk.bytes, out) != 0) {
|
|
54
|
+
reject(@"ERR", @"Invalid private key", nil);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
resolve(dataToHex(out, 33));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
RCT_EXPORT_METHOD(ecPubkeyCreateUncompressed:(NSString *)privkeyHex
|
|
61
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
62
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
63
|
+
NSData *pk = hexToData(privkeyHex);
|
|
64
|
+
uint8_t out[65];
|
|
65
|
+
if (secp256k1_ec_pubkey_create_uncompressed(pk.bytes, out) != 0) {
|
|
66
|
+
reject(@"ERR", @"Invalid private key", nil);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
resolve(dataToHex(out, 65));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
RCT_EXPORT_METHOD(ecPubkeyParse:(NSString *)pubkeyHex
|
|
73
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
74
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
75
|
+
NSData *inp = hexToData(pubkeyHex);
|
|
76
|
+
uint8_t out[33];
|
|
77
|
+
if (secp256k1_ec_pubkey_parse(inp.bytes, inp.length, out) != 0) {
|
|
78
|
+
reject(@"ERR", @"Invalid public key", nil);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
resolve(dataToHex(out, 33));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
RCT_EXPORT_METHOD(ecSeckeyVerify:(NSString *)privkeyHex
|
|
85
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
86
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
87
|
+
NSData *pk = hexToData(privkeyHex);
|
|
88
|
+
resolve(@(secp256k1_ec_seckey_verify(pk.bytes) == 1));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
RCT_EXPORT_METHOD(ecPrivkeyNegate:(NSString *)privkeyHex
|
|
92
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
93
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
94
|
+
NSMutableData *pk = [hexToData(privkeyHex) mutableCopy];
|
|
95
|
+
secp256k1_ec_privkey_negate(pk.mutableBytes);
|
|
96
|
+
resolve(dataToHex(pk.bytes, 32));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
RCT_EXPORT_METHOD(ecPrivkeyTweakAdd:(NSString *)privkeyHex
|
|
100
|
+
tweak:(NSString *)tweakHex
|
|
101
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
102
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
103
|
+
NSMutableData *pk = [hexToData(privkeyHex) mutableCopy];
|
|
104
|
+
NSData *tw = hexToData(tweakHex);
|
|
105
|
+
if (secp256k1_ec_privkey_tweak_add(pk.mutableBytes, tw.bytes) != 0) {
|
|
106
|
+
reject(@"ERR", @"Tweak add failed", nil);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
resolve(dataToHex(pk.bytes, 32));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
RCT_EXPORT_METHOD(ecPrivkeyTweakMul:(NSString *)privkeyHex
|
|
113
|
+
tweak:(NSString *)tweakHex
|
|
114
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
115
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
116
|
+
NSMutableData *pk = [hexToData(privkeyHex) mutableCopy];
|
|
117
|
+
NSData *tw = hexToData(tweakHex);
|
|
118
|
+
if (secp256k1_ec_privkey_tweak_mul(pk.mutableBytes, tw.bytes) != 0) {
|
|
119
|
+
reject(@"ERR", @"Tweak mul failed", nil);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
resolve(dataToHex(pk.bytes, 32));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── ECDSA ────────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
RCT_EXPORT_METHOD(ecdsaSign:(NSString *)msgHashHex
|
|
128
|
+
privkey:(NSString *)privkeyHex
|
|
129
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
130
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
131
|
+
NSData *mh = hexToData(msgHashHex);
|
|
132
|
+
NSData *pk = hexToData(privkeyHex);
|
|
133
|
+
uint8_t sig[64];
|
|
134
|
+
if (secp256k1_ecdsa_sign(mh.bytes, pk.bytes, sig) != 0) {
|
|
135
|
+
reject(@"ERR", @"ECDSA signing failed", nil);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
resolve(dataToHex(sig, 64));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
RCT_EXPORT_METHOD(ecdsaVerify:(NSString *)msgHashHex
|
|
142
|
+
sig:(NSString *)sigHex
|
|
143
|
+
pubkey:(NSString *)pubkeyHex
|
|
144
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
145
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
146
|
+
NSData *mh = hexToData(msgHashHex);
|
|
147
|
+
NSData *s = hexToData(sigHex);
|
|
148
|
+
NSData *pk = hexToData(pubkeyHex);
|
|
149
|
+
resolve(@(secp256k1_ecdsa_verify(mh.bytes, s.bytes, pk.bytes) == 1));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
RCT_EXPORT_METHOD(ecdsaSerializeDer:(NSString *)sigHex
|
|
153
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
154
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
155
|
+
NSData *s = hexToData(sigHex);
|
|
156
|
+
uint8_t der[72];
|
|
157
|
+
size_t der_len = 72;
|
|
158
|
+
if (secp256k1_ecdsa_signature_serialize_der(s.bytes, der, &der_len) != 0) {
|
|
159
|
+
reject(@"ERR", @"DER serialization failed", nil);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
resolve(dataToHex(der, der_len));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Recovery ─────────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
RCT_EXPORT_METHOD(ecdsaSignRecoverable:(NSString *)msgHashHex
|
|
168
|
+
privkey:(NSString *)privkeyHex
|
|
169
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
170
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
171
|
+
NSData *mh = hexToData(msgHashHex);
|
|
172
|
+
NSData *pk = hexToData(privkeyHex);
|
|
173
|
+
uint8_t sig[64];
|
|
174
|
+
int recid;
|
|
175
|
+
if (secp256k1_ecdsa_sign_recoverable(mh.bytes, pk.bytes, sig, &recid) != 0) {
|
|
176
|
+
reject(@"ERR", @"Recoverable signing failed", nil);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
resolve(@{@"signature": dataToHex(sig, 64), @"recid": @(recid)});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
RCT_EXPORT_METHOD(ecdsaRecover:(NSString *)msgHashHex
|
|
183
|
+
sig:(NSString *)sigHex
|
|
184
|
+
recid:(int)recid
|
|
185
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
186
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
187
|
+
NSData *mh = hexToData(msgHashHex);
|
|
188
|
+
NSData *s = hexToData(sigHex);
|
|
189
|
+
uint8_t pubkey[33];
|
|
190
|
+
if (secp256k1_ecdsa_recover(mh.bytes, s.bytes, recid, pubkey) != 0) {
|
|
191
|
+
reject(@"ERR", @"Recovery failed", nil);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
resolve(dataToHex(pubkey, 33));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Schnorr ──────────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
RCT_EXPORT_METHOD(schnorrSign:(NSString *)msgHex
|
|
200
|
+
privkey:(NSString *)privkeyHex
|
|
201
|
+
auxRand:(NSString *)auxRandHex
|
|
202
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
203
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
204
|
+
NSData *m = hexToData(msgHex);
|
|
205
|
+
NSData *pk = hexToData(privkeyHex);
|
|
206
|
+
NSData *ar = hexToData(auxRandHex);
|
|
207
|
+
uint8_t sig[64];
|
|
208
|
+
if (secp256k1_schnorr_sign(m.bytes, pk.bytes, ar.bytes, sig) != 0) {
|
|
209
|
+
reject(@"ERR", @"Schnorr sign failed", nil);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
resolve(dataToHex(sig, 64));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
RCT_EXPORT_METHOD(schnorrVerify:(NSString *)msgHex
|
|
216
|
+
sig:(NSString *)sigHex
|
|
217
|
+
pubkeyX:(NSString *)pubkeyXHex
|
|
218
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
219
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
220
|
+
NSData *m = hexToData(msgHex);
|
|
221
|
+
NSData *s = hexToData(sigHex);
|
|
222
|
+
NSData *px = hexToData(pubkeyXHex);
|
|
223
|
+
resolve(@(secp256k1_schnorr_verify(m.bytes, s.bytes, px.bytes) == 1));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
RCT_EXPORT_METHOD(schnorrPubkey:(NSString *)privkeyHex
|
|
227
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
228
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
229
|
+
NSData *pk = hexToData(privkeyHex);
|
|
230
|
+
uint8_t out[32];
|
|
231
|
+
if (secp256k1_schnorr_pubkey(pk.bytes, out) != 0) {
|
|
232
|
+
reject(@"ERR", @"Invalid private key", nil);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
resolve(dataToHex(out, 32));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── ECDH ─────────────────────────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
RCT_EXPORT_METHOD(ecdh:(NSString *)privkeyHex
|
|
241
|
+
pubkey:(NSString *)pubkeyHex
|
|
242
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
243
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
244
|
+
NSData *pk = hexToData(privkeyHex);
|
|
245
|
+
NSData *pub = hexToData(pubkeyHex);
|
|
246
|
+
uint8_t out[32];
|
|
247
|
+
if (secp256k1_ecdh(pk.bytes, pub.bytes, out) != 0) {
|
|
248
|
+
reject(@"ERR", @"ECDH failed", nil); return;
|
|
249
|
+
}
|
|
250
|
+
resolve(dataToHex(out, 32));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
RCT_EXPORT_METHOD(ecdhXonly:(NSString *)privkeyHex
|
|
254
|
+
pubkey:(NSString *)pubkeyHex
|
|
255
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
256
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
257
|
+
NSData *pk = hexToData(privkeyHex);
|
|
258
|
+
NSData *pub = hexToData(pubkeyHex);
|
|
259
|
+
uint8_t out[32];
|
|
260
|
+
if (secp256k1_ecdh_xonly(pk.bytes, pub.bytes, out) != 0) {
|
|
261
|
+
reject(@"ERR", @"ECDH xonly failed", nil); return;
|
|
262
|
+
}
|
|
263
|
+
resolve(dataToHex(out, 32));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
RCT_EXPORT_METHOD(ecdhRaw:(NSString *)privkeyHex
|
|
267
|
+
pubkey:(NSString *)pubkeyHex
|
|
268
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
269
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
270
|
+
NSData *pk = hexToData(privkeyHex);
|
|
271
|
+
NSData *pub = hexToData(pubkeyHex);
|
|
272
|
+
uint8_t out[32];
|
|
273
|
+
if (secp256k1_ecdh_raw(pk.bytes, pub.bytes, out) != 0) {
|
|
274
|
+
reject(@"ERR", @"ECDH raw failed", nil); return;
|
|
275
|
+
}
|
|
276
|
+
resolve(dataToHex(out, 32));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ── Hashing ──────────────────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
RCT_EXPORT_METHOD(sha256:(NSString *)dataHex
|
|
282
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
283
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
284
|
+
NSData *d = hexToData(dataHex);
|
|
285
|
+
uint8_t out[32];
|
|
286
|
+
secp256k1_sha256(d.bytes, d.length, out);
|
|
287
|
+
resolve(dataToHex(out, 32));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
RCT_EXPORT_METHOD(hash160:(NSString *)dataHex
|
|
291
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
292
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
293
|
+
NSData *d = hexToData(dataHex);
|
|
294
|
+
uint8_t out[20];
|
|
295
|
+
secp256k1_hash160(d.bytes, d.length, out);
|
|
296
|
+
resolve(dataToHex(out, 20));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
RCT_EXPORT_METHOD(taggedHash:(NSString *)tag
|
|
300
|
+
data:(NSString *)dataHex
|
|
301
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
302
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
303
|
+
NSData *d = hexToData(dataHex);
|
|
304
|
+
uint8_t out[32];
|
|
305
|
+
secp256k1_tagged_hash(tag.UTF8String, d.bytes, d.length, out);
|
|
306
|
+
resolve(dataToHex(out, 32));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Addresses ────────────────────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
RCT_EXPORT_METHOD(addressP2PKH:(NSString *)pubkeyHex
|
|
312
|
+
network:(int)network
|
|
313
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
314
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
315
|
+
NSData *pk = hexToData(pubkeyHex);
|
|
316
|
+
char addr[128]; size_t alen = 128;
|
|
317
|
+
if (secp256k1_address_p2pkh(pk.bytes, network, addr, &alen) != 0) {
|
|
318
|
+
reject(@"ERR", @"P2PKH failed", nil); return;
|
|
319
|
+
}
|
|
320
|
+
resolve([[NSString alloc] initWithBytes:addr length:alen encoding:NSUTF8StringEncoding]);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
RCT_EXPORT_METHOD(addressP2WPKH:(NSString *)pubkeyHex
|
|
324
|
+
network:(int)network
|
|
325
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
326
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
327
|
+
NSData *pk = hexToData(pubkeyHex);
|
|
328
|
+
char addr[128]; size_t alen = 128;
|
|
329
|
+
if (secp256k1_address_p2wpkh(pk.bytes, network, addr, &alen) != 0) {
|
|
330
|
+
reject(@"ERR", @"P2WPKH failed", nil); return;
|
|
331
|
+
}
|
|
332
|
+
resolve([[NSString alloc] initWithBytes:addr length:alen encoding:NSUTF8StringEncoding]);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
RCT_EXPORT_METHOD(addressP2TR:(NSString *)internalKeyXHex
|
|
336
|
+
network:(int)network
|
|
337
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
338
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
339
|
+
NSData *ik = hexToData(internalKeyXHex);
|
|
340
|
+
char addr[128]; size_t alen = 128;
|
|
341
|
+
if (secp256k1_address_p2tr(ik.bytes, network, addr, &alen) != 0) {
|
|
342
|
+
reject(@"ERR", @"P2TR failed", nil); return;
|
|
343
|
+
}
|
|
344
|
+
resolve([[NSString alloc] initWithBytes:addr length:alen encoding:NSUTF8StringEncoding]);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ── WIF ──────────────────────────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
RCT_EXPORT_METHOD(wifEncode:(NSString *)privkeyHex
|
|
350
|
+
compressed:(BOOL)compressed
|
|
351
|
+
network:(int)network
|
|
352
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
353
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
354
|
+
NSData *pk = hexToData(privkeyHex);
|
|
355
|
+
char wif[128]; size_t wlen = 128;
|
|
356
|
+
if (secp256k1_wif_encode(pk.bytes, compressed ? 1 : 0, network, wif, &wlen) != 0) {
|
|
357
|
+
reject(@"ERR", @"WIF encode failed", nil); return;
|
|
358
|
+
}
|
|
359
|
+
resolve([[NSString alloc] initWithBytes:wif length:wlen encoding:NSUTF8StringEncoding]);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
RCT_EXPORT_METHOD(wifDecode:(NSString *)wif
|
|
363
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
364
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
365
|
+
uint8_t pk[32]; int comp, net;
|
|
366
|
+
if (secp256k1_wif_decode(wif.UTF8String, pk, &comp, &net) != 0) {
|
|
367
|
+
reject(@"ERR", @"Invalid WIF", nil); return;
|
|
368
|
+
}
|
|
369
|
+
resolve(@{
|
|
370
|
+
@"privkey": dataToHex(pk, 32),
|
|
371
|
+
@"compressed": @(comp == 1),
|
|
372
|
+
@"network": @(net)
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ── BIP-32 ───────────────────────────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
RCT_EXPORT_METHOD(bip32MasterKey:(NSString *)seedHex
|
|
379
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
380
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
381
|
+
NSData *seed = hexToData(seedHex);
|
|
382
|
+
secp256k1_bip32_key key;
|
|
383
|
+
if (secp256k1_bip32_master_key(seed.bytes, seed.length, &key) != 0) {
|
|
384
|
+
reject(@"ERR", @"Master key failed", nil); return;
|
|
385
|
+
}
|
|
386
|
+
resolve(dataToHex((const uint8_t *)&key, 79));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
RCT_EXPORT_METHOD(bip32DeriveChild:(NSString *)parentKeyHex
|
|
390
|
+
index:(int)index
|
|
391
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
392
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
393
|
+
NSData *pdata = hexToData(parentKeyHex);
|
|
394
|
+
secp256k1_bip32_key parent, child;
|
|
395
|
+
memcpy(&parent, pdata.bytes, 79);
|
|
396
|
+
if (secp256k1_bip32_derive_child(&parent, (uint32_t)index, &child) != 0) {
|
|
397
|
+
reject(@"ERR", @"Derive child failed", nil); return;
|
|
398
|
+
}
|
|
399
|
+
resolve(dataToHex((const uint8_t *)&child, 79));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
RCT_EXPORT_METHOD(bip32DerivePath:(NSString *)masterKeyHex
|
|
403
|
+
path:(NSString *)path
|
|
404
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
405
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
406
|
+
NSData *mdata = hexToData(masterKeyHex);
|
|
407
|
+
secp256k1_bip32_key master, out;
|
|
408
|
+
memcpy(&master, mdata.bytes, 79);
|
|
409
|
+
if (secp256k1_bip32_derive_path(&master, path.UTF8String, &out) != 0) {
|
|
410
|
+
reject(@"ERR", @"Path derivation failed", nil); return;
|
|
411
|
+
}
|
|
412
|
+
resolve(dataToHex((const uint8_t *)&out, 79));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
RCT_EXPORT_METHOD(bip32GetPrivkey:(NSString *)keyHex
|
|
416
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
417
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
418
|
+
NSData *kdata = hexToData(keyHex);
|
|
419
|
+
secp256k1_bip32_key k;
|
|
420
|
+
memcpy(&k, kdata.bytes, 79);
|
|
421
|
+
uint8_t pk[32];
|
|
422
|
+
if (secp256k1_bip32_get_privkey(&k, pk) != 0) {
|
|
423
|
+
reject(@"ERR", @"Not a private key", nil); return;
|
|
424
|
+
}
|
|
425
|
+
resolve(dataToHex(pk, 32));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
RCT_EXPORT_METHOD(bip32GetPubkey:(NSString *)keyHex
|
|
429
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
430
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
431
|
+
NSData *kdata = hexToData(keyHex);
|
|
432
|
+
secp256k1_bip32_key k;
|
|
433
|
+
memcpy(&k, kdata.bytes, 79);
|
|
434
|
+
uint8_t pub[33];
|
|
435
|
+
if (secp256k1_bip32_get_pubkey(&k, pub) != 0) {
|
|
436
|
+
reject(@"ERR", @"Pubkey extraction failed", nil); return;
|
|
437
|
+
}
|
|
438
|
+
resolve(dataToHex(pub, 33));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ── Taproot ──────────────────────────────────────────────────────────────────
|
|
442
|
+
|
|
443
|
+
RCT_EXPORT_METHOD(taprootOutputKey:(NSString *)internalKeyXHex
|
|
444
|
+
merkleRoot:(NSString *)merkleRootHex
|
|
445
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
446
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
447
|
+
NSData *ik = hexToData(internalKeyXHex);
|
|
448
|
+
NSData *mr = merkleRootHex ? hexToData(merkleRootHex) : nil;
|
|
449
|
+
uint8_t out[32]; int parity;
|
|
450
|
+
if (secp256k1_taproot_output_key(ik.bytes, mr ? mr.bytes : NULL, out, &parity) != 0) {
|
|
451
|
+
reject(@"ERR", @"Taproot output key failed", nil); return;
|
|
452
|
+
}
|
|
453
|
+
resolve(@{@"outputKeyX": dataToHex(out, 32), @"parity": @(parity)});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
RCT_EXPORT_METHOD(taprootTweakPrivkey:(NSString *)privkeyHex
|
|
457
|
+
merkleRoot:(NSString *)merkleRootHex
|
|
458
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
459
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
460
|
+
NSData *pk = hexToData(privkeyHex);
|
|
461
|
+
NSData *mr = merkleRootHex ? hexToData(merkleRootHex) : nil;
|
|
462
|
+
uint8_t out[32];
|
|
463
|
+
if (secp256k1_taproot_tweak_privkey(pk.bytes, mr ? mr.bytes : NULL, out) != 0) {
|
|
464
|
+
reject(@"ERR", @"Taproot tweak failed", nil); return;
|
|
465
|
+
}
|
|
466
|
+
resolve(dataToHex(out, 32));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
RCT_EXPORT_METHOD(taprootVerifyCommitment:(NSString *)outputKeyXHex
|
|
470
|
+
parity:(int)parity
|
|
471
|
+
internalKeyX:(NSString *)internalKeyXHex
|
|
472
|
+
merkleRoot:(NSString *)merkleRootHex
|
|
473
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
474
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
475
|
+
NSData *ok = hexToData(outputKeyXHex);
|
|
476
|
+
NSData *ik = hexToData(internalKeyXHex);
|
|
477
|
+
NSData *mr = merkleRootHex ? hexToData(merkleRootHex) : nil;
|
|
478
|
+
int rc = secp256k1_taproot_verify_commitment(
|
|
479
|
+
ok.bytes, parity, ik.bytes, mr ? mr.bytes : NULL, mr ? mr.length : 0);
|
|
480
|
+
resolve(@(rc == 1));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
@end
|
package/lib/ufsecp.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UltrafastSecp256k1 — React Native binding (ufsecp stable C ABI v1).
|
|
3
|
+
*
|
|
4
|
+
* Bridges to native via NativeModules.Ufsecp (Android: Java JNI, iOS: ObjC bridge).
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { Ufsecp } from 'react-native-ufsecp';
|
|
8
|
+
* const ctx = await Ufsecp.create();
|
|
9
|
+
* const pubkey = await ctx.pubkeyCreate(privkeyHex);
|
|
10
|
+
* ctx.destroy();
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { NativeModules, Platform } from 'react-native';
|
|
14
|
+
|
|
15
|
+
const { Ufsecp: NativeUfsecp } = NativeModules;
|
|
16
|
+
|
|
17
|
+
if (!NativeUfsecp) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'react-native-ufsecp: NativeModule not linked. Run `npx react-native link react-native-ufsecp` or rebuild.',
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const hex2buf = (hex) => {
|
|
24
|
+
const bytes = [];
|
|
25
|
+
for (let i = 0; i < hex.length; i += 2) bytes.push(parseInt(hex.substr(i, 2), 16));
|
|
26
|
+
return bytes;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const buf2hex = (arr) =>
|
|
30
|
+
(Array.isArray(arr) ? arr : [...arr]).map((b) => ('0' + (b & 0xff).toString(16)).slice(-2)).join('');
|
|
31
|
+
|
|
32
|
+
class UfsecpError extends Error {
|
|
33
|
+
constructor(op, msg) {
|
|
34
|
+
super(`ufsecp ${op}: ${msg}`);
|
|
35
|
+
this.name = 'UfsecpError';
|
|
36
|
+
this.operation = op;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class UfsecpContext {
|
|
41
|
+
constructor(handle) {
|
|
42
|
+
this._h = handle;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static async create() {
|
|
46
|
+
const h = await NativeUfsecp.create();
|
|
47
|
+
return new UfsecpContext(h);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async destroy() {
|
|
51
|
+
if (this._h != null) {
|
|
52
|
+
await NativeUfsecp.destroy(this._h);
|
|
53
|
+
this._h = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── Version ──────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
static version() { return NativeUfsecp.version(); }
|
|
60
|
+
static versionString() { return NativeUfsecp.versionString(); }
|
|
61
|
+
|
|
62
|
+
// ── Key ops ──────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/** @param {string} privkeyHex - 32-byte hex */
|
|
65
|
+
async pubkeyCreate(privkeyHex) {
|
|
66
|
+
this._alive();
|
|
67
|
+
return NativeUfsecp.pubkeyCreate(this._h, privkeyHex);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async pubkeyCreateUncompressed(privkeyHex) {
|
|
71
|
+
this._alive();
|
|
72
|
+
return NativeUfsecp.pubkeyCreateUncompressed(this._h, privkeyHex);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async seckeyVerify(privkeyHex) {
|
|
76
|
+
this._alive();
|
|
77
|
+
return NativeUfsecp.seckeyVerify(this._h, privkeyHex);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── ECDSA ────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
async ecdsaSign(msgHashHex, privkeyHex) {
|
|
83
|
+
this._alive();
|
|
84
|
+
return NativeUfsecp.ecdsaSign(this._h, msgHashHex, privkeyHex);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async ecdsaVerify(msgHashHex, sigHex, pubkeyHex) {
|
|
88
|
+
this._alive();
|
|
89
|
+
return NativeUfsecp.ecdsaVerify(this._h, msgHashHex, sigHex, pubkeyHex);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async ecdsaSignRecoverable(msgHashHex, privkeyHex) {
|
|
93
|
+
this._alive();
|
|
94
|
+
return NativeUfsecp.ecdsaSignRecoverable(this._h, msgHashHex, privkeyHex);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async ecdsaRecover(msgHashHex, sigHex, recid) {
|
|
98
|
+
this._alive();
|
|
99
|
+
return NativeUfsecp.ecdsaRecover(this._h, msgHashHex, sigHex, recid);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Schnorr ──────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
async schnorrSign(msgHex, privkeyHex, auxRandHex) {
|
|
105
|
+
this._alive();
|
|
106
|
+
return NativeUfsecp.schnorrSign(this._h, msgHex, privkeyHex, auxRandHex);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async schnorrVerify(msgHex, sigHex, pubkeyXHex) {
|
|
110
|
+
this._alive();
|
|
111
|
+
return NativeUfsecp.schnorrVerify(this._h, msgHex, sigHex, pubkeyXHex);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── ECDH ─────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
async ecdh(privkeyHex, pubkeyHex) {
|
|
117
|
+
this._alive();
|
|
118
|
+
return NativeUfsecp.ecdh(this._h, privkeyHex, pubkeyHex);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Hashing ──────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
static sha256(dataHex) { return NativeUfsecp.sha256(dataHex); }
|
|
124
|
+
static hash160(dataHex) { return NativeUfsecp.hash160(dataHex); }
|
|
125
|
+
|
|
126
|
+
// ── Addresses ────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
async addrP2pkh(pubkeyHex, network = 0) { this._alive(); return NativeUfsecp.addrP2pkh(this._h, pubkeyHex, network); }
|
|
129
|
+
async addrP2wpkh(pubkeyHex, network = 0) { this._alive(); return NativeUfsecp.addrP2wpkh(this._h, pubkeyHex, network); }
|
|
130
|
+
async addrP2tr(xonlyHex, network = 0) { this._alive(); return NativeUfsecp.addrP2tr(this._h, xonlyHex, network); }
|
|
131
|
+
|
|
132
|
+
// ── WIF ──────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
async wifEncode(privkeyHex, compressed = true, network = 0) {
|
|
135
|
+
this._alive();
|
|
136
|
+
return NativeUfsecp.wifEncode(this._h, privkeyHex, compressed, network);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── BIP-32 ───────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
async bip32Master(seedHex) {
|
|
142
|
+
this._alive();
|
|
143
|
+
return NativeUfsecp.bip32Master(this._h, seedHex);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async bip32Derive(parentHex, index) {
|
|
147
|
+
this._alive();
|
|
148
|
+
return NativeUfsecp.bip32Derive(this._h, parentHex, index);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async bip32DerivePath(masterHex, path) {
|
|
152
|
+
this._alive();
|
|
153
|
+
return NativeUfsecp.bip32DerivePath(this._h, masterHex, path);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Taproot ──────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
async taprootOutputKey(internalXHex, merkleRootHex = null) {
|
|
159
|
+
this._alive();
|
|
160
|
+
return NativeUfsecp.taprootOutputKey(this._h, internalXHex, merkleRootHex);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async taprootTweakSeckey(privkeyHex, merkleRootHex = null) {
|
|
164
|
+
this._alive();
|
|
165
|
+
return NativeUfsecp.taprootTweakSeckey(this._h, privkeyHex, merkleRootHex);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── Internal ─────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
_alive() {
|
|
171
|
+
if (this._h == null) throw new UfsecpError('context', 'already destroyed');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export { UfsecpContext, UfsecpError };
|
|
176
|
+
export default UfsecpContext;
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-ufsecp",
|
|
3
|
+
"version": "3.10.0",
|
|
4
|
+
"description": "React Native bindings for UltrafastSecp256k1 — high-performance secp256k1 ECC (ufsecp C ABI v1)",
|
|
5
|
+
"main": "lib/ufsecp.js",
|
|
6
|
+
"react-native": "lib/ufsecp.js",
|
|
7
|
+
"files": ["lib/ufsecp.js", "android/", "ios/", "react-native-ufsecp.podspec", "README.md"],
|
|
8
|
+
"scripts": {},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"react-native", "secp256k1", "bitcoin", "crypto", "ecdsa",
|
|
11
|
+
"schnorr", "taproot", "ufsecp"
|
|
12
|
+
],
|
|
13
|
+
"author": "UltrafastSecp256k1",
|
|
14
|
+
"license": "AGPL-3.0-only",
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"react": ">=18.0.0",
|
|
17
|
+
"react-native": ">=0.71.0"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/AvraSasmo/UltrafastSecp256k1"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/AvraSasmo/UltrafastSecp256k1"
|
|
24
|
+
}
|