Skip to content

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:

ProjectWhat it forcedWhat the compiler protected
logwise, kvliteownership; traits; Arc<RwLock>double-frees; data races on one shared map
apilite, askrasync/await; error modelinguse-after-free across .await; unhandled failures
Bitcoin / Ethereumhashing, state machines, atomicityhalf-applied state on a reverted transaction
Solana (solmini)sequential vs parallel work; declared footprintsthe 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! is TransferProgram::id() / CounterProgram::id() — a program’s address.
  • The #[program] module is a bundle of Program::process handlers. 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)] is Transaction.accounts — the up-front footprint declaration the scheduler reads. #[account(mut)] is is_writable: true; a plain account is read-only. Writing this struct is feeding Sealevel its parallelism information. That’s the sentence to remember.
  • #[account] is CounterState: a typed buffer. Anchor uses Borsh and prepends an 8-byte discriminator (the type tag we noted on Day 28); we used serde_json for readability. init, payer, space constraints handle the account creation + rent we only gestured at.
  • Context<Increment> / ctx.accounts.counter is your accounts: &mut [Account] slice — Anchor just gives the positions names and deserializes the buffers into typed structs for you.
  • The step: u64 argument is the data: &[u8] blob; Anchor deserializes the instruction args from it (you encoded the same thing by hand as amount.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.

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.”

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 sizinginit, 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 toolchainsolana-cli, the anchor CLI, a localnet, and anchor test. Re-read one of your own solana-contracts/ programs with this page open; the annotations should now read as obvious.

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

  1. 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.
  2. 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.
  3. In the Anchor counter, which solmini concept does each map to: #[derive(Accounts)], #[account(mut)], #[account] (the struct), and the step: u64 argument?
  4. 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?
  5. 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
  1. 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.
  2. 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.
  3. #[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 → the data: &[u8] instruction blob (Anchor deserializes the args from it).
  4. 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.
  5. 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.