Files
p2pk-bf/CLAUDE.md
T
davide 1a91b81de5 Aggiungi CLAUDE.md per future istanze di Claude Code
Documenta comandi di build/run per scanner Python e bruteforce C++, il
flusso dati tra i due componenti e i dettagli architetturali non ovvi
(partizionamento keyspace, euristica di rilevamento P2PK, accoppiamento
tra formati file).
2026-07-03 15:41:51 +02:00

71 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project purpose
Educational/research suite for studying Bitcoin P2PK (Pay-to-Public-Key) transactions and demonstrating why ECDSA secp256k1 bruteforce is computationally infeasible (keyspace 2^256). All docs and CLI output are in Italian. Two independent components share data via files, not code:
1. `databases/` — Python scanner that walks the Bitcoin blockchain via the mempool.space API, finds P2PK outputs, stores them in SQLite, and checks UTXO spent/unspent status.
2. `bruteforce/` — C++ program that loads target public keys and searches the private-key space for matches, as a performance demonstration (not a realistic attack — success probability is ~2^-256).
Data flows one way: scanner → SQLite DB (`databases/bitcoin_p2pk_study.db`) → `extract_p2pk_utxo.py` filters unspent P2PK → `target_keys.txt` → C++ bruteforce consumes it.
## Commands
### Python scanner (`databases/`)
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cd databases
python3 scan_blockchain.py # interactive: prompts for start/end block + delay
python3 view_db.py # generates p2pk_report.html
python3 view_db.py --stats # prints stats to terminal
```
### C++ bruteforce (`bruteforce/`)
```bash
cd bruteforce
make install-deps # apt packages: build-essential, libsecp256k1-dev, libgmp-dev, autoconf, libtool, pkg-config
make # builds secp256k1 locally from source if bruteforce/secp256k1/ doesn't exist (~5 min first time), then compiles p2pk_bruteforce
make clean # remove binaries/object files
make clean-all # also removes the locally-built secp256k1 tree
make debug # -g -O0 build: p2pk_bruteforce_debug
make bench # 10-second timed run
make pgo # profile-guided optimization build (3-step: generate → run 30s → use)
make valgrind # leak check on the debug build
make help # list all targets
python3 extract_p2pk_utxo.py [db_path] [output.txt] # default: ../databases/bitcoin_p2pk_study.db -> target_keys.txt
python3 extract_p2pk_utxo.py --stats # DB stats only, no extraction
./p2pk_bruteforce [target_keys.txt] # runs until Ctrl+C; logs to progress.csv, matches to found_keys.txt
```
There is no test suite in this repo.
## Architecture notes
### `bruteforce/p2pk_bruteforce.cpp` (single file, everything lives here)
- One pthread per (core count 1); each thread gets a disjoint slice of the 256-bit keyspace via `partition_keyspace` (only the top 64 bits are partitioned — the search relies on random start offsets within each thread's slice, not a full 256-bit range split).
- Target pubkeys are loaded from a `.txt` file (uncompressed hex, `04` prefix, one per line, header line skipped) into both an `unordered_map<secp256k1_pubkey,...>` (exact match) and a 64MB Bloom filter (fast negative rejection, checked first via `check_match_fast`).
- Performance trick: instead of computing `privkey * G` per candidate, it computes one EC multiplication then derives the next `EC_BATCH_SIZE` (256) keys via EC point addition against precomputed multiples of G (`precompute_generator_multiples`), which is much cheaper than repeated scalar multiplication.
- Build system auto-detects a local `bruteforce/secp256k1/` (built by `build_secp256k1.sh` targeting this specific CPU with `-march=native`) and links against it via rpath instead of the system lib; falls back to system `libsecp256k1`/`libgmp` if absent.
- Matches are written to `found_keys.txt`; periodic throughput stats go to stdout and `progress.csv` every 10s.
### `databases/scan_blockchain.py`
- `P2PKBlockchainScanner` class wraps all mempool.space API access (`get_block_hash`, `get_block_transactions` with pagination, `check_utxo_status`) and SQLite persistence.
- P2PK detection is deliberately redundant — a script is classified as P2PK if ANY of 4 independent checks match: explicit `scriptpubkey_type`, script byte length (67 or 35 bytes), ASM pattern (`<pubkey> OP_CHECKSIG`), or raw hex pattern (`41<pubkey>ac` / `21<pubkey>ac`). This exists because the API's `scriptpubkey_type` field is not reliable for very old (pre-2012) P2PK outputs.
- Scanning is resumable: `scan_progress` table (single row, `id=1`) tracks `last_scanned_block`; reruns default to `last_scanned_block + 1`. `UNIQUE(txid, output_index)` on `p2pk_addresses` prevents duplicate inserts, so overlapping scan ranges are safe.
- This script and its SQLite DB/CSV outputs are intended to be committed and shared across contributors scanning different block ranges (see `.gitignore` — DB/CSV/HTML are NOT excluded).
### `bruteforce/extract_p2pk_utxo.py`
- Reads only `is_unspent = 1` rows from the scanner's DB, strips the `41.../21...ac` script wrapper to get the raw pubkey, and re-adds the `04` prefix.
- Compressed pubkeys (33-byte, script length 70/hex prefix `21`) are explicitly skipped — the C++ bruteforce only generates and matches uncompressed public keys.
## Key coupling to be aware of
- The bruteforce binary's target file format (uncompressed hex pubkeys, `04` prefix, header line) is produced exclusively by `extract_p2pk_utxo.py` — if editing one side's format, update the other.
- `Makefile` CFLAGS use `-march=native`/`-mtune=native` and `-ffast-math`; binaries are not portable across different CPUs and should be rebuilt (`make clean && make`) after moving to different hardware.