Early Bitcoin source archives.
The archived source code is taken from the Satoshi Nakamoto Institute: https://satoshi.nakamotoinstitute.org/code/
- bitcoin-nov08/ — Satoshi's November 2008 pre-release source preview (only
main+node; doesn't compile on its own). - bitcoin-nov08-rebuild/ — a compilable reconstruction of that preview as a wxWidgets GUI node (
ui_app.cpp). The consensus/genesis are byte-exact; the UI is a recreation, not a recovery (nov08's ownui.cppnever shipped). Builds and runs (mines from a button); includes a prebuiltbitcoin.exe. See its README. - bitcoin-0.1.0/ — Satoshi's first public release, v0.1.0 ALPHA (January 2009): original binary + source (
src/, buildable with the shared toolchain). - bitcoin-0.1.3/ — Bitcoin v0.1.3 release: original binary + source.
- irc/ — the 0.1.0 and 0.1.3 GUI clients rebuilt from source with an IRC fix so they can still bootstrap peers in 2026.
irc/freenode/andirc/lfnet/each holdbitcoin-0.1.0/andbitcoin-0.1.3/— self-contained flat folders that are exactly(bitcoin-0.1.x/src + the network's patch), Satoshi's ownmakefileincluded, plus the ready-to-runbitcoin.exe(network baked intoirc.cpp). See "Reaching peers via IRC" below. - toolchain/ — the shared period build stack used by every from-source build (the nov08 rebuild, 0.1.0/0.1.3, the IRC clients).
build-toolchain.shcross-builds wxWidgets 2.8.12 + Berkeley DB 4.8 + Boost intotoolchain/build/(git-ignored);toolchain/openssl/holds the period OpenSSL 0.9.8 headers. - patches/ — version-to-version source diffs (
code/: nov08 → rebuild, nov08 → 0.1.0, 0.1.0 → 0.1.3, and the IRC fixes per network) and modern-toolchain library fixes (compilation/: wxWidgets 2.8.12, Berkeley DB 4.8). Seepatches/README.md.
Everything except bitcoin-nov08/ builds from source (it is just Satoshi's preview and does not compile). The originals' binaries are kept as committed; the rebuilds/IRC clients ship a freshly-built bitcoin.exe.
Every folder holds a bitcoin.exe (always that name) plus the libeay32.dll it
needs alongside. On Windows XP just double-click it; on Linux run it under Wine
(WINEDEBUG=-all wine ./bitcoin.exe to silence noise). nov08/ is the only
folder with no exe — it is Satoshi's source preview and does not compile on its own.
# Satoshi's original GUI releases
cd bitcoin-0.1.0 && wine ./bitcoin.exe # GUI (Jan 2009)
cd bitcoin-0.1.3 && wine ./bitcoin.exe # GUI
# nov08 preview, reconstructed as a wxWidgets GUI (byte-exact consensus core)
cd bitcoin-nov08-rebuild && wine ./bitcoin.exe # GUI (Start-mining button)
# IRC-patched 0.1.x GUI clients that still find peers in 2026 — one exe per network:
cd irc/freenode/bitcoin-0.1.0 && wine ./bitcoin.exe # today's Freenode (registers, mines)
cd irc/freenode/bitcoin-0.1.3 && wine ./bitcoin.exe
cd irc/lfnet/bitcoin-0.1.0 && wine ./bitcoin.exe # LFnet (discovers live peers)
cd irc/lfnet/bitcoin-0.1.3 && wine ./bitcoin.exeThe 0.1.x GUI clients (stock and IRC) and the nov08 rebuild can look frozen / "Not Responding" for a minute or two at startup — let them wait, don't force-close.
Build the shared toolchain once, then make in any project. Everything except
bitcoin-nov08/ builds from source.
toolchain/build-toolchain.sh # one time: wx 2.8 + Berkeley DB 4.8 + Boost + libeay32.a (~15 min)| Target | How (after the toolchain is built) |
|---|---|
bitcoin-nov08-rebuild |
cd there, ../toolchain/make.sh — a makefile modelled on Satoshi's 0.1.0 (adapted: flat-file DB so only wxWidgets is linked; -std=gnu++0x + -fpermissive for the reconstructed code) |
bitcoin-0.1.0 / bitcoin-0.1.3 |
cd src && ../../toolchain/make.sh — builds from Satoshi's own unmodified makefile. The result is placed at the folder root, over the original bitcoin.exe (so a rebuild replaces it in place), but the committed exe stays Satoshi's original — see below |
| IRC clients (all 4) | cd into any irc/<freenode|lfnet>/bitcoin-<0.1.0|0.1.3>/ and run ../../../toolchain/make.sh — also Satoshi's unmodified makefile; the network is baked into the patched irc.cpp |
The IRC clients can equivalently be reached as source by applying the
per-network code patches — see patches/README.md.
Two kinds live in patches/ (full details there):
patches/code/— version-to-version source diffs. Apply with-p1onto a copy of the "from" tree, e.g.:cd bitcoin-0.1.0/src && patch -p1 < ../../patches/code/lfnet/0.1.0.patch # -> LFnet client source cd bitcoin-nov08 && patch -p1 < ../patches/code/nov08-to-rebuild.patch # -> the GUI rebuild source
patches/compilation/— modern-toolchain fixes for the libraries, applied inside each dependency's own tree (toolchain/build-toolchain.shdoes this automatically):patch -p1 < patches/compilation/wxWidgets-2.8.12-mingw.patch # in wxWidgets-2.8.12/ patch -p1 < patches/compilation/berkeley-db-4.8-gcc-atomic.patch # in db-4.8.30.NC/
| Binary | debug.log? |
trace | wallet/chain/peers |
|---|---|---|---|
| Satoshi's original 0.1.0/0.1.3 (debug builds) | yes (+ db.log), in the run folder |
— | %APPDATA%\Bitcoin |
0.1.x rebuilt from source, and the irc/ clients (release) |
no | stdout (block-buffered under Wine) |
%APPDATA%\Bitcoin |
bitcoin-nov08-rebuild (release) |
no | stdout |
the run folder (blk0001.dat, wallet.dat, …) |
Mind the split for the 0.1.0/0.1.3 builds: logs and data go to different
places. The two logs are opened with a relative path, so they land in the
current working directory — the folder you cd'd into to launch — not in the
data dir: debug.log (util.h, fopen("debug.log", "a")) and db.log
(Berkeley DB's error file, db.cpp, dbenv.set_errfile(fopen("db.log", ...))).
The wallet, chain and peer data go to GetAppDir() = %APPDATA%\Bitcoin
(main.cpp), which under Wine is
~/.wine/drive_c/users/$USER/Application Data/Bitcoin/. That folder holds
wallet.dat, blkindex.dat, blk0001.dat, addr.dat, and a database/
subfolder — the Berkeley DB environment (db.cpp sets set_lg_dir(AppDir + "\\database")), whose log.000000000N files are recreated on every run.
Preview vs release — same database in Satoshi's design, different layer in this
rebuild. Satoshi used Berkeley DB throughout: the November 2008 preview's
own source already calls CTxDB/CWalletDB (the BDB-backed classes over CDB),
exactly as 0.1.0/0.1.3 do. But the leaked nov08 archive shipped without
db.cpp/db.h, so bitcoin-nov08-rebuild reconstructs that missing layer as
flat files — plain relative fopen (blk0001.dat, wallet.dat, …) that land
in the run folder, with no database/ environment at all (hence no
DB_RUNRECOVERY failure mode). That flat-file store is a rebuild simplification,
not Satoshi's choice. The real Berkeley DB 4.8 environment under
%APPDATA%\Bitcoin\database\ therefore exists only in the 0.1.0/0.1.3 release
builds (stock and the irc/ rebuilds) — the only build family where the recovery
note below applies.
The release-vs-debug reason is explained under "Building the GUI from source" below.
bitcoin-0.1.0/src and bitcoin-0.1.3/src are kept exactly as Satoshi shipped
them, his makefile included. To rebuild from that original makefile:
toolchain/build-toolchain.sh # one time
cd bitcoin-0.1.0/src && ../../toolchain/make.shtoolchain/make.sh does not edit the makefile or the source. It only adapts
the environment so a 2009 makefile drives a 2026 cross-compiler:
- a PATH shim so the makefile's literal
g++/windresresolve toi686-w64-mingw32-*, withg++forced into the period-std=gnu++98(2009 code uses an unqualifiedarray, which clashes with C++11'sstd::array); - overrides of the makefile's own
INCLUDEPATHS/LIBPATHS/LIBS(andBUILD=release) to point attoolchain/instead of the 2009 Windows install paths (/boost,/DB,/OpenSSL,/wxWidgets).
make.sh then places the result at the folder root — the stripped bitcoin.exe
over the existing one, plus the two MinGW runtime DLLs it needs
(libgcc_s_dw2-1.dll, libstdc++-6.dll) — so a rebuild replaces the exe in
place, ready to run. This is for anyone who wants to regenerate the binaries from
this repo.
But the committed exe stays Satoshi's ORIGINAL. In this working copy it is kept
canonical with git update-index --skip-worktree bitcoin-0.1.x/bitcoin.exe, so a
local rebuild never dirties or commits it (and the rebuild's runtime DLLs at the
root are git-ignored).
Note that skip-worktree is a per-clone git flag — it is not stored in the
repo and does not travel on clone. A fresh clone therefore gets the committed
original, and (lacking the flag) a rebuild simply shows up as an ordinary
modification — so whoever clones can just commit the regenerated exe if they
want to rewrite it, or git checkout -- bitcoin-0.1.0/bitcoin.exe to restore the
original. To get the same don't-dirty protection locally, set the flag yourself;
to deliberately replace it here, clear it once with
git update-index --no-skip-worktree bitcoin-0.1.0/bitcoin.exe.
The source and the makefiles are Satoshi's, unchanged — every difference is in the toolchain around them, forced by building with a 2026 compiler instead of a 2009 one. The full list:
| Satoshi, 2009 | here, 2026 | why / consequence | |
|---|---|---|---|
| Build host | native Windows (MinGW shell) | Linux, cross-compiling (also buildable on Windows) | convenience; the makefile supports both |
| Compiler | MinGW GCC ~3.4 | MinGW-w64 GCC 10 | the root cause of everything below |
| C++ standard | the era default (≈C++98) | forced -std=gnu++98 (0.1.x/IRC); -std=gnu++0x + -fpermissive (nov08 rebuild) |
GCC 10 defaults to gnu++14: an unqualified array clashes with std::array; the reconstructed code also uses std::true_type (C++11) and takes the address of an rvalue |
| Library source fixes | none | 2 patches (patches/compilation/) |
GCC 10 rejects Berkeley DB's __atomic_compare_exchange (now a builtin) and wxWidgets' _mkdir/_rmdir (need <direct.h> on 32-bit MinGW-w64); a case -1 over a DWORD is sidestepped by gnu++98, no flag |
| Resource compiler | plain windres |
windres fed only -I/-D |
wx-config --cxxflags now emits flags (-mthreads, …) that windres rejects |
| C/C++ runtime DLLs | mingwm10.dll (in his exe's imports) |
libgcc_s_dw2-1.dll + libstdc++-6.dll |
the makefile links the runtime dynamically (no -static); these are the modern equivalents, shipped next to the exe |
| OpenSSL import lib | linked his .lib directly |
libeay32.a generated from his libeay32.dll via gendef/dlltool |
cross-tools need an import lib; the DLL itself is his, unchanged |
| wxWidgets | 2.8.x (ANSI, static) | 2.8.12 (ANSI, static) — pinned | — |
| Berkeley DB | 4.x | 4.8.30.NC | — |
| Boost | period (~1.3x, headers only) | 1.55 (headers only) | Bitcoin links no Boost libs, so the version is flexible |
| OpenSSL | 0.9.8 + his libeay32.dll |
the same — period 0.9.8 headers + his own libeay32.dll |
identical crypto |
| Build mode | his distributed binaries were debug (they write debug.log) |
release (BUILD=release, no debug.log) |
must match the release wxWidgets libs (see "Release vs. debug" below) |
| Packaging | shipped as-is | bitcoin.exe + runtime DLLs stripped (~6.4 MB, his original's size) |
a packaging step; machine code unchanged |
Net result: because the compiler differs, a rebuilt exe is not byte-identical to Satoshi's — which is exactly why his originals (0.1.0/0.1.3) are kept committed and a rebuild only replaces them in the working tree. The detailed write-ups of the build errors, the release/debug split and the IP-echo startup fix are in the sections below.
The makefile is the original Windows build file — bare g++/windres (off
the PATH), its clean target uses del, and its default INCLUDEPATHS /
LIBPATHS point at C:-drive install roots (\boost, \DB\build_unix,
\OpenSSL, \wxWidgets). So the executables can be produced natively on
Windows, exactly as Satoshi built them — no Linux or Wine involved:
:: in a MinGW shell, with g++/windres on PATH and the period deps installed
:: at the makefile's expected roots (or pass the same INCLUDEPATHS/LIBPATHS/LIBS
:: overrides toolchain/make.sh uses):
cd bitcoin-0.1.0\src
make :: BUILD=debug — what Satoshi shipped (writes debug.log)
make BUILD=release :: release buildThis works the same for 0.1.0/0.1.3, the IRC clients (irc/<net>/bitcoin-*
— same makefile, network baked into the source), and bitcoin-nov08-rebuild
(its makefile is modelled on the same one). Two cases:
- Period MinGW (GCC ~3.4, as in 2009): builds clean, no patches/flags — it is the 2009 build verbatim.
- A modern MinGW (GCC 10+) on Windows: hits the same issues we bridge when
cross-building, so apply the same
patches/compilation/fixes and the-std=gnu++98(-std=gnu++0xfor the nov08 rebuild) standard.
In short: Linux (cross, via toolchain/make.sh) and Windows (native make)
both produce the Windows .exe from the unchanged source + makefile.
This is a different target, and it is not provided here. Producing a native
Linux ELF (rather than a Windows .exe run under Wine) runs into four things:
- The Win32→POSIX shim is partial. The nov08 rebuild ships
compat.hand awinsock2.hshim (Winsock→BSD sockets) "for native verification", and pure files do compile to ELF (sha.cppbuilds cleanly). But the shim is incomplete: compilingutil.cppnatively still fails on un-shimmed Win32 APIs — thread sync (CreateMutex/WaitForSingleObject/ReleaseMutex/CloseHandle,INFINITE) and registry-based entropy (HKEY_PERFORMANCE_DATA, whichRandAddSeedreads). A native build must map these to POSIX (pthread mutexes,/dev/urandom), plus other touch-points (ShellExecute, the single-instance mutex, the%APPDATA%data path). - OpenSSL version. The code targets the 0.9.8 API (
bignum.h/key.huse structs and calls that became opaque or changed in 1.1/3.0); a typical Linux system ships OpenSSL 3.0+. Cross-compiling sidesteps this by linking Satoshi's ownlibeay32.dll+ the period headers; a native link would need a period OpenSSL 0.9.8 (or API-compat shims). - The GUI is the MSW port. wxWidgets here is built and used as
__WXMSW__(ui.cpp/uibase.cppand evenui_app.cpp, theui.rcresources). A native Linux GUI needs a wxGTK build and porting the windowing/resource code. - No byte-fidelity anyway. Like the Windows rebuilds, a native binary would be a different artifact from Satoshi's 2009 exe.
So scope-wise: a native Linux console node is a bounded project (finish the
POSIX shim + use a period OpenSSL); a native Linux GUI is a larger port
(wxGTK). Neither is done here — on Linux the supported path is the Windows .exe
under Wine.
Notes on the 0.1.0 / 0.1.3 GUI clients:
- The stock clients may look frozen / "Not Responding" for a minute or two at
startup — let them wait, don't force them to close. Before bringing up the
network, the client calls
GetMyExternalIP, which dials hard-coded IPs the IP-echo services used back in 2008 (72.233.89.199for whatismyip.com in 0.1.0;70.86.96.218and208.78.68.70in 0.1.3). Those sites are still alive but moved off those addresses years ago (they now sit behind Cloudflare/Oracle), so the baked-in IPs no longer answer and the connect blocks until it times out — twice, in 0.1.3. If Wine — or Windows, running the.exenatively — pops a "the program is not responding" dialog, choose Wait (you may have to a few times); do not click End/Close process. It recovers on its own and runs normally. (Theirc/builds fix this: they resolve those services by domain name instead of the dead IPs, so they reach the network in seconds — seeGetMyExternalIPinirc/*/bitcoin-*/net.cpp.) - They enforce a single instance (shared between both). If one is already
open, or a previous run was killed instead of closed, the next launch quits
silently ("Existing instance found"). Run
wineserver -kbefore relaunching. - They are windowed apps (need a display) and store their data in
%APPDATA%\Bitcoin— under Wine that's~/.wine/drive_c/users/<user>/Application Data/Bitcoin/(wallet.dat,blkindex.dat,blk0001.dat,addr.dat, plus adatabase/Berkeley DB environment), not the exe folder — thedebug.log/db.logtraces are the exception, they go to whatever folder you launched from. They connect to the real network on port 8333. - If a launch dies instantly with
DB_RUNRECOVERY—DbRunRecoveryExceptioninOnInit(), anddb.logshowsunsupported log version NN/PANIC: Invalid argument— the Berkeley DB environment is corrupt; wipe it, there is no in-place recovery for this old code. These debug builds link Berkeley DB 4.8, and the environment lives in%APPDATA%\Bitcoin\database\(log.000000000N). If a newer BDB ever touched that Wine prefix, or a run was killed mid-write, its log format is no longer one BDB 4.8 understands and every subsequent start aborts. Fix it by deleting the environment and letting the client rebuild it:rm -rf "$HOME/.wine/drive_c/users/$USER/Application Data/Bitcoin/database"(or clear the wholeBitcoin/folder to also dropwallet.datand the chain and restart from the January 2009 genesis). Everything is regenerated on the next launch. - Out of the box they stay at height 0 (the real genesis, carrying the
Times headline) for two reasons. First, peer discovery (IRC, via Freenode)
still connects but no longer completes registration ("Registration timeout"),
so they find no peers and download nothing — though this can be bridged (see
"Reaching peers via IRC" below). Second, the miner deliberately refuses to run
while isolated — it blocks on
while (vNodes.empty()) Sleep(1000)— so enabling Options → Generate Coins printsBitcoinMiner startedbut then just sleeps (CPU ~0%, no blocks). The chain never grows until it has at least one peer.
By contrast, bitcoin-nov08-rebuild is even more isolated: the November 2008
preview has no peer discovery at all — no IRC, no seeds, nothing but an (empty)
addr.dat — so it can never find anyone (Satoshi added the IRC bootstrap only
for the public 0.1.0 release). But the nov08 miner also lacks the
while (vNodes.empty()) check, so the rebuild's Start mining button mines
standalone at a trivial test difficulty and best_height climbs anyway. So the reason the 0.1.x
clients can't grow their chain isn't the difficulty — it's that their miner won't
run without a connection, a safeguard the older preview didn't have yet.
This was verified experimentally. A node adds an inbound socket to vNodes the
moment it accept()s it, before any handshake (net.cpp), so simply holding a
TCP connection open to its port 8333 is enough to make vNodes non-empty. With
Generate Coins on and that one fake peer, both clients left the
while (vNodes.empty()) loop, started hashing (CPU ~100%, Running BitcoinMiner)
and mined real blocks on top of the January 2009 genesis at difficulty 1, both
reaching height 2. At difficulty 1 with this single-threaded
2009 code under Wine a block takes roughly 15–20 min. Drop the connection and the
miner goes back to sleep. This confirms the gate is peer connectivity, not
proof-of-work difficulty.
So the 0.1.x clients can still grow their height — you just have to supply the
peer yourself, because IRC is their only auto-discovery and neither 0.1.0 nor
0.1.3 has a -connect/-addnode option (they're identical here). Give them at
least one peer — two 0.1.x nodes on the same LAN (peered via a pre-seeded
addr.dat), the held-socket trick above, or the IRC route below — and with one
of them mining the height climbs, building a private fork on top of the
original January 2009 blockchain that the public network would never accept.
Out of the box both clients are hard-coded to chat.freenode.net (#bitcoin,
port 6667 — see irc.cpp) with no flag to change it. The discovery itself is
simple: register on IRC, JOIN #bitcoin, WHO, and decode the channel's
u<base58check> nicks — each encodes a peer's IP:port via a packed
struct ircaddr { int ip; short port; } (EncodeAddress in irc.cpp) — back
into addresses and connect to them. The question is whether the IRC servers of
2026 still let a 2009 client through. We tried both networks and made each work.
The irc/ folder holds the 0.1.0 and 0.1.3 GUI clients recompiled from
source with a one-line-class fix in irc.cpp, so each talks to a live IRC
network again:
irc/
build-toolchain.sh # builds the shared period toolchain
freenode/
bitcoin-0.1.0/ bitcoin-0.1.3/ # flat source + Makefile + bitcoin.exe
lfnet/
bitcoin-0.1.0/ bitcoin-0.1.3/ # flat source + Makefile + bitcoin.exe
Each client folder is self-contained and flat (like the root bitcoin-0.1.0/):
it is exactly the result of applying patches/code/<network>/<version>.patch to
the matching bitcoin-0.1.x/src — Satoshi's own makefile included, unchanged
— plus the prebuilt bitcoin.exe and the DLLs it needs alongside. The IRC network
is baked into irc.cpp, so it builds with no flags via
../../../toolchain/make.sh (which drives that original makefile). Run any one
directly (GUI): cd irc/lfnet/bitcoin-0.1.0 && wine ./bitcoin.exe.
The fix (irc/<network>/bitcoin-*/irc.cpp). Three small changes:
- Every IRC line the client sends now ends in
\r\ninstead of the original bare\r— the fourSend()format strings (NICK,USER,JOIN,WHO) and thestrLine += '\r'that builds thePONG. Modern Freenode (InspIRCd-3) requires\r\n(it does accept a bare\n, but\r\nis the RFC-correct form and LFnet tolerates it too), so this single change makes the client register on both networks. - The hard-coded server became an
IRC_SERVERmacro (defaultchat.freenode.net); the LFnet folders bake#define IRC_SERVER "irc.lfnet.org"intoirc.cpp. - The Freenode folders bake
#define IRC_FORCE_XNICK, which forces thex<digits>nick. This is the subtle one. Once theGetMyExternalIPfix gives the node its real (routable) external IP, it would advertise itself with a nick that base58-encodes that address — and Freenode's anti-spam Z-lines any base58-address nick (layer 4 below; we watched it register fully, then get Z-lined seconds later). Forcingx<digits>(which carries no address, so the filter ignores it) lets the client register and stay; you can't advertise a peer on Freenode anyway. On LFnet the address nick is kept, because LFnet doesn't ban it and it advertises a real connectable address.
Those are the only per-network differences (IRC_SERVER for LFnet,
IRC_FORCE_XNICK for Freenode), and they are the one line that the freenode and
lfnet code patches differ by.
Rebuilding it. These are real GUI clients, so the build needs the period
toolchain — wxWidgets 2.8.12, Berkeley DB 4.8, Boost, and Satoshi's OpenSSL 0.9.8
libeay32.dll (its import lib is generated from bitcoin-0.1.0/libeay32.dll).
Build the shared toolchain once, then make in any client folder:
toolchain/build-toolchain.sh # fetches + cross-builds wx 2.8 + BDB 4.8 + boost (~15 min)
cd irc/freenode/bitcoin-0.1.0 && ../../../toolchain/make.sh # Satoshi's makefile; network baked in
cd ../../lfnet/bitcoin-0.1.3 && ../../../toolchain/make.shTwo build fixes were needed for a modern MinGW-w64 + GCC 10 cross-compiler and
are applied automatically: Berkeley DB 4.8 renames its __atomic_compare_exchange
(now a GCC builtin), and wxWidgets 2.8's filefn.h needs <direct.h> for
_mkdir/_rmdir. The result is a static bitcoin.exe depending only on
libeay32.dll and XP system DLLs — the same dependency footprint as Satoshi's
original.
All four builds were verified end to end under Wine: each registers on its live
network, JOIN #bitcoin, WHO, and decodes the channel (the LFnet builds even
pick up real peer addresses). See the per-network notes below.
LFnet (irc.lfnet.org) is still alive, still hosts #bitcoin, and still has
real Bitcoin nodes advertising there. The irc/lfnet build registers with no
tricks and its WHO actually returns u… peer nicks, which it decodes to live
addresses — in our run (captured from the from-source build):
GOT WHO: [u9ueBogRwgiMGAn] new CAddress(158.140.180.83:8333)
GOT WHO: [u5Gsj76PRzZwVQ4] new CAddress(76.17.176.13:8333)
(Bitcoin Core's BlueMatt is even still sitting in the channel.) Important
caveat — NAT: advertising an address on IRC is not the same as being
reachable at it. Every node we saw advertised on LFnet was behind NAT and
refused the connection (76.17.176.13:8333 and 158.140.180.83:8333 both did)
— so the discovery half works (the nick decodes to a real address) but the connect
half fails, exactly as it would have for an un-forwarded home node in 2009. A node
is only usable if it is actually reachable: port-forwarded, or on your own LAN. To
guarantee a connectable peer for the mining demo we therefore advertised one
ourselves — an IRC bot joining #bitcoin with a nick encoding a reachable address,
plus a listener there; the client decoded it, connected (vNodes non-empty), and
with Generate Coins on it mined. If you only want the original
2009 behaviour with no recompile at all, the same redirect works on the stock
unmodified client via /etc/hosts (LFnet speaks the old protocol, bare \r and
all):
echo "45.76.142.73 chat.freenode.net" | sudo tee -a /etc/hosts # an irc.lfnet.org IP
# undo: sudo sed -i '/45.76.142.73 chat.freenode.net/d' /etc/hostsGetting a 2009 client onto today's live Freenode means peeling back four
separate incompatibilities. The irc.cpp fix handles the protocol ones; the
rest are about the network's modern anti-abuse:
-
Line endings (fixed in
irc.cpp). The client terminates every IRC line with a bare\r; Freenode's InspIRCd-3 requires\r\nand so never parses the client'sNICK/PONG("Registration timeout"). The\r→\r\nchange above fixes it. (If you can't rebuild, the no-recompile alternatives are a one-byte patch of each terminator in the.exe— modern Freenode also accepts a bare\n— or a tiny MITM TCP proxy that rewrites the client→server stream.) -
Hostname NOTICE format (usually a non-issue). Before sending
NICKthe client blocks inRecvUntil(... "Found your hostname", "using your IP address instead" ...). If your IP has reverse DNS, Freenode replies*** Found your hostname (host) -- cached, which still matches — so nothing is needed. Only if your IP has no rDNS does Freenode sendusing your IP address (1.2.3.4) instead, whose inserted IP breaks the exact-substring match and hangs the client; there a proxy has to translate that one NOTICE back to the literal string. Our residential IP had rDNS, so the rebuilt client sailed through. -
IP reputation. Even with the protocol fixed, Freenode rejects many IPs. Our residential IP got Z-lined ("Suspected spam, temporary ban, 24 HRS") after too many connection attempts, and routing through Cloudflare WARP made it worse — those egress IPs are listed in DroneBL (a proxy/VPN/drone blocklist) and Freenode auto-Z-lines anything in it (
You are listed in DroneBL). What worked was a clean residential IP: a router reconnect handed us a fresh dynamic IP (not banned, not in DroneBL) and registration went straight through —PONG,001…004,JOIN,WHO, full channel list, ChanServ welcome. (The Freenode build sidesteps the separate nick filter with itsx<digits>nick, which carries no address for the filter to flag — see the next layer.) We later pinned this down to a pure connection-rate effect: from a fresh IP, a single client connection — no verification probes, no bot, no reconnect storm — registers and stays connected indefinitely (we held one open and watched it answer serverPINGs with no Z-line). Every ban we suffered was self-inflicted by our own volume of test connections in a short window; the discipline that works is one connection and leave it alone. -
The advertisement nick is itself banned — by shape, not by prefix. A node advertises by setting its nick to the base58check of its address; Freenode's anti-spam Z-lines any such nick (
432 ... Suspected spamat registration, or a465Z-line moments afterJOIN), precisely because 2009-era Bitcoin clients once flooded it with exactly those nicks. We confirmed the consequence directly: of the 77 members our client saw in aWHO/NAMESof#bitcoin, zero decoded to an address and all 77 were people — and not random ones: Adam Back (adam3us), Luke Dashjr (luke-jr/LukeDashjr), Ruben Somsen (RubenSomsen), Lisa Neigut (niftynei),shesek,jarolrodand other Bitcoin developers. (EvenNeo`Nemesis, the anti-spam entity whose name appears in ourZ-linederrors, sits in the channel — the guard that evicted the nodes is itself a member.) We tested whether a different prefix would dodge it — building a client that advertises withn<base58check>instead ofu<base58check>— and ran a clean side-by-side on one fresh IP:- the
x<digits>client (x787746620) registered, stayed connected, and mined 4 blocks (height 0 → 4) — no ban for the whole session; - then, on that same IP, an
n<base58check>nick (nC8r5KBgMr2ZkVP) registered (001–004),JOINed#bitcoin, and was Z-lined ~1 second after the JOIN.
Same clean IP, opposite outcomes — so the ban is triggered by the nick, and the filter targets the base58-address shape (a letter plus a long mixed-case blob), not the
ucharacter. Thenprefix doesn't evade anything; it only delays the Z-line to just afterJOIN. Only the addresslessx<digits>form survives, which is why the Freenode build forces it (-DIRC_FORCE_XNICK). You cannot advertise an address on Freenode at all. For the mining demo we supply the peer ourselves — a held TCP socket to the client's own port, or a MITM proxy that injects a fabricatedJOINfor a reachable address — after which, with Generate Coins on, the Freenode client mined those blocks on the January 2009 genesis (proof-of-work found). - the
Bottom line: neither IRC bootstrap is dead, but they fail differently.
Freenode is reachable again — with the \r\n fix, the x<digits> nick, and a
clean IP used sparingly, the irc/freenode build registers, joins, runs, and (with
a supplied peer) mines on the January 2009 genesis. But it can never advertise or
discover: every address-carrying nick is Z-lined, so its #bitcoin channel is
77 people, not nodes, and you must hand the client a peer yourself. (Freenode
also Z-lines by IP connection rate, so: one connection, left alone.) LFnet, by
contrast, still does real peer discovery: the irc/lfnet build reads genuine
u<base58> node advertisements out of the channel and decodes them to addresses —
except that every node we found there sat behind NAT and refused connections
(76.17.176.13, 158.140.180.83), so discovery succeeds but the connect doesn't,
unless the node is actually reachable (port-forwarded, or on your LAN). In short:
Freenode = register and mine, but no advertising or discovery; LFnet = real
discovery, but the advertised peers are unreachable behind NAT.
Worth stating precisely: Freenode's failure is a policy limit, not a protocol
one. The client and the 2009 IRC bootstrap are fully capable of advertising and
discovering there — the \r\n fix already gets a node registered, joined and
reading #bitcoin — and the only remaining blocker is the anti-spam rule that
Z-lines the base58-address nick shape. We confirmed this is by shape, not
prefix: building a client that advertises with n<base58check> instead of
u<base58check> does not dodge it — on one clean IP, the x<digits> client
stayed connected and mined four blocks, while an n<base58check> nick registered,
JOINed, and was Z-lined ~1s after the JOIN. So the "n trick" only delays the
ban to just after JOIN; it never evades it. If that rule were lifted — or the
same build were pointed at any network that permits the advertisement nick (LFnet
today, or a private/self-hosted IRC server you control) — the
u<base58check> advertise-and-discover path would light up unchanged. This is the
relevant fact for anyone wanting to revive the legacy IRC bootstrap: nothing in
the client needs further repair; you only need a host network whose policy allows
the address nick.
The prebuilt clients in irc/ are the easy route, but every fix here can also be
applied to the stock, unmodified 0.1.0/0.1.3 bitcoin.exe without the
wxWidgets/BDB toolchain:
- Switch IRC network —
/etc/hosts. LFnet speaks the 2009 protocol, so the original client just needs its hard-codedchat.freenode.netpointed at an LFnet IP (the/etc/hostsline shown above). No recompile, no binary edit. - Line endings — one-byte
.exepatch. Modern Freenode accepts a bare\n, so the five line terminators the client sends can be flipped0x0D→0x0Adirectly in the official binary: the four format stringsNICK %s\r,USER %s 8 * : %s\r,JOIN #bitcoin\r,WHO #bitcoin\r, plus the\rthePONGbuilder appends (amov $0xdimmediate right after thestrLine[1]='O'that turnsPINGintoPONG). Five single-byte edits, no recompile. - Everything via a MITM TCP proxy. Redirect
chat.freenode.netto127.0.0.1(one/etc/hostsline) and run a tiny proxy that forwards to a real Freenode IP while: rewriting the client→server stream\r→\r\n(line endings); translating the hostnameNOTICEback to the literal the 2009 client expects (only needed for IPs without rDNS); and injecting the fabricatedu<base58check>peerJOINtoward the client (the only way to advertise a peer on Freenode, whose anti-spam bans that nick shape). This is what we used to take the unmodified client all the way to mining on Freenode.
Recompiling 0.1.0/0.1.3 is harder than it looks: they are full wxWidgets
desktop apps, so the build needs a period GUI/database stack cross-compiled for
32-bit Windows. toolchain/build-toolchain.sh fetches and builds it; the pieces
and the snags are worth recording. The two library fixes below are kept as
ready-to-apply patches in patches/compilation/ (the toolchain script applies
them automatically).
The toolchain (all cross-built for i686-w64-mingw32):
| Component | Version | Role | Notes |
|---|---|---|---|
| wxWidgets | 2.8.12 | GUI toolkit | built ANSI (not Unicode) + static, matching the wxmsw28 (no u) names in Satoshi's makefile |
| Berkeley DB | 4.8.30.NC | wallet + block db (db_cxx) |
C++ API, static |
| Boost | 1.55 | foreach / lexical_cast / tuple |
headers only — Bitcoin links no Boost libs |
| OpenSSL | 0.9.8 | ECDSA / SHA / RIPEMD | the period openssl/ headers in toolchain/openssl/, with a libeay32.a import lib generated from Satoshi's libeay32.dll |
Build errors hit with a modern (GCC 10 / MinGW-w64) cross-compiler, and the fixes:
- Berkeley DB —
__atomic_compare_exchangeredefined. BDB 4.8 ships its own function by that name, which is now a GCC built-in. Fix: rename it (to__atomic_compare_exchange_db) indbinc/atomic.h— the long-known BDB-4.8 patch. - wxWidgets —
_mkdir/_rmdirnot declared (filefn.cpp). wx 2.8'swxPOSIX_IDENTmacro picks the underscore (MSVC) spellings for everyone except__MINGW64__, but a 32-bit MinGW-w64 toolchain defines__MINGW32__, not__MINGW64__, and the underscore functions live in<direct.h>which the file never includes. Fix:#include <direct.h>under#ifdef __MINGW32__inwx/filefn.h. windreschokes on-mthreads.wx-config --cxxflagsincludes compiler flags the resource compiler rejects; each clientMakefilefilters the resource-compiler flags down to just-I/-D.- MinGW runtime DLLs at launch. Satoshi's own
makefilelinks the C/C++ runtime dynamically (it has no-static), so the exe importslibgcc_s_dw2-1.dllandlibstdc++-6.dll— the modern MinGW-w64 equivalents of themingwm10.dllSatoshi shipped in 2009. Since we build from his unmodified makefile, eachirc/client folder ships those two DLLs (stripped: ~0.1 MB and ~1.8 MB) next tobitcoin.exeandlibeay32.dll, just as Satoshi shippedmingwm10.dllnext to his.
Release vs. debug, and where the log goes. In util.h, printf is
#defined to OutputDebugStringF, and that function only opens debug.log
when __WXDEBUG__ is defined — i.e. in a debug build. Satoshi's distributed
0.1.0/0.1.3 binaries were debug builds (that is why running them writes a
debug.log next to the exe). Our from-source binaries are release builds: we
must match the wxWidgets libraries, which are built --disable-debug, because a
__WXDEBUG__ app linked against release wx would mismatch wx's object layout and
crash. A release build therefore writes no debug.log; its trace goes to
stdout, which under Wine is block-buffered into the redirect file and only
appears on flush — so during a run the IRC dialogue is invisible even though the
client is registered (the give-away is a live, persistent TCP connection to the
ircd: an unregistered client is dropped within a minute). To watch a from-source
build's IRC exchange live we pointed it at a logging TCP proxy instead.
A startup delay we also fixed. Before IRC, StartNode calls
GetMyExternalIP, which in the original code connects to hard-coded IPs the
IP-echo services had in 2008 (72.233.89.199 for whatismyip.com in 0.1.0; two
of them in 0.1.3) and requests long-gone endpoints. The domains still work today
but moved off those IPs years ago, so each connect sits in SYN-SENT until it
times out (minutes under Wine) before the IRC thread even starts. The irc/
source resolves the services by domain name instead
(gethostbyname("www.whatismyip.com") etc., with a null-guard), so the dead IPs
are never dialled and the node reaches IRC in seconds. Even so, GetMyExternalIP
may now fail fast to parse a modern response — harmless, since its only use is to
pick the address the node advertises, which falls back to the LAN IP.
Size. The GUI links big (~12 MB with debug symbol tables). The committed
bitcoin.exe is run through i686-w64-mingw32-strip (a packaging step — it does
not touch the source or makefile), dropping it to ~6.4 MB, essentially the size of
Satoshi's original binary. Stripping removes only the symbol/line tables used by a
debugger; the machine code and behaviour are identical. The shipped runtime DLLs
are stripped the same way.
A curiosity: across these clients there are two historical genesis blocks.
0.1.0 and 0.1.3 load the official January 2009 genesis
(000000000019d6…, carrying the Times headline), while bitcoin-nov08-rebuild
reproduces Satoshi's pre-release September 2008 genesis (000006b1…, no
headline). Three codebases, two genesis blocks — the rebuild grows its chain
standalone (no peer gate), while 0.1.0/0.1.3 grow theirs only once you hand them
a peer (held socket, a second node, or the IRC route above).