Skip to content

Commit ce75198

Browse files
Merge pull request #51 from NyanCatTW1/202603hardfork
ASERT DAA and timestamp hardening (activation: 2026-04-20)
2 parents bc7a564 + b4fcb34 commit ce75198

35 files changed

+2533
-358
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ src/test/fuzz/fuzz
1414
src/test/test_factorn
1515
src/bench/bench_factorn
1616
src/qt/test/test_bitcoin-qt
17+
src/contrib/testgen/gen_asert_test_vectors
18+
contrib/bench/bench_factor_rho
19+
contrib/bench/bench_factor_popen
1720

1821
# autoreconf
1922
Makefile.in

configure.ac

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
AC_PREREQ([2.69])
2-
define(_CLIENT_VERSION_MAJOR, 5)
3-
define(_CLIENT_VERSION_MINOR, 3)
4-
define(_CLIENT_VERSION_BUILD, 420)
2+
define(_CLIENT_VERSION_MAJOR, 6)
3+
define(_CLIENT_VERSION_MINOR, 0)
4+
define(_CLIENT_VERSION_BUILD, 42)
55
define(_CLIENT_VERSION_RC, 0)
66
define(_CLIENT_VERSION_IS_RELEASE, true )
77
define(_COPYRIGHT_YEAR, 2026)

contrib/bench/bench_factor_popen.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* bench_factor_popen.c
3+
*
4+
* Benchmarks block solving using coreutils `factor` via popen (batch).
5+
* Simulates solving NBLOCKS blocks at nBits=32 (regtest difficulty).
6+
*
7+
* Build: gcc -O2 -o bench_factor_popen bench_factor_popen.c
8+
* Usage: poop ./bench_factor_rho ./bench_factor_popen
9+
*/
10+
11+
#include <stdint.h>
12+
#include <stdio.h>
13+
#include <stdlib.h>
14+
#include <string.h>
15+
16+
#define NBITS 32
17+
#define NBLOCKS 200
18+
#define TILDEN (16 * NBITS) /* candidate search radius */
19+
20+
/* Maximum candidates per W: 2 per jj step (+ and - direction) */
21+
#define MAX_CANDIDATES (TILDEN * 2)
22+
23+
/* Small-prime sieve: check divisibility by small primes individually.
24+
* We can't use a single product here since we don't have GMP. */
25+
static const uint64_t SMALL_P[] = {3,5,7,11,13,17,19,23,29,31,37,41,43,47};
26+
#define NSMALL (sizeof(SMALL_P) / sizeof(SMALL_P[0]))
27+
28+
static int coprime_to_small(uint64_t n)
29+
{
30+
for (int i = 0; i < (int)NSMALL; i++) {
31+
if (n % SMALL_P[i] == 0)
32+
return 0;
33+
}
34+
return 1;
35+
}
36+
37+
/*
38+
* Factor candidates for a single W using one batched `factor` call.
39+
* Returns 1 if a valid semiprime was found.
40+
*/
41+
static int solve_one(uint64_t W, uint64_t *out_factor, int64_t *out_offset)
42+
{
43+
uint64_t one = (W & 1) ? 0 : 1;
44+
45+
/* Collect sieve-surviving candidates with their offsets */
46+
uint64_t cands[MAX_CANDIDATES];
47+
int64_t offsets[MAX_CANDIDATES];
48+
int ncands = 0;
49+
50+
for (int jj = 0; jj < TILDEN; jj += 2) {
51+
uint64_t N1 = W + jj + one;
52+
uint64_t N2 = W - jj - one;
53+
54+
if (coprime_to_small(N1)) {
55+
cands[ncands] = N1;
56+
offsets[ncands] = jj + (int64_t)one;
57+
ncands++;
58+
}
59+
if (coprime_to_small(N2)) {
60+
cands[ncands] = N2;
61+
offsets[ncands] = -jj - (int64_t)one;
62+
ncands++;
63+
}
64+
}
65+
66+
if (ncands == 0)
67+
return 0;
68+
69+
/* Build the command: "factor N1 N2 N3 ..." */
70+
/* Each number is at most 10 digits + space, plus "factor " prefix */
71+
size_t cmd_cap = 8 + (size_t)ncands * 12;
72+
char *cmd = malloc(cmd_cap);
73+
if (!cmd) return 0;
74+
75+
int pos = snprintf(cmd, cmd_cap, "factor");
76+
for (int i = 0; i < ncands; i++) {
77+
pos += snprintf(cmd + pos, cmd_cap - pos, " %lu", (unsigned long)cands[i]);
78+
}
79+
80+
FILE *fp = popen(cmd, "r");
81+
free(cmd);
82+
if (!fp) return 0;
83+
84+
/* Parse output line by line: "N: f1 f2 f3 ..." */
85+
char line[1024];
86+
int found = 0;
87+
88+
while (fgets(line, sizeof(line), fp) && !found) {
89+
/* Parse "N: f1 f2 ..." */
90+
char *colon = strchr(line, ':');
91+
if (!colon) continue;
92+
93+
uint64_t N = strtoull(line, NULL, 10);
94+
char *rest = colon + 1;
95+
96+
/* Collect distinct prime factors and their total count */
97+
uint64_t factors[64];
98+
int nfactors = 0;
99+
int total_factors = 0;
100+
char *tok = strtok(rest, " \t\n");
101+
while (tok && nfactors < 64) {
102+
uint64_t f = strtoull(tok, NULL, 10);
103+
total_factors++;
104+
/* Only add distinct factors */
105+
if (nfactors == 0 || factors[nfactors - 1] != f) {
106+
factors[nfactors++] = f;
107+
}
108+
tok = strtok(NULL, " \t\n");
109+
}
110+
111+
/* A semiprime is p*q (exactly 2 prime factors total, both distinct) */
112+
if (total_factors != 2 || nfactors != 2) continue;
113+
114+
/* factors[] is already sorted (factor outputs in ascending order) */
115+
116+
/* Smaller factor must have exactly expected bits (matches consensus) */
117+
int b0 = 64 - __builtin_clzll(factors[0]);
118+
int expected = NBITS / 2 + (NBITS & 1);
119+
120+
if (b0 != expected) continue;
121+
122+
/* Edge case: factor must not be a power of 2 */
123+
if (factors[0] == (1ULL << (NBITS/2 - 1))) continue;
124+
125+
/* Find matching offset */
126+
for (int i = 0; i < ncands; i++) {
127+
if (cands[i] == N) {
128+
*out_factor = factors[0];
129+
*out_offset = offsets[i];
130+
found = 1;
131+
break;
132+
}
133+
}
134+
}
135+
136+
pclose(fp);
137+
return found;
138+
}
139+
140+
int main(void)
141+
{
142+
/* Same PRNG and seed as rho benchmark for identical W values */
143+
uint64_t state = 0xDEADBEEFCAFEBABEULL;
144+
int solved = 0;
145+
146+
for (int i = 0; i < NBLOCKS; i++) {
147+
state ^= state << 13;
148+
state ^= state >> 7;
149+
state ^= state << 17;
150+
151+
uint64_t W = state;
152+
W |= (1ULL << (NBITS - 1));
153+
W &= (1ULL << NBITS) - 1;
154+
W |= 1;
155+
156+
if (i < 5)
157+
printf("W[%d] = %lu\n", i, (unsigned long)W);
158+
159+
uint64_t factor;
160+
int64_t offset;
161+
if (solve_one(W, &factor, &offset))
162+
solved++;
163+
}
164+
165+
printf("popen: solved %d/%d blocks\n", solved, NBLOCKS);
166+
return 0;
167+
}

contrib/bench/bench_factor_rho.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* bench_factor_rho.c
3+
*
4+
* Benchmarks block solving using in-process Pollard's rho (GMP).
5+
* Simulates solving NBLOCKS blocks at nBits=32 (regtest difficulty).
6+
*
7+
* Build: gcc -O2 -o bench_factor_rho bench_factor_rho.c -lgmp
8+
* Usage: poop ./bench_factor_rho ./bench_factor_popen
9+
*/
10+
11+
#include <gmp.h>
12+
#include <stdint.h>
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
16+
#define NBITS 32
17+
#define NBLOCKS 200
18+
#define TILDEN (16 * NBITS) /* candidate search radius */
19+
20+
/* Small-prime sieve product: 3*5*7*11*13*17*19*23*29*31*37*41*43*47 */
21+
static const uint64_t SMALL_PRIMES = 3ULL*5*7*11*13*17*19*23*29*31*37*41*43*47;
22+
23+
/* f(z) = z^2 + 1 mod n (Pollard rho step) */
24+
static void f(mpz_t z, const mpz_t n, const mpz_t two)
25+
{
26+
mpz_powm(z, z, two, n);
27+
mpz_add_ui(z, z, 1ULL);
28+
mpz_mod(z, z, n);
29+
}
30+
31+
/*
32+
* Pollard rho factoring.
33+
* Returns 1 if n = g * (n/g) with both factors prime.
34+
* Returns 0 if n is prime or factoring failed.
35+
*/
36+
static int rho(mpz_t g, mpz_t n)
37+
{
38+
if (mpz_probab_prime_p(n, 25) != 0)
39+
return 0;
40+
41+
mpz_t x, y, two, temp;
42+
mpz_init_set_ui(x, 2);
43+
mpz_init_set_ui(y, 2);
44+
mpz_init_set_ui(two, 2);
45+
mpz_set_ui(g, 1);
46+
mpz_init(temp);
47+
48+
while (mpz_cmp_ui(g, 1) == 0) {
49+
f(x, n, two);
50+
f(y, n, two);
51+
f(y, n, two);
52+
53+
mpz_sub(temp, x, y);
54+
mpz_gcd(g, temp, n);
55+
}
56+
57+
mpz_divexact(temp, n, g);
58+
59+
int u_p = mpz_probab_prime_p(temp, 30);
60+
int g_p = mpz_probab_prime_p(g, 30);
61+
62+
mpz_clear(x);
63+
mpz_clear(y);
64+
mpz_clear(temp);
65+
mpz_clear(two);
66+
67+
if ((u_p != 0) && (g_p != 0) && (mpz_cmp(g, n) != 0))
68+
return 1;
69+
70+
return 0;
71+
}
72+
73+
/*
74+
* Try to solve one "block" given W (a random nBits-bit number).
75+
* Returns 1 if a valid semiprime factorization was found, 0 otherwise.
76+
*/
77+
static int solve_one(uint64_t W, uint64_t *out_factor, int64_t *out_offset)
78+
{
79+
mpz_t n, g, gcd_val;
80+
mpz_inits(n, g, gcd_val, NULL);
81+
82+
uint64_t one = (W & 1) ? 0 : 1;
83+
int found = 0;
84+
85+
for (int jj = 0; jj < TILDEN && !found; jj += 2) {
86+
uint64_t candidates[2] = { W + jj + one, W - jj - one };
87+
int64_t offsets[2] = { jj + (int64_t)one, -jj - (int64_t)one };
88+
89+
for (int k = 0; k < 2 && !found; k++) {
90+
uint64_t N = candidates[k];
91+
mpz_set_ui(n, N);
92+
mpz_gcd_ui(gcd_val, n, SMALL_PRIMES);
93+
94+
if (mpz_cmp_ui(gcd_val, 1) != 0)
95+
continue;
96+
97+
int valid = rho(g, n);
98+
int bitsize = (int)mpz_sizeinbase(g, 2);
99+
100+
if (valid == 1 && bitsize == (NBITS >> 1)) {
101+
uint64_t f1 = mpz_get_ui(g);
102+
uint64_t f2 = N / f1;
103+
uint64_t factor = (f1 < f2) ? f1 : f2;
104+
105+
/* Edge case: cofactor must have same number of bits */
106+
int edge_check = f2 & (1ULL << (bitsize - 1));
107+
if (edge_check != 0 && factor != (1ULL << (NBITS/2 - 1))) {
108+
*out_factor = factor;
109+
*out_offset = offsets[k];
110+
found = 1;
111+
}
112+
}
113+
}
114+
}
115+
116+
mpz_clears(n, g, gcd_val, NULL);
117+
return found;
118+
}
119+
120+
int main(void)
121+
{
122+
/* Seed a simple PRNG (xorshift64) for reproducible W values */
123+
uint64_t state = 0xDEADBEEFCAFEBABEULL;
124+
int solved = 0;
125+
126+
for (int i = 0; i < NBLOCKS; i++) {
127+
/* Generate a random nBits-bit W */
128+
state ^= state << 13;
129+
state ^= state >> 7;
130+
state ^= state << 17;
131+
132+
uint64_t W = state;
133+
/* Force exactly NBITS bits: set bit 31, clear bits above */
134+
W |= (1ULL << (NBITS - 1));
135+
W &= (1ULL << NBITS) - 1;
136+
/* Make sure it's odd (most semiprimes are) */
137+
W |= 1;
138+
139+
if (i < 5)
140+
printf("W[%d] = %lu\n", i, (unsigned long)W);
141+
142+
uint64_t factor;
143+
int64_t offset;
144+
if (solve_one(W, &factor, &offset))
145+
solved++;
146+
}
147+
148+
printf("rho: solved %d/%d blocks\n", solved, NBLOCKS);
149+
return 0;
150+
}

0 commit comments

Comments
 (0)