* fix: CUDA RIPEMD160 r2 table + ECDH y-parity + GPU-side conversion - hash160.cuh: fix transposed r2[46..47] (was 13,4 -> correct 4,13) - ecdh.cuh: compute y-parity for SHA-256(02/03||x) to match CPU ecdh_compute - gpu_backend_cuda.cu: GPU-side Jacobian<->compressed conversion via batch_jac_to_compressed_kernel and batch_compressed_to_jac_kernel; fix bytes_to_scalar/bytes_to_field byte order; add msm_reduce_and_compress_kernel for GPU-side MSM accumulation; remove dead host-side FieldElement code - gpu/CMakeLists.txt: remove gpu_backend_cuda_host.cpp - bindings/rust: add hex dev-dep, fix abi_version() call in smoke test - bindings/nodejs: add koffi-based smoke test (Node 22 compatible) GPU test results: 154 passed, 0 failed gpu_abi_gate: 44/44 gpu_ops_equivalence: 55/55 gpu_host_api_negative: 55/55 Binding tests: Rust (cargo test): 13/13 Node.js (koffi): 12/12 * examples: add 6-language example suite (C, Python, Rust, Node.js, Go, Java) Comprehensive CPU + GPU examples for all supported binding languages: - C: Direct C ABI calls, 16 demo sections (CPU + GPU) - Python: ctypes + Ufsecp wrapper, 14 sections (CPU + GPU) - Rust: Safe ufsecp crate wrapper, 9 sections (CPU only) - Node.js: koffi FFI, 12 sections (CPU + GPU) - Go: Pure cgo, 14 sections (CPU + GPU) - Java: JNA FFI, 14 sections (CPU + GPU) Each example covers: key generation, ECDSA (sign/verify/recover/DER), Schnorr (BIP-340), ECDH, hashing (SHA-256, Hash160), Bitcoin addresses (P2PKH, P2WPKH, P2TR), WIF encoding, BIP-32 HD derivation, and Taproot. GPU examples demonstrate: backend discovery, batch key generation, batch ECDSA verify, batch Hash160, and multi-scalar multiplication. All 6 examples tested and verified against RTX 5060 Ti (CUDA + OpenCL). * examples: add GPU+Pedersen to all 6 languages, expand README - Rust: add GPU sections [10]-[15] + Pedersen, add GPU+Pedersen FFI to ufsecp-sys - Node.js: add BIP-32, Taproot, Pedersen sections [8]-[10], renumber GPU [11]-[15] - Python: add Pedersen section [10] via direct ctypes - Go: add Pedersen section [10] via cgo - Java: add Pedersen JNA declarations + section [10] - Rust safe wrapper: add Context::as_ptr() for GPU FFI access - examples/README.md: comprehensive rewrite with all 6 languages, build/run instructions, feature coverage matrix, embedded platforms, troubleshooting - README.md: add Highlights, Performance, Architecture stack diagram, Hardware Compatibility table (16 platforms), Embedded Targets, Examples index, Use Cases section; expand Architecture with bindings + source tree All 6 examples tested: C 16/16, Rust 15/15, Python 15/15, Node.js 15/15, Go 15/15, Java 15/15 -- all sections pass including GPU operations. * docs: fix GPU backend maturity labels, add docs links, clean repo hygiene - .gitignore: add rules for example build artifacts (binaries, node_modules, target/, Cargo.lock, package-lock.json, .class files) - README.md: fix GPU overclaims -- OpenCL is partial (4/6 ops), Metal is experimental (discovery only); add GPU API, Validation Matrix, Feature Maturity, Supported Guarantees, Examples to Documentation table - FEATURE_MATURITY.md: fix contradictions -- ECDSA/Schnorr verify GPU column corrected from 'all 3' to 'CUDA' (OpenCL UNSUPPORTED per ufsecp_gpu.h); BIP-32 HD GPU corrected from 'all 3' to '-' (no GPU C ABI path) - GPU_VALIDATION_MATRIX.md: add CI and Local Verification table documenting that CUDA/OpenCL tests pass locally (RTX 5060 Ti), GH Actions lacks GPU runners; all 4 GPU C ABI tests (gpu_abi_gate, gpu_ops_equivalence, gpu_host_api_negative, gpu_backend_matrix) confirmed in ctest matrix (49 total) and pass * fix: Metal device index validation + canonical GPU audit presets - Metal backend: reject out-of-range device_index in init() before creating MetalRuntime (fixes gpu_host_api_negative on macOS CI) - CMakePresets.json: add cuda-audit, cuda-audit-5060ti configure/build presets and testPresets for reproducible 49-test GPU verification - docs/BUILDING.md: document canonical GPU audit build path - docs/LOCAL_CI.md: add GPU proof-path quick-reference - docs/README.md: update docs index entry --------- Co-authored-by: shrec <shrec@users.noreply.github.com>
379 lines
16 KiB
JavaScript
379 lines
16 KiB
JavaScript
/**
|
|
* UltrafastSecp256k1 -- Node.js Example (CPU + GPU)
|
|
*
|
|
* Demonstrates the ufsecp C ABI via koffi FFI: key ops, ECDSA, Schnorr,
|
|
* ECDH, hashing, Bitcoin addresses, WIF, BIP-32, Taproot, Pedersen,
|
|
* and GPU batch operations.
|
|
*
|
|
* Requirements:
|
|
* npm install koffi
|
|
*
|
|
* Run:
|
|
* UFSECP_LIB=../../build-linux/include/ufsecp/libufsecp.so node example.js
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const koffi = require('koffi');
|
|
const path = require('path');
|
|
|
|
// ── Load Library ─────────────────────────────────────────────────────────
|
|
|
|
const LIB_PATH = process.env.UFSECP_LIB ||
|
|
path.join(__dirname, '..', '..', 'build-linux', 'include', 'ufsecp', 'libufsecp.so');
|
|
|
|
const lib = koffi.load(LIB_PATH);
|
|
|
|
// ── CPU Function Declarations ────────────────────────────────────────────
|
|
|
|
const UFSECP_OK = 0;
|
|
|
|
// Context
|
|
const ctx_create = lib.func('int ufsecp_ctx_create(_Out_ void **ctx)');
|
|
const ctx_destroy = lib.func('void ufsecp_ctx_destroy(void *ctx)');
|
|
const abi_version = lib.func('uint32_t ufsecp_abi_version()');
|
|
const version_string = lib.func('const char *ufsecp_version_string()');
|
|
const error_str = lib.func('const char *ufsecp_error_str(int code)');
|
|
|
|
// Keys
|
|
const seckey_verify = lib.func('int ufsecp_seckey_verify(void *ctx, const uint8_t *k32)');
|
|
const pubkey_create = lib.func('int ufsecp_pubkey_create(void *ctx, const uint8_t *sk32, uint8_t *pk33)');
|
|
const pubkey_xonly = lib.func('int ufsecp_pubkey_xonly(void *ctx, const uint8_t *sk32, uint8_t *xo32)');
|
|
|
|
// ECDSA
|
|
const ecdsa_sign = lib.func('int ufsecp_ecdsa_sign(void *ctx, const uint8_t *msg32, const uint8_t *sk32, uint8_t *sig64)');
|
|
const ecdsa_verify = lib.func('int ufsecp_ecdsa_verify(void *ctx, const uint8_t *msg32, const uint8_t *sig64, const uint8_t *pk33)');
|
|
const ecdsa_sign_rec = lib.func('int ufsecp_ecdsa_sign_recoverable(void *ctx, const uint8_t *msg32, const uint8_t *sk32, uint8_t *sig64, _Out_ int *recid)');
|
|
const ecdsa_recover = lib.func('int ufsecp_ecdsa_recover(void *ctx, const uint8_t *msg32, const uint8_t *sig64, int recid, uint8_t *pk33)');
|
|
const ecdsa_to_der = lib.func('int ufsecp_ecdsa_sig_to_der(void *ctx, const uint8_t *sig64, uint8_t *der, _Inout_ size_t *len)');
|
|
|
|
// Schnorr
|
|
const schnorr_sign = lib.func('int ufsecp_schnorr_sign(void *ctx, const uint8_t *msg32, const uint8_t *sk32, const uint8_t *aux32, uint8_t *sig64)');
|
|
const schnorr_verify = lib.func('int ufsecp_schnorr_verify(void *ctx, const uint8_t *msg32, const uint8_t *sig64, const uint8_t *xo32)');
|
|
|
|
// ECDH
|
|
const ecdh = lib.func('int ufsecp_ecdh(void *ctx, const uint8_t *sk32, const uint8_t *pk33, uint8_t *secret32)');
|
|
|
|
// Hashing
|
|
const sha256 = lib.func('int ufsecp_sha256(const uint8_t *data, size_t len, uint8_t *digest32)');
|
|
const hash160 = lib.func('int ufsecp_hash160(const uint8_t *data, size_t len, uint8_t *digest20)');
|
|
|
|
// Addresses
|
|
const addr_p2pkh = lib.func('int ufsecp_addr_p2pkh(void *ctx, const uint8_t *pk33, int net, uint8_t *addr, _Inout_ size_t *len)');
|
|
const addr_p2wpkh = lib.func('int ufsecp_addr_p2wpkh(void *ctx, const uint8_t *pk33, int net, uint8_t *addr, _Inout_ size_t *len)');
|
|
const addr_p2tr = lib.func('int ufsecp_addr_p2tr(void *ctx, const uint8_t *xo32, int net, uint8_t *addr, _Inout_ size_t *len)');
|
|
|
|
// WIF
|
|
const wif_encode = lib.func('int ufsecp_wif_encode(void *ctx, const uint8_t *sk32, int comp, int net, uint8_t *wif, _Inout_ size_t *len)');
|
|
|
|
// BIP-32
|
|
const bip32_master = lib.func('int ufsecp_bip32_master(void *ctx, const uint8_t *seed, size_t seed_len, uint8_t *key82)');
|
|
const bip32_derive = lib.func('int ufsecp_bip32_derive_path(void *ctx, const uint8_t *master82, const char *path, uint8_t *key82)');
|
|
const bip32_privkey = lib.func('int ufsecp_bip32_privkey(void *ctx, const uint8_t *key82, uint8_t *priv32)');
|
|
const bip32_pubkey = lib.func('int ufsecp_bip32_pubkey(void *ctx, const uint8_t *key82, uint8_t *pub33)');
|
|
|
|
// Taproot
|
|
const taproot_output = lib.func('int ufsecp_taproot_output_key(void *ctx, const uint8_t *ix32, const uint8_t *mr, uint8_t *ox32, _Out_ int *parity)');
|
|
const taproot_verify = lib.func('int ufsecp_taproot_verify(void *ctx, const uint8_t *ox32, int parity, const uint8_t *ix32, const uint8_t *mr, size_t mr_len)');
|
|
|
|
// Pedersen
|
|
const pedersen_commit = lib.func('int ufsecp_pedersen_commit(void *ctx, const uint8_t *v32, const uint8_t *b32, uint8_t *c33)');
|
|
const pedersen_verify = lib.func('int ufsecp_pedersen_verify(void *ctx, const uint8_t *c33, const uint8_t *v32, const uint8_t *b32)');
|
|
|
|
// GPU
|
|
const gpu_backend_count = lib.func('uint32_t ufsecp_gpu_backend_count(_Out_ uint32_t *ids, uint32_t max)');
|
|
const gpu_backend_name = lib.func('const char *ufsecp_gpu_backend_name(uint32_t bid)');
|
|
const gpu_is_available = lib.func('int ufsecp_gpu_is_available(uint32_t bid)');
|
|
const gpu_device_count = lib.func('uint32_t ufsecp_gpu_device_count(uint32_t bid)');
|
|
const gpu_ctx_create = lib.func('int ufsecp_gpu_ctx_create(_Out_ void **ctx, uint32_t bid, uint32_t dev)');
|
|
const gpu_ctx_destroy = lib.func('void ufsecp_gpu_ctx_destroy(void *ctx)');
|
|
const gpu_generator_mul = lib.func('int ufsecp_gpu_generator_mul_batch(void *ctx, const uint8_t *s32, size_t n, uint8_t *pk33)');
|
|
const gpu_ecdsa_verify = lib.func('int ufsecp_gpu_ecdsa_verify_batch(void *ctx, const uint8_t *msg, const uint8_t *pk, const uint8_t *sig, size_t n, uint8_t *res)');
|
|
const gpu_hash160 = lib.func('int ufsecp_gpu_hash160_pubkey_batch(void *ctx, const uint8_t *pk33, size_t n, uint8_t *h20)');
|
|
const gpu_msm = lib.func('int ufsecp_gpu_msm(void *ctx, const uint8_t *s32, const uint8_t *p33, size_t n, uint8_t *out33)');
|
|
const gpu_error_str = lib.func('const char *ufsecp_gpu_error_str(int code)');
|
|
|
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
|
|
function hex(buf) { return Buffer.from(buf).toString('hex'); }
|
|
|
|
function check(rc, op) {
|
|
if (rc !== UFSECP_OK) {
|
|
throw new Error(`${op} failed: ${error_str(rc)} (code ${rc})`);
|
|
}
|
|
}
|
|
|
|
function createCtx() {
|
|
const pp = [null];
|
|
check(ctx_create(pp), 'ctx_create');
|
|
return pp[0];
|
|
}
|
|
|
|
function getAddr(fn, ctx, key, net) {
|
|
const buf = Buffer.alloc(128);
|
|
const len = [128];
|
|
check(fn(ctx, key, net, buf, len), 'addr');
|
|
return buf.slice(0, len[0]).toString();
|
|
}
|
|
|
|
// ── Golden Vectors ───────────────────────────────────────────────────────
|
|
|
|
const PRIVKEY = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex');
|
|
const PRIVKEY2 = Buffer.from('0000000000000000000000000000000000000000000000000000000000000002', 'hex');
|
|
|
|
// ── CPU Demo ─────────────────────────────────────────────────────────────
|
|
|
|
function demoCPU() {
|
|
console.log('=== CPU Operations ===\n');
|
|
const ctx = createCtx();
|
|
|
|
// 1. Key Generation
|
|
console.log('[1] Key Generation');
|
|
const pub33 = Buffer.alloc(33);
|
|
const xonly = Buffer.alloc(32);
|
|
check(pubkey_create(ctx, PRIVKEY, pub33), 'pubkey_create');
|
|
check(pubkey_xonly(ctx, PRIVKEY, xonly), 'pubkey_xonly');
|
|
console.log(` Private key: ${hex(PRIVKEY)}`);
|
|
console.log(` Compressed (33B): ${hex(pub33)}`);
|
|
console.log(` X-only (32B): ${hex(xonly)}`);
|
|
console.log();
|
|
|
|
// 2. ECDSA
|
|
console.log('[2] ECDSA Sign / Verify (RFC 6979)');
|
|
const msg = Buffer.alloc(32);
|
|
check(sha256(Buffer.from('Hello UltrafastSecp256k1!'), 24, msg), 'sha256');
|
|
console.log(` Message hash: ${hex(msg)}`);
|
|
|
|
const sig = Buffer.alloc(64);
|
|
check(ecdsa_sign(ctx, msg, PRIVKEY, sig), 'ecdsa_sign');
|
|
console.log(` ECDSA signature: ${hex(sig)}`);
|
|
|
|
const vrc = ecdsa_verify(ctx, msg, sig, pub33);
|
|
console.log(` Verify: ${vrc === UFSECP_OK ? 'VALID' : 'INVALID'}`);
|
|
|
|
// DER encoding
|
|
const der = Buffer.alloc(72);
|
|
const derLen = [72];
|
|
check(ecdsa_to_der(ctx, sig, der, derLen), 'sig_to_der');
|
|
console.log(` DER length: ${derLen[0]} bytes`);
|
|
|
|
// Recovery
|
|
const rsig = Buffer.alloc(64);
|
|
const recid = [0];
|
|
check(ecdsa_sign_rec(ctx, msg, PRIVKEY, rsig, recid), 'sign_recoverable');
|
|
const recovered = Buffer.alloc(33);
|
|
check(ecdsa_recover(ctx, msg, rsig, recid[0], recovered), 'recover');
|
|
console.log(` Recovery: recid=${recid[0]}, match=${recovered.equals(pub33) ? 'YES' : 'NO'}`);
|
|
console.log();
|
|
|
|
// 3. Schnorr
|
|
console.log('[3] Schnorr Sign / Verify (BIP-340)');
|
|
const aux = Buffer.alloc(32);
|
|
const schnorrSig = Buffer.alloc(64);
|
|
check(schnorr_sign(ctx, msg, PRIVKEY, aux, schnorrSig), 'schnorr_sign');
|
|
console.log(` Schnorr signature: ${hex(schnorrSig)}`);
|
|
const sv = schnorr_verify(ctx, msg, schnorrSig, xonly);
|
|
console.log(` Verify: ${sv === UFSECP_OK ? 'VALID' : 'INVALID'}`);
|
|
console.log();
|
|
|
|
// 4. ECDH
|
|
console.log('[4] ECDH Key Agreement');
|
|
const pub2 = Buffer.alloc(33);
|
|
check(pubkey_create(ctx, PRIVKEY2, pub2), 'pubkey2');
|
|
const secretA = Buffer.alloc(32);
|
|
const secretB = Buffer.alloc(32);
|
|
check(ecdh(ctx, PRIVKEY, pub2, secretA), 'ecdh_a');
|
|
check(ecdh(ctx, PRIVKEY2, pub33, secretB), 'ecdh_b');
|
|
console.log(` Secret (A->B): ${hex(secretA)}`);
|
|
console.log(` Secret (B->A): ${hex(secretB)}`);
|
|
console.log(` Match: ${secretA.equals(secretB) ? 'YES' : 'NO'}`);
|
|
console.log();
|
|
|
|
// 5. Hashing
|
|
console.log('[5] Hashing');
|
|
const sha = Buffer.alloc(32);
|
|
const h160 = Buffer.alloc(20);
|
|
check(sha256(pub33, 33, sha), 'sha256_pub');
|
|
check(hash160(pub33, 33, h160), 'hash160_pub');
|
|
console.log(` SHA-256(pubkey): ${hex(sha)}`);
|
|
console.log(` Hash160(pubkey): ${hex(h160)}`);
|
|
console.log();
|
|
|
|
// 6. Bitcoin Addresses
|
|
console.log('[6] Bitcoin Addresses');
|
|
console.log(` P2PKH: ${getAddr(addr_p2pkh, ctx, pub33, 0)}`);
|
|
console.log(` P2WPKH: ${getAddr(addr_p2wpkh, ctx, pub33, 0)}`);
|
|
console.log(` P2TR: ${getAddr(addr_p2tr, ctx, xonly, 0)}`);
|
|
console.log();
|
|
|
|
// 7. WIF
|
|
console.log('[7] WIF Encoding');
|
|
const wifBuf = Buffer.alloc(128);
|
|
const wifLen = [128];
|
|
check(wif_encode(ctx, PRIVKEY, 1, 0, wifBuf, wifLen), 'wif_encode');
|
|
const wif = wifBuf.slice(0, wifLen[0]).toString();
|
|
console.log(` WIF: ${wif}`);
|
|
console.log();
|
|
|
|
// 8. BIP-32
|
|
console.log('[8] BIP-32 HD Key Derivation');
|
|
const seed = Buffer.alloc(64, 0x42);
|
|
const masterKey = Buffer.alloc(82);
|
|
check(bip32_master(ctx, seed, 64, masterKey), 'bip32_master');
|
|
const childKey = Buffer.alloc(82);
|
|
check(bip32_derive(ctx, masterKey, "m/44'/0'/0'/0/0", childKey), 'bip32_derive');
|
|
const childPriv = Buffer.alloc(32);
|
|
const childPub = Buffer.alloc(33);
|
|
check(bip32_privkey(ctx, childKey, childPriv), 'bip32_privkey');
|
|
check(bip32_pubkey(ctx, childKey, childPub), 'bip32_pubkey');
|
|
console.log(` BIP-32 child priv: ${hex(childPriv)}`);
|
|
console.log(` BIP-32 child pub: ${hex(childPub)}`);
|
|
console.log();
|
|
|
|
// 9. Taproot
|
|
console.log('[9] Taproot (BIP-341)');
|
|
const tapOut = Buffer.alloc(32);
|
|
const tapParity = [0];
|
|
check(taproot_output(ctx, xonly, null, tapOut, tapParity), 'taproot_output');
|
|
console.log(` Output key: ${hex(tapOut)}`);
|
|
console.log(` Parity: ${tapParity[0]}`);
|
|
const tapVrc = taproot_verify(ctx, tapOut, tapParity[0], xonly, null, 0);
|
|
console.log(` Verify: ${tapVrc === UFSECP_OK ? 'VALID' : 'INVALID'}`);
|
|
console.log();
|
|
|
|
// 10. Pedersen
|
|
console.log('[10] Pedersen Commitment');
|
|
const pedVal = Buffer.alloc(32); pedVal[31] = 42;
|
|
const pedBlind = Buffer.alloc(32); pedBlind[31] = 7;
|
|
const pedCommit = Buffer.alloc(33);
|
|
check(pedersen_commit(ctx, pedVal, pedBlind, pedCommit), 'pedersen_commit');
|
|
console.log(` Commitment: ${hex(pedCommit)}`);
|
|
const pvrc = pedersen_verify(ctx, pedCommit, pedVal, pedBlind);
|
|
console.log(` Verify: ${pvrc === UFSECP_OK ? 'VALID' : 'INVALID'}`);
|
|
console.log();
|
|
|
|
ctx_destroy(ctx);
|
|
}
|
|
|
|
// ── GPU Demo ─────────────────────────────────────────────────────────────
|
|
|
|
function demoGPU() {
|
|
console.log('=== GPU Operations ===\n');
|
|
|
|
// 11. Backend Discovery
|
|
console.log('[11] GPU Backend Discovery');
|
|
const bids = new Uint32Array(4);
|
|
const nBackends = gpu_backend_count(bids, 4);
|
|
console.log(` Backends compiled: ${nBackends}`);
|
|
|
|
let useBackend = 0;
|
|
for (let i = 0; i < nBackends; i++) {
|
|
const bid = bids[i];
|
|
const name = gpu_backend_name(bid);
|
|
const avail = gpu_is_available(bid);
|
|
const devs = gpu_device_count(bid);
|
|
console.log(` Backend ${bid}: ${name.padEnd(8)} available=${avail} devices=${devs}`);
|
|
if (avail && !useBackend) useBackend = bid;
|
|
}
|
|
|
|
if (!useBackend) {
|
|
console.log(' No GPU backends available -- skipping GPU demos.\n');
|
|
return;
|
|
}
|
|
|
|
// Create GPU context
|
|
const gpp = [null];
|
|
const grc = gpu_ctx_create(gpp, useBackend, 0);
|
|
if (grc !== UFSECP_OK) {
|
|
console.log(` GPU context creation failed: ${gpu_error_str(grc)}`);
|
|
return;
|
|
}
|
|
const gpu = gpp[0];
|
|
|
|
const N = 4;
|
|
|
|
// 12. Batch Key Generation
|
|
console.log('\n[12] GPU Batch Key Generation (4 keys)');
|
|
const scalars = Buffer.alloc(N * 32);
|
|
for (let i = 0; i < N; i++) scalars[i * 32 + 31] = i + 1;
|
|
|
|
const pubkeys = Buffer.alloc(N * 33);
|
|
let rc = gpu_generator_mul(gpu, scalars, N, pubkeys);
|
|
if (rc === UFSECP_OK) {
|
|
for (let i = 0; i < N; i++) {
|
|
console.log(` GPU pubkey[${i}]: ${hex(pubkeys.slice(i*33, (i+1)*33))}`);
|
|
}
|
|
} else {
|
|
console.log(` gpu_generator_mul_batch: ${gpu_error_str(rc)}`);
|
|
}
|
|
|
|
// 13. ECDSA Batch Verify
|
|
console.log('\n[13] GPU ECDSA Batch Verify');
|
|
const cpuCtx = createCtx();
|
|
const msgs = Buffer.alloc(N * 32);
|
|
const sigs = Buffer.alloc(N * 64);
|
|
const pubs = Buffer.alloc(N * 33);
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
const msgHash = Buffer.alloc(32);
|
|
sha256(Buffer.from([i]), 1, msgHash);
|
|
msgHash.copy(msgs, i * 32);
|
|
|
|
const sk = Buffer.alloc(32);
|
|
sk[31] = i + 1;
|
|
const s = Buffer.alloc(64);
|
|
ecdsa_sign(cpuCtx, msgHash, sk, s);
|
|
s.copy(sigs, i * 64);
|
|
|
|
pubkeys.copy(pubs, i * 33, i * 33, (i + 1) * 33);
|
|
}
|
|
ctx_destroy(cpuCtx);
|
|
|
|
const results = Buffer.alloc(N);
|
|
rc = gpu_ecdsa_verify(gpu, msgs, pubs, sigs, N, results);
|
|
if (rc === UFSECP_OK) {
|
|
const parts = [];
|
|
for (let i = 0; i < N; i++) parts.push(`[${i}]=${results[i] ? 'VALID' : 'INVALID'}`);
|
|
console.log(` Results: ${parts.join(' ')}`);
|
|
} else {
|
|
console.log(` gpu_ecdsa_verify_batch: ${gpu_error_str(rc)}`);
|
|
}
|
|
|
|
// 14. Hash160 Batch
|
|
console.log('\n[14] GPU Hash160 Batch');
|
|
const hashes = Buffer.alloc(N * 20);
|
|
rc = gpu_hash160(gpu, pubkeys, N, hashes);
|
|
if (rc === UFSECP_OK) {
|
|
for (let i = 0; i < N; i++) {
|
|
console.log(` Hash160[${i}]: ${hex(hashes.slice(i*20, (i+1)*20))}`);
|
|
}
|
|
} else {
|
|
console.log(` gpu_hash160_pubkey_batch: ${gpu_error_str(rc)}`);
|
|
}
|
|
|
|
// 15. MSM
|
|
console.log('\n[15] GPU Multi-Scalar Multiplication');
|
|
const msmResult = Buffer.alloc(33);
|
|
rc = gpu_msm(gpu, scalars, pubkeys, N, msmResult);
|
|
if (rc === UFSECP_OK) {
|
|
console.log(` MSM result: ${hex(msmResult)}`);
|
|
} else {
|
|
console.log(` gpu_msm: ${gpu_error_str(rc)}`);
|
|
}
|
|
|
|
gpu_ctx_destroy(gpu);
|
|
console.log();
|
|
}
|
|
|
|
// ── Main ─────────────────────────────────────────────────────────────────
|
|
|
|
console.log('UltrafastSecp256k1 -- Node.js Example');
|
|
console.log(`ABI version: ${abi_version()}`);
|
|
console.log(`Library: ${version_string()}`);
|
|
console.log();
|
|
|
|
demoCPU();
|
|
demoGPU();
|
|
|
|
console.log('All examples completed successfully.');
|