UltrafastSecp256k1 3.50.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
ufsecp_gpu_impl.cpp
Go to the documentation of this file.
1/* ============================================================================
2 * UltrafastSecp256k1 -- GPU C ABI Implementation
3 * ============================================================================
4 * Implements the ufsecp_gpu_* functions declared in ufsecp_gpu.h.
5 * Delegates all work to gpu::GpuBackend instances from gpu_backend.hpp.
6 *
7 * Build with: -DUFSECP_BUILDING (sets dllexport on Windows)
8 * ============================================================================ */
9
10#ifndef UFSECP_BUILDING
11#define UFSECP_BUILDING
12#endif
13
14#include "ufsecp_gpu.h"
15#include "../../gpu/include/gpu_backend.hpp"
16
17#include <cstring>
18#include <cstdlib>
19#include <memory>
20#include <mutex>
21#include <new>
22#include <limits>
23
24using namespace secp256k1::gpu;
25
26/* Hard upper bound on user-supplied GPU batch counts.
27 * Prevents hostile callers from triggering multi-GB allocations. */
28static constexpr std::size_t kMaxGpuBatchN = std::size_t{1} << 26; /* 64 M */
29
30/* Macro: catch C++ exceptions at the extern "C" boundary. */
31#define UFSECP_GPU_CATCH \
32 catch (const std::bad_alloc&) { return UFSECP_ERR_GPU_MEMORY; } \
33 catch (...) { return UFSECP_ERR_INTERNAL; }
34
35/* ===========================================================================
36 * Opaque GPU context definition
37 * =========================================================================== */
38
40 std::unique_ptr<GpuBackend> backend;
41 uint32_t backend_id;
42 uint32_t device_index;
43};
44
45/* ===========================================================================
46 * Internal helpers
47 * =========================================================================== */
48
49static inline ufsecp_error_t to_abi_error(GpuError e) {
50 return static_cast<ufsecp_error_t>(static_cast<int>(e));
51}
52
53static bool has_valid_compressed_pubkeys(const uint8_t* pubkeys33, size_t count) {
54 for (size_t index = 0; index < count; ++index) {
55 const uint8_t prefix = pubkeys33[index * 33];
56 if (prefix != 0x02 && prefix != 0x03) {
57 return false;
58 }
59 }
60 return true;
61}
62
63static bool has_valid_uncompressed_pubkeys(const uint8_t* pubkeys65, size_t count) {
64 for (size_t index = 0; index < count; ++index) {
65 if (pubkeys65[index * 65] != 0x04) {
66 return false;
67 }
68 }
69 return true;
70}
71
72static bool has_valid_recovery_ids(const int* recids, size_t count) {
73 for (size_t index = 0; index < count; ++index) {
74 if (recids[index] < 0 || recids[index] > 3) {
75 return false;
76 }
77 }
78 return true;
79}
80
81static bool has_valid_bip324_sizes(const uint32_t* sizes, size_t count, uint32_t max_payload) {
82 for (size_t index = 0; index < count; ++index) {
83 if (sizes[index] > max_payload) {
84 return false;
85 }
86 }
87 return true;
88}
89
90static bool has_valid_bulletproof_prefixes(const uint8_t* proofs324, size_t count) {
91 for (size_t index = 0; index < count; ++index) {
92 const uint8_t* proof = proofs324 + (index * 324);
93 if (proof[0] != 0x04 || proof[65] != 0x04 || proof[130] != 0x04 || proof[195] != 0x04) {
94 return false;
95 }
96 }
97 return true;
98}
99
100/* ===========================================================================
101 * Backend & device discovery
102 * =========================================================================== */
103
104uint32_t ufsecp_gpu_backend_count(uint32_t* backend_ids_out, uint32_t max_ids) {
105 try {
106 const uint32_t count = backend_count();
107 if (backend_ids_out && max_ids > 0) {
108 backend_ids(backend_ids_out, max_ids);
109 }
110 return count;
111 } catch (...) { return 0; }
112}
113
114const char* ufsecp_gpu_backend_name(uint32_t bid) {
115 switch (bid) {
116 case 1: return "CUDA";
117 case 2: return "OpenCL";
118 case 3: return "Metal";
119 default: return "none";
120 }
121}
122
123int ufsecp_gpu_is_available(uint32_t bid) {
124 try { return is_available(bid) ? 1 : 0; }
125 catch (...) { return 0; }
126}
127
128uint32_t ufsecp_gpu_device_count(uint32_t bid) {
129 try {
130 auto b = create_backend(bid);
131 if (!b) return 0;
132 return b->device_count();
133 } catch (...) { return 0; }
134}
135
137 uint32_t bid, uint32_t device_index,
138 ufsecp_gpu_device_info_t* info_out)
139{
140 if (!info_out) return UFSECP_ERR_NULL_ARG;
141 try {
142 auto b = create_backend(bid);
143 if (!b) return UFSECP_ERR_GPU_UNAVAILABLE;
144
145 DeviceInfo di;
146 auto err = b->device_info(device_index, di);
147 if (err != GpuError::Ok) return to_abi_error(err);
148
149 std::memcpy(info_out->name, di.name, sizeof(info_out->name));
150 info_out->global_mem_bytes = di.global_mem_bytes;
151 info_out->compute_units = di.compute_units;
152 info_out->max_clock_mhz = di.max_clock_mhz;
153 info_out->max_threads_per_block = di.max_threads_per_block;
154 info_out->backend_id = di.backend_id;
155 info_out->device_index = di.device_index;
156 return UFSECP_OK;
158}
159
160/* ===========================================================================
161 * GPU context lifecycle
162 * =========================================================================== */
163
165 ufsecp_gpu_ctx** ctx_out,
166 uint32_t bid,
167 uint32_t device_index)
168{
169 if (!ctx_out) return UFSECP_ERR_NULL_ARG;
170 *ctx_out = nullptr;
171 try {
172 auto backend = create_backend(bid);
173 if (!backend) return UFSECP_ERR_GPU_UNAVAILABLE;
174
175 auto err = backend->init(device_index);
176 if (err != GpuError::Ok) return to_abi_error(err);
177
178 auto* ctx = new (std::nothrow) ufsecp_gpu_ctx;
179 if (!ctx) return UFSECP_ERR_INTERNAL;
180
181 ctx->backend = std::move(backend);
182 ctx->backend_id = bid;
183 ctx->device_index = device_index;
184 *ctx_out = ctx;
185 return UFSECP_OK;
187}
188
190 if (ctx) {
191 ctx->backend.reset();
192 delete ctx;
193 }
194}
195
197 if (!ctx) return UFSECP_ERR_NULL_ARG;
198 return to_abi_error(ctx->backend->last_error());
199}
200
202 if (!ctx) return "NULL GPU context";
203 return ctx->backend->last_error_msg();
204}
205
206/* ===========================================================================
207 * First-wave GPU batch operations
208 * =========================================================================== */
209
211 ufsecp_gpu_ctx* ctx,
212 const uint8_t* scalars32,
213 size_t count,
214 uint8_t* out_pubkeys33)
215{
216 if (!ctx) return UFSECP_ERR_NULL_ARG;
217 if (count == 0) return UFSECP_OK;
218 if (!scalars32 || !out_pubkeys33) return UFSECP_ERR_NULL_ARG;
219 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
220 try {
221 return to_abi_error(
222 ctx->backend->generator_mul_batch(scalars32, count, out_pubkeys33));
224}
225
227 ufsecp_gpu_ctx* ctx,
228 const uint8_t* msg_hashes32,
229 const uint8_t* pubkeys33,
230 const uint8_t* sigs64,
231 size_t count,
232 uint8_t* out_results)
233{
234 if (!ctx) return UFSECP_ERR_NULL_ARG;
235 if (count == 0) return UFSECP_OK;
236 if (!msg_hashes32 || !pubkeys33 || !sigs64 || !out_results) {
237 return UFSECP_ERR_NULL_ARG;
238 }
239 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
240 try {
241 return to_abi_error(
242 ctx->backend->ecdsa_verify_batch(
243 msg_hashes32, pubkeys33, sigs64, count, out_results));
245}
246
248 ufsecp_gpu_ctx* ctx,
249 const uint8_t* msg_hashes32,
250 const uint8_t* pubkeys_x32,
251 const uint8_t* sigs64,
252 size_t count,
253 uint8_t* out_results)
254{
255 if (!ctx) return UFSECP_ERR_NULL_ARG;
256 if (count == 0) return UFSECP_OK;
257 if (!msg_hashes32 || !pubkeys_x32 || !sigs64 || !out_results) {
258 return UFSECP_ERR_NULL_ARG;
259 }
260 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
261 try {
262 return to_abi_error(
263 ctx->backend->schnorr_verify_batch(
264 msg_hashes32, pubkeys_x32, sigs64, count, out_results));
266}
267
269 ufsecp_gpu_ctx* ctx,
270 const uint8_t* privkeys32,
271 const uint8_t* peer_pubkeys33,
272 size_t count,
273 uint8_t* out_secrets32)
274{
275 if (!ctx) return UFSECP_ERR_NULL_ARG;
276 if (count == 0) return UFSECP_OK;
277 if (!privkeys32 || !peer_pubkeys33 || !out_secrets32) {
278 return UFSECP_ERR_NULL_ARG;
279 }
280 if (!has_valid_compressed_pubkeys(peer_pubkeys33, count)) {
282 }
283 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
284 try {
285 return to_abi_error(
286 ctx->backend->ecdh_batch(
287 privkeys32, peer_pubkeys33, count, out_secrets32));
289}
290
292 ufsecp_gpu_ctx* ctx,
293 const uint8_t* pubkeys33,
294 size_t count,
295 uint8_t* out_hash160)
296{
297 if (!ctx) return UFSECP_ERR_NULL_ARG;
298 if (count == 0) return UFSECP_OK;
299 if (!pubkeys33 || !out_hash160) return UFSECP_ERR_NULL_ARG;
300 if (!has_valid_compressed_pubkeys(pubkeys33, count)) {
302 }
303 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
304 try {
305 return to_abi_error(
306 ctx->backend->hash160_pubkey_batch(pubkeys33, count, out_hash160));
308}
309
311 ufsecp_gpu_ctx* ctx,
312 const uint8_t* scalars32,
313 const uint8_t* points33,
314 size_t n,
315 uint8_t* out_result33)
316{
317 if (!ctx) return UFSECP_ERR_NULL_ARG;
318 if (n == 0) return UFSECP_OK;
319 if (!scalars32 || !points33 || !out_result33) return UFSECP_ERR_NULL_ARG;
320 if (n > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
321 try {
322 return to_abi_error(
323 ctx->backend->msm(scalars32, points33, n, out_result33));
325}
326
328 ufsecp_gpu_ctx* ctx,
329 const uint8_t* z_i32,
330 const uint8_t* D_i33,
331 const uint8_t* E_i33,
332 const uint8_t* Y_i33,
333 const uint8_t* rho_i32,
334 const uint8_t* lambda_ie32,
335 const uint8_t* negate_R,
336 const uint8_t* negate_key,
337 size_t count,
338 uint8_t* out_results)
339{
340 if (!ctx) return UFSECP_ERR_NULL_ARG;
341 if (count == 0) return UFSECP_OK;
342 if (!z_i32 || !D_i33 || !E_i33 || !Y_i33 || !rho_i32 ||
343 !lambda_ie32 || !negate_R || !negate_key || !out_results) {
344 return UFSECP_ERR_NULL_ARG;
345 }
346 if (!has_valid_compressed_pubkeys(D_i33, count) ||
347 !has_valid_compressed_pubkeys(E_i33, count) ||
348 !has_valid_compressed_pubkeys(Y_i33, count)) {
350 }
351 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
352 try {
353 return to_abi_error(
354 ctx->backend->frost_verify_partial_batch(
355 z_i32, D_i33, E_i33, Y_i33, rho_i32, lambda_ie32,
356 negate_R, negate_key, count, out_results));
358}
359
361 ufsecp_gpu_ctx* ctx,
362 const uint8_t* msg_hashes32,
363 const uint8_t* sigs64,
364 const int* recids,
365 size_t count,
366 uint8_t* out_pubkeys33,
367 uint8_t* out_valid)
368{
369 if (!ctx) return UFSECP_ERR_NULL_ARG;
370 if (count == 0) return UFSECP_OK;
371 if (!msg_hashes32 || !sigs64 || !recids || !out_pubkeys33 || !out_valid) {
372 return UFSECP_ERR_NULL_ARG;
373 }
374 if (!has_valid_recovery_ids(recids, count)) {
376 }
377 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
378 try {
379 return to_abi_error(
380 ctx->backend->ecrecover_batch(
381 msg_hashes32, sigs64, recids, count, out_pubkeys33, out_valid));
383}
384
385/* ===========================================================================
386 * ZK proof batch operations
387 * =========================================================================== */
388
390 ufsecp_gpu_ctx* ctx,
391 const uint8_t* proofs64,
392 const uint8_t* pubkeys65,
393 const uint8_t* messages32,
394 size_t count,
395 uint8_t* out_results)
396{
397 if (!ctx) return UFSECP_ERR_NULL_ARG;
398 if (count == 0) return UFSECP_OK;
399 if (!proofs64 || !pubkeys65 || !messages32 || !out_results)
400 return UFSECP_ERR_NULL_ARG;
401 if (!has_valid_uncompressed_pubkeys(pubkeys65, count)) {
403 }
404 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
405 try {
406 return to_abi_error(
407 ctx->backend->zk_knowledge_verify_batch(
408 proofs64, pubkeys65, messages32, count, out_results));
410}
411
413 ufsecp_gpu_ctx* ctx,
414 const uint8_t* proofs64,
415 const uint8_t* G_pts65,
416 const uint8_t* H_pts65,
417 const uint8_t* P_pts65,
418 const uint8_t* Q_pts65,
419 size_t count,
420 uint8_t* out_results)
421{
422 if (!ctx) return UFSECP_ERR_NULL_ARG;
423 if (count == 0) return UFSECP_OK;
424 if (!proofs64 || !G_pts65 || !H_pts65 || !P_pts65 || !Q_pts65 || !out_results)
425 return UFSECP_ERR_NULL_ARG;
426 if (!has_valid_uncompressed_pubkeys(G_pts65, count) ||
427 !has_valid_uncompressed_pubkeys(H_pts65, count) ||
428 !has_valid_uncompressed_pubkeys(P_pts65, count) ||
429 !has_valid_uncompressed_pubkeys(Q_pts65, count)) {
431 }
432 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
433 try {
434 return to_abi_error(
435 ctx->backend->zk_dleq_verify_batch(
436 proofs64, G_pts65, H_pts65, P_pts65, Q_pts65, count, out_results));
438}
439
441 ufsecp_gpu_ctx* ctx,
442 const uint8_t* proofs324,
443 const uint8_t* commitments65,
444 const uint8_t* H_generator65,
445 size_t count,
446 uint8_t* out_results)
447{
448 if (!ctx) return UFSECP_ERR_NULL_ARG;
449 if (count == 0) return UFSECP_OK;
450 if (!proofs324 || !commitments65 || !H_generator65 || !out_results)
451 return UFSECP_ERR_NULL_ARG;
452 if (!has_valid_bulletproof_prefixes(proofs324, count) ||
453 !has_valid_uncompressed_pubkeys(commitments65, count) ||
454 !has_valid_uncompressed_pubkeys(H_generator65, 1)) {
456 }
457 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
458 try {
459 return to_abi_error(
460 ctx->backend->bulletproof_verify_batch(
461 proofs324, commitments65, H_generator65, count, out_results));
463}
464
465/* ===========================================================================
466 * BIP-324 transport batch operations
467 * =========================================================================== */
468
470 ufsecp_gpu_ctx* ctx,
471 const uint8_t* keys32,
472 const uint8_t* nonces12,
473 const uint8_t* plaintexts,
474 const uint32_t* sizes,
475 uint32_t max_payload,
476 size_t count,
477 uint8_t* wire_out)
478{
479 if (!ctx) return UFSECP_ERR_NULL_ARG;
480 if (count == 0) return UFSECP_OK;
481 if (!keys32 || !nonces12 || !plaintexts || !sizes || !wire_out)
482 return UFSECP_ERR_NULL_ARG;
483 if (!has_valid_bip324_sizes(sizes, count, max_payload)) {
485 }
486 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
487 try {
488 return to_abi_error(
489 ctx->backend->bip324_aead_encrypt_batch(
490 keys32, nonces12, plaintexts, sizes, max_payload, count, wire_out));
492}
493
495 ufsecp_gpu_ctx* ctx,
496 const uint8_t* keys32,
497 const uint8_t* nonces12,
498 const uint8_t* wire_in,
499 const uint32_t* sizes,
500 uint32_t max_payload,
501 size_t count,
502 uint8_t* plaintext_out,
503 uint8_t* out_valid)
504{
505 if (!ctx) return UFSECP_ERR_NULL_ARG;
506 if (count == 0) return UFSECP_OK;
507 if (!keys32 || !nonces12 || !wire_in || !sizes || !plaintext_out || !out_valid)
508 return UFSECP_ERR_NULL_ARG;
509 if (!has_valid_bip324_sizes(sizes, count, max_payload)) {
511 }
512 if (count > kMaxGpuBatchN) return UFSECP_ERR_BAD_INPUT;
513 try {
514 return to_abi_error(
515 ctx->backend->bip324_aead_decrypt_batch(
516 keys32, nonces12, wire_in, sizes, max_payload, count,
517 plaintext_out, out_valid));
519}
520
521/* ===========================================================================
522 * GPU error string
523 * =========================================================================== */
524
526 switch (err) {
527 case UFSECP_OK: return "OK";
528 case UFSECP_ERR_NULL_ARG: return "NULL argument";
529 case UFSECP_ERR_BAD_KEY: return "invalid private key";
530 case UFSECP_ERR_BAD_PUBKEY: return "invalid public key";
531 case UFSECP_ERR_BAD_SIG: return "invalid signature";
532 case UFSECP_ERR_BAD_INPUT: return "malformed input";
533 case UFSECP_ERR_VERIFY_FAIL: return "verification failed";
534 case UFSECP_ERR_ARITH: return "arithmetic error";
535 case UFSECP_ERR_SELFTEST: return "self-test failed";
536 case UFSECP_ERR_INTERNAL: return "internal error";
537 case UFSECP_ERR_BUF_TOO_SMALL: return "buffer too small";
538 case UFSECP_ERR_GPU_UNAVAILABLE: return "GPU backend unavailable";
539 case UFSECP_ERR_GPU_DEVICE: return "GPU device error";
540 case UFSECP_ERR_GPU_LAUNCH: return "GPU kernel launch failed";
541 case UFSECP_ERR_GPU_MEMORY: return "GPU memory error";
542 case UFSECP_ERR_GPU_UNSUPPORTED: return "operation not supported on this GPU backend";
543 case UFSECP_ERR_GPU_BACKEND: return "GPU backend driver error";
544 case UFSECP_ERR_GPU_QUEUE: return "GPU command queue error";
545 default: return "unknown error";
546 }
547}
std::unique_ptr< GpuBackend > backend
#define UFSECP_ERR_INTERNAL
#define UFSECP_ERR_BAD_PUBKEY
#define UFSECP_ERR_NULL_ARG
#define UFSECP_ERR_VERIFY_FAIL
#define UFSECP_ERR_SELFTEST
#define UFSECP_ERR_BAD_SIG
#define UFSECP_ERR_BUF_TOO_SMALL
#define UFSECP_ERR_BAD_INPUT
#define UFSECP_OK
#define UFSECP_ERR_ARITH
int ufsecp_error_t
#define UFSECP_ERR_BAD_KEY
#define UFSECP_ERR_GPU_UNSUPPORTED
Definition ufsecp_gpu.h:76
#define UFSECP_ERR_GPU_UNAVAILABLE
Definition ufsecp_gpu.h:72
#define UFSECP_ERR_GPU_BACKEND
Definition ufsecp_gpu.h:77
#define UFSECP_ERR_GPU_MEMORY
Definition ufsecp_gpu.h:75
#define UFSECP_ERR_GPU_LAUNCH
Definition ufsecp_gpu.h:74
#define UFSECP_ERR_GPU_DEVICE
Definition ufsecp_gpu.h:73
#define UFSECP_ERR_GPU_QUEUE
Definition ufsecp_gpu.h:78
ufsecp_error_t ufsecp_gpu_schnorr_verify_batch(ufsecp_gpu_ctx *ctx, const uint8_t *msg_hashes32, const uint8_t *pubkeys_x32, const uint8_t *sigs64, size_t count, uint8_t *out_results)
static bool has_valid_recovery_ids(const int *recids, size_t count)
ufsecp_error_t ufsecp_gpu_ecdsa_verify_batch(ufsecp_gpu_ctx *ctx, const uint8_t *msg_hashes32, const uint8_t *pubkeys33, const uint8_t *sigs64, size_t count, uint8_t *out_results)
ufsecp_error_t ufsecp_gpu_zk_dleq_verify_batch(ufsecp_gpu_ctx *ctx, const uint8_t *proofs64, const uint8_t *G_pts65, const uint8_t *H_pts65, const uint8_t *P_pts65, const uint8_t *Q_pts65, size_t count, uint8_t *out_results)
ufsecp_error_t ufsecp_gpu_device_info(uint32_t bid, uint32_t device_index, ufsecp_gpu_device_info_t *info_out)
ufsecp_error_t ufsecp_gpu_ctx_create(ufsecp_gpu_ctx **ctx_out, uint32_t bid, uint32_t device_index)
static constexpr std::size_t kMaxGpuBatchN
static bool has_valid_bip324_sizes(const uint32_t *sizes, size_t count, uint32_t max_payload)
#define UFSECP_GPU_CATCH
const char * ufsecp_gpu_error_str(ufsecp_error_t err)
ufsecp_error_t ufsecp_gpu_last_error(const ufsecp_gpu_ctx *ctx)
ufsecp_error_t ufsecp_gpu_ecdh_batch(ufsecp_gpu_ctx *ctx, const uint8_t *privkeys32, const uint8_t *peer_pubkeys33, size_t count, uint8_t *out_secrets32)
ufsecp_error_t ufsecp_gpu_bip324_aead_decrypt_batch(ufsecp_gpu_ctx *ctx, const uint8_t *keys32, const uint8_t *nonces12, const uint8_t *wire_in, const uint32_t *sizes, uint32_t max_payload, size_t count, uint8_t *plaintext_out, uint8_t *out_valid)
ufsecp_error_t ufsecp_gpu_frost_verify_partial_batch(ufsecp_gpu_ctx *ctx, const uint8_t *z_i32, const uint8_t *D_i33, const uint8_t *E_i33, const uint8_t *Y_i33, const uint8_t *rho_i32, const uint8_t *lambda_ie32, const uint8_t *negate_R, const uint8_t *negate_key, size_t count, uint8_t *out_results)
static bool has_valid_bulletproof_prefixes(const uint8_t *proofs324, size_t count)
ufsecp_error_t ufsecp_gpu_bip324_aead_encrypt_batch(ufsecp_gpu_ctx *ctx, const uint8_t *keys32, const uint8_t *nonces12, const uint8_t *plaintexts, const uint32_t *sizes, uint32_t max_payload, size_t count, uint8_t *wire_out)
ufsecp_error_t ufsecp_gpu_ecrecover_batch(ufsecp_gpu_ctx *ctx, const uint8_t *msg_hashes32, const uint8_t *sigs64, const int *recids, size_t count, uint8_t *out_pubkeys33, uint8_t *out_valid)
static ufsecp_error_t to_abi_error(GpuError e)
const char * ufsecp_gpu_backend_name(uint32_t bid)
ufsecp_error_t ufsecp_gpu_bulletproof_verify_batch(ufsecp_gpu_ctx *ctx, const uint8_t *proofs324, const uint8_t *commitments65, const uint8_t *H_generator65, size_t count, uint8_t *out_results)
ufsecp_error_t ufsecp_gpu_zk_knowledge_verify_batch(ufsecp_gpu_ctx *ctx, const uint8_t *proofs64, const uint8_t *pubkeys65, const uint8_t *messages32, size_t count, uint8_t *out_results)
uint32_t ufsecp_gpu_device_count(uint32_t bid)
ufsecp_error_t ufsecp_gpu_msm(ufsecp_gpu_ctx *ctx, const uint8_t *scalars32, const uint8_t *points33, size_t n, uint8_t *out_result33)
ufsecp_error_t ufsecp_gpu_hash160_pubkey_batch(ufsecp_gpu_ctx *ctx, const uint8_t *pubkeys33, size_t count, uint8_t *out_hash160)
uint32_t ufsecp_gpu_backend_count(uint32_t *backend_ids_out, uint32_t max_ids)
const char * ufsecp_gpu_last_error_msg(const ufsecp_gpu_ctx *ctx)
static bool has_valid_compressed_pubkeys(const uint8_t *pubkeys33, size_t count)
int ufsecp_gpu_is_available(uint32_t bid)
static bool has_valid_uncompressed_pubkeys(const uint8_t *pubkeys65, size_t count)
ufsecp_error_t ufsecp_gpu_generator_mul_batch(ufsecp_gpu_ctx *ctx, const uint8_t *scalars32, size_t count, uint8_t *out_pubkeys33)
void ufsecp_gpu_ctx_destroy(ufsecp_gpu_ctx *ctx)