Porta la stessa strategia algoritmica della versione CPU (batch EC in
coordinate Jacobiane con Z1=1 + un'unica inversione di campo per batch
via trucco di Montgomery) su GPU NVIDIA, dove però GMP non è
disponibile: l'aritmetica a 256 bit mod il primo di secp256k1 è scritta
a mano (limb a 64 bit + __int128, riduzione veloce sfruttando
2^256 ≡ 2^32+977 mod p), e ogni thread CUDA agisce come una lane di
ricerca indipendente con la propria chiave privata iniziale casuale —
esattamente come un thread CPU, ma migliaia in parallelo invece di una
manciata.
La moltiplicazione scalare su device è un semplice double-and-add
(nessun wNAF/tabelle precalcolate come in libsecp256k1 lato host), il
principale margine di ottimizzazione futura. Il matching usa un Bloom
filter + ricerca binaria su un array di target ordinato, entrambi
caricati una volta sola in memoria device.
Aggiunge i target `make gpu` / `make gpu-info` al Makefile (NVCC_ARCH
configurabile, default sm_61/Pascal) e documenta in README che su WSL2
il driver NVIDIA arriva dall'host Windows: dentro WSL serve installare
solo il CUDA Toolkit, mai un driver.
Verificato: aritmetica di campo, moltiplicazione scalare e batch add
confrontati bit-per-bit con secp256k1_ec_pubkey_create (nessuna
discrepanza su ~30 valori di test); run end-to-end con privkey nota
trova la chiave corretta; nessun falso positivo su chiavi pubbliche
valide casuali. Su una Quadro P4000 (Pascal): ~15M keys/sec, contro
~3.5-4M keys/sec della CPU con 11 thread nello stesso ambiente.
Sostituisce le N chiamate a secp256k1_ec_pubkey_combine per batch (una
per chiave, ciascuna con una inversione di campo interna) con addizione
EC in coordinate Jacobiane calcolata a mano (formule madd specializzate
per Z1=1) e un'unica inversione modulare per l'intero batch di 256
chiavi tramite il trucco di Montgomery (GMP, già linkata via -lgmp). Il
matching ora confronta byte grezzi X||Y invece di oggetti
secp256k1_pubkey, eliminando overhead di parse/serialize nel loop caldo.
Corregge inoltre due bug:
- off-by-one nel codice combine-based originale: usava
precomputed_G[i] invece di precomputed_G[i-1], per cui una chiave
trovata sarebbe stata riportata con la privkey sbagliata
- add_to_privkey (che tratta l'array a 32 byte come 4 word native a
64 bit, non big-endian) veniva usata per ricostruire la privkey
esatta di un match nel batch; sostituita con add_small_be256, la
cui aritmetica big-endian corrisponde a quella usata da secp256k1
e dai punti EC precalcolati
Migliora inoltre il reporting a terminale:
- aggiornamento ogni 2s (PROGRESS_INTERVAL_SEC) invece di 10s
- velocità istantanea (tentativi nella finestra trascorsa, via
gettimeofday) invece della media cumulata dall'avvio, così il
numero riflette il ritmo reale anche su run lunghe
- corretto il sync dei contatori per-thread: usava una maschera
bitwise che assumeva SYNC_BATCH potenza di due (100000 non lo è),
ora un modulo esplicito
Verificato con test standalone (formule EC confrontate con
secp256k1_ec_pubkey_create indipendente), test end-to-end con privkey
nota, e nessun falso positivo su chiavi pubbliche valide casuali.