Bitcoin 1.01ORION · SLH-DSA · √2

Bitcoin 1.01 — Design Decisions

This document records the decisions taken in the project. If a decision changes, this page is updated. Status tags: IN CODE written and tested · DECIDED agreed, not yet in code · OPEN not fully solved yet.

Commands, file names and code identifiers (kaz, dugum, src/zincir.hpp …) are Turkish words; they are kept unchanged here so that they match the program.


0. Story

Bitcoin 1.01 is a version updated with architectural changes that also makes communication possible. It needs no website, hosting, domain name or third-party server. The only "server" is the project owner's own computer, and even that is not required for the network to run.


1. Hash: the ORION family IN CODE

  • Not our own invention: the Keccak-f[1600] permutation, the heart of SHA-3.
  • Parameters: capacity 1024 bits (the same as SHA3-512), rate 72 bytes.
  • ORION-1024: block fingerprint, 1024 bits (256 hex characters). Generic security 512 bits.
  • ORION-512: transaction IDs and Merkle nodes, 512 bits.
  • Tag (in front of every call): [length] "orion-stars-up-my-sleeve/" + purpose Purposes: blok, islem, genesis-kaydi, merkle-yaprak, merkle-ic, adres …
  • Transparency: the ORION-512 result is identical to the official SHA3-512 result for the same input.
  • The code was checked against official SHA3-512 / SHAKE256 values and an independent Python Keccak implementation.

Note: stretching SHAKE-256 output to 1024 bits does not raise security above 256 bits. What raises security is increasing the capacity to 1024 bits; ORION does that.

2. Square root of 2 IN CODE

  • The bits of √2 are computed inside the program, without libraries. No separate file is needed.
  • Bit 0 = the first bit after the binary point. The first 64 bits are 6a09e667f3bcc908 (the same as the first initial constant of SHA-256 and SHA-512).
  • Genesis: bits [0, 2048).
  • Era constant: in every halving era (1,050,000 blocks, about 4 years) the next 1024 bits of √2 enter the chain rules: block hash = ORION-1024("blok", header || era constant). No vote is needed; every device computes the same result. (This replaces the earlier "50% coin holder approval" idea.)
  • √2 is not secret. No secret key is derived from √2.

3. 3x+1 (symbolic) IN CODE

  • Every block header has a collatz field = the number of steps (height + 27) takes to reach 1.
  • At genesis this is 27: 111 steps, with a peak of 9232 along the way.
  • Genesis mining starts from nonce = 27.
  • It plays no role in security; it is a signature, like the chain's "heartbeat".

4. Block header IN CODE

228 bytes, little-endian:

FieldBytesDescription
version4101
height8
previous hash128ORION-1024
merkle root64ORION-512
time8Unix seconds (UTC)
target_bits4compact target
nonce8
collatz43x+1 step count
  • Merkle: leaves and inner nodes use different tags. When an odd number of nodes is left, the last node is not duplicated; it is carried up one level unchanged (to avoid Bitcoin's CVE-2012-2459 bug).
  • That a transaction is in a block can be shown with a short proof of log2(n) steps.

5. Genesis IN CODE

  • Previous hash: the first 1024 bits of √2 ("before the chain there was √2").
  • Record: "orion-stars-up-my-sleeve" + the first 2048 bits of √2. It has no spendable output.
  • Time: 2026-10-23 20:12:00 Istanbul = 1792775520.
  • Target: 0x7E00FFFF (2^24 attempts on average).
  • Nonce: 599548.
  • Hash: 000000729d827999fbaab777aab0bba8e6cfdf7646af1d78acea54300463226189321ff01b097953e6f0b246c797640da04bf1a7aa438819fa0799d7bc5e591ee523e85b4fa14845a45ad6adcce7191847621a639f2b424fca9b6fd14b3ed30823d185728d56b886e04d8b21922e81fbb4e35fecec2f71d39d6a16129a069659

6. Money, time and difficulty IN CODE

  • Target block time: 120 seconds.
  • ASERT, every block (the integer arithmetic of Bitcoin Cash's aserti3-2d rule, adapted to a 1024-bit target): target = anchor_target × 2^((actual time − 120 × block difference) / 7200)
    • Half-life 2 hours: if the chain falls 2 hours behind, the target doubles (mining gets easier); if it is 2 hours ahead, the target halves.
    • A 3rd-degree polynomial for 2^x; worst relative error 0.012% (measured). Results match an independent Python reference exactly.
    • Anchor = block 1. The time between genesis and the day the network opens does not distort the difficulty.
    • Block 1 target = genesis target 0x7E00FFFF (2^24 attempts).
    • Easiest limit: 0x7F00FFFF (2^16 attempts).
  • Block reward: 10 coins. It halves every 1,050,000 blocks; this is the same moment the √2 era constant changes. Total supply: 20,999,989.8635 coins (just under 21 million because genesis has no reward).
  • 1 coin = 100,000,000 smallest units.
  • Timestamp rule (the same as Bitcoin): must be greater than the median of the last 11 blocks and at most 2 hours ahead of the device clock.

6b. Blocks, files and mining IN CODE

  • Block = header (228 bytes) + transaction count (4 bytes) + transactions. The body is at most 4 MB.
  • Storage file: <data>/ana/bloklar.dat (data folder: section 17), per block "ORN1" | length | block. On every start the program re-verifies all blocks from the beginning, signatures included. If the end of the file is truncated (for example after a power cut), only the kaz command, which writes to the chain, repairs it: it first makes bloklar.dat.yedek, then truncates to the last sound block. Read-only commands never touch the file.
  • The Phase 2 test chain (whose bodies held only the reward record) is moved, not deleted, to <data>/ana_faz2_eski on the first start of Phase 3. Genesis is the same.
  • Miner: all cores, mid-state Keccak (the unchanging first 216 bytes of the header are absorbed once), early comparison on the first 72 bytes. Every block found is also verified through the normal path. The nonce search starts from 27.
  • Keccak-f[1600] is written in unrolled form; the code is generated from the FIPS 202 formula by araclar/keccak_uret.py. Single-core speed rose about 3.7×.
  • Every block and transaction rule is listed one by one at the top of src/zincir.hpp.
  • Forks (two miners finding a block at the same time) and the "chain with the most work" rule arrive in Phase 4, together with the network.

7. Keys, addresses, signatures IN CODE

  • Signature: SLH-DSA-SHAKE-256s (NIST FIPS 205), written from scratch.
    • Public key 64 bytes, secret key 128 bytes, signature 29,792 bytes. Many-time (a limit of 2^64 signatures per key).
    • Validated with NIST's official ACVP test vectors: key generation 10/10, signing 28/28 (deterministic and randomized, internal and external interface), verification 28/28 (including deliberately corrupted signatures). The vectors are in testvektor/slhdsa_shake256s.txt; the source is NIST's usnistgov/ACVP-Server repository, the converter is araclar/acvp_donustur.py. bitcoin101 nist-test runs them all.
    • Signing splits the FORS trees and hypertree layers across all cores.
    • Wallet signatures are "hedged" (randomized): 32 bytes of fresh randomness from the operating system for every signature.
    • Context string (FIPS 205 "context"): orion-stars-up-my-sleeve/islem.
  • True randomness: BCryptGenRandom on Windows, getrandom() on Linux. If it fails, the program stops; it never falls back to weak randomness. std::random_device is not used.
  • Wallet: a single 256-bit master secret. For address number i, t = ORION-1024("cuzdan-tohum", master_secret || i), SK.seed = t[0..32), SK.prf = t[32..64), PK.seed = t[64..96). One backup (64 hex characters) restores every address. Change always goes to a new address.
    • The wallet file is encrypted with a password (section 14).
  • Address = 768 bits = the first 768 bits of ORION-1024("adres", public key) (the leading 384 + trailing 384 bits are public; the 256-bit secret seed "in the middle" is never published).
  • Address text: "orn" + Base58(address || checksum), checksum = the first 4 bytes of ORION-512("adres-kontrol", address). 140 characters. If a single letter is mistyped, the address is rejected.
  • √2 coordinate (symbolic): the first 48 bits of the address, shown as the bit position where the address "lives" in √2.

7b. Transactions and UTXO IN CODE

  • Two kinds of transaction: the reward transaction (the first in each block, no inputs, contains the height) and the normal transaction.
  • Transaction ID (txid) = ORION-512("islem", body), signatures excluded, so a signature cannot change the ID. The Merkle tree, however, takes the full ID including signatures ("islem-tam"); the block commits to everything.
  • Several inputs from the same address share a single signature (important, since each signature is about 30 KB).
  • Signed message = ORION-512("imza-mesaji", txid || amount and address of every output being spent).
  • Every transaction has a note field of up to 80 bytes (the basis for future √2 public posts).
  • Rules (src/zincir.hpp): the output must be in the UTXO ledger; reward outputs cannot be spent before maturing for 100 blocks; the signature's public key must belong to the address being spent; every SLH-DSA signature must be valid; there can be no unused signature record; outputs ≤ inputs (the difference is the fee); reward transaction ≤ block reward + total fees.
  • Fee policy: 1000 units (0.00001 coin) per 1000 bytes. A single-signature transaction is about 30 KB, so its fee is about 0.0003 coin. This is not a consensus rule but a pool admission rule.
  • Pending pool (<data>/ana/bekleyen.dat; at most 32 MB / 5000 transactions, section 17): transactions not yet in a block. For now they can only spend confirmed outputs (unconfirmed change waits for the next block). kaz re-reads the pool on every block; a transaction added with gonder from another terminal goes into the next block.
  • Version 2 transactions (HTLC output, claim/refund paths): section 16a.
  • Quantum note: going from an address to its public key means finding a 768-bit ORION preimage. The signature already rests only on hashing; there is no elliptic curve for Shor's algorithm to target.

7c. Test network (--lab) IN CODE

  • Adding --lab to the end of any command runs it on a separate network (<data>/lab). Difficulty sits at the easiest limit and mining is almost instant. The 100-block maturity rule is the same, but 101 blocks are mined in seconds.
  • The tests use the same lab network.

8. Messaging DECIDED

8a. Public post — √2 encoding

  • Alphabet: 64 characters, 6 bits. The 29 letters of the Turkish alphabet + q + x + space = 32, plus the 32 most used symbols/digits = 64.
  • √2 dictionary: the first 2^21 = 2,097,152 bits of √2.
  • Measurements (actual computation):
    • 1 character (6 bits): all 64 patterns occur within the first 452 bits.
    • 2 characters (12 bits): all 4,096 patterns occur within the first 32,218 bits.
    • 3 characters (18 bits): 99.969% of the 262,144 patterns occur within the first 2^21 bits. Only 81 patterns are missing.
    • 4 characters (24 bits): only 11.7% occur, so this is not used.
  • Format: the message is split into 3-character pieces. For each piece, [2-bit type][position]:
    • 3 characters → 21-bit position
    • if not found, split into 2 + 1 characters (15- and 9-bit positions).
  • Honest cost: a 3-character piece is written with 23 bits instead of 18 (about 28% longer). By information theory, writing a position in a random sequence takes as much space as the piece being searched for. This is not compression but a symbolic encoding. It is certainly not encryption: anyone who sees the key can read it.
  • Short codes for frequent words (a Huffman-style dictionary): to be evaluated in Phase 7.

8b. Private direct message

  • Key exchange: ML-KEM-1024 (NIST FIPS 203).
  • Encryption: symmetric, with a 256-bit key and authentication (AES-256-GCM or a Keccak-based construction; to be chosen in Phase 7).
  • Messages are not written to the chain. They are relayed over the peer-to-peer network; if the recipient is offline, open nodes keep them encrypted for a while.

9. Network architecture IN CODE

One command: bitcoin101 dugum. Peer connections, synchronization, block and transaction relay, mining (--kaz) and the explorer all run in the same program. No domain name, hosting or third-party server is needed.

How peers find each other

PathHowExtra setup
Same Wi-Fi / wired home networkUDP 10101 broadcast every 30 seconds; automatic TCP connection to any node foundNone (firewall permission)
Seed computerDirect TCP 10102 to the IP:port in tohumlar.txtPort forwarding on the seed
Address sharingEvery peer shares the addresses it knows (ADRESLER message); saved to <data>/ana/esler.txt. Local/private addresses and domain names are not accepted from a peer on the internet; the list is taken once per connection. Nodes connect to each other even when the seed is offlineNone
Manual / invitation--baglan IP:port; bitcoin101 davet shows this computer's addressNone
IPv6The listening socket is IPv4 + IPv6 together ("dual stack")Usually none
Without internetComputers connected to a phone's hotspot or to the same switch are on the same local network; automatic discovery works there too. Some devices isolate clients from each other (AP isolation); then it does not workDepends on the device

Not implemented: Bluetooth, Wi-Fi Direct, Tor, automatic port opening on the router (UPnP/PCP). These can be added later.

Protocol (TCP, every message "O101" | type | length | content; "OLAB" on the test network): SELAM (version, node ID, listening port, height, total work, tip hash, clock), BASLIK_ISTE/BASLIKLAR (at most 500), BLOK_ISTE/BLOK (at most 16), ISLEM, ADRES_ISTE/ADRESLER, PING/PONG. Protocol version 2 (a node that knows the HTLC rules); protocol 1 peers are accepted until HTLC activates (section 16a).

Synchronization ("headers first"):

  1. If a peer's total work is greater than ours, headers are requested with a locator list (10 blocks back from the tip, then doubling, finally genesis).
  2. Headers are validated from the common block onward with every header rule (ASERT, median time, 3x+1, proof of work).
  3. If the branch's total work is strictly greater than ours, the blocks are downloaded and the chain is replaced. On a tie, the first one seen stays.
  4. If even one block is invalid during the replacement, the chain and the UTXO ledger return to their previous state and the peer is dropped.
  5. Transactions in rolled-back blocks (if still valid) return to the pending pool.

Total work: per block 2^(1024 − 8·(exponent−3)) / mantissa (integer, at most 2^120); the valid chain with the most total work wins.

Self-protection: a peer that sends an invalid block/header/transaction is dropped and not retried for an hour; at most 8 outgoing and 64 incoming connections; messages at most 6 MB; a peer silent for 180 seconds is closed; a warning is shown if the difference from a peer's clock exceeds 20 minutes (blocks can be dated at most 30 minutes ahead, so the clock must be correct; section 13).

File safety: only one program at a time can write to the chain (yazici.kilit, an operating system lock; it is released by itself even if the program crashes). The gonder command does not write to a shared file; it drops a separate file into the gelen/ folder, and the running node picks it up, validates and relays it.

Seed computer: the project owner's computer. Setup: TOHUM_KURULUMU.md (firewall, sleep, port forwarding, CGNAT check).

10. Explorer IN CODE

  • A read-only web server inside the node. Default http://127.0.0.1:10103 (this computer only); with --web-genel it is reachable from outside too (seed computer).
  • A single page that refreshes live every 5 seconds: summary cards, latest blocks, pending transactions, connected peers, a verification panel; block, transaction and address detail pages; search by height, hash prefix or orn… address.
  • JSON API: /api/ozet, /api/blok/<n|hash>, /api/islem/<id>, /api/adres/<address>, /api/bekleyen, /api/esler, /api/baslik/<n>.
  • Independent verification: /api/baslik/<n> returns the raw header and the SHA3-512 input. The first half of the block's hash can be computed with any SHA3-512 tool and compared (tested: identical to Python's hashlib).
  • Security: GET only; user data (notes) is always written with textContent (never interpreted as HTML); a strict Content-Security-Policy; at most 64 concurrent connections, 8 KB requests, 15-second timeout.
  • Limit: without a domain name there is no HTTPS. Content can be altered in transit; that is why comparing with your own node is essential.
  • Anyone can take the data from the API and save it to their own disk, server or website.

11. Archive rewards (abuse-resistant design) DECIDED / partly OPEN

Designed against two kinds of attack:

  1. Sybil: one person presents 1,000 fake "devices".
  2. Spam: someone sends themselves thousands of transactions to inflate the reward.

Rules:

  • Stake: to become an archiver, at least T coins are locked for 5,040 blocks (about 1 week).

  • Lottery: in every block, 8 archivers are chosen in proportion to stake, using the previous block hash. Splitting a stake across 10 identities does not raise earnings, because earnings are directly proportional to stake. So Sybil does not work.

  • Proof of access (challenge): each chosen archiver is asked about a past block, also chosen at random from the block hash, and a transaction in that block. Public key records are among them; this is how the "public key store" becomes part of mining. Answer = the transaction itself + a Merkle proof. Anyone can verify it against the old block header. Whoever does not keep the data cannot answer.

  • Deadline: the answer must enter the chain within 10 blocks. An archiver that does not answer loses a small share (1%) of its stake. So "registering without keeping the data" costs money.

  • Reward function:

    Pool(block)            = 0.05 × block subsidy + 0.20 × transaction fees in the block
    Share per chosen       = Pool / 8
    Expected earnings_i    ≈ Pool × (stake_i / total stake)
    • As the number of transactions grows, fees and therefore the pool grow.
    • As the number of archivers grows, the share per person shrinks, but the total pool does not grow. So adding fake archivers does not increase the total reward.
    • Spam does not pay: the pool consists only of fees actually paid. Someone who sends transactions to themselves at best breaks even.
  • OPEN: that records are truly "openly accessible" on the internet cannot be proven conclusively on chain. This is an unsolved problem worldwide. A network address is declared in the archiver registration and nodes request random data from it. But this is a reputation signal, not a rule.

12. Roadmap

PhaseWorkStatus
1ORION hash, √2 generator, 3x+1, block header, Merkle, genesisDone
2Saving blocks to disk, ASERT, multi-core mining, chain validationDone
3SLH-DSA (with FIPS 205 test vectors), wallet, address, transaction, UTXODone
4Network: Wi-Fi discovery, seed, address sharing, sync, forks, transaction relay; explorerDone
551% countermeasures, wallet encryption (Argon2id + ChaCha20-Poly1305), Windows / Android appDone
5bBridge: HTLC atomic swaps with Bitcoin (version 2 transactions, activate at #150 on mainnet)Done
5cSharing security and hardening (section 17)Done
6Encrypted peer connections (ML-KEM), signed seed announcements, UPnP/PCP, a real Android APK, spending unconfirmed change
7Public posts (√2), private messages (ML-KEM), archive rewards
8Science layer: drug-candidate screening and materials discovery with half of the reward (BILIM_KATMANI.md; mining formula in section 9, lab pages in araclar/)Draft

13. Countermeasures against 51% attacks IN CODE

The honest situation: no proof-of-work chain can be fully protected against someone who takes over the majority of the computing power. Bitcoin cannot either. What can be done: make the attack very expensive, limit its damage and warn the user in time. These measures are in src/zincir.cpp (catal_politikasi) and src/dugum.cpp.

MeasureRuleAgainst
Short forkForks of ≤ 6 blocks follow the normal rule: the chain with strictly more total work winsSo that natural forks keep working
Deep fork penaltyFor a fork 6 < d ≤ 100 blocks deep, the new branch must contain at least d/6 times the work of the old oneChains mined in secret and published all at once
FinalityA fork deeper than 100 blocks is never accepted automaticallyRewriting history (double spending)
Timestamp shieldA block time can be at most 30 minutes ahead (2 hours in Bitcoin)Lowering the difficulty by playing with time
Reward maturityA mining reward cannot be spent for 100 blocksAn attacker running off with earnings at once
Miner distributionA warning (log + app) if a single address mined more than half of the last 100 blocksMaking centralization visible
Clock warningA warning if a peer's clock differs by more than 20 minutesA wrong clock breaking finality and time rules

What the deep fork penalty means: let the attacker's share of computing power be q and the honest miners' 1−q. In the same time the attacker produces work at rate q, the honest miners at 1−q. Rolling back d blocks requires q/(1−q) ≥ d/6:

Blocks to roll backRequired work ratioAttacker's required share
61× (normal rule)> 50%
122×≥ 67%
305×≥ 83%
6010×≥ 91%
101+—Impossible automatically

The cost (honestly): these rules are each node's own local rules. A node that stayed cut off from the network for longer than 100 blocks and advanced on a different chain does not switch back to the main chain by itself when it returns (it warns; the fix is to back up the veri folder by hand and download the chain from scratch). New nodes syncing from scratch are not affected, because they have no blocks to roll back. Also, someone who takes over the majority can censor new transactions; these rules only make rewriting the past harder.

Advice to users (also written on the app's Security page): wait for 6 confirmations for small payments, 30 for large payments and 100 for very large payments.

14. Wallet encryption and password rules IN CODE

  • Rules follow NIST SP 800-63B-4 (src/sifre_kurali.cpp): a password used on its own is at least 15 characters and at most 256 characters (the standard asks to allow at least 64). Spaces, Turkish letters and emoji are allowed (normalized to Unicode NFC). No uppercase/digit/symbol requirement and no forced periodic change (the standard forbids these). Instead, known/leaked passwords and patterns (bitcoin, orion, şifre, qwerty, 123123, team names…), repeated patterns and sequential runs are rejected. The app checks live while typing and shows the estimated strength.
  • Key derivation: Argon2id (RFC 9106), 64 MiB memory, 3 passes, 4 lanes, 16-byte random salt. Because it needs memory, guessing with graphics cards or special hardware becomes very expensive. RFC 9106's official test vector and argon2-cffi give the same result, 9/9.
  • Encryption: ChaCha20-Poly1305 (RFC 8439), 12-byte random nonce. The file format, Argon2 settings and salt are bound as "additional authenticated data" (AAD); if a single byte of the file changes, it will not open. With a wrong password the authentication tag does not match, so the master secret is never decrypted incorrectly.
  • File (<data>/ana/cuzdan.dat; on Windows %LOCALAPPDATA%\Bitcoin101, section 17):
    BITCOIN101-CUZDAN 2
    kdf argon2id 65536 3 4 <salt>
    sir <nonce> <encrypted master secret + tag>
    adres <no> <public key>        (so the balance can be seen without the password)
  • The password and master secret are wiped from memory after use (guvenli_sil). The lock opens only for signing/backup, for the duration of a single request.
  • Old (Phase 3–4) unencrypted wallets keep working; the app suggests setting a password with a yellow warning (there is also the bitcoin101 cuzdan sifrele command).
  • Rule: a forgotten password cannot be recovered. The only way to recover is the 64-character master secret written on paper. Quantum note: Grover's algorithm reduces symmetric keys to at most half strength; a 256-bit key leaves 128 bits of quantum security. The weak point is the password itself, which is why long sentences are recommended.

15. Application (Windows and Android) IN CODE

Architecture (the same idea as Bitcoin Core: core + interface in one program):

bitcoin101(.exe)  ── chain, peers, mining, wallet (C++ core)
   ├─ 127.0.0.1:10104  app interface (ONLY this device)  ── Edge "app window" / phone browser / installed PWA
   ├─ 127.0.0.1:10103  explorer (read-only; outside with --web-genel)
   └─ 0.0.0.0:10102    peers (P2P)
  • Windows: double-clicking bin\bitcoin101.exe (running it without a command) starts the core and opens Microsoft Edge's app window without an address bar (the default browser if Edge is missing). The program is built with its own icon and version information (src\bitcoin101.rc). In Edge, "Install this site as an app" adds it to the Start menu and taskbar. The black console window is the core itself; closing it closes the program.
  • Android: the same C++ core is built inside Termux (ANDROID.md). The interface opens in the phone's browser and becomes an app with an icon via "Add to home screen / Install app". A real APK (a Play Store-style package) is not part of this phase: the Android SDK could not be downloaded in this development environment. It will be done with Android Studio in Phase 6.
  • Design: Bitcoin Core's layout (Overview, Send, Receive, Transactions + status bar) with the Windows 11 Fluent look: the Segoe UI Variable typeface, Windows 11 colour tones, a left navigation bar with an accent line on the selected item, 8 px rounded cards, light/dark theme (follows the system), a bottom navigation bar on phones. Pages: Overview, Send, Receive, Transactions, Mining, Network, Chain (links to the explorer), Security, Bridges, Settings.
  • Icon (araclar/simge_uret.py, DejaVu Sans Bold): a Spanish red background (#E60026, darkening downward), a white √2, and above the left tick of the root sign, where the degree of a root is written (as in ³√), a red ₿ on a yellow (#F1BF00) coin: the icon reads "₿√2". App PNGs are in src/uygulama_simge.hpp, the Windows icon in src/bitcoin101.ico (16–256 px). Icon URLs carry a version suffix for the browser cache (/simge-192.png?s=2).
  • Security:
    • The interface binds only to 127.0.0.1; another device on the network cannot open it.
    • A 24-byte random token; /c/... requests without the X-Jeton header are rejected (constant-time comparison). Another website cannot send this header through the browser (CORS), nor can it read the token. Since Phase 5c the token is not embedded in the page (section 17): another user or program on this computer cannot learn it by downloading the page.
    • The Host and Origin headers are checked (against DNS rebinding and cross-site request forgery).
    • A strict Content-Security-Policy, framing forbidden (frame-ancestors 'none', X-Frame-Options: DENY), nosniff; user data is always written with textContent.
    • The master secret is shown only when the wallet is created and when a "backup" is requested with the password. Sending, backup and password change ask for the password every time.
    • When mempool.space is enabled on the Bridges page, only the swaps' BTC addresses and transactions are queried from mempool.space (section 16c).
    • Tested end to end (araclar/uygulama_testi.py, 44/44): foreign Host/Origin, requests without or with a wrong token, weak password, wrong password and malformed addresses are rejected; creation, mining, sending and password change work. Phase 5c additions: no token in the page, a different CSP nonce in every response, a wait after the 3rd wrong password (429), the node keeps responding while a send is being signed.
  • API (local, token required): GET /c/durum, /c/guvenlik, /c/ag; POST /c/sifre-kontrol, /c/olustur, /c/geri-yukle, /c/sifrele, /c/sifre-degistir, /c/yedek, /c/alma-adresi, /c/gonder, /c/madencilik, /c/baglan. The explorer's /api/... endpoints can also be read from the same address.
  • Bridge API (local, token required; section 16): GET /c/takaslar; POST /c/takas/baslat, /teklif-coz, /katil, /kabul, /kilitle, /btc-fon, /btc-talep, /sir, /talep, /iade, /btc-iade, /yayinlandi, /gizle.

16. Bitcoin bridge: HTLC atomic swaps IN CODE

Why not a vault: cross-chain bridges are where the most money has been lost in crypto history (Ronin, Wormhole, Nomad…). The common cause is that the locked funds sit in a vault controlled by a few keys or a contract. Bitcoin 1.01 uses atomic swaps instead: no vault, no intermediary, no server. Code: src/takas.* (flow), src/btc.* (Bitcoin transactions), src/secp256k1.* (Bitcoin signatures), src/sha256.*.

Roles: the initiator (Ali) gives 1.01 and receives BTC; the secret value s comes from his wallet. The participant (Ayşe) gives BTC and receives 1.01.

Flow (the Bridges page in the app):

  1. Ali creates an offer. The offer code (b101-teklif-…, Base58 + ORION checksum): H = SHA-256(s), Ali's BTC public key, the amounts, T1 (1.01 timeout), T2 (BTC timeout), the networks. The code contains no secret; it can be passed along any channel.
  2. Ayşe pastes the code; the app shows the terms. When she accepts, an acceptance code (b101-kabul-…) appears: a new 1.01 address of Ayşe and her BTC refund key. Ayşe passes it to Ali.
  3. Ali pastes the acceptance code: the 1.01 is locked in an HTLC ("Ayşe takes it by revealing s; after T1 Ali takes it back").
  4. Ayşe's node finds the lock on the chain by itself, compares it with the offer (H, recipient, amount, T1) and waits for enough confirmations. Only then is the BTC address shown; Ayşe sends the BTC to this P2WSH address from any Bitcoin wallet.
  5. Ali's core decodes the funding transaction and checks that a sufficient amount went to the right address; after at least 1 confirmation it signs the BTC claim transaction. The witness of this transaction reveals s.
  6. Ayşe's core extracts s from that transaction (verifying it with SHA-256) and claims the 1.01.
  7. Cancellation: Ayşe takes her BTC back after T2, Ali takes his 1.01 back after T1.

Timeouts and safety margins (takas.cpp):

MainnetTest network
T1 (from the moment of the offer)24 / 48 / 72 hoursat least 4 minutes
T2half of the T1 durationsame
T1 − T2, and T2 remaining at acceptance, at least6 hours1 minute
1.01 confirmations Ayşe waits for before sending BTC61
Ali claims the BTC at the latestT2 − 2 hoursT2 − 1/8 of the duration
Ayşe sends the BTC at the latestmidway between the offer and T2same
Minimum BTC amount0.0001 BTCsame

Ali is not allowed to be late: if he revealed s close to T2, Ayşe could both get her BTC refunded and claim the 1.01. Ali cannot claim with an unconfirmed funding transaction either (if the funding transaction were replaced, s would be revealed for nothing).

16a. The 1.01 side: version 2 transactions (a consensus rule change)

  • The format is at the top of src/islem.hpp. Inputs gained a yol (path: 0 normal, 1 claim + 32-byte preimage, 2 refund) and outputs a tür (type: 0 address, 1 HTLC: lock H, recipient address, refund address, timeout). Version 1 transactions stay valid as they are.
  • Rules (src/zincir.hpp): to claim, SHA-256(preimage) = H and the signature belongs to the recipient; to refund, the block's median time (last 11 blocks) ≥ timeout and the signature belongs to the refund address; an HTLC cannot be spent through the normal path, nor a normal output through the claim/refund paths; a version 1 transaction cannot contain an HTLC. The preimage is exactly 32 bytes (the same as OP_SIZE 32 on the Bitcoin side; a swap cannot be broken with an s that is valid on one chain and invalid on the other).
  • In version 2 the signed message covers every field of the HTLC being spent ("imza-mesaji-2" tag).
  • Activation: at #150 on mainnet (the chain was at #111 when the rule was added), #1 on the test network. Everyone on the network has to upgrade: old versions reject blocks containing version 2 transactions. The peer protocol went up to 2; protocol 1 peers are accepted until activation (with a warning) and disconnected afterwards. Version 2 transactions are not relayed to old peers.
  • Wallet: HTLCs do not count toward the spendable balance; those whose refund address is ours appear as "locked in a swap". The history has the types takas-kilit, takas-talep and takas-iade. The chain explorer shows HTLC outputs and the revealed s.
  • s and the BTC key are derived from the master secret with the swap number (cuzdan.hpp); they need no separate backup.

16b. The Bitcoin side

  • HTLC witness script (P2WSH, segwit v0):
    OP_IF   OP_SIZE 32 OP_EQUALVERIFY OP_SHA256 <H> OP_EQUALVERIFY <Ali's key>
    OP_ELSE <T2> OP_CHECKLOCKTIMEVERIFY OP_DROP <Ayşe's key>
    OP_ENDIF OP_CHECKSIG

    Claim witness <signature> <s> 01 <script>, refund witness <signature> <> <script>, nLockTime = T2 in the refund transaction. Bitcoin applies the time lock against the median time of the last 11 blocks (BIP 113): the refund is accepted about 1 hour after T2.

  • Written from scratch: SHA-256 / HMAC-SHA256, secp256k1 ECDSA (RFC 6979 deterministic nonce, low S, DER), the segwit transaction format, the BIP 143 signature hash, bech32 / bech32m addresses, Base58Check. Fee = vbytes × sat/vB, deducted from the output.
  • P2PKH, P2SH, P2WPKH, P2WSH and P2TR are accepted as payout addresses. Segwit versions not yet defined (v2+) are rejected: today anyone can spend money sent to them.
  • secp256k1 multiplies by a secret scalar with a Montgomery ladder, but it is not as constant-time as libsecp256k1. Quantum note: the Bitcoin side is ECDSA; it is not resistant to Shor. The keys are new for every swap and used for a few hours.

16c. Reading the Bitcoin chain and broadcasting

  • The core does not connect to a Bitcoin node. When mempool.space is enabled (it can be turned off on the Bridges page; on by default), the app window reads the HTLC address, funding transaction, spending transaction and fee suggestion from there and sends signed transactions there. The browser handles HTTPS; no TLS is needed on the C++ side. This permission is granted in the CSP to the app page only (connect-src 'self' https://mempool.space); the service worker does not touch requests to other origins.
  • What the core does not trust: it decodes every raw transaction itself and checks itself that the funding went to the right script with a sufficient amount and that s opens H. What it trusts: the confirmation count reported by mempool.space. mempool.space cannot steal money, but it sees the queried addresses and the IP and could report wrong confirmations. For large amounts, check confirmations with your own Bitcoin node.
  • When it is off, everything is done by hand: the raw funding transaction and the counterparty's claim transaction (or s) are pasted, and the signed transaction is copied and broadcast with any tool you like.
  • Record: <data>/<network>/takaslar.txt (one JSON per line). It contains no secret information.

16d. Verification

  • bitcoin101 test, parts 17–18: SHA-256 (FIPS 180-4), HMAC (RFC 4231), a known ECDSA test value, BIP 173/350 address vectors; the HTLC script, P2WSH address, claim and refund transactions are byte-for-byte identical to the independent Python library embit, and the signatures to python-ecdsa (also compared with 203 random keys during development). 1.01 rules (wrong s, wrong key, early refund, before activation, HTLC in version 1) and a full swap + cancellation scenario with two wallets.
  • araclar/kopru_testi.py: end to end with two Bitcoin 1.01 nodes + Bitcoin Core 31.1 regtest, 41/41. Bitcoin Core accepted the claim transaction and the BTC reached the recipient; it rejected the refund transaction as "non-final" before the median time reached T2 and accepted it afterwards; Ayşe extracted s from the transaction Core mined and took the 1.01; the 1.01 refund worked after T1.
  • The response format of the mempool.space testnet4 API was checked with read-only queries; the app window was run in headless Edge (page, watcher, CSP).

16e. Honest status and limits

  • No swap has been done with real BTC yet (mainnet or testnet4); the Bitcoin side was tested against a real Bitcoin Core on regtest. Do the first real swap on testnet4, then on mainnet with a small amount.
  • For a real swap the counterparty must run a Bitcoin 1.01 node synced on the same network (to see the lock with its own eyes). There is no public seed in tohumlar.txt yet: the same local network, --baglan IP:port or a seed setup is needed.
  • Blocks must be produced on the 1.01 chain while a swap is running: confirmations and median time advance with blocks. If nobody mines, Ayşe's claim cannot be confirmed before T1. If you are the only miner on the network, keep mining on throughout the swap.
  • Offer and acceptance codes are passed by hand (with private messaging in Phase 7 they can go from inside the app).
  • The prebuilt android/bitcoin101-arm64 on Android is an old version; it must be rebuilt in Termux with android/termux_derle.sh.
  • Litecoin can be added with the same script (address prefix and network settings); Ethereum needs a separate contract.
  • Experimental limit (Phase 5c): at most 0.01 BTC in a swap with real BTC (Bitcoin mainnet) (GERCEK_BTC_EN_FAZLA). Reason: the security of confirmations on the 1.01 side equals the network's total mining power. While the network is small, a malicious Ali could mine a longer branch that takes back his own 1.01 lock after Ayşe has sent the BTC. The Bridges page states this clearly on mainnet. The limit can be lifted once the network grows.

17. Phase 5c: sharing security and hardening IN CODE

The applied results of the security review (2026-10-26) carried out before sharing the program with other users. Genesis and the difficulty floor were deliberately not changed; they will be decided together at launch (see "Left open" below).

Data folder and sharing

MeasureDetails
Data in the user profileWindows %LOCALAPPDATA%\Bitcoin101\{ana,lab}, Linux/Termux ~/.bitcoin101 (src/sistem.*). When the program folder is shared, the wallet does not go with it; the profile folder is closed to other Windows users. On POSIX the folder is 0700, the wallet 0600.
Automatic moveThe old veri\ folder is moved on first start. If real data (chain, wallet, swaps) already exists in the new place, nothing is overwritten. If the move is left half-done because an old version is open, the program stops (so that it does not open with an empty folder and give the impression that "the wallet is gone"). A folder moved on the same drive keeps the loose permissions of its old place; after the move the permissions are reset and inherited from the profile folder (izinleri_devral, the same as icacls /reset /T). Folders moved by the first 5c release are repaired on start.
UTF-8 code pagesrc/bitcoin101.manifest (activeCodePage UTF-8, asInvoker). Under MinGW, std::ifstream interprets narrow paths in the system code page while std::filesystem::path uses UTF-8; this is needed for the two to agree in user folders with Turkish characters (C:\Users\Gökçe). If the manifest is not in effect (before Windows 10 1903), the short (8.3) path is used.
Sharing packagepaketle.bat: a zip built from an allow list; it stops if a wallet/chain/token/peer file or a plain master secret line is found; SHA256SUMS.txt; SLH-DSA signature with imzali.
File signaturesbitcoin101 imzala / imza-dogrula: message = ORION-512("dosya-imzasi", SHA-256(file)), context orion-stars-up-my-sleeve/dosya. --adres checks the expected signer.
Program file checkA warning on the Security page if the program or data folder is writable by Everyone / Authenticated Users / Users (baskalari_yazabilir). Folders under C:\ are like this by default.
Version and hashIn the app, Settings → About: version, the program's SHA-256 hash, the data folder. bitcoin101 surum. Peers report their version in an optional last field of SELAM (old versions ignore the extra bytes); it is shown on the Network page.

Wallet and local interface

MeasureDetails
Token not in the pageThe program opens the window with .../#jeton=<48 hex>; the page takes the token, writes it to the browser's site-specific storage and removes it from the address bar. The token persists in <data>/uygulama.jeton (for a second double-click and the installed PWA). To renew it: delete the file while the program is closed.
Wrong-password waitAfter 3 wrong attempts, every further wrong attempt doubles the waiting time (5 s → 5 min); during the wait even the correct password is not tried (429). A warning on the Security page.
Wallet worker/c/... requests are handled on a separate thread. While Argon2id and SLH-DSA (key generation, signing) run, the chain lock is released (Cuzdan::agir_is_kancasi); during that time only the wallet's own data is used. The network and miner do not freeze. The master secret is wiped from memory after every request.
Checksummed backup72 characters = 64 hex master secret + the first 4 bytes of ORION-512("ana-sir-kontrol", secret). If one character is mistyped from paper, restoring is refused (previously a different, empty wallet would silently open). Old 64-character backups are accepted.
Secret not on the command linecuzdan geri-yukle asks for the secret without echoing it (a command line stays in the process list and in the PowerShell history file).
No unencrypted copy left behindWhen an unencrypted wallet is encrypted, the old file and the unencrypted .yedek copy are overwritten with zeros and deleted (console and app alike).
NFCThe password is also brought to NFC on the C++ side (Latin-1 + Latin Extended-A compositions, Turkish included); a combining accent that cannot be composed is rejected in a new password. For old passwords, unlocking tries NFC first and then the raw form.
CSP nonceInline scripts run with a random nonce unique to each response; script-src 'unsafe-inline' was removed.
MemoryWhen app connections close, the request (password) and response (possibly the master secret) buffers are wiped.

Network (P2P) and explorer

MeasureDetails
Violation classificationislem_dogrula(..., bool* ihlal): state-independent errors such as a broken signature, a key that does not belong to the spent output, a format error or outputs exceeding inputs are violations; the sending peer is banned. A missing / spent output, immaturity, an unexpired timeout, a rule not yet active or an unknown version is not a violation (the peer may have seen another branch or a newer version).
IP banA banned peer's IP is not accepted as an incoming connection for an hour either (except 127.0.0.1). At most 4 incoming peers from the same IP.
Transaction rate limitAt most 120 transactions per minute are validated from one peer.
Signature cacheValid signatures are cached by full ID (body + signatures); when the pool and the miner validate the same transaction again, SLH-DSA is not recomputed. Safe, because the content of the spent outputs is fixed by the txid.
Pending pool limitAt most 32 MB / 5000 transactions; var_mi / harcandi_mi are O(log n) with a set.
Incremental syncFirst, the shortest prefix whose work passes the policy is applied together (with a reorg if needed); the remaining blocks are added one by one as they arrive. Unvalidated blocks wait in a buffer of at most 128 MB (previously 500 × 4 MB = 2 GB).
Address filteringADRESLER is accepted once per connection; numeric IPs only; local/private addresses (127/8, 10/8, 192.168/16, 100.64/10, fc00::/7 ...) are not taken from a peer on the internet. UDP discovery only from local addresses. When the address book is full, an entry that was never connected to is removed.
Network group diversityOutgoing connections made to addresses learned from peers choose at most one peer from each /16 (IPv6: /32) group (against eclipse attacks). Seeds and manually given addresses are exempt from this limit.
Separate connection poolsThe wallet interface (16) and the explorer (64, 8 per IP) are separate. With --web-genel, slow connections opened from outside cannot lock up the wallet.
Peer IPs hiddenIn the explorer's /api/esler response, addresses read "gizli" (hidden) for requests from outside.
IndexesThe chain keeps address → outputs and output → spender indexes (undone on reorg). Transaction/block prefix search, the address page, the wallet balance and history no longer scan the chain from the start.

Mining protection

On mainnet, mining runs while at least one peer is connected, sync is finished and no peer shows more work; when the peers leave, it waits. Reason: a new computer that is not connected to the network would, while difficulty is low, mine a chain of its own of 100+ blocks within seconds and, because of the finality rule (section 13), never merge back with the main chain by itself. For the first / only node of the network there is --tek-basina ("Mine without peers" in the app); the kaz command does not run on mainnet without this flag. There is no limit on the test network.

Build

-fstack-protector-strong -D_FORTIFY_SOURCE=2; on Windows --dynamicbase --nxcompat --high-entropy-va, in Termux -fPIE -pie -z relro -z now. bitcoin101 fuzz: corrupted data fed to the transaction, block, HTTP, JSON, Bitcoin transaction, address and offer code parsers; a transaction/block that decodes successfully must be written back identically. During development, 6 million inputs with -D_GLIBCXX_ASSERTIONS (array bounds checking): no crashes, no inconsistencies.

Verification: bitcoin101 test parts 19–20 (backup checksum, NFC, address filtering, CSP nonce, deleting the unencrypted copy, violation classification, signature cache, index consistency and indexes after a reorg); araclar/uygulama_testi.py 44/44; that a peer sending a transaction with a forged signature is dropped was tested by hand with a raw P2P client, and the mining wait with two temporary mainnet nodes.

Left open (deliberately)

  • Genesis / difficulty floor: mainnet is at the difficulty floor (2^16) at #249 and far behind schedule. A new genesis, a raised floor and an anchor at the launch block will be decided together at launch.
  • Encrypted P2P (ML-KEM-1024 + X25519): Phase 6. Today peer-to-peer traffic is unencrypted (the content is the public chain anyway; the issue is privacy and interception).
  • Code signing (Authenticode): requires a certificate. For now, SHA-256 + SLH-DSA file signatures.
  • Scale: the whole chain is in RAM and all signatures are re-verified on every start; a disk-based block store and checkpoints (similar to assumevalid) are needed.
  • The prebuilt bitcoin101-arm64 on Android is an old version; it is not included in the sharing package. The Termux build could not be tried in this environment (no Linux compiler); the POSIX code paths must be verified on a phone with test.

Generated from TASARIM.md in the project.