Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ src/test/fuzz/fuzz
src/test/test_factorn
src/bench/bench_factorn
src/qt/test/test_bitcoin-qt
src/contrib/testgen/gen_asert_test_vectors
contrib/bench/bench_factor_rho
contrib/bench/bench_factor_popen

# autoreconf
Makefile.in
Expand Down
6 changes: 3 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
AC_PREREQ([2.69])
define(_CLIENT_VERSION_MAJOR, 5)
define(_CLIENT_VERSION_MINOR, 3)
define(_CLIENT_VERSION_BUILD, 420)
define(_CLIENT_VERSION_MAJOR, 6)
define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_BUILD, 42)
define(_CLIENT_VERSION_RC, 0)
define(_CLIENT_VERSION_IS_RELEASE, true )
define(_COPYRIGHT_YEAR, 2026)
Expand Down
167 changes: 167 additions & 0 deletions contrib/bench/bench_factor_popen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* bench_factor_popen.c
*
* Benchmarks block solving using coreutils `factor` via popen (batch).
* Simulates solving NBLOCKS blocks at nBits=32 (regtest difficulty).
*
* Build: gcc -O2 -o bench_factor_popen bench_factor_popen.c
* Usage: poop ./bench_factor_rho ./bench_factor_popen
*/

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NBITS 32
#define NBLOCKS 200
#define TILDEN (16 * NBITS) /* candidate search radius */

/* Maximum candidates per W: 2 per jj step (+ and - direction) */
#define MAX_CANDIDATES (TILDEN * 2)

/* Small-prime sieve: check divisibility by small primes individually.
* We can't use a single product here since we don't have GMP. */
static const uint64_t SMALL_P[] = {3,5,7,11,13,17,19,23,29,31,37,41,43,47};
#define NSMALL (sizeof(SMALL_P) / sizeof(SMALL_P[0]))

static int coprime_to_small(uint64_t n)
{
for (int i = 0; i < (int)NSMALL; i++) {
if (n % SMALL_P[i] == 0)
return 0;
}
return 1;
}

/*
* Factor candidates for a single W using one batched `factor` call.
* Returns 1 if a valid semiprime was found.
*/
static int solve_one(uint64_t W, uint64_t *out_factor, int64_t *out_offset)
{
uint64_t one = (W & 1) ? 0 : 1;

/* Collect sieve-surviving candidates with their offsets */
uint64_t cands[MAX_CANDIDATES];
int64_t offsets[MAX_CANDIDATES];
int ncands = 0;

for (int jj = 0; jj < TILDEN; jj += 2) {
uint64_t N1 = W + jj + one;
uint64_t N2 = W - jj - one;

if (coprime_to_small(N1)) {
cands[ncands] = N1;
offsets[ncands] = jj + (int64_t)one;
ncands++;
}
if (coprime_to_small(N2)) {
cands[ncands] = N2;
offsets[ncands] = -jj - (int64_t)one;
ncands++;
}
}

if (ncands == 0)
return 0;

/* Build the command: "factor N1 N2 N3 ..." */
/* Each number is at most 10 digits + space, plus "factor " prefix */
size_t cmd_cap = 8 + (size_t)ncands * 12;
char *cmd = malloc(cmd_cap);
if (!cmd) return 0;

int pos = snprintf(cmd, cmd_cap, "factor");
for (int i = 0; i < ncands; i++) {
pos += snprintf(cmd + pos, cmd_cap - pos, " %lu", (unsigned long)cands[i]);
}

FILE *fp = popen(cmd, "r");
free(cmd);
if (!fp) return 0;

/* Parse output line by line: "N: f1 f2 f3 ..." */
char line[1024];
int found = 0;

while (fgets(line, sizeof(line), fp) && !found) {
/* Parse "N: f1 f2 ..." */
char *colon = strchr(line, ':');
if (!colon) continue;

uint64_t N = strtoull(line, NULL, 10);
char *rest = colon + 1;

/* Collect distinct prime factors and their total count */
uint64_t factors[64];
int nfactors = 0;
int total_factors = 0;
char *tok = strtok(rest, " \t\n");
while (tok && nfactors < 64) {
uint64_t f = strtoull(tok, NULL, 10);
total_factors++;
/* Only add distinct factors */
if (nfactors == 0 || factors[nfactors - 1] != f) {
factors[nfactors++] = f;
}
tok = strtok(NULL, " \t\n");
}

/* A semiprime is p*q (exactly 2 prime factors total, both distinct) */
if (total_factors != 2 || nfactors != 2) continue;

/* factors[] is already sorted (factor outputs in ascending order) */

/* Smaller factor must have exactly expected bits (matches consensus) */
int b0 = 64 - __builtin_clzll(factors[0]);
int expected = NBITS / 2 + (NBITS & 1);

if (b0 != expected) continue;

/* Edge case: factor must not be a power of 2 */
if (factors[0] == (1ULL << (NBITS/2 - 1))) continue;

/* Find matching offset */
for (int i = 0; i < ncands; i++) {
if (cands[i] == N) {
*out_factor = factors[0];
*out_offset = offsets[i];
found = 1;
break;
}
}
}

pclose(fp);
return found;
}

int main(void)
{
/* Same PRNG and seed as rho benchmark for identical W values */
uint64_t state = 0xDEADBEEFCAFEBABEULL;
int solved = 0;

for (int i = 0; i < NBLOCKS; i++) {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;

uint64_t W = state;
W |= (1ULL << (NBITS - 1));
W &= (1ULL << NBITS) - 1;
W |= 1;

if (i < 5)
printf("W[%d] = %lu\n", i, (unsigned long)W);

uint64_t factor;
int64_t offset;
if (solve_one(W, &factor, &offset))
solved++;
}

printf("popen: solved %d/%d blocks\n", solved, NBLOCKS);
return 0;
}
150 changes: 150 additions & 0 deletions contrib/bench/bench_factor_rho.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* bench_factor_rho.c
*
* Benchmarks block solving using in-process Pollard's rho (GMP).
* Simulates solving NBLOCKS blocks at nBits=32 (regtest difficulty).
*
* Build: gcc -O2 -o bench_factor_rho bench_factor_rho.c -lgmp
* Usage: poop ./bench_factor_rho ./bench_factor_popen
*/

#include <gmp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define NBITS 32
#define NBLOCKS 200
#define TILDEN (16 * NBITS) /* candidate search radius */

/* Small-prime sieve product: 3*5*7*11*13*17*19*23*29*31*37*41*43*47 */
static const uint64_t SMALL_PRIMES = 3ULL*5*7*11*13*17*19*23*29*31*37*41*43*47;

/* f(z) = z^2 + 1 mod n (Pollard rho step) */
static void f(mpz_t z, const mpz_t n, const mpz_t two)
{
mpz_powm(z, z, two, n);
mpz_add_ui(z, z, 1ULL);
mpz_mod(z, z, n);
}

/*
* Pollard rho factoring.
* Returns 1 if n = g * (n/g) with both factors prime.
* Returns 0 if n is prime or factoring failed.
*/
static int rho(mpz_t g, mpz_t n)
{
if (mpz_probab_prime_p(n, 25) != 0)
return 0;

mpz_t x, y, two, temp;
mpz_init_set_ui(x, 2);
mpz_init_set_ui(y, 2);
mpz_init_set_ui(two, 2);
mpz_set_ui(g, 1);
mpz_init(temp);

while (mpz_cmp_ui(g, 1) == 0) {
f(x, n, two);
f(y, n, two);
f(y, n, two);

mpz_sub(temp, x, y);
mpz_gcd(g, temp, n);
}

mpz_divexact(temp, n, g);

int u_p = mpz_probab_prime_p(temp, 30);
int g_p = mpz_probab_prime_p(g, 30);

mpz_clear(x);
mpz_clear(y);
mpz_clear(temp);
mpz_clear(two);

if ((u_p != 0) && (g_p != 0) && (mpz_cmp(g, n) != 0))
return 1;

return 0;
}

/*
* Try to solve one "block" given W (a random nBits-bit number).
* Returns 1 if a valid semiprime factorization was found, 0 otherwise.
*/
static int solve_one(uint64_t W, uint64_t *out_factor, int64_t *out_offset)
{
mpz_t n, g, gcd_val;
mpz_inits(n, g, gcd_val, NULL);

uint64_t one = (W & 1) ? 0 : 1;
int found = 0;

for (int jj = 0; jj < TILDEN && !found; jj += 2) {
uint64_t candidates[2] = { W + jj + one, W - jj - one };
int64_t offsets[2] = { jj + (int64_t)one, -jj - (int64_t)one };

for (int k = 0; k < 2 && !found; k++) {
uint64_t N = candidates[k];
mpz_set_ui(n, N);
mpz_gcd_ui(gcd_val, n, SMALL_PRIMES);

if (mpz_cmp_ui(gcd_val, 1) != 0)
continue;

int valid = rho(g, n);
int bitsize = (int)mpz_sizeinbase(g, 2);

if (valid == 1 && bitsize == (NBITS >> 1)) {
uint64_t f1 = mpz_get_ui(g);
uint64_t f2 = N / f1;
uint64_t factor = (f1 < f2) ? f1 : f2;

/* Edge case: cofactor must have same number of bits */
int edge_check = f2 & (1ULL << (bitsize - 1));
if (edge_check != 0 && factor != (1ULL << (NBITS/2 - 1))) {
*out_factor = factor;
*out_offset = offsets[k];
found = 1;
}
}
}
}

mpz_clears(n, g, gcd_val, NULL);
return found;
}

int main(void)
{
/* Seed a simple PRNG (xorshift64) for reproducible W values */
uint64_t state = 0xDEADBEEFCAFEBABEULL;
int solved = 0;

for (int i = 0; i < NBLOCKS; i++) {
/* Generate a random nBits-bit W */
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;

uint64_t W = state;
/* Force exactly NBITS bits: set bit 31, clear bits above */
W |= (1ULL << (NBITS - 1));
W &= (1ULL << NBITS) - 1;
/* Make sure it's odd (most semiprimes are) */
W |= 1;

if (i < 5)
printf("W[%d] = %lu\n", i, (unsigned long)W);

uint64_t factor;
int64_t offset;
if (solve_one(W, &factor, &offset))
solved++;
}

printf("rho: solved %d/%d blocks\n", solved, NBLOCKS);
return 0;
}
Loading