Day 30 · Capstone & the Anchor Bridge
You’ve now built the defining mechanism of three blockchains from scratch:
Bitcoin’s UTXO ledger and proof-of-work, Ethereum’s
account world-state and gas-metered VM, and Solana’s verifiable clock and parallel
scheduler. This page does two things. First it answers the question that’s been humming under all of Phase
2 — why do all three exist? — not with hand-waving but with the code you wrote. Then it connects
solmini to the real thing: the Anchor programs in your solana-contracts/ are this runtime model with
the ergonomics turned on, and by the end you’ll be able to point at any line of an Anchor program and name
the solmini piece it stands for.
Why BTC, ETH, and SOL all exist — in the code you built
Section titled “Why BTC, ETH, and SOL all exist — in the code you built”Three teams, three non-negotiables, three completely different codebases. Lined up, the mini-chains make the reason concrete:
Bitcoin (p5) Ethereum (p6) Solana (p7) ────────── ───────────────────── ─────────────────────── ────────────────────────── you built a UTXO set + PoW a world-state map + a a SHA-256 PoH clock + longest-chain rule gas-metered stack VM a conflict scheduler state coins, created once mutable storage INSIDE typed data in EXTERNAL & spent once each contract accounts owned by programs execution fixed script EVM, one tx at a time stateless programs, in parallel the "no" no rich programs no free computation no hidden footprints (stays simple/secure) (gas bounds the halting (declare your accounts, so problem) the runtime can parallelize) optimized decentralization & programmability throughput & cost for security (a shared world computer) (a fast global machine)Read the “optimized for” row against the “you built” row and the whole picture resolves. Bitcoin’s minimalism — a coin set with no mutable state and a deliberately limited script — is why it runs on a Raspberry Pi and resists capture: there’s almost nothing to attack or centralize. Ethereum’s mutable world-state and VM are why it can host a token, an auction, a whole DeFi ecosystem — and why it must run transactions serially and meter every step with gas: arbitrary code with hidden state can’t be parallelized or trusted to halt. Solana’s externalized state and declared footprints are why it parallelizes to thousands of transactions a second — and why it needs beefy validators and chokes on hot accounts. Each chain’s superpower and each chain’s limitation come from the same design decision. You didn’t read that in a comparison table; you felt it compile.
The unifying frame is the trilemma from Day 26: decentralization, security, scalability — push one corner and you pay in another. Bitcoin, Ethereum, and Solana are three different answers to “which corner is non-negotiable for us,” and all three exist because there is no single right answer — only trade-offs made on purpose.
The Rust thread, across all seven projects
Section titled “The Rust thread, across all seven projects”The playbook’s recurring question — what does building this force you to understand, and what is Rust’s compiler protecting you from? — has one answer that grew sharper each project:
| Project | What it forced | What the compiler protected |
|---|---|---|
logwise, kvlite | ownership; traits; Arc<RwLock> | double-frees; data races on one shared map |
apilite, askr | async/await; error modeling | use-after-free across .await; unhandled failures |
| Bitcoin / Ethereum | hashing, state machines, atomicity | half-applied state on a reverted transaction |
Solana (solmini) | sequential vs parallel work; declared footprints | the data race in a thread pool — made unrepresentable |
The arc lands here: in a runtime that throws live, mutating transactions at a pool of threads — the
single most dangerous thing in this whole book — the scariest bug class was a compile error, because
par_iter_mut and the scheduler enforce the same “shared XOR mutable” rule you met on page one. That’s the
payoff of fearless concurrency, at the scale a real system needs it.
The bridge: solmini → real Solana/Anchor
Section titled “The bridge: solmini → real Solana/Anchor”You’ve written Anchor programs with CPI, so this is recognition, not new learning. Here’s a real Anchor counter on the left idea-for-idea against what you built:
use anchor_lang::prelude::*;
declare_id!("Counter111111111111111111111111111111111111"); // ← Program::id()
#[program] // ← the program: stateless code pub mod counter { use super::*; pub fn increment(ctx: Context<Increment>, step: u64) -> Result<()> { // ← Program::process let c = &mut ctx.accounts.counter; // ← accounts: &mut [Account], indexed c.count = c.count.checked_add(step).unwrap(); // ← mutate the typed account Ok(()) } }
#[derive(Accounts)] // ← Transaction.accounts (the declaration) pub struct Increment<'info> { #[account(mut)] // ← AccountMeta { is_writable: true } pub counter: Account<'info, Counter>, // ← a typed data account pub user: Signer<'info>, // ← (signatures: not modeled in solmini) }
#[account] // ← CounterState (Borsh + 8-byte discriminator) pub struct Counter { pub count: u64 }Every annotation is a piece of the runtime you built:
declare_id!isTransferProgram::id()/CounterProgram::id()— a program’s address.- The
#[program]module is a bundle ofProgram::processhandlers. The program is stateless; all state is in the accounts. Anchor compiles it to SBF/eBPF bytecode that runs in the Solana runtime (the SVM); we kept programs in a registry instead of loading bytecode, but the shape is identical. #[derive(Accounts)]isTransaction.accounts— the up-front footprint declaration the scheduler reads.#[account(mut)]isis_writable: true; a plain account is read-only. Writing this struct is feeding Sealevel its parallelism information. That’s the sentence to remember.#[account]isCounterState: a typed buffer. Anchor uses Borsh and prepends an 8-byte discriminator (the type tag we noted on Day 28); we usedserde_jsonfor readability.init, payer, spaceconstraints handle the account creation + rent we only gestured at.Context<Increment>/ctx.accounts.counteris youraccounts: &mut [Account]slice — Anchor just gives the positions names and deserializes the buffers into typed structs for you.- The
step: u64argument is thedata: &[u8]blob; Anchor deserializes the instruction args from it (you encoded the same thing by hand asamount.to_le_bytes()).
Under the hood — CPI, and why parallel scheduling survives composition
Section titled “Under the hood — CPI, and why parallel scheduling survives composition”The one piece solmini doesn’t build is CPI — cross-program invocation, when one program calls
another (your transfer program invoking the System program, say). In Anchor that’s a CpiContext and an
invoke / invoke_signed. It maps cleanly onto our model: a CPI is just one stateless process calling
another stateless process, passing along a subset of the accounts it was given. The account model is
what makes composition tractable.
But notice the invariant it must preserve: the top-level transaction still has to declare every
account any CPI will touch, transitively — which is exactly why Anchor makes you thread those accounts
through the CpiContext. That’s not bureaucracy; it’s the thing that keeps Day 29’s scheduler correct
under composition:
tx declares: [A, B, C] ──► program P.process(A, B) └─ CPI ─► program Q.process(B, C) (B, C were declared up top)
the scheduler still sees the FULL footprint {A,B,C} before anything runs, so it can parallelize this tx against others — composition doesn't hide a write.If a CPI could touch an undeclared account, the footprint would be a lie and the parallel scheduler could run two conflicting transactions at once. Solana forbids it; that’s why “pass the accounts down” is a hard rule, not a style preference. You now know why from the inside.
Program-derived addresses, in one breath
Section titled “Program-derived addresses, in one breath”PDAs (seeds, bump, find_program_address, invoke_signed) look mysterious until you have the account
model. A PDA is just an account a program owns at a deterministic address — derived by hashing seeds +
the program id — that the program can “sign” for. In solmini terms: an Account whose owner is the
program, whose Pubkey the program can recompute on demand. Nothing in the runtime model changes; PDAs are
an addressing convenience on top of “programs own accounts.”
Where to go next
Section titled “Where to go next”solmini is the runtime’s mental model; Anchor is the author’s ergonomics over that runtime. With the
model in hand, the real things to study next are the parts we deliberately skipped — and they’ll land
faster now:
- Signatures &
Signer— ed25519 verification, the per-transaction CPU cost we simulated with a hash spin on Day 29. (The real “Sigverify” stage is the parallelizable work that motivates the pipeline.) - Compute budget — the analogue of Ethereum’s gas: every transaction has a compute-unit cap. Pair this
with
ethmini’s gas day and the idea is one concept across both chains. - Rent & account sizing —
init,payer,space, rent-exemption; the economics of “state lives in accounts.” - PDAs & CPI in anger — the composition patterns above, which is where real Solana programs live.
- The toolchain —
solana-cli, theanchorCLI, a localnet, andanchor test. Re-read one of your ownsolana-contracts/programs with this page open; the annotations should now read as obvious.
The thread, closed
Section titled “The thread, closed”Two questions ran through this project. What did Solana optimize for? Throughput and cost — a single synchronous chain made fast by a verifiable clock and a parallel scheduler, paid for in hardware and contention ceilings. And what is Rust protecting you from? The data race at the heart of a parallel runtime — rendered unrepresentable by the same ownership rule that started this whole book. You set out new to Rust and curious about why Bitcoin, Ethereum, and Solana all exist. You can now answer that question in running code, and read a real Anchor program as what it is: stateless programs over typed accounts, with their footprints declared up front so a scheduler you understand can run them in parallel.
That’s Phase 2. Go open Anchor — you’ve already built the runtime it targets.
← Prev: Day 29 · Parallel Execution · Back to the Project 7 overview · Family tree: Bitcoin · Ethereum · Solana
Check your understanding
Section titled “Check your understanding”- Using the “you built” vs “optimized for” rows, explain in one sentence each why Bitcoin, Ethereum, and Solana all exist — i.e. what non-negotiable each chose.
- The page claims each chain’s superpower and its limitation come from the same design decision. Pick Solana and name the decision, the superpower, and the limitation it implies.
- In the Anchor counter, which
solminiconcept does each map to:#[derive(Accounts)],#[account(mut)],#[account](the struct), and thestep: u64argument? - A CPI lets one program call another. Why must the top-level transaction still declare every account a CPI will touch, and what would break in Day 29’s scheduler if it didn’t?
- The Ethereum Merge is offered as evidence for the page’s thesis. What is the thesis, and how does the Merge support it?
Show answers
- Bitcoin chose decentralization/security — a minimal UTXO set + PoW with no rich programs, so it stays simple and hard to capture. Ethereum chose programmability — a mutable world-state + a gas-metered VM, accepting serial execution. Solana chose throughput/cost — externalized state + declared footprints + a parallel scheduler, accepting heavy hardware.
- The decision: put state in external accounts and require transactions to declare their footprint up front. The superpower: the runtime can see which transactions are independent and run them in parallel. The limitation: transactions writing the same hot account can’t parallelize and serialize — a contention ceiling no number of cores fixes.
#[derive(Accounts)]→Transaction.accounts, the up-front footprint declaration the scheduler reads;#[account(mut)]→AccountMeta { is_writable: true };#[account]struct →CounterState, the typed data buffer (Anchor adds Borsh + an 8-byte discriminator);step: u64→ thedata: &[u8]instruction blob (Anchor deserializes the args from it).- Because the scheduler decides parallelism from each transaction’s declared account set before
execution; if a CPI could touch an undeclared account, the footprint would be incomplete and the
scheduler might run two actually-conflicting transactions at once — a data/logic race. Declaring the
full transitive footprint (threading accounts through the
CpiContext) keeps the scheduler’s conflict-freedom guarantee true under composition. - The thesis is that decentralization, security, and scalability are deliberate design dials, not fixed laws — which is why three chains can make three different choices. The Merge supports it by showing a chain change one dial (PoW → PoS, ~99.9% less energy) on 15 September 2022 while keeping its account model and VM — proving consensus was a choice all along.