Skip to content

mattsta/Plutus

 
 

Repository files navigation

Plutus Bitcoin Brute Forcer

A high-performance tool for scanning Bitcoin private keys against a database of ~49M funded address hashes. Supports three execution tiers: custom C accelerator with NEON 4-wide RIPEMD-160 and ARM SHA-256 hardware instructions (~22M keys/sec on 9 cores), Metal GPU with Montgomery batch inverse (~2.3M keys/sec), or pure Python on PyPy (~570K keys/sec with zero compiled dependencies).

About

I found some speed improvements here in this fork vs the original.

Originally when I downloaded and ran this package, it was testing 64,000 addresses per second with the default architecture.

After about 10 hours of working on speed improvements, I now get over 22 million addresses checked per second (on my laptop).

Some highlights of the architecture redesign:

  • moved address lookup to mmap'd binary bloom filter instead of python pickle restore
  • moved address lookup to match the data being generated so it doesn't require conversions to check
  • removed coincurve library usage and rewrote everything to be optimized internally for our exact purposes
  • also created pure-python implementations of all hash and crypto utilities to see of pypy could make it go faster (not really)
  • added specialized (i.e. non-general-purpose) hash operations to just calculate the required lengths and sizes of data here
  • added ability to resume from previous seeds
  • added more utilities for structured guessing from source key formulas
  • added some NEON/SSE extensions for more rapid sha256 calculations
  • added some GPU helpers for non-sequential lookups
  • used more optimized magic crypto math tricks throughout reducing the number of expensive divisions required

Quick Start

# Install (no dependencies required for basic usage)
git clone https://github.com/mattsta/plutus
cd plutus

# build the C accelerator for maximum speed
./build.sh

# Run
python3 plutus.py

How It Works

Scanning Strategy

Each worker process picks a random 256-bit starting private key, then scans sequentially from that point using elliptic curve point addition: key, key+1, key+2, .... This is ~100x faster than generating independent random keys because point addition (P + G) is a single field operation vs full scalar multiplication for a random key.

The keyspace is 2^256 (~10^77) possible keys. With ~49M funded addresses, the probability of any single key matching is ~10^-71. The tool scans as fast as possible to maximize coverage.

Important: On restart, workers pick NEW random starting points. Previous progress is not resumed. See "Distributed Usage" below for coordination strategies.

Address Types

Each private key derives one public key, but that public key can be encoded as multiple Bitcoin address formats:

Type Prefix Example Description Cost
P2PKH 1... 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH Original format (2009). hash160(pubkey) with Base58Check encoding. Holds a large share of historical Bitcoin wealth — early adopters, long-held coins. Base cost
Bech32 bc1q... bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 Native SegWit (2017, BIP 141/173). Uses the same hash160(pubkey) as P2PKH, just encoded differently. Now the dominant format for new transactions. Free — shares P2PKH's hash
P2SH 3... 3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN Pay-to-Script-Hash (2012, BIP 16). Used for multisig and wrapped SegWit (P2SH-P2WPKH). Hash is hash160(0x0014 || hash160(pubkey)) — requires an extra hash160 computation per key. +40% slower

Default: -t p2pkh,bech32 — checks both P2PKH and Bech32 for free (same hash). Add -t p2pkh,p2sh,bech32 to include P2SH at ~40% performance cost.

Database and Bloom Filter

The database contains ~49M hash160 values extracted from all three address types. On first run, it's built from the text files in database/ and cached as a binary file (.plutus_cache.bin) with:

  • Bloom filter (256MB) indexed by raw 20-byte hash160 — fast probabilistic membership test
  • Sorted hex hash160 list for exact verification via binary search on mmap'd data
  • SHA-256 integrity checksum — detects corruption

Subsequent runs load the cache in seconds via memory-mapped I/O (zero-copy).

Execution Tiers

The tool auto-selects the fastest available backend:

Tier Requirement Speed (9-core) Hot Path
C accelerator plutus_accel.so built ~22M keys/sec Batch Jacobian EC + Montgomery inverse + NEON 4-wide RIPEMD-160 + bloom prefetch, all in one C call
Metal GPU macOS Apple Silicon ~2.3M keys/sec Montgomery batch inverse + hash160 + bloom on GPU, double-buffered async pipeline
CPython + coincurve pip install coincurve ~1M keys/sec Direct libsecp256k1 CFFI calls + hashlib
PyPy pure Python --native-ec flag ~570K keys/sec 100% Python, zero compiled deps, full PyPy JIT

Use --native-ec to force the pure Python backend (required for PyPy).

Key Generation Strategies

Beyond random scanning, Plutus supports targeted strategies that exploit documented weaknesses in how early Bitcoin wallets generated private keys. Sequential strategies run at full C accelerator speed (~22M keys/sec). Non-sequential strategies use the Metal GPU path (~2.3M keys/sec) or coincurve (~200K keys/sec).

Exhaustion Time Estimates (9-core, C accelerator)

Strategy Keyspace Type Speed Time to exhaust
low-entropy --max-bits 32 4.3B Sequential 12M/s 6 minutes
low-entropy --max-bits 40 1.1T Sequential 12M/s 25 hours
timestamp seconds 2009-2013 157M Sequential 12M/s 13 seconds
timestamp milliseconds 2009-2013 157B Sequential 12M/s 3.6 hours
timestamp microseconds 2009-2013 157T Sequential 12M/s 152 days
timestamp nanoseconds 1 day 86T Sequential 12M/s 83 days
timestamp --timestamp-mode sha256 157M Non-sequential 200K/s 13 minutes
brain-wallet (10K phrases) 10K Non-sequential 200K/s instant
brain-wallet (5K words × 3) 125B Non-sequential 200K/s 7 days
weak-mnemonic 2 words 4.2M Non-sequential 1K/s 70 minutes
weak-mnemonic 3 words 8.6B Non-sequential 1K/s 100 CPU-days
random 2^256 Sequential 12M/s heat death of universe

random (default)

Random 256-bit starting point, sequential scan via point addition. Covers the full keyspace probabilistically. Fastest throughput — uses the C accelerator at full speed.

low-entropy

Scans keys with low bits of entropy: 1, 2, 3, ..., 2^N. Many early wallets used weak random number generators that produced keys in restricted ranges.

python3 plutus.py --strategy low-entropy --max-bits 32   # 4.3B keys, ~6 min
python3 plutus.py --strategy low-entropy --max-bits 40   # 1.1T keys, ~25 hours

timestamp

Private keys derived from Unix timestamps — mimicking weak RNG implementations that used time(), gettimeofday(), or clock_gettime() as entropy sources.

Modes: raw (default) uses timestamps as sequential integer keys at full C accelerator speed. sha256 computes SHA-256(timestamp) per key (non-sequential, slower).

Resolutions: seconds (default), milliseconds, microseconds, nanoseconds. Higher resolution covers more keyspace but takes proportionally longer.

Resolution 1 day keyspace 2009-2013 keyspace Time (9-core)
seconds 86K 157M 13 seconds
milliseconds 86M 157B 3.6 hours
microseconds 86B 157T 152 days
nanoseconds 86T 157 quadrillion impractical (narrow the date range)
python3 plutus.py --strategy timestamp                                        # seconds, 2009-2013
python3 plutus.py --strategy timestamp --timestamp-resolution milliseconds    # ms resolution
python3 plutus.py --strategy timestamp --timestamp-resolution microseconds \
    --time-start 2009-01-03 --time-end 2009-02-01                             # 1 month at μs
python3 plutus.py --strategy timestamp --timestamp-resolution nanoseconds \
    --time-start 2009-01-03 --time-end 2009-01-04                             # 1 day at ns
python3 plutus.py --strategy timestamp --timestamp-mode sha256                # SHA-256 hashed

brain-wallet

Tests candidate passphrases using multiple historically accurate key derivation methods from the 2008-2013 Bitcoin era. Different early tools derived keys differently — Plutus tries all known methods for each phrase:

Method Description Era
SHA-256(phrase) Standard brain wallet — most common 2009-present
SHA-256(SHA-256(phrase)) Double-SHA — mimics Bitcoin's internal hashing 2010-2012
SHA-512(phrase)[:32] Truncated SHA-512 — some early deterministic wallets 2011-2013
Raw UTF-8 zero-padded Very early/naive implementations — phrase bytes as key 2009-2010
SHA-256^10/100/1000(phrase) Iterated hashing — "hardened" brain wallets 2011-2013

Each phrase produces ~7 candidate keys (one per method). At ~200K keys/sec this adds negligible overhead since the scalar multiplication dominates.

Supply a phrase file (one per line) for known/suspected passphrases, and/or a wordlist for combinatorial generation:

python3 plutus.py --strategy brain-wallet --phrases known_phrases.txt
python3 plutus.py --strategy brain-wallet --wordlist words.txt --max-words 3
python3 plutus.py --strategy brain-wallet --phrases phrases.txt --wordlist words.txt

A 10K-phrase file (~70K keys with all methods) completes in under a second. A 5,000-word wordlist with 3-word combinations (125 billion phrases × 7 methods ≈ 875 billion keys) takes ~50 days.

weak-mnemonic

Short BIP-39 mnemonics. Tests candidate seeds from the standard 2048-word BIP-39 wordlist with fewer words than the standard 12-24. Very slow (~1K/sec due to PBKDF2 with 2048 iterations per mnemonic).

python3 plutus.py --strategy weak-mnemonic --mnemonic-words 2   # 4.2M, ~70 min
python3 plutus.py --strategy weak-mnemonic --mnemonic-words 3   # 8.6B, ~100 CPU-days

Requires the BIP-39 English wordlist at wordlists/english.txt (download).

composite

Chains multiple strategies in sequence. Run the fastest/smallest keyspaces first.

python3 plutus.py --strategy composite \
    --strategies "low-entropy,timestamp,brain-wallet" \
    --max-bits 32 --phrases phrases.txt

Parameters & Options

Strategy

Flag Default Description
-s, --strategy random Key generation strategy
--max-bits 40 low-entropy: bits of entropy (2^N keys)
--time-start 2009-01-03 timestamp: start date (YYYY-MM-DD)
--time-end 2013-12-31 timestamp: end date (YYYY-MM-DD)
--timestamp-mode raw timestamp: raw (sequential) or sha256 (hashed)
--timestamp-resolution seconds timestamp: seconds, milliseconds, microseconds, nanoseconds
--wordlist none brain-wallet: path to wordlist file
--phrases none brain-wallet: path to known phrases file (one per line)
--min-words 1 brain-wallet: minimum words per phrase
--max-words 4 brain-wallet: maximum words per phrase
--mnemonic-words 3 weak-mnemonic: BIP-39 word count
--strategies none composite: comma-separated strategy list

Scanning

Flag Default Description
-t, --address-types p2pkh,bech32 Address types to scan. Add p2sh at ~40% cost.
-c, --cpu-count CPU count - 1 Number of worker processes
-n, --max-keys 0 (unlimited) Stop after N keys total
--max-time 0 (unlimited) Stop after N seconds
-o, --output plutus.txt File for match output

Database

Flag Default Description
-d, --database database/12_26_2025/ Path to database directory
--bloom-size 256 Bloom filter size in MB
--no-cache off Force rebuild from text files
--rebuild-cache off Build/rebuild cache and exit
--seed-file plutus_seeds.json Seed file for resume support

Output

Flag Default Description
-v, --verbose 0 Show generated addresses (slow)
-q, --quiet off Suppress progress, log matches only
--log-file none Write logs to file
--status-interval 1.0 Status update interval (seconds)

Backend

Flag Description
--native-ec Force pure Python EC math (no coincurve; required for PyPy)

Examples

# Default: P2PKH + Bech32, all cores, C accelerator if available
python3 plutus.py

# Include P2SH addresses (slower)
python3 plutus.py -t p2pkh,p2sh,bech32

# Bounded run: stop after 10M keys or 60 seconds
python3 plutus.py -n 10000000 --max-time 60

# Use 4 cores, quiet mode, log to file
python3 plutus.py -c 4 -q --log-file scan.log

# Pre-build the database cache (for automation)
python3 plutus.py --rebuild-cache

# Run on PyPy (pure Python, no compiled deps)
pypy3 plutus.py --native-ec

# Run speed benchmark
python3 plutus.py time

# Run correctness tests
python3 plutus.py test

Resumability & Seed Persistence

Automatic Resume

Plutus automatically saves each worker's position on shutdown (including Ctrl+C) to a seed file (plutus_seeds.json by default). On the next run, workers resume exactly where they left off — no repeated work.

# First run — workers pick random starting points, scan for 60 seconds
python3 plutus.py --max-time 60
# Output: "Seeds saved to plutus_seeds.json (9 workers)"
# Output: "Resume with: python3 plutus.py --seed-file plutus_seeds.json"

# Resume — workers continue from saved positions
python3 plutus.py
# Output: "Loaded 9 worker seeds from plutus_seeds.json"
# Output: "Worker 0: starting at key 0x7bfc45179cd84645..."  (matches previous next_key)

The seed file is human-readable JSON:

{
  "total_keys_checked": 9750528,
  "elapsed_seconds": 5.1,
  "workers": [
    {
      "start_key": "0x7bfc45179cd84645...",
      "keys_checked": 4885504,
      "next_key": "0x7bfc45179cd84645..."
    }
  ]
}

Fresh Start

To discard saved progress and start fresh with new random keys:

rm plutus_seeds.json   # Delete the seed file
python3 plutus.py      # New random starting points

Or use a different seed file:

python3 plutus.py --seed-file run2_seeds.json

Changing Worker Count

If you resume with a different --cpu-count than the previous run:

  • Fewer workers: Only the first N seeds are used, remaining are discarded
  • More workers: Extra workers get new random starting points
  • The seed file is overwritten with the new worker set on next shutdown

Distributed Usage

For multiple machines scanning independently:

  1. Independent random: Each machine runs normally — the 2^256 keyspace is so vast that random starting points will never overlap in practice
  2. Partitioned ranges: Use different --seed-file per machine. Manually set starting keys in the JSON to partition the keyspace
  3. Progress tracking: Each machine's seed file records total_keys_checked and per-worker positions. Collect these to track aggregate coverage

The random approach is statistically sound — the keyspace is ~10^77, funded addresses occupy ~10^7, and overlap probability between any two billion-key scans is essentially zero.

Building Native Accelerators

A unified build script detects your platform and builds everything available:

./build.sh

This builds:

  • CPU C accelerator (plutus_accel.so) — all platforms, ARM SHA-256 hardware auto-detected
  • Metal GPU accelerator (plutus_gpu.metallib + plutus_gpu_bridge.so) — macOS with Apple Silicon only

Or build manually:

# CPU accelerator (all platforms)
cc -O3 -march=native -mtune=native -shared -fPIC -o plutus_accel.so plutus_accel.c

# GPU accelerator (macOS Apple Silicon)
xcrun metal -O2 -c -o plutus_gpu.air plutus_gpu.metal
xcrun metallib -o plutus_gpu.metallib plutus_gpu.air && rm plutus_gpu.air
cc -O3 -shared -fPIC -framework Metal -framework Foundation -o plutus_gpu_bridge.so plutus_gpu_bridge.m

Automatic Backend Selection

The tool auto-selects the best backend for each workload at runtime:

Strategy type Backend selected Throughput
Sequential (random, low-entropy, raw timestamps) CPU C accelerator ~22M keys/sec (9 cores)
Non-sequential with GPU available Metal GPU ~2.3M keys/sec (32 GPU cores)
Non-sequential without GPU CPU coincurve ~200K keys/sec
Any strategy with --native-ec Pure Python ~570K keys/sec (PyPy)

For non-sequential strategies (brain wallets, SHA-256 timestamps), the GPU is 11x faster than CPU scalar multiplication. The GPU timestamp kernel generates keys on-GPU with zero CPU→GPU transfer. For sequential strategies, the CPU's point addition optimization is unbeatable.

PGO Build (optional)

Profile-guided optimization can provide 5-15% additional throughput:

./build.sh --pgo    # 3-phase: instrument → train 30s → rebuild with profiles

Running Tests

python3 plutus.py test              # End-to-end crypto verification
python3 test_accel_correctness.py   # C extension unit tests (field math, hashing, EC)
python3 test_accel.py               # C extension integration test (batch scanning)
python3 test_pyhash.py              # Pure Python hash verification
python3 test_gpu.py                 # GPU correctness + benchmark (requires local terminal)

Output Format

When a match is found, it's written to the output file (plutus.txt by default) and a backup (plutus.txt.bak):

hex private key: 0000000000000000000000000000000000000000000000000000000000000001
WIF private key: KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn
public key: 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
address (p2pkh): 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
blockchain.com: https://www.blockchain.com/btc/address/1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
mempool.space:  https://mempool.space/address/1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH

Each match includes direct links to blockchain explorers where you can verify the address balance and transaction history.

What to Do With a Match

  1. Verify the balance — Open the blockchain explorer link to confirm the address has funds. Many addresses in the database may have been emptied since the database was last updated.

  2. Import the private key — The WIF private key (starts with K or L for compressed) is the standard import format supported by all Bitcoin wallets:

    • Electrum: File → New/Restore → Import Bitcoin addresses or private keys → paste the WIF key
    • Bitcoin Core: importprivkey "KwDiBf89Q..." "recovered" false in the console
    • BlueWallet (mobile): Add Wallet → Import Wallet → paste WIF key
    • Sparrow Wallet: File → New Wallet → Airgapped Hardware Wallet → paste WIF in the Keystore tab
    • Coinomi (mobile): Add Wallet → Bitcoin → Restore Wallet → paste WIF key
  3. Transfer funds immediately — Once imported, send the balance to an address you fully control. Discovered private keys should be considered compromised since the same key generation weakness that made them discoverable could be exploited by others.

  4. Security notes:

    • The hex private key is the raw 256-bit secret. Keep it confidential.
    • The WIF (Wallet Import Format) is the same key with a checksum, encoded in Base58. This is what wallets accept.
    • The public key is safe to share — it cannot be used to derive the private key.
    • Always verify on a blockchain explorer before attempting to import or spend.

About

An automated bitcoin wallet brute-forcer (updated for speed)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 91.0%
  • Shell 9.0%