Setup & Project Layout
Welcome to the workshop. In the overview we promised a small but complete blockchain you can run — one that mines blocks, signs payments with real secp256k1 keys, validates everything, and even gossips blocks between nodes. Before we write a single line of that, we need two things: a working Rust toolchain, and a clear mental map of how the project is laid out.
This chapter is deliberately light on code and heavy on orientation. Think of it as walking the floor plan before you start building rooms. Once you can picture where each piece lives and what each dependency does, every later chapter will feel like filling in a labeled blank.
Installing Rust with rustup
Section titled “Installing Rust with rustup”Rust is installed through a tool called rustup, which manages the compiler (rustc), the build
tool and package manager (cargo), and lets you keep everything up to date. On macOS or Linux, the
one-liner from rustup.rs is:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shFollow the prompts (the defaults are fine), then restart your terminal so the new tools are on your
PATH. Verify it worked:
rustc --versioncargo --versionYou should see version numbers print out. If you do, you have everything you need — Rust ships its compiler, package manager, test runner, and documentation tool together, so there’s nothing else to install.
Creating the project
Section titled “Creating the project”You can either clone the companion crate that ships with this book, or create your own from scratch and
fill it in chapter by chapter. To make a fresh project, use cargo new:
cargo new --bin blockchain-from-scratchcd blockchain-from-scratchThe --bin flag says “this is a binary crate” — a program with an entry point you can run — as
opposed to --lib, a library crate meant to be used by other code. (Our finished project is
actually both, and we’ll see why in a moment.) cargo new drops you a Cargo.toml file and a
src/main.rs with a hello-world fn main.
Crates and modules
Section titled “Crates and modules”Two words will follow you through this whole book, so let’s pin them down now.
A crate is the unit Rust compiles at once — roughly, “one project.” Our blockchain is one crate.
The external libraries we depend on (like sha2 for hashing) are also crates; we just pull them in
rather than write them.
A module is a namespace inside a crate that groups related code. Modules are how we keep a single
crate from becoming one giant unreadable file. Each .rs file under src/ is its own module, and we
declare them with pub mod. Our blockchain has one module per concept — hashing, blocks, transactions,
and so on — which keeps each idea in its own tidy box.
The Cargo.toml file
Section titled “The Cargo.toml file”Cargo.toml is the project’s manifest — its name, version, Rust edition, and dependencies. Here is the
exact manifest for our crate:
[package]name = "blockchain-from-scratch"version = "0.1.0"edition = "2024"
[dependencies]sha2 = "0.10"secp256k1 = "0.30"rand = "0.8"serde = { version = "1", features = ["derive"] }serde_json = "1"hex = "0.4"The [package] section names the crate and pins it to edition 2024 — editions are Rust’s way of
introducing new language conveniences without breaking old code, and we use the latest.
The [dependencies] section is the interesting part. Each line pulls in an external crate from
crates.io, Rust’s package registry. The string is a version requirement
("0.10" means “0.10.x, the latest compatible patch”). cargo downloads and compiles these the first
time you build. Here’s what each one does for us, and which chapter it shows up in:
| Crate | What it does | First used in |
|---|---|---|
| sha2 | SHA-256 hashing — the cryptographic fingerprint at the heart of everything | Hashing |
| secp256k1 | The elliptic-curve signature scheme Bitcoin uses, for keys and signing | Keys & Signatures |
| rand | A random number generator, used to create fresh wallet private keys | Keys & Signatures |
| serde | A serialization framework; the derive feature auto-generates the boilerplate | Persistence |
| serde_json | Turns our data structures into JSON text (and back) for saving to disk | Persistence |
| hex | Encodes raw bytes as readable hexadecimal strings (the 0000... hashes you saw) | Hashing |
Notice serde is written differently:
serde = { version = "1", features = ["derive"] }That table form lets us turn on a feature. The derive feature unlocks #[derive(Serialize, Deserialize)], an annotation that writes all the tedious “how to convert this struct to JSON” code for
us. We’ll lean on it heavily when we get to saving the chain.
The lib.rs + main.rs structure
Section titled “The lib.rs + main.rs structure”Most tiny projects have only main.rs. Ours has both src/lib.rs and src/main.rs, and the split
is intentional:
src/lib.rsis the library — all the reusable logic of the blockchain. It declares the modules and is what our tests import.src/main.rsis the binary — a thin program that wires the library into a command-line tool (thedemoandservecommands you saw in the overview).
This is a common and healthy Rust pattern: keep the real logic in a library so it can be tested and
reused, and keep main.rs as a slim front door. Here is our complete src/lib.rs:
//! A minimal but complete blockchain, built from scratch in Rust to learn how//! the pieces fit together. Companion code for Part 11 of *Bitcoin · First//! Principles*.
pub mod block;pub mod blockchain;pub mod hashing;pub mod merkle;pub mod message;pub mod net;pub mod node;pub mod pow;pub mod storage;pub mod transaction;pub mod utxo;pub mod wallet;That’s the whole file — and it’s basically a table of contents for the rest of the book. The //!
lines at the top are a module-level doc comment: documentation that describes the file it sits in
(regular /// comments document the next item; //! documents the enclosing one).
Each pub mod line tells Rust “there’s a public module here, find it in the matching .rs file.” So
pub mod hashing; pulls in src/hashing.rs, the very next thing we’ll write. Reading the list top to
bottom is reading our roadmap: block, blockchain, hashing, merkle, the peer-to-peer trio
(message, net, node), pow, storage, transaction, utxo, and wallet.
Here’s the same map with each module’s job, drawn from the crate’s own README:
| File | Responsibility |
|---|---|
hashing.rs | SHA-256 / double-SHA-256 helpers |
transaction.rs | Transaction, inputs/outputs, txid & sighash |
merkle.rs | Merkle root + inclusion proofs |
block.rs | BlockHeader, Block, mining |
pow.rs | Proof-of-Work difficulty check |
wallet.rs | secp256k1 keys, signing, verification, addresses |
utxo.rs | the unspent-output set (current state) |
blockchain.rs | the chain, coinbase, validation, coin selection |
storage.rs | save/load the chain as JSON |
message.rs / node.rs / net.rs | the peer-to-peer layer |
main.rs | the demo and serve CLI |
The three commands you’ll live in
Section titled “The three commands you’ll live in”Three cargo commands cover almost everything you’ll do:
cargo build— compile the project (and download dependencies the first time). It produces an executable but doesn’t run it. The first build is slow because it compiles every dependency; later builds only recompile what changed.cargo run— build and then run the binary. Anything after--is passed to your program, not to cargo. That’s howcargo run -- demotells our CLI to run thedemocommand.cargo test— build and run every test in the crate. This is how we’ll prove the chain rejects cheating: forged signatures, double-spends, and inflated payouts all become failing-on-purpose tests that confirm our rules hold.
With the toolchain installed and the floor plan in your head, you’re ready to lay the foundation. The very first module — and the thing every other piece depends on — is hashing.
Try it
Section titled “Try it”If you cloned the companion crate, prove the whole thing builds and its rules hold before you change anything:
cd rust/blockchain-from-scratchcargo testThe first run will download and compile the six dependencies above (give it a minute), then run the
test suite. A line ending in test result: ok. means your toolchain is healthy and you’re ready for
the next chapter.
Check your understanding
Section titled “Check your understanding”- What is the difference between a crate and a module?
- Why does this project have both a
src/lib.rsand asrc/main.rsinstead of justmain.rs? - Which dependency do we use to create random private keys, and which chapter introduces it?
- What does the
features = ["derive"]part of theserdeline turn on, and why do we want it? - What does the
--incargo run -- demodo?
Show answers
- A crate is the unit Rust compiles at once — roughly “one project” (our whole blockchain, or each external library like
sha2). A module is a namespace inside a crate that groups related code; each.rsfile undersrc/is a module, declared withpub mod, controlling visibility and keeping the crate from becoming one giant file. - To keep the reusable logic in a library (
lib.rs) that can be tested and reused, withmain.rsas a thin binary front door wiring it into thedemo/serveCLI. The sameBlockchainandNodetypes then power the demo, the tests, and the networked node with no copy-paste. - The
randcrate creates random private keys, and it’s introduced in Keys, Signatures & Addresses. - It turns on the
derivefeature, which unlocks#[derive(Serialize, Deserialize)]— auto-generating the tedious “convert this struct to/from JSON” code. We want it because it makes saving the chain (persistence) a one-line annotation per struct instead of hand-written boilerplate. - The
--tells Cargo that everything after it goes to your program, not to Cargo — sodemois passed as an argument your CLI reads (asargs[1]), rather than being interpreted as a Cargo flag.