{"id":50717052,"url":"https://github.com/eandersson/erikslund-solo-pool","last_synced_at":"2026-06-09T19:30:20.812Z","repository":{"id":362786592,"uuid":"1258275265","full_name":"eandersson/erikslund-solo-pool","owner":"eandersson","description":"Bitcoin solo mining pool","archived":false,"fork":false,"pushed_at":"2026-06-05T21:53:47.000Z","size":372,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T23:14:09.934Z","etag":null,"topics":["bitcoin"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eandersson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-03T12:34:12.000Z","updated_at":"2026-06-05T21:53:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/eandersson/erikslund-solo-pool","commit_stats":null,"previous_names":["eandersson/erikslund-solo-pool"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/eandersson/erikslund-solo-pool","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eandersson%2Ferikslund-solo-pool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eandersson%2Ferikslund-solo-pool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eandersson%2Ferikslund-solo-pool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eandersson%2Ferikslund-solo-pool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eandersson","download_url":"https://codeload.github.com/eandersson/erikslund-solo-pool/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eandersson%2Ferikslund-solo-pool/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34123169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin"],"created_at":"2026-06-09T19:30:18.555Z","updated_at":"2026-06-09T19:30:20.806Z","avatar_url":"https://github.com/eandersson.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# erikslund-solo-pool\n\nTwo independent implementations of a **Bitcoin solo mining pool** -- one in modern\n**C++**, one in **free-threaded Python** -- sharing a config format, an\nHTTP/Prometheus API, and a Docker regtest harness. Both speak Stratum v1, build\nblock templates from `bitcoind`, validate miner shares, and submit solved blocks.\n\n*Solo* means every block a miner finds pays **that miner's address in full**\n(minus an optional donation); the pool coordinates and distributes work, it does\nnot split rewards across participants.\n\n\u003e WARNING: **This builds and submits real Bitcoin blocks.** Run it on regtest/signet/testnet with your actual hardware first.\n\n## Two implementations, one behavior\n\n| | [`cpp/`](cpp/) | [`python/`](python/) |\n| --- | --- | --- |\n| Language / runtime | C++26 (GCC 16.1) | Free-threaded CPython 3.14t (no-GIL) |\n| Concurrency | epoll reactor, one thread per core | N asyncio event loops (`SO_REUSEPORT`), one per core |\n| Share hashing | Vendored Bitcoin Core SHA-256 -- SSE4 / AVX2 / SHA-NI runtime dispatch | `hashlib` (OpenSSL) |\n| JSON | simdjson (parse) | msgspec |\n| Allocator | mimalloc | CPython default |\n| Leans toward | Throughput, memory, latency | Readability, hackability |\n\n## Quick start\n\n**Real network** -- Docker Compose stacks in [`deploy/`](deploy/), with or without\nan embedded Bitcoin Core.\n\n**Bring your own Bitcoin node** -- these stacks run the pool only and bind-mount\nits config + data from the host. The pool runs as **UID/GID 1000**, so create the\nhost dirs owned by `1000:1000` (Docker does not chown bind-mounts) or it can't\npersist stats or spool found blocks:\n\n```sh\nsudo mkdir -p /opt/erikslund-pool/etc /opt/erikslund-pool/data /opt/erikslund-pool/logs\nsudo cp conf/pool.yml /opt/erikslund-pool/etc/pool.yml\nsudo chown -R 1000:1000 /opt/erikslund-pool\n\ndocker compose -f deploy/docker-compose.cpp.yml    up -d --build   # C++ pool\ndocker compose -f deploy/docker-compose.python.yml up -d --build   # Python pool\n```\n\n## Configuration\n\nOne YAML file, [`conf/pool.yml`](conf/pool.yml), validated by\n[`conf/pool.schema.json`](conf/pool.schema.json) and read by **both** pools:\n\n```yaml\nbitcoin_nodes:\n  - address: bitcoind:8332      # extra entries are failover nodes\n    username: erikslund\n    password: CHANGE_ME_before_deploying\nstratum_listen:  [0.0.0.0:3333] # multiple entries bind multiple ports\napi_listen:      [127.0.0.1:7777] # status + /metrics -- loopback default; override to expose\ninitial_difficulty: 10000       # vardiff adjusts from here\nminimum_difficulty: 1\nzmq_block_endpoint: tcp://bitcoind:28332   # instant new-block work\n```\n\n| Port | Purpose                                                                       |\n| --- |--------------------------------------------------------------------------------|\n| `3333` | Stratum V1 -- miners connect here                                            |\n| `7777` | HTTP status JSON + Prometheus `/metrics` (`/health`, `/status`, `/stats/*`) |\n\n## Benchmarks\n\n[`tools/bench/`](tools/bench/) measures the **share-validation hot path** -- parse\n`mining.submit` -\u003e rebuild coinbase/merkle/header -\u003e double-SHA256 -\u003e target\ncompare -- on a private regtest chain. A synthetic Stratum client\n([`stratum_bench.py`](tools/bench/stratum_bench.py)) floods structurally-valid,\nalways-above-target shares (no proof-of-work, so no blocks), exercising the\npool's *full* validation path. Both pools run the identical\n[`tools/bench/bench.yml`](tools/bench/bench.yml) and the same generator.\n\nMeasured on a **32-core x86-64** host, Docker, 30 s window after a 5 s warmup\n(`bash tools/bench/run_bench.sh`, Release build). The generator hashes each share too,\nso it's CPU-bound: throughput at saturation (16 workers) is *generator-bound* for\nboth pools (~310-320k/s), making the **per-core** and **memory** columns -- not\nraw shares/s -- the efficiency signal.\n\n| impl | shares/s | per core | p50 ms | p95 ms | p99 ms | CPU % | peak RSS |\n| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |\n| **C++** | 320,990 | **~47,000** | 12.4 | 17.0 | 18.9 | 682 | **242 MiB** |\n| **Python** | 305,433 | ~20,300 | 14.6 | 34.7 | 70.3 | 1504 | 945 MiB |\n\nBoth clear ~310k shares/s, but C++ does it on **~6.8 cores vs ~15** -- about\n**2.3x the throughput per core**, with **~3.9x less memory** and a far tighter\ntail (**p99 19 ms vs 70 ms**).\n\n**Memory at connection scale** -- idle connections (each does a real\n`mining.subscribe` + `mining.authorize`, then holds), pool RSS via `docker stats`\n([`tools/bench/connscale.sh`](tools/bench/connscale.sh)):\n\n| idle connections | C++ RSS | Python RSS |\n| --- | ---: | ---: |\n| 50 | 92 MiB | 426 MiB |\n| 64,000 | **318 MiB** (~5 KiB/conn) | **1,332 MiB** (~20 KiB/conn) |\n\nBoth hold 64k idle connections without dropping any -- each connection is a\nsocket, so raise the deploy's `nofile` / `max_clients` to match.\n\n## Layout\n\n| Path | What |\n| --- | --- |\n| [`cpp/`](cpp/) | C++26 pool -- sources, tests (doctest), Docker build + smoke test |\n| [`python/`](python/) | Free-threaded Python pool -- package `erikslund_pool`, tests, Docker image |\n| [`conf/`](conf/) | Shared `pool.yml` + JSON-Schema |\n| [`tools/`](tools/) | Dev \u0026 test tooling -- `regtest/` harness (bitcoind + miner), `bench/` (share-validation benchmark), CPU miner, fake bitcoind, status page |\n| [`deploy/`](deploy/) | Production Compose stacks (embedded / external bitcoind) |\n\n## License\n\nLicensed under the **[GNU General Public License v3.0 or later](LICENSE)**. The\nvendored Bitcoin Core SHA-256 in [`cpp/third_party/bitcoin/`](cpp/third_party/bitcoin/)\nis **MIT** (Bitcoin Core developers, GPL-compatible), used unmodified with its\nlicense/attribution preserved there.\n\n## Credits\n\n- Stratum V1; design lineage from [ckpool](https://bitbucket.org/ckolivas/ckpool).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feandersson%2Ferikslund-solo-pool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feandersson%2Ferikslund-solo-pool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feandersson%2Ferikslund-solo-pool/lists"}