How BitLooper is built, and how it gets updated
One Rust program, bitlooper, is the node, the validator and the wallet. This page shows its structure, how to run everything locally, and how changes reach the network.
Architecture
Wallets and the website talk to a node's HTTP API. The node orders blocks with other nodes, and applies them through the state machine, which is the only place the rules live. Blocks, snapshots and the double-sign guard are stored on disk.
Run everything locally
Install Rust (once)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shStart the development network and this website
Builds the node, creates a 4-validator network in
devnet/(kept between runs), starts it, and serves this site athttp://localhost:8090.scripts/dev.sh # start (Ctrl-C stops everything) scripts/dev.sh --reset # wipe the chain and start from a new genesisUse it
Open the Explorer, get faucet coins, and follow How to use.
How code changes are made
Change the code
Rules live in
node/src/state.rsandparams.rs, consensus innode.rs, the website inwebsite/. The web wallet compiles the node's owncrypto.rsandtypes.rsto WebAssembly, so it signs exactly as nodes verify. After changing either file, runscripts/build-wallet.sh.Test
cd node && cargo test --releaseEvery rule change needs a unit test. The supply invariant (C + R = 100,000,000) is checked after every block in tests and in production.
Try it on the devnet
Restart
scripts/dev.sh. Rule changes that alter state need--reset, because old blocks were made under old rules. Website changes only need a browser refresh.Review
Every change is reviewed by someone other than its author before release.
How the live network gets updated
There are no admin keys, so no one can push an update into the network. Updates spread only because node operators choose to run them.
| Kind of change | Example | Process |
|---|---|---|
| Non-consensus | Faster sync, better logs, API additions | Release any time. Operators upgrade at their convenience, and old and new nodes interoperate. |
| Consensus rule | New transaction type, new signature algorithm | Published specification, review, audit, then a release naming an activation height at least 6 months ahead. Operators who disagree keep the old rules. |
| Monetary rules | Supply cap, emission rate, fee rule | Not changeable by vote. Only a chain split by operators choosing different software could change them, and users can stay on the original chain. |
app_version, and each release compiles in a schedule of (height, version) pairs (APP_VERSION_SCHEDULE, SPEC 3.13). A rule change ships as a release that adds a future height; upgraded nodes switch rules at exactly that block, and a node that did not upgrade refuses the block instead of forking silently. Headers also have a version and a reserved proof slot for future validity proofs.HTTP API
| Endpoint | Returns |
|---|---|
GET /status | Chain id, height, peers, base fee, supply (circulating, reserve, staked, issued) |
GET /blocks/recent?n=20 | Summaries of the latest blocks |
GET /block/{height} | A full block and its commit certificate |
GET /account/{address} | Balance, nonce, key, safety settings, pending actions, recovery, name; ?proof=1 adds a Merkle proof |
GET /proof/account/{address} | A Merkle proof of the account against the latest state root (also /proof/validator/, /proof/name/, /proof/asset/{id}/) |
GET /name/{name} | Who owns a name, with a proof (wallets resolve @name here, then sign to the address) |
GET /header/{height} | A header and its commit certificate, for light clients |
GET /stripped/{height} | A block without signatures (history nodes) |
GET /validators | Stake, power, signed blocks (uptime), rewards, jailed status |
GET /checkpoints | Snapshot heights and hashes a node can serve to new nodes |
POST /tx | Submit a signed transaction (JSON); returns its tx_id (stable, without signatures) and witness_hash |
POST /faucet/{address} | Testnet coins, if the node runs a faucet |
Addresses in paths may be written in the checksummed form (tblpr1… on testnets, blpr1… on mainnet; a mistyped one is refused) or as 66 hex characters. JSON responses give the hex form in address and the checksummed one in display.
Formats (frozen for mainnet)
Everything hashed or signed uses one binary encoding (big-endian integers, length-prefixed byte strings, nothing skipped); JSON is only the transport. The byte-exact rules are in docs/SPEC.md sections 2-3, with test vectors in docs/test-vectors.json.
| What | Format |
|---|---|
| Signatures | ML-DSA-44/65, SLH-DSA-SHA2-128s with context bitlooper-v1; every signed message binds the chain id and the genesis hash, so a signature for one network never verifies on another |
| Transactions | tx_id = SHA3 of the signing bytes (no signatures; stable); witness_hash covers both signatures, length-prefixed |
| Block header | Version 2 (BLP-HDR-v2): version, app version, chain id, genesis hash, height, parent, time, proposer, and roots of transactions, witnesses, evidence, state, validator sets, the last commit and late commits, plus a reserved proof slot |
| Lists in a block | RFC 6962 Merkle trees (tagged leaves and nodes, no duplication) with the count bound |
| State | A sparse Merkle tree per part of the state (accounts, validators, assets, names…) under one state root: any balance can be proven to a phone, and a block re-hashes only what it changed |
| Addresses | 33 bytes on chain; shown as Bech32m with the prefix blpr (mainnet) or tblpr (testnets) |
| Names | register_name: 3-20 characters a-z 0-9, one per account, first come first served; 100 / 10 / 1 coins for 3 / 4 / 5+ characters, paid to the reserve |
Repository layout
blockchain/
├── node/ Rust node, validator and wallet (bitlooper)
│ ├── src/crypto.rs ML-DSA / SLH-DSA, addresses (Bech32m), list trees (RFC 6962)
│ ├── src/smt.rs the Merkle state tree (sparse Merkle trees, proofs)
│ ├── src/types.rs transactions, blocks, votes, canonical encodings
│ ├── src/state.rs the rules: balances, safe accounts, staking, emission, slashing
│ ├── src/node.rs BFT consensus, mempool, sync, snapshots, pruning
│ ├── src/p2p.rs peer-to-peer transport
│ ├── src/store.rs blocks, snapshots, double-sign guard
│ ├── src/rpc.rs HTTP API
│ └── README.md
├── wallet-wasm/ browser wallet core: node crypto + transactions compiled to WebAssembly
├── website/ this site (static HTML) and wallet/pkg (generated WebAssembly)
├── scripts/dev.sh local devnet + website
├── scripts/build-wallet.sh rebuild the browser wallet core
└── devnet/ local chain data (created by dev.sh)